ECIEncoderSet port

This commit is contained in:
Henry Schimke
2022-09-01 16:45:47 -05:00
parent 2f0de79c38
commit 14a5df3404
2 changed files with 581 additions and 542 deletions

View File

@@ -1,199 +0,0 @@
/*
* Copyright 2021 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.List;
/**
* Set of CharsetEncoders for a given input string
*
* Invariants:
* - The list contains only encoders from CharacterSetECI (list is shorter then the list of encoders available on
* the platform for which ECI values are defined).
* - The list contains encoders at least one encoder for every character in the input.
* - The first encoder in the list is always the ISO-8859-1 encoder even of no character in the input can be encoded
* by it.
* - If the input contains a character that is not in ISO-8859-1 then the last two entries in the list will be the
* UTF-8 encoder and the UTF-16BE encoder.
*
* @author Alex Geller
*/
public final class ECIEncoderSet {
// List of encoders that potentially encode characters not in ISO-8859-1 in one byte.
private static final List<CharsetEncoder> ENCODERS = new ArrayList<>();
static {
String[] names = { "IBM437",
"ISO-8859-2",
"ISO-8859-3",
"ISO-8859-4",
"ISO-8859-5",
"ISO-8859-6",
"ISO-8859-7",
"ISO-8859-8",
"ISO-8859-9",
"ISO-8859-10",
"ISO-8859-11",
"ISO-8859-13",
"ISO-8859-14",
"ISO-8859-15",
"ISO-8859-16",
"windows-1250",
"windows-1251",
"windows-1252",
"windows-1256",
"Shift_JIS" };
for (String name : names) {
if (CharacterSetECI.getCharacterSetECIByName(name) != null) {
try {
ENCODERS.add(Charset.forName(name).newEncoder());
} catch (UnsupportedCharsetException e) {
// continue
}
}
}
}
private final CharsetEncoder[] encoders;
private final int priorityEncoderIndex;
/**
* Constructs an encoder set
*
* @param stringToEncode the string that needs to be encoded
* @param priorityCharset The preferred {@link Charset} or null.
* @param fnc1 fnc1 denotes the character in the input that represents the FNC1 character or -1 for a non-GS1 bar
* code. When specified, it is considered an error to pass it as argument to the methods canEncode() or encode().
*/
public ECIEncoderSet(String stringToEncode, Charset priorityCharset, int fnc1) {
List<CharsetEncoder> neededEncoders = new ArrayList<>();
//we always need the ISO-8859-1 encoder. It is the default encoding
neededEncoders.add(StandardCharsets.ISO_8859_1.newEncoder());
boolean needUnicodeEncoder = priorityCharset != null && priorityCharset.name().startsWith("UTF");
//Walk over the input string and see if all characters can be encoded with the list of encoders
for (int i = 0; i < stringToEncode.length(); i++) {
boolean canEncode = false;
for (CharsetEncoder encoder : neededEncoders) {
char c = stringToEncode.charAt(i);
if (c == fnc1 || encoder.canEncode(c)) {
canEncode = true;
break;
}
}
if (!canEncode) {
//for the character at position i we don't yet have an encoder in the list
for (CharsetEncoder encoder : ENCODERS) {
if (encoder.canEncode(stringToEncode.charAt(i))) {
//Good, we found an encoder that can encode the character. We add him to the list and continue scanning
//the input
neededEncoders.add(encoder);
canEncode = true;
break;
}
}
}
if (!canEncode) {
//The character is not encodeable by any of the single byte encoders so we remember that we will need a
//Unicode encoder.
needUnicodeEncoder = true;
}
}
if (neededEncoders.size() == 1 && !needUnicodeEncoder) {
//the entire input can be encoded by the ISO-8859-1 encoder
encoders = new CharsetEncoder[] { neededEncoders.get(0) };
} else {
// we need more than one single byte encoder or we need a Unicode encoder.
// In this case we append a UTF-8 and UTF-16 encoder to the list
encoders = new CharsetEncoder[neededEncoders.size() + 2];
int index = 0;
for (CharsetEncoder encoder : neededEncoders) {
encoders[index++] = encoder;
}
encoders[index] = StandardCharsets.UTF_8.newEncoder();
encoders[index + 1] = StandardCharsets.UTF_16BE.newEncoder();
}
//Compute priorityEncoderIndex by looking up priorityCharset in encoders
int priorityEncoderIndexValue = -1;
if (priorityCharset != null) {
for (int i = 0; i < encoders.length; i++) {
if (encoders[i] != null && priorityCharset.name().equals(encoders[i].charset().name())) {
priorityEncoderIndexValue = i;
break;
}
}
}
priorityEncoderIndex = priorityEncoderIndexValue;
//invariants
assert encoders[0].charset().equals(StandardCharsets.ISO_8859_1);
}
public int length() {
return encoders.length;
}
public String getCharsetName(int index) {
assert index < length();
return encoders[index].charset().name();
}
public Charset getCharset(int index) {
assert index < length();
return encoders[index].charset();
}
public int getECIValue(int encoderIndex) {
return CharacterSetECI.getCharacterSetECI(encoders[encoderIndex].charset()).getValue();
}
/*
* returns -1 if no priority charset was defined
*/
public int getPriorityEncoderIndex() {
return priorityEncoderIndex;
}
public boolean canEncode(char c, int encoderIndex) {
assert encoderIndex < length();
CharsetEncoder encoder = encoders[encoderIndex];
return encoder.canEncode("" + c);
}
public byte[] encode(char c, int encoderIndex) {
assert encoderIndex < length();
CharsetEncoder encoder = encoders[encoderIndex];
assert encoder.canEncode("" + c);
return ("" + c).getBytes(encoder.charset());
}
public byte[] encode(String s, int encoderIndex) {
assert encoderIndex < length();
CharsetEncoder encoder = encoders[encoderIndex];
return s.getBytes(encoder.charset());
}
}

View File

@@ -2374,7 +2374,6 @@ impl GridSampler for DefaultGridSampler {
}
}
/*
* Copyright 2008 ZXing authors
*
@@ -2407,7 +2406,6 @@ impl GridSampler for DefaultGridSampler {
* @author Sean Owen
*/
pub enum CharacterSetECI {
// Enum name is a Java encoding valid for java.lang and java.io
Cp437, //(new int[]{0,2}),
ISO8859_1, //(new int[]{1,3}, "ISO-8859-1"),
@@ -2438,7 +2436,6 @@ pub enum CharacterSetECI {
EUC_KR, //(30, "EUC-KR");
}
impl CharacterSetECI {
// private static final Map<Integer,CharacterSetECI> VALUE_TO_ECI = new HashMap<>();
// private static final Map<String,CharacterSetECI> NAME_TO_ECI = new HashMap<>();
// static {
@@ -2502,7 +2499,6 @@ impl CharacterSetECI {
}
}
pub fn getCharset(cs_eci: &CharacterSetECI) -> &'static dyn Encoding {
let name = match cs_eci {
CharacterSetECI::Cp437 => "CP437",
@@ -2609,7 +2605,7 @@ impl CharacterSetECI {
28 => Ok(CharacterSetECI::Big5),
29 => Ok(CharacterSetECI::GB18030),
30 => Ok(CharacterSetECI::EUC_KR),
_ => Err(Exceptions::NotFoundException("Bad ECI Value".to_owned()))
_ => Err(Exceptions::NotFoundException("Bad ECI Value".to_owned())),
}
}
@@ -2650,10 +2646,8 @@ impl CharacterSetECI {
_ => None,
}
}
}
/*
* Copyright 2022 ZXing authors
*
@@ -2690,11 +2684,18 @@ pub struct ECIStringBuilder {
impl ECIStringBuilder {
pub fn new() -> Self {
Self { current_bytes: Vec::new(), result: String::new(), current_charset: encoding::all::UTF_8 }
Self {
current_bytes: Vec::new(),
result: String::new(),
current_charset: encoding::all::UTF_8,
}
}
pub fn with_capacity(initial_capacity: usize) -> Self {
Self { current_bytes: Vec::with_capacity(initial_capacity), result: String::new(), current_charset: encoding::all::ISO_8859_1 }
Self {
current_bytes: Vec::with_capacity(initial_capacity),
result: String::new(),
current_charset: encoding::all::ISO_8859_1,
}
}
/**
@@ -2757,7 +2758,8 @@ impl ECIStringBuilder {
// result = currentBytes;
// currentBytes = new StringBuilder();
// } else {
self.result.push_str(&String::from_utf8(self.current_bytes.clone()).unwrap());
self.result
.push_str(&String::from_utf8(self.current_bytes.clone()).unwrap());
self.current_bytes.clear();
// }
}
@@ -2767,7 +2769,10 @@ impl ECIStringBuilder {
// if (result == null) {
// result = new StringBuilder(new String(bytes, currentCharset));
// } else {
let encoded_value = self.current_charset.decode(&bytes, encoding::DecoderTrap::Replace).unwrap();
let encoded_value = self
.current_charset
.decode(&bytes, encoding::DecoderTrap::Replace)
.unwrap();
self.result.push_str(&encoded_value);
// }
}
@@ -2799,7 +2804,6 @@ impl ECIStringBuilder {
pub fn is_empty(&self) -> bool {
return self.current_bytes.is_empty() && self.result.is_empty();
}
}
impl fmt::Display for ECIStringBuilder {
@@ -2808,3 +2812,237 @@ impl fmt::Display for ECIStringBuilder{
write!(f, "{}", self.result)
}
}
/*
* Copyright 2021 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// package com.google.zxing.common;
// import java.nio.charset.Charset;
// import java.nio.charset.CharsetEncoder;
// import java.nio.charset.StandardCharsets;
// import java.nio.charset.UnsupportedCharsetException;
// import java.util.ArrayList;
// import java.util.List;
/**
* Set of CharsetEncoders for a given input string
*
* Invariants:
* - The list contains only encoders from CharacterSetECI (list is shorter then the list of encoders available on
* the platform for which ECI values are defined).
* - The list contains encoders at least one encoder for every character in the input.
* - The first encoder in the list is always the ISO-8859-1 encoder even of no character in the input can be encoded
* by it.
* - If the input contains a character that is not in ISO-8859-1 then the last two entries in the list will be the
* UTF-8 encoder and the UTF-16BE encoder.
*
* @author Alex Geller
*/
pub struct ECIEncoderSet {
encoders: Vec<&'static dyn encoding::Encoding>,
priorityEncoderIndex: usize,
}
impl ECIEncoderSet {
/**
* Constructs an encoder set
*
* @param stringToEncode the string that needs to be encoded
* @param priorityCharset The preferred {@link Charset} or null.
* @param fnc1 fnc1 denotes the character in the input that represents the FNC1 character or -1 for a non-GS1 bar
* code. When specified, it is considered an error to pass it as argument to the methods canEncode() or encode().
*/
pub fn new(
stringToEncode: &str,
priorityCharset: &'static dyn encoding::Encoding,
fnc1: char,
) -> Self {
// List of encoders that potentially encode characters not in ISO-8859-1 in one byte.
let mut ENCODERS = Vec::new();
let names = [
"IBM437",
"ISO-8859-2",
"ISO-8859-3",
"ISO-8859-4",
"ISO-8859-5",
"ISO-8859-6",
"ISO-8859-7",
"ISO-8859-8",
"ISO-8859-9",
"ISO-8859-10",
"ISO-8859-11",
"ISO-8859-13",
"ISO-8859-14",
"ISO-8859-15",
"ISO-8859-16",
"windows-1250",
"windows-1251",
"windows-1252",
"windows-1256",
"Shift_JIS",
];
for name in names {
if let Some(enc) = CharacterSetECI::getCharacterSetECIByName(name) {
// try {
ENCODERS.push(CharacterSetECI::getCharset(&enc));
// } catch (UnsupportedCharsetException e) {
// continue
// }
}
}
let mut encoders: Vec<&'static dyn Encoding>;
let mut priorityEncoderIndexValue = 0;
let mut neededEncoders: Vec<&'static dyn encoding::Encoding> = Vec::new();
//we always need the ISO-8859-1 encoder. It is the default encoding
neededEncoders.push(encoding::all::ISO_8859_1);
neededEncoders.push(encoding::all::UTF_8);
let mut needUnicodeEncoder = priorityCharset.name().starts_with("UTF");
//Walk over the input string and see if all characters can be encoded with the list of encoders
for i in 0..stringToEncode.len() {
// for (int i = 0; i < stringToEncode.length(); i++) {
let mut canEncode = false;
for encoder in &neededEncoders {
// for (CharsetEncoder encoder : neededEncoders) {
let c = stringToEncode.chars().nth(i).unwrap();
if c == fnc1
|| encoder
.encode(&c.to_string(), encoding::EncoderTrap::Strict)
.is_ok()
{
canEncode = true;
break;
}
}
if !canEncode {
//for the character at position i we don't yet have an encoder in the list
for encoder in &ENCODERS {
// for (CharsetEncoder encoder : ENCODERS) {
if encoder
.encode(
&stringToEncode.chars().nth(i).unwrap().to_string(),
encoding::EncoderTrap::Strict,
)
.is_ok()
{
//Good, we found an encoder that can encode the character. We add him to the list and continue scanning
//the input
neededEncoders.push(*encoder);
canEncode = true;
break;
}
}
}
if !canEncode {
//The character is not encodeable by any of the single byte encoders so we remember that we will need a
//Unicode encoder.
needUnicodeEncoder = true;
}
}
if neededEncoders.len() == 1 && !needUnicodeEncoder {
//the entire input can be encoded by the ISO-8859-1 encoder
encoders = vec![encoding::all::ISO_8859_1];
} else {
// we need more than one single byte encoder or we need a Unicode encoder.
// In this case we append a UTF-8 and UTF-16 encoder to the list
// encoders = [] new CharsetEncoder[neededEncoders.size() + 2];
encoders = Vec::new();
let index = 0;
encoders.push(encoding::all::UTF_8);
encoders.push(encoding::all::UTF_16BE);
for encoder in neededEncoders {
// for (CharsetEncoder encoder : neededEncoders) {
//encoders[index++] = encoder;
encoders.push(encoder);
}
}
//Compute priorityEncoderIndex by looking up priorityCharset in encoders
// if priorityCharset != null {
for i in 0..encoders.len() {
// for (int i = 0; i < encoders.length; i++) {
if priorityCharset.name() == encoders[i].name() {
priorityEncoderIndexValue = i;
break;
}
}
// }
//invariants
assert_eq!(encoders[0].name(), encoding::all::ISO_8859_1.name());
Self {
encoders: encoders,
priorityEncoderIndex: priorityEncoderIndexValue,
}
}
pub fn len(&self) -> usize {
return self.encoders.len();
}
pub fn getCharsetName(&self, index: usize) -> &'static str {
assert!(index < self.len());
return self.encoders[index].name();
}
pub fn getCharset(&self, index: usize) -> &'static dyn Encoding {
assert!(index < self.len());
return self.encoders[index];
}
pub fn getECIValue(&self, encoderIndex: usize) -> u32 {
CharacterSetECI::getValue(
&CharacterSetECI::getCharacterSetECI(self.encoders[encoderIndex]).unwrap(),
)
}
/*
* returns -1 if no priority charset was defined
*/
pub fn getPriorityEncoderIndex(&self) -> usize {
return self.priorityEncoderIndex;
}
pub fn canEncode(&self, c: char, encoderIndex: usize) -> bool {
assert!(encoderIndex < self.len());
let encoder = self.encoders[encoderIndex];
let enc_data = encoder.encode(&c.to_string(), encoding::EncoderTrap::Strict);
enc_data.is_ok()
}
pub fn encode_char(&self, c: char, encoderIndex: usize) -> Vec<u8> {
assert!(encoderIndex < self.len());
let encoder = self.encoders[encoderIndex];
let enc_data = encoder.encode(&c.to_string(), encoding::EncoderTrap::Strict);
assert!(enc_data.is_ok());
return enc_data.unwrap();
}
pub fn encode_string(&self, s: &str, encoderIndex: usize) -> Vec<u8> {
assert!(encoderIndex < self.len());
let encoder = self.encoders[encoderIndex];
encoder.encode(s, encoding::EncoderTrap::Replace).unwrap()
}
}