mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-28 05:12:34 +00:00
port decoded_bit_stream_parser
This commit is contained in:
@@ -38,7 +38,7 @@ fn test_random() {
|
|||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
encoding::all::UTF_8.name(),
|
encoding::all::UTF_8.name(),
|
||||||
StringUtils::guessCharset(&bytes, HashMap::new()).name()
|
StringUtils::guessCharset(&bytes, &HashMap::new()).name()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,8 +103,8 @@ fn test_utf16_le() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn do_test(bytes: &Vec<u8>, charset: &dyn Encoding, encoding: &str) {
|
fn do_test(bytes: &Vec<u8>, charset: &dyn Encoding, encoding: &str) {
|
||||||
let guessedCharset = StringUtils::guessCharset(bytes, HashMap::new());
|
let guessedCharset = StringUtils::guessCharset(bytes, &HashMap::new());
|
||||||
let guessedEncoding = StringUtils::guessEncoding(bytes, HashMap::new());
|
let guessedEncoding = StringUtils::guessEncoding(bytes, &HashMap::new());
|
||||||
assert_eq!(charset.name(), guessedCharset.name());
|
assert_eq!(charset.name(), guessedCharset.name());
|
||||||
assert_eq!(encoding, guessedEncoding);
|
assert_eq!(encoding, guessedEncoding);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use crate::Binarizer;
|
use crate::Binarizer;
|
||||||
use crate::DecodeHintType;
|
use crate::DecodeHintType;
|
||||||
|
use crate::DecodeHintValue;
|
||||||
|
use crate::DecodingHintDictionary;
|
||||||
use crate::Exceptions;
|
use crate::Exceptions;
|
||||||
use crate::LuminanceSource;
|
use crate::LuminanceSource;
|
||||||
use crate::RXingResultPoint;
|
use crate::RXingResultPoint;
|
||||||
@@ -102,7 +104,7 @@ impl StringUtils {
|
|||||||
* "SJIS", "UTF8", "ISO8859_1", or the platform default encoding if none
|
* "SJIS", "UTF8", "ISO8859_1", or the platform default encoding if none
|
||||||
* of these can possibly be correct
|
* of these can possibly be correct
|
||||||
*/
|
*/
|
||||||
pub fn guessEncoding(bytes: &[u8], hints: HashMap<DecodeHintType, String>) -> String {
|
pub fn guessEncoding(bytes: &[u8], hints: &DecodingHintDictionary) -> String {
|
||||||
let c = StringUtils::guessCharset(bytes, hints);
|
let c = StringUtils::guessCharset(bytes, hints);
|
||||||
if c.name()
|
if c.name()
|
||||||
== encoding::label::encoding_from_whatwg_label("SJIS")
|
== encoding::label::encoding_from_whatwg_label("SJIS")
|
||||||
@@ -127,13 +129,12 @@ impl StringUtils {
|
|||||||
* or the platform default encoding if
|
* or the platform default encoding if
|
||||||
* none of these can possibly be correct
|
* none of these can possibly be correct
|
||||||
*/
|
*/
|
||||||
pub fn guessCharset(
|
pub fn guessCharset(bytes: &[u8], hints: &DecodingHintDictionary) -> &'static dyn Encoding {
|
||||||
bytes: &[u8],
|
|
||||||
hints: HashMap<DecodeHintType, String>,
|
|
||||||
) -> &'static dyn Encoding {
|
|
||||||
match hints.get(&DecodeHintType::CHARACTER_SET) {
|
match hints.get(&DecodeHintType::CHARACTER_SET) {
|
||||||
Some(hint) => {
|
Some(hint) => {
|
||||||
return encoding::label::encoding_from_whatwg_label(hint).unwrap();
|
if let DecodeHintValue::CharacterSet(cs_name) = hint {
|
||||||
|
return encoding::label::encoding_from_whatwg_label(cs_name).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
@@ -1836,7 +1837,7 @@ pub struct DecoderRXingResult {
|
|||||||
rawBytes: Vec<u8>,
|
rawBytes: Vec<u8>,
|
||||||
numBits: usize,
|
numBits: usize,
|
||||||
text: String,
|
text: String,
|
||||||
byteSegments: Vec<u8>,
|
byteSegments: Vec<Vec<u8>>,
|
||||||
ecLevel: String,
|
ecLevel: String,
|
||||||
errorsCorrected: u64,
|
errorsCorrected: u64,
|
||||||
erasures: u64,
|
erasures: u64,
|
||||||
@@ -1847,14 +1848,14 @@ pub struct DecoderRXingResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DecoderRXingResult {
|
impl DecoderRXingResult {
|
||||||
pub fn new(rawBytes: Vec<u8>, text: String, byteSegments: Vec<u8>, ecLevel: String) -> Self {
|
pub fn new(rawBytes: Vec<u8>, text: String, byteSegments: Vec<Vec<u8>>, ecLevel: String) -> Self {
|
||||||
Self::with_all(rawBytes, text, byteSegments, ecLevel, -2, -2, 0)
|
Self::with_all(rawBytes, text, byteSegments, ecLevel, -2, -2, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_symbology(
|
pub fn with_symbology(
|
||||||
rawBytes: Vec<u8>,
|
rawBytes: Vec<u8>,
|
||||||
text: String,
|
text: String,
|
||||||
byteSegments: Vec<u8>,
|
byteSegments: Vec<Vec<u8>>,
|
||||||
ecLevel: String,
|
ecLevel: String,
|
||||||
symbologyModifier: u32,
|
symbologyModifier: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -1872,7 +1873,7 @@ impl DecoderRXingResult {
|
|||||||
pub fn with_sa(
|
pub fn with_sa(
|
||||||
rawBytes: Vec<u8>,
|
rawBytes: Vec<u8>,
|
||||||
text: String,
|
text: String,
|
||||||
byteSegments: Vec<u8>,
|
byteSegments: Vec<Vec<u8>>,
|
||||||
ecLevel: String,
|
ecLevel: String,
|
||||||
saSequence: i32,
|
saSequence: i32,
|
||||||
saParity: i32,
|
saParity: i32,
|
||||||
@@ -1891,7 +1892,7 @@ impl DecoderRXingResult {
|
|||||||
pub fn with_all(
|
pub fn with_all(
|
||||||
rawBytes: Vec<u8>,
|
rawBytes: Vec<u8>,
|
||||||
text: String,
|
text: String,
|
||||||
byteSegments: Vec<u8>,
|
byteSegments:Vec<Vec<u8>>,
|
||||||
ecLevel: String,
|
ecLevel: String,
|
||||||
saSequence: i32,
|
saSequence: i32,
|
||||||
saParity: i32,
|
saParity: i32,
|
||||||
@@ -1946,7 +1947,7 @@ impl DecoderRXingResult {
|
|||||||
/**
|
/**
|
||||||
* @return list of byte segments in the result, or {@code null} if not applicable
|
* @return list of byte segments in the result, or {@code null} if not applicable
|
||||||
*/
|
*/
|
||||||
pub fn getByteSegments(&self) -> &Vec<u8> {
|
pub fn getByteSegments(&self) -> &Vec<Vec<u8>> {
|
||||||
&self.byteSegments
|
&self.byteSegments
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2922,7 +2923,11 @@ impl ECIEncoderSet {
|
|||||||
* @param fnc1 fnc1 denotes the character in the input that represents the FNC1 character or -1 for a non-GS1 bar
|
* @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().
|
* code. When specified, it is considered an error to pass it as argument to the methods canEncode() or encode().
|
||||||
*/
|
*/
|
||||||
pub fn new(stringToEncodeMain: &str, priorityCharset: Option<EncodingRef>, fnc1: Option<&str>) -> Self {
|
pub fn new(
|
||||||
|
stringToEncodeMain: &str,
|
||||||
|
priorityCharset: Option<EncodingRef>,
|
||||||
|
fnc1: Option<&str>,
|
||||||
|
) -> Self {
|
||||||
// List of encoders that potentially encode characters not in ISO-8859-1 in one byte.
|
// List of encoders that potentially encode characters not in ISO-8859-1 in one byte.
|
||||||
|
|
||||||
let mut encoders: Vec<EncodingRef>;
|
let mut encoders: Vec<EncodingRef>;
|
||||||
@@ -2948,9 +2953,7 @@ impl ECIEncoderSet {
|
|||||||
// for (CharsetEncoder encoder : neededEncoders) {
|
// for (CharsetEncoder encoder : neededEncoders) {
|
||||||
let c = stringToEncode.get(i).unwrap();
|
let c = stringToEncode.get(i).unwrap();
|
||||||
if (fnc1.is_some() && c == fnc1.as_ref().unwrap())
|
if (fnc1.is_some() && c == fnc1.as_ref().unwrap())
|
||||||
|| encoder
|
|| encoder.encode(c, encoding::EncoderTrap::Strict).is_ok()
|
||||||
.encode(c, encoding::EncoderTrap::Strict)
|
|
||||||
.is_ok()
|
|
||||||
{
|
{
|
||||||
canEncode = true;
|
canEncode = true;
|
||||||
break;
|
break;
|
||||||
@@ -3003,8 +3006,6 @@ impl ECIEncoderSet {
|
|||||||
|
|
||||||
encoders.push(encoding::all::UTF_8);
|
encoders.push(encoding::all::UTF_8);
|
||||||
encoders.push(encoding::all::UTF_16BE);
|
encoders.push(encoding::all::UTF_16BE);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Compute priorityEncoderIndex by looking up priorityCharset in encoders
|
//Compute priorityEncoderIndex by looking up priorityCharset in encoders
|
||||||
@@ -3016,7 +3017,8 @@ impl ECIEncoderSet {
|
|||||||
priorityEncoderIndexValue = Some(i);
|
priorityEncoderIndexValue = Some(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
// }
|
// }
|
||||||
//invariants
|
//invariants
|
||||||
assert_eq!(encoders[0].name(), encoding::all::ISO_8859_1.name());
|
assert_eq!(encoders[0].name(), encoding::all::ISO_8859_1.name());
|
||||||
@@ -3269,7 +3271,11 @@ impl MinimalECIInput {
|
|||||||
* @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not GS1
|
* @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not GS1
|
||||||
* input.
|
* input.
|
||||||
*/
|
*/
|
||||||
pub fn new(stringToEncodeInput: &str, priorityCharset: Option<EncodingRef>, fnc1: Option<&str>) -> Self {
|
pub fn new(
|
||||||
|
stringToEncodeInput: &str,
|
||||||
|
priorityCharset: Option<EncodingRef>,
|
||||||
|
fnc1: Option<&str>,
|
||||||
|
) -> Self {
|
||||||
let stringToEncode = stringToEncodeInput.graphemes(true).collect::<Vec<&str>>();
|
let stringToEncode = stringToEncodeInput.graphemes(true).collect::<Vec<&str>>();
|
||||||
let encoderSet = ECIEncoderSet::new(stringToEncodeInput, priorityCharset, fnc1);
|
let encoderSet = ECIEncoderSet::new(stringToEncodeInput, priorityCharset, fnc1);
|
||||||
let bytes = if encoderSet.len() == 1 {
|
let bytes = if encoderSet.len() == 1 {
|
||||||
@@ -3278,11 +3284,19 @@ impl MinimalECIInput {
|
|||||||
for i in 0..stringToEncode.len() {
|
for i in 0..stringToEncode.len() {
|
||||||
// for (int i = 0; i < bytes.length; i++) {
|
// for (int i = 0; i < bytes.length; i++) {
|
||||||
let c = stringToEncode.get(i).unwrap();
|
let c = stringToEncode.get(i).unwrap();
|
||||||
bytes_hld[i] = if fnc1.is_some() && c == fnc1.as_ref().unwrap() { 1000 } else { c.chars().nth(0).unwrap() as u16 };
|
bytes_hld[i] = if fnc1.is_some() && c == fnc1.as_ref().unwrap() {
|
||||||
|
1000
|
||||||
|
} else {
|
||||||
|
c.chars().nth(0).unwrap() as u16
|
||||||
|
};
|
||||||
}
|
}
|
||||||
bytes_hld
|
bytes_hld
|
||||||
} else {
|
} else {
|
||||||
Self::encodeMinimally(stringToEncodeInput, &encoderSet, fnc1.as_ref().unwrap().chars().nth(0).unwrap() as u16)
|
Self::encodeMinimally(
|
||||||
|
stringToEncodeInput,
|
||||||
|
&encoderSet,
|
||||||
|
fnc1.as_ref().unwrap().chars().nth(0).unwrap() as u16,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@@ -3339,7 +3353,8 @@ impl MinimalECIInput {
|
|||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
let mut end = encoderSet.len();
|
let mut end = encoderSet.len();
|
||||||
if encoderSet.getPriorityEncoderIndex().is_some()
|
if encoderSet.getPriorityEncoderIndex().is_some()
|
||||||
&& (ch.chars().nth(0).unwrap() as u16 == fnc1 || encoderSet.canEncode(ch, encoderSet.getPriorityEncoderIndex().unwrap()))
|
&& (ch.chars().nth(0).unwrap() as u16 == fnc1
|
||||||
|
|| encoderSet.canEncode(ch, encoderSet.getPriorityEncoderIndex().unwrap()))
|
||||||
{
|
{
|
||||||
start = encoderSet.getPriorityEncoderIndex().unwrap();
|
start = encoderSet.getPriorityEncoderIndex().unwrap();
|
||||||
end = start + 1;
|
end = start + 1;
|
||||||
@@ -3469,7 +3484,11 @@ impl InputEdge {
|
|||||||
size += prev.cachedTotalSize;
|
size += prev.cachedTotalSize;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
c: if c == fnc1Str { String::from("\u{1000}") } else { String::from(c) },
|
c: if c == fnc1Str {
|
||||||
|
String::from("\u{1000}")
|
||||||
|
} else {
|
||||||
|
String::from(c)
|
||||||
|
},
|
||||||
encoderIndex,
|
encoderIndex,
|
||||||
previous: Some(prev.clone()),
|
previous: Some(prev.clone()),
|
||||||
cachedTotalSize: size,
|
cachedTotalSize: size,
|
||||||
@@ -3481,7 +3500,11 @@ impl InputEdge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
c: if c == fnc1Str { String::from("\u{1000}") } else { String::from(c) },
|
c: if c == fnc1Str {
|
||||||
|
String::from("\u{1000}")
|
||||||
|
} else {
|
||||||
|
String::from(c)
|
||||||
|
},
|
||||||
encoderIndex,
|
encoderIndex,
|
||||||
previous: None,
|
previous: None,
|
||||||
cachedTotalSize: size,
|
cachedTotalSize: size,
|
||||||
|
|||||||
@@ -924,7 +924,7 @@ pub enum RXingResultMetadataValue {
|
|||||||
* <p>This maps to a {@link java.util.List} of byte arrays corresponding to the
|
* <p>This maps to a {@link java.util.List} of byte arrays corresponding to the
|
||||||
* raw bytes in the byte segments in the barcode, in order.</p>
|
* raw bytes in the byte segments in the barcode, in order.</p>
|
||||||
*/
|
*/
|
||||||
ByteSegments(Vec<u8>),
|
ByteSegments(Vec<Vec<u8>>),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error correction level used, if applicable. The value type depends on the
|
* Error correction level used, if applicable. The value type depends on the
|
||||||
|
|||||||
@@ -14,10 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::{Exceptions, common::{BitSource, StringUtils, DecoderRXingResult, CharacterSetECI}, DecodingHintDictionary};
|
use crate::{
|
||||||
|
common::{BitSource, CharacterSetECI, DecoderRXingResult, StringUtils},
|
||||||
use super::{VersionRef, Mode, ErrorCorrectionLevel};
|
DecodingHintDictionary, Exceptions,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{ErrorCorrectionLevel, Mode, VersionRef};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
|
* <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||||
@@ -31,100 +33,121 @@ use super::{VersionRef, Mode, ErrorCorrectionLevel};
|
|||||||
/**
|
/**
|
||||||
* See ISO 18004:2006, 6.4.4 Table 5
|
* See ISO 18004:2006, 6.4.4 Table 5
|
||||||
*/
|
*/
|
||||||
const ALPHANUMERIC_CHARS : &str=
|
const ALPHANUMERIC_CHARS: &str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
|
||||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
|
const GB2312_SUBSET: u32 = 1;
|
||||||
const GB2312_SUBSET :u8 = 1;
|
|
||||||
|
|
||||||
|
pub fn decode(
|
||||||
pub fn decode(bytes : &[u8],
|
bytes: &Vec<u8>,
|
||||||
version: VersionRef,
|
version: VersionRef,
|
||||||
ecLevel: ErrorCorrectionLevel,
|
ecLevel: ErrorCorrectionLevel,
|
||||||
hints:DecodingHintDictionary) -> Result<DecoderRXingResult,Exceptions> {
|
hints: &DecodingHintDictionary,
|
||||||
let bits = BitSource::new(bytes);
|
) -> Result<DecoderRXingResult, Exceptions> {
|
||||||
let result = String::with_capacity(50);
|
let mut bits = BitSource::new(bytes.clone());
|
||||||
let byteSegments = vec![vec![0u8;0];0];
|
let mut result = String::with_capacity(50);
|
||||||
let symbolSequence = -1;
|
let mut byteSegments = vec![vec![0u8; 0]; 0];
|
||||||
let parityData = -1;
|
let mut symbolSequence = -1i32;
|
||||||
|
let mut parityData = -1i32;
|
||||||
let symbologyModifier;
|
let symbologyModifier;
|
||||||
|
|
||||||
// try {
|
// try {
|
||||||
let currentCharacterSetECI = None;
|
let mut currentCharacterSetECI = None;
|
||||||
let fc1InEffect = false;
|
let mut fc1InEffect = false;
|
||||||
let hasFNC1first = false;
|
let mut hasFNC1first = false;
|
||||||
let hasFNC1second = false;
|
let mut hasFNC1second = false;
|
||||||
let mode;
|
let mut mode;
|
||||||
loop {
|
loop {
|
||||||
// While still another segment to read...
|
// While still another segment to read...
|
||||||
if (bits.available() < 4) {
|
if bits.available() < 4 {
|
||||||
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
||||||
mode = Mode::TERMINATOR;
|
mode = Mode::TERMINATOR;
|
||||||
} else {
|
} else {
|
||||||
mode = Mode::forBits(bits.readBits(4)); // mode is encoded by 4 bits
|
mode = Mode::forBits(bits.readBits(4)? as u8)?; // mode is encoded by 4 bits
|
||||||
}
|
}
|
||||||
match mode {
|
match mode {
|
||||||
Mode::TERMINATOR => {},
|
Mode::TERMINATOR => {}
|
||||||
Mode::FNC1_FIRST_POSITION => {
|
Mode::FNC1_FIRST_POSITION => {
|
||||||
hasFNC1first = true; // symbology detection
|
hasFNC1first = true; // symbology detection
|
||||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||||
fc1InEffect = true;},
|
fc1InEffect = true;
|
||||||
|
}
|
||||||
Mode::FNC1_SECOND_POSITION => {
|
Mode::FNC1_SECOND_POSITION => {
|
||||||
hasFNC1second = true; // symbology detection
|
hasFNC1second = true; // symbology detection
|
||||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||||
fc1InEffect = true;},
|
fc1InEffect = true;
|
||||||
|
}
|
||||||
Mode::STRUCTURED_APPEND => {
|
Mode::STRUCTURED_APPEND => {
|
||||||
if (bits.available() < 16) {
|
if (bits.available() < 16) {
|
||||||
return Err(Exceptions::FormatException(format!("Mode::Structured append expected bits.available() < 16, found bits of {}", bits.available())))
|
return Err(Exceptions::FormatException(format!(
|
||||||
|
"Mode::Structured append expected bits.available() < 16, found bits of {}",
|
||||||
|
bits.available()
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
// sequence number and parity is added later to the result metadata
|
// sequence number and parity is added later to the result metadata
|
||||||
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
||||||
symbolSequence = bits.readBits(8);
|
symbolSequence = bits.readBits(8)? as i32;
|
||||||
parityData = bits.readBits(8);},
|
parityData = bits.readBits(8)? as i32;
|
||||||
|
}
|
||||||
Mode::ECI => {
|
Mode::ECI => {
|
||||||
// Count doesn't apply to ECI
|
// Count doesn't apply to ECI
|
||||||
let value = parseECIValue(&bits)?;
|
let value = parseECIValue(&mut bits)?;
|
||||||
currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue(value);
|
currentCharacterSetECI = Some(CharacterSetECI::getCharacterSetECIByValue(value)?);
|
||||||
if (currentCharacterSetECI.is_none()) {
|
if currentCharacterSetECI.is_none() {
|
||||||
return Err(Exceptions::FormatException(format!("Value of {} not valid", value)))
|
return Err(Exceptions::FormatException(format!(
|
||||||
|
"Value of {} not valid",
|
||||||
}},
|
value
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
Mode::HANZI => {
|
Mode::HANZI => {
|
||||||
// First handle Hanzi mode which does not start with character count
|
// First handle Hanzi mode which does not start with character count
|
||||||
// Chinese mode contains a sub set indicator right after mode indicator
|
// Chinese mode contains a sub set indicator right after mode indicator
|
||||||
let subset = bits.readBits(4);
|
let subset = bits.readBits(4)?;
|
||||||
let countHanzi = bits.readBits(mode.getCharacterCountBits(version));
|
let countHanzi =
|
||||||
if (subset == GB2312_SUBSET) {
|
bits.readBits(mode.getCharacterCountBits(version) as usize)? as usize;
|
||||||
decodeHanziSegment(&bits, &result, countHanzi)?;
|
if subset == GB2312_SUBSET {
|
||||||
}},
|
decodeHanziSegment(&mut bits, &mut result, countHanzi)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// "Normal" QR code modes:
|
// "Normal" QR code modes:
|
||||||
// How many characters will follow, encoded in this mode?
|
// How many characters will follow, encoded in this mode?
|
||||||
let count = bits.readBits(mode.getCharacterCountBits(version));
|
let count = bits.readBits(mode.getCharacterCountBits(version) as usize)? as usize;
|
||||||
match mode {
|
match mode {
|
||||||
Mode::NUMERIC=>
|
Mode::NUMERIC => decodeNumericSegment(&mut bits, &mut result, count)?,
|
||||||
decodeNumericSegment(&bits, &result, count)?,
|
Mode::ALPHANUMERIC => {
|
||||||
Mode::ALPHANUMERIC=>decodeAlphanumericSegment(&bits, &result, count, fc1InEffect)?,
|
decodeAlphanumericSegment(&mut bits, &mut result, count, fc1InEffect)?
|
||||||
Mode::BYTE=>decodeByteSegment(&bits, &result, count, currentCharacterSetECI, &byteSegments, hints)?,
|
}
|
||||||
Mode::KANJI=>decodeKanjiSegment(&bits, &result, count)?,
|
Mode::BYTE => decodeByteSegment(
|
||||||
_=>
|
&mut bits,
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
&mut result,
|
||||||
}},
|
count,
|
||||||
|
currentCharacterSetECI,
|
||||||
|
&mut byteSegments,
|
||||||
|
hints,
|
||||||
|
)?,
|
||||||
|
Mode::KANJI => decodeKanjiSegment(&mut bits, &mut result, count)?,
|
||||||
|
_ => return Err(Exceptions::FormatException("".to_owned())),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if mode != Mode::TERMINATOR {break}
|
if mode != Mode::TERMINATOR {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentCharacterSetECI != null) {
|
if currentCharacterSetECI.is_some() {
|
||||||
if (hasFNC1first) {
|
if hasFNC1first {
|
||||||
symbologyModifier = 4;
|
symbologyModifier = 4;
|
||||||
} else if (hasFNC1second) {
|
} else if hasFNC1second {
|
||||||
symbologyModifier = 6;
|
symbologyModifier = 6;
|
||||||
} else {
|
} else {
|
||||||
symbologyModifier = 2;
|
symbologyModifier = 2;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (hasFNC1first) {
|
if hasFNC1first {
|
||||||
symbologyModifier = 3;
|
symbologyModifier = 3;
|
||||||
} else if (hasFNC1second) {
|
} else if hasFNC1second {
|
||||||
symbologyModifier = 5;
|
symbologyModifier = 5;
|
||||||
} else {
|
} else {
|
||||||
symbologyModifier = 1;
|
symbologyModifier = 1;
|
||||||
@@ -136,164 +159,187 @@ use super::{VersionRef, Mode, ErrorCorrectionLevel};
|
|||||||
// throw FormatException.getFormatInstance();
|
// throw FormatException.getFormatInstance();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return DecoderRXingResult::new(bytes,
|
Ok(DecoderRXingResult::with_all(
|
||||||
result.toString(),
|
bytes.clone(),
|
||||||
if byteSegments.isEmpty() {None} else {byteSegments},
|
result,
|
||||||
if ecLevel.is_none() {None} else {ecLevel.toString()},
|
byteSegments.to_vec(),
|
||||||
|
format!("{}", u8::from(ecLevel)),
|
||||||
symbolSequence,
|
symbolSequence,
|
||||||
parityData,
|
parityData,
|
||||||
symbologyModifier);
|
symbologyModifier,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See specification GBT 18284-2000
|
* See specification GBT 18284-2000
|
||||||
*/
|
*/
|
||||||
fn decodeHanziSegment( bits:&BitSource,
|
fn decodeHanziSegment(
|
||||||
result:&String,
|
bits: &mut BitSource,
|
||||||
count:u32) -> Result<(),Exceptions> {
|
result: &mut String,
|
||||||
|
count: usize,
|
||||||
|
) -> Result<(), Exceptions> {
|
||||||
// Don't crash trying to read more bits than we have available.
|
// Don't crash trying to read more bits than we have available.
|
||||||
if (count * 13 > bits.available()) {
|
if count * 13 > bits.available() {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||||
// and decode as GB2312 afterwards
|
// and decode as GB2312 afterwards
|
||||||
let buffer = vec![0u8;2 * count];
|
let mut buffer = vec![0u8; 2 * count];
|
||||||
let offset = 0;
|
let mut offset = 0;
|
||||||
while (count > 0) {
|
let mut count = count;
|
||||||
|
while count > 0 {
|
||||||
// Each 13 bits encodes a 2-byte character
|
// Each 13 bits encodes a 2-byte character
|
||||||
let twoBytes = bits.readBits(13)?;
|
let twoBytes = bits.readBits(13)?;
|
||||||
let assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
|
let mut assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
|
||||||
if (assembledTwoBytes < 0x00A00) {
|
if assembledTwoBytes < 0x00A00 {
|
||||||
// In the 0xA1A1 to 0xAAFE range
|
// In the 0xA1A1 to 0xAAFE range
|
||||||
assembledTwoBytes += 0x0A1A1;
|
assembledTwoBytes += 0x0A1A1;
|
||||||
} else {
|
} else {
|
||||||
// In the 0xB0A1 to 0xFAFE range
|
// In the 0xB0A1 to 0xFAFE range
|
||||||
assembledTwoBytes += 0x0A6A1;
|
assembledTwoBytes += 0x0A6A1;
|
||||||
}
|
}
|
||||||
buffer[offset] = ((assembledTwoBytes >> 8) & 0xFF);
|
// buffer[offset] = ((assembledTwoBytes >> 8) & 0xFF);
|
||||||
buffer[offset + 1] = (assembledTwoBytes & 0xFF);
|
// buffer[offset + 1] = (assembledTwoBytes & 0xFF);
|
||||||
|
buffer[offset] = (assembledTwoBytes >> 8) as u8;
|
||||||
|
buffer[offset + 1] = assembledTwoBytes as u8;
|
||||||
offset += 2;
|
offset += 2;
|
||||||
count -= 1;
|
count -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let gb_encoder = encoding::label::encoding_from_whatwg_label("GB2312").unwrap();
|
let gb_encoder = encoding::label::encoding_from_whatwg_label("GB2312").unwrap();
|
||||||
let encode_string = gb_encoder.decode(&buffer, encoding::DecoderTrap::Strict).unwrap();
|
let encode_string = gb_encoder
|
||||||
result.append(encode_string);
|
.decode(&buffer, encoding::DecoderTrap::Strict)
|
||||||
|
.unwrap();
|
||||||
|
result.push_str(&encode_string);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decodeKanjiSegment( bits:&BitSource,
|
fn decodeKanjiSegment(
|
||||||
result:&String,
|
bits: &mut BitSource,
|
||||||
count:u32) -> Result<(),Exceptions> {
|
result: &mut String,
|
||||||
|
count: usize,
|
||||||
|
) -> Result<(), Exceptions> {
|
||||||
// Don't crash trying to read more bits than we have available.
|
// Don't crash trying to read more bits than we have available.
|
||||||
if (count * 13 > bits.available()) {
|
if (count * 13 > bits.available()) {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||||
// and decode as Shift_JIS afterwards
|
// and decode as Shift_JIS afterwards
|
||||||
let buffer = vec![0u8;2 * count];
|
let mut buffer = vec![0u8; 2 * count];
|
||||||
let offset = 0;
|
let mut offset = 0;
|
||||||
while (count > 0) {
|
let mut count = count;
|
||||||
|
while count > 0 {
|
||||||
// Each 13 bits encodes a 2-byte character
|
// Each 13 bits encodes a 2-byte character
|
||||||
let twoBytes = bits.readBits(13);
|
let twoBytes = bits.readBits(13)?;
|
||||||
let assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
|
let mut assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
|
||||||
if (assembledTwoBytes < 0x01F00) {
|
if assembledTwoBytes < 0x01F00 {
|
||||||
// In the 0x8140 to 0x9FFC range
|
// In the 0x8140 to 0x9FFC range
|
||||||
assembledTwoBytes += 0x08140;
|
assembledTwoBytes += 0x08140;
|
||||||
} else {
|
} else {
|
||||||
// In the 0xE040 to 0xEBBF range
|
// In the 0xE040 to 0xEBBF range
|
||||||
assembledTwoBytes += 0x0C140;
|
assembledTwoBytes += 0x0C140;
|
||||||
}
|
}
|
||||||
buffer[offset] = (assembledTwoBytes >> 8);
|
buffer[offset] = (assembledTwoBytes >> 8) as u8;
|
||||||
buffer[offset + 1] = assembledTwoBytes;
|
buffer[offset + 1] = assembledTwoBytes as u8;
|
||||||
offset += 2;
|
offset += 2;
|
||||||
count -= 1;
|
count -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sjs_encoder = encoding::label::encoding_from_whatwg_label("SJIS").unwrap();
|
let sjs_encoder = encoding::label::encoding_from_whatwg_label("SJIS").unwrap();
|
||||||
let encode_string = sjs_encoder.decode(&buffer, encoding::DecoderTrap::Strict).unwrap();
|
let encode_string = sjs_encoder
|
||||||
result.append(encode_string);
|
.decode(&buffer, encoding::DecoderTrap::Strict)
|
||||||
|
.unwrap();
|
||||||
|
result.push_str(&encode_string);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decodeByteSegment( bits:&BitSource,
|
fn decodeByteSegment(
|
||||||
result:&String,
|
bits: &mut BitSource,
|
||||||
count:u32,
|
result: &mut String,
|
||||||
currentCharacterSetECI:Option<&CharacterSetECI>,
|
count: usize,
|
||||||
byteSegments:&Vec<Vec<u8>>,
|
currentCharacterSetECI: Option<CharacterSetECI>,
|
||||||
hints:DecodingHintDictionary) -> Result<(),Exceptions> {
|
byteSegments: &mut Vec<Vec<u8>>,
|
||||||
|
hints: &DecodingHintDictionary,
|
||||||
|
) -> Result<(), Exceptions> {
|
||||||
// Don't crash trying to read more bits than we have available.
|
// Don't crash trying to read more bits than we have available.
|
||||||
if (8 * count > bits.available()) {
|
if 8 * count > bits.available() {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let readBytes = vec![0u8;count];
|
let mut readBytes = vec![0u8; count];
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
// for (int i = 0; i < count; i++) {
|
// for (int i = 0; i < count; i++) {
|
||||||
readBytes[i] = bits.readBits(8);
|
readBytes[i] = bits.readBits(8)? as u8;
|
||||||
}
|
}
|
||||||
let encoding;
|
let encoding;
|
||||||
if (currentCharacterSetECI.is_none()) {
|
if currentCharacterSetECI.is_none() {
|
||||||
// The spec isn't clear on this mode; see
|
// The spec isn't clear on this mode; see
|
||||||
// section 6.4.5: t does not say which encoding to assuming
|
// section 6.4.5: t does not say which encoding to assuming
|
||||||
// upon decoding. I have seen ISO-8859-1 used as well as
|
// upon decoding. I have seen ISO-8859-1 used as well as
|
||||||
// Shift_JIS -- without anything like an ECI designator to
|
// Shift_JIS -- without anything like an ECI designator to
|
||||||
// give a hint.
|
// give a hint.
|
||||||
encoding = StringUtils::guessCharset(&readBytes, hints);
|
encoding = StringUtils::guessCharset(&readBytes, &hints);
|
||||||
} else {
|
} else {
|
||||||
encoding = currentCharacterSetECI.getCharset();
|
encoding = CharacterSetECI::getCharset(currentCharacterSetECI.as_ref().unwrap());
|
||||||
}
|
}
|
||||||
let encode_string = encoding.decode(&readBytes, encoding::DecoderTrap::Strict).unwrap();
|
let encode_string = encoding
|
||||||
result.append(encode_string);
|
.decode(&readBytes, encoding::DecoderTrap::Strict)
|
||||||
byteSegments.add(readBytes);
|
.unwrap();
|
||||||
|
result.push_str(&encode_string);
|
||||||
|
byteSegments.push(readBytes);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toAlphaNumericChar(value: u32) -> Result<char, Exceptions> {
|
fn toAlphaNumericChar(value: u32) -> Result<char, Exceptions> {
|
||||||
if (value >= ALPHANUMERIC_CHARS.len()) {
|
if value as usize >= ALPHANUMERIC_CHARS.len() {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
Ok( ALPHANUMERIC_CHARS[value])
|
Ok(ALPHANUMERIC_CHARS.chars().nth(value as usize).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decodeAlphanumericSegment( bits:&BitSource,
|
fn decodeAlphanumericSegment(
|
||||||
result:&String,
|
bits: &mut BitSource,
|
||||||
count:u32,
|
result: &mut String,
|
||||||
fc1InEffect:bool) -> Result<(),Exceptions> {
|
count: usize,
|
||||||
|
fc1InEffect: bool,
|
||||||
|
) -> Result<(), Exceptions> {
|
||||||
// Read two characters at a time
|
// Read two characters at a time
|
||||||
let start = result.len();
|
let start = result.len();
|
||||||
|
let mut count = count;
|
||||||
while count > 1 {
|
while count > 1 {
|
||||||
if bits.available() < 11 {
|
if bits.available() < 11 {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
let nextTwoCharsBits = bits.readBits(11);
|
let nextTwoCharsBits = bits.readBits(11)?;
|
||||||
result.append(toAlphaNumericChar(nextTwoCharsBits / 45));
|
result.push(toAlphaNumericChar(nextTwoCharsBits / 45)?);
|
||||||
result.append(toAlphaNumericChar(nextTwoCharsBits % 45));
|
result.push(toAlphaNumericChar(nextTwoCharsBits % 45)?);
|
||||||
count -= 2;
|
count -= 2;
|
||||||
}
|
}
|
||||||
if (count == 1) {
|
if count == 1 {
|
||||||
// special case: one character left
|
// special case: one character left
|
||||||
if (bits.available() < 6) {
|
if bits.available() < 6 {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
result.append(toAlphaNumericChar(bits.readBits(6)));
|
result.push(toAlphaNumericChar(bits.readBits(6)?)?);
|
||||||
}
|
}
|
||||||
// See section 6.4.8.1, 6.4.8.2
|
// See section 6.4.8.1, 6.4.8.2
|
||||||
if fc1InEffect {
|
if fc1InEffect {
|
||||||
// We need to massage the result a bit if in an FNC1 mode:
|
// We need to massage the result a bit if in an FNC1 mode:
|
||||||
for i in start..result.len() {
|
for i in start..result.len() {
|
||||||
// for (int i = start; i < result.length(); i++) {
|
// for (int i = start; i < result.length(); i++) {
|
||||||
if (result.charAt(i) == '%') {
|
if result.chars().nth(i).unwrap() == '%' {
|
||||||
if (i < result.length() - 1 && result.charAt(i + 1) == '%') {
|
if i < result.len() - 1 && result.chars().nth(i + 1).unwrap() == '%' {
|
||||||
// %% is rendered as %
|
// %% is rendered as %
|
||||||
result.deleteCharAt(i + 1);
|
result.remove(i + 1);
|
||||||
|
// result.deleteCharAt(i + 1);
|
||||||
} else {
|
} else {
|
||||||
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
||||||
result.setCharAt(i, 0x1D);
|
result.replace_range(i..i + 1, "\u{1D}");
|
||||||
|
// result.setCharAt(i, 0x1D);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,51 +348,54 @@ use super::{VersionRef, Mode, ErrorCorrectionLevel};
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decodeNumericSegment( bits:&BitSource,
|
fn decodeNumericSegment(
|
||||||
result:&String,
|
bits: &mut BitSource,
|
||||||
count:u32) -> Result<(),Exceptions> {
|
result: &mut String,
|
||||||
|
count: usize,
|
||||||
|
) -> Result<(), Exceptions> {
|
||||||
|
let mut count = count;
|
||||||
// Read three digits at a time
|
// Read three digits at a time
|
||||||
while count >= 3 {
|
while count >= 3 {
|
||||||
// Each 10 bits encodes three digits
|
// Each 10 bits encodes three digits
|
||||||
if bits.available() < 10 {
|
if bits.available() < 10 {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
let threeDigitsBits = bits.readBits(10)?;
|
let threeDigitsBits = bits.readBits(10)?;
|
||||||
if threeDigitsBits >= 1000 {
|
if threeDigitsBits >= 1000 {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
result.append(toAlphaNumericChar(threeDigitsBits / 100));
|
result.push(toAlphaNumericChar(threeDigitsBits / 100)?);
|
||||||
result.append(toAlphaNumericChar((threeDigitsBits / 10) % 10));
|
result.push(toAlphaNumericChar((threeDigitsBits / 10) % 10)?);
|
||||||
result.append(toAlphaNumericChar(threeDigitsBits % 10));
|
result.push(toAlphaNumericChar(threeDigitsBits % 10)?);
|
||||||
count -= 3;
|
count -= 3;
|
||||||
}
|
}
|
||||||
if count == 2 {
|
if count == 2 {
|
||||||
// Two digits left over to read, encoded in 7 bits
|
// Two digits left over to read, encoded in 7 bits
|
||||||
if bits.available() < 7 {
|
if bits.available() < 7 {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
let twoDigitsBits = bits.readBits(7)?;
|
let twoDigitsBits = bits.readBits(7)?;
|
||||||
if twoDigitsBits >= 100 {
|
if twoDigitsBits >= 100 {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
result.append(toAlphaNumericChar(twoDigitsBits / 10));
|
result.push(toAlphaNumericChar(twoDigitsBits / 10)?);
|
||||||
result.append(toAlphaNumericChar(twoDigitsBits % 10));
|
result.push(toAlphaNumericChar(twoDigitsBits % 10)?);
|
||||||
} else if count == 1 {
|
} else if count == 1 {
|
||||||
// One digit left over to read
|
// One digit left over to read
|
||||||
if bits.available() < 4 {
|
if bits.available() < 4 {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
let digitBits = bits.readBits(4)?;
|
let digitBits = bits.readBits(4)?;
|
||||||
if digitBits >= 10 {
|
if digitBits >= 10 {
|
||||||
return Err(Exceptions::FormatException("".to_owned()))
|
return Err(Exceptions::FormatException("".to_owned()));
|
||||||
}
|
}
|
||||||
result.append(toAlphaNumericChar(digitBits));
|
result.push(toAlphaNumericChar(digitBits)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseECIValue( bits:&BitSource) -> Result<u32,Exceptions> {
|
fn parseECIValue(bits: &mut BitSource) -> Result<u32, Exceptions> {
|
||||||
let firstByte = bits.readBits(8)?;
|
let firstByte = bits.readBits(8)?;
|
||||||
if (firstByte & 0x80) == 0 {
|
if (firstByte & 0x80) == 0 {
|
||||||
// just one byte
|
// just one byte
|
||||||
@@ -354,15 +403,14 @@ use super::{VersionRef, Mode, ErrorCorrectionLevel};
|
|||||||
}
|
}
|
||||||
if (firstByte & 0xC0) == 0x80 {
|
if (firstByte & 0xC0) == 0x80 {
|
||||||
// two bytes
|
// two bytes
|
||||||
let secondByte = bits.readBits(8);
|
let secondByte = bits.readBits(8)?;
|
||||||
return Ok(((firstByte & 0x3F) << 8) | secondByte);
|
return Ok(((firstByte & 0x3F) << 8) | secondByte);
|
||||||
}
|
}
|
||||||
if (firstByte & 0xE0) == 0xC0 {
|
if (firstByte & 0xE0) == 0xC0 {
|
||||||
// three bytes
|
// three bytes
|
||||||
let secondThirdBytes = bits.readBits(16);
|
let secondThirdBytes = bits.readBits(16)?;
|
||||||
return Ok(((firstByte & 0x1F) << 16) | secondThirdBytes);
|
return Ok(((firstByte & 0x1F) << 16) | secondThirdBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Exceptions::FormatException("".to_owned()))
|
Err(Exceptions::FormatException("".to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user