mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
partial stub out of decoder
This commit is contained in:
@@ -116,6 +116,10 @@ impl BitSource {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn peak_bits(&self, numBits: usize) -> Result<u32> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of bits that can be read successfully
|
||||
*/
|
||||
|
||||
@@ -65,4 +65,8 @@ impl Version {
|
||||
// If we didn't find a close enough match, fail
|
||||
return Err(Exceptions::ILLEGAL_STATE);
|
||||
}
|
||||
|
||||
pub fn isMicroQRCode(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
200
src/common/cpp_essentials/decoder_result.rs
Normal file
200
src/common/cpp_essentials/decoder_result.rs
Normal file
@@ -0,0 +1,200 @@
|
||||
use std::{any::Any, rc::Rc};
|
||||
|
||||
use crate::common::ECIStringBuilder;
|
||||
|
||||
use super::StructuredAppendInfo;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub struct DecoderResult<T>
|
||||
where
|
||||
T: Copy + Clone + Default + Eq + PartialEq,
|
||||
{
|
||||
content: ECIStringBuilder,
|
||||
ecLevel: String,
|
||||
lineCount: u32, // = 0;
|
||||
versionNumber: u32, // = 0;
|
||||
structuredAppend: StructuredAppendInfo,
|
||||
isMirrored: bool, // = false;
|
||||
readerInit: bool, // = false;
|
||||
//Error _error;
|
||||
//std::shared_ptr<CustomData> _extra;
|
||||
extra: Rc<T>,
|
||||
}
|
||||
|
||||
impl<T> Default for DecoderResult<T>
|
||||
where
|
||||
T: Copy + Clone + Default + Eq + PartialEq,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
content: Default::default(),
|
||||
ecLevel: Default::default(),
|
||||
lineCount: 0,
|
||||
versionNumber: 0,
|
||||
structuredAppend: Default::default(),
|
||||
isMirrored: false,
|
||||
readerInit: false,
|
||||
extra: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DecoderResult<T>
|
||||
where
|
||||
T: Copy + Clone + Default + Eq + PartialEq,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn with_eci_string_builder(src: ECIStringBuilder) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn isValid(&self) -> bool {
|
||||
//return includeErrors || (_content.symbology.code != 0 && !_error);
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn content(&self) -> &ECIStringBuilder {
|
||||
&self.content
|
||||
}
|
||||
}
|
||||
|
||||
// DecoderResult(const DecoderResult &) = delete;
|
||||
// DecoderResult& operator=(const DecoderResult &) = delete;
|
||||
// }
|
||||
|
||||
// public:
|
||||
// DecoderResult() = default;
|
||||
// DecoderResult(Error error) : _error(std::move(error)) {}
|
||||
// DecoderResult(Content&& bytes) : _content(std::move(bytes)) {}
|
||||
|
||||
// DecoderResult(DecoderResult&&) noexcept = default;
|
||||
// DecoderResult& operator=(DecoderResult&&) noexcept = default;
|
||||
|
||||
// bool isValid(bool includeErrors = false) const
|
||||
// {
|
||||
// return includeErrors || (_content.symbology.code != 0 && !_error);
|
||||
// }
|
||||
|
||||
// const Content& content() const & { return _content; }
|
||||
// Content&& content() && { return std::move(_content); }
|
||||
|
||||
// to keep the unit tests happy for now:
|
||||
// std::wstring text() const { return _content.utfW(); }
|
||||
// std::string symbologyIdentifier() const { return _content.symbology.toString(false); }
|
||||
|
||||
// Simple macro to set up getter/setter methods that save lots of boilerplate.
|
||||
// It sets up a standard 'const & () const', 2 setters for setting lvalues via
|
||||
// copy and 2 for setting rvalues via move. They are provided each to work
|
||||
// either on lvalues (normal 'void (...)') or on rvalues (returning '*this' as
|
||||
// rvalue). The latter can be used to optionally initialize a temporary in a
|
||||
// return statement, e.g.
|
||||
// return DecoderResult(bytes, text).setEcLevel(level);
|
||||
// #define ZX_PROPERTY(TYPE, GETTER, SETTER) \
|
||||
// const TYPE& GETTER() const & { return _##GETTER; } \
|
||||
// TYPE&& GETTER() && { return std::move(_##GETTER); } \
|
||||
// void SETTER(const TYPE& v) & { _##GETTER = v; } \
|
||||
// void SETTER(TYPE&& v) & { _##GETTER = std::move(v); } \
|
||||
// DecoderResult&& SETTER(const TYPE& v) && { _##GETTER = v; return std::move(*this); } \
|
||||
// DecoderResult&& SETTER(TYPE&& v) && { _##GETTER = std::move(v); return std::move(*this); }
|
||||
|
||||
// ZX_PROPERTY(std::string, ecLevel, setEcLevel)
|
||||
// ZX_PROPERTY(int, lineCount, setLineCount)
|
||||
// ZX_PROPERTY(int, versionNumber, setVersionNumber)
|
||||
// ZX_PROPERTY(StructuredAppendInfo, structuredAppend, setStructuredAppend)
|
||||
|
||||
// ZX_PROPERTY(Error, error, setError)
|
||||
|
||||
// ZX_PROPERTY(bool, isMirrored, setIsMirrored)
|
||||
// ZX_PROPERTY(bool, readerInit, setReaderInit)
|
||||
// ZX_PROPERTY(std::shared_ptr<CustomData>, extra, setExtra)
|
||||
|
||||
// #undef ZX_PROPERTY
|
||||
// };
|
||||
|
||||
impl<T> DecoderResult<T>
|
||||
where
|
||||
T: Copy + Clone + Default + Eq + PartialEq,
|
||||
{
|
||||
pub fn ecLevel(&self) -> &str {
|
||||
&self.ecLevel
|
||||
}
|
||||
pub fn setEcLevel(&mut self, ecLevel: String) {
|
||||
self.ecLevel = ecLevel
|
||||
}
|
||||
pub fn withEcLevel(mut self, ecLevel: String) -> DecoderResult<T> {
|
||||
self.setEcLevel(ecLevel);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn lineCount(&self) -> u32 {
|
||||
self.lineCount
|
||||
}
|
||||
pub fn setLineCount(&mut self, lc: u32) {
|
||||
self.lineCount = lc
|
||||
}
|
||||
pub fn withLineCount(mut self, lc: u32) -> DecoderResult<T> {
|
||||
self.setLineCount(lc);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn versionNumber(&self) -> u32 {
|
||||
self.versionNumber
|
||||
}
|
||||
pub fn setVersionNumber(&mut self, vn: u32) {
|
||||
self.versionNumber = vn
|
||||
}
|
||||
pub fn withVersionNumber(mut self, vn: u32) -> DecoderResult<T> {
|
||||
self.setVersionNumber(vn);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn structuredAppend(&self) -> &StructuredAppendInfo {
|
||||
&self.structuredAppend
|
||||
}
|
||||
pub fn setStructuredAppend(&mut self, sai: StructuredAppendInfo) {
|
||||
self.structuredAppend = sai
|
||||
}
|
||||
pub fn withStructuredAppend(mut self, sai: StructuredAppendInfo) -> DecoderResult<T> {
|
||||
self.setStructuredAppend(sai);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn isMirrored(&self) -> bool {
|
||||
self.isMirrored
|
||||
}
|
||||
pub fn setIsMirrored(&mut self, is_mirrored: bool) {
|
||||
self.isMirrored = is_mirrored
|
||||
}
|
||||
pub fn withIsMirrored(mut self, is_mirrored: bool) -> DecoderResult<T> {
|
||||
self.setIsMirrored(is_mirrored);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn readerInit(&self) -> bool {
|
||||
self.readerInit
|
||||
}
|
||||
pub fn setReaderInit(&mut self, reader_init: bool) {
|
||||
self.readerInit = reader_init
|
||||
}
|
||||
pub fn withReaderInit(mut self, reader_init: bool) -> DecoderResult<T> {
|
||||
self.setReaderInit(reader_init);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extra(&self) -> Rc<T> {
|
||||
self.extra.clone()
|
||||
}
|
||||
pub fn setExtra(&mut self, extra: Rc<T>) {
|
||||
self.extra = extra
|
||||
}
|
||||
pub fn withExtra(mut self, extra: Rc<T>) -> DecoderResult<T> {
|
||||
self.setExtra(extra);
|
||||
self
|
||||
}
|
||||
|
||||
// pub fn build(self) -> DecoderResult<T> {
|
||||
|
||||
// }
|
||||
}
|
||||
@@ -3,6 +3,7 @@ mod base_extentions;
|
||||
pub mod bitmatrix_cursor;
|
||||
pub mod bitmatrix_cursor_trait;
|
||||
pub mod concentric_finder;
|
||||
pub mod decoder_result;
|
||||
pub mod direction;
|
||||
pub mod dm_regression_line;
|
||||
pub mod edge_tracer;
|
||||
@@ -12,12 +13,14 @@ pub mod pattern;
|
||||
pub mod regression_line;
|
||||
pub mod regression_line_trait;
|
||||
pub mod step_result;
|
||||
pub mod structured_append;
|
||||
pub mod util;
|
||||
pub mod value;
|
||||
|
||||
pub use bitmatrix_cursor::*;
|
||||
pub use bitmatrix_cursor_trait::*;
|
||||
pub use concentric_finder::*;
|
||||
pub use decoder_result::*;
|
||||
pub use direction::*;
|
||||
pub use dm_regression_line::*;
|
||||
pub use edge_tracer::*;
|
||||
@@ -27,5 +30,6 @@ pub use pattern::*;
|
||||
pub use regression_line::*;
|
||||
pub use regression_line_trait::*;
|
||||
pub use step_result::*;
|
||||
pub use structured_append::*;
|
||||
pub use util::*;
|
||||
pub use value::*;
|
||||
|
||||
16
src/common/cpp_essentials/structured_append.rs
Normal file
16
src/common/cpp_essentials/structured_append.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct StructuredAppendInfo {
|
||||
pub index: i32, // -1;
|
||||
pub count: i32, // = -1;
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
impl Default for StructuredAppendInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
index: -1,
|
||||
count: -1,
|
||||
id: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,3 +38,29 @@ pub fn UpdateMinMaxFloat(min: &mut f64, max: &mut f64, val: f64) {
|
||||
*min = f64::min(*min, val);
|
||||
*max = f64::max(*max, val);
|
||||
}
|
||||
|
||||
// template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
||||
pub fn ToString<T: Into<usize>>(val: T, len: usize) -> Result<String> {
|
||||
let mut len = len;
|
||||
let mut val = val.into();
|
||||
|
||||
let mut result: String = vec!['0'; len].iter().collect();
|
||||
len -= 1;
|
||||
// std::string result(len--, '0');
|
||||
if (val < 0) {
|
||||
return Err(Exceptions::format_with("Invalid value"));
|
||||
}
|
||||
while len >= 0 && val != 0 {
|
||||
result.replace_range(len..len, &char::from(b'0' + (val % 10) as u8).to_string());
|
||||
|
||||
len -= 1;
|
||||
val /= 10;
|
||||
}
|
||||
// for (; len >= 0 && val != 0; --len, val /= 10) {
|
||||
// result[len] = '0' + val % 10;}
|
||||
if (val != 0) {
|
||||
return Err(Exceptions::format_with("Invalid value"));
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -30,12 +30,13 @@ use super::{CharacterSet, Eci};
|
||||
*
|
||||
* @author Alex Geller
|
||||
*/
|
||||
#[derive(Default)]
|
||||
#[derive(Default, PartialEq, Eq, Debug, Clone)]
|
||||
pub struct ECIStringBuilder {
|
||||
is_eci: bool,
|
||||
eci_result: Option<String>,
|
||||
bytes: Vec<u8>,
|
||||
eci_positions: Vec<(Eci, usize, usize)>, // (Eci, start, end)
|
||||
pub symbology: SymbologyIdentifier,
|
||||
}
|
||||
|
||||
impl ECIStringBuilder {
|
||||
@@ -45,6 +46,7 @@ impl ECIStringBuilder {
|
||||
bytes: Vec::with_capacity(initial_capacity),
|
||||
eci_positions: Vec::default(),
|
||||
is_eci: false,
|
||||
symbology: SymbologyIdentifier::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +122,11 @@ impl ECIStringBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the current encoding characterset, finding an eci to do so
|
||||
pub fn switch_encoding(&mut self, charset: CharacterSet) {
|
||||
self.append_eci(Eci::from(charset))
|
||||
}
|
||||
|
||||
/// Finishes encoding anything in the buffer using the current ECI and resets.
|
||||
///
|
||||
/// This function can panic
|
||||
@@ -198,6 +205,11 @@ impl ECIStringBuilder {
|
||||
self.bytes.len()
|
||||
}
|
||||
|
||||
/// Reserve an additional number of bytes for storage
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
self.bytes.reserve(additional);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff nothing has been appended
|
||||
*/
|
||||
@@ -221,3 +233,57 @@ impl fmt::Display for ECIStringBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign<u8> for ECIStringBuilder {
|
||||
fn add_assign(&mut self, rhs: u8) {
|
||||
self.append_byte(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign<String> for ECIStringBuilder {
|
||||
fn add_assign(&mut self, rhs: String) {
|
||||
self.append_string(&rhs)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ContentType {
|
||||
Text,
|
||||
Binary,
|
||||
Mixed,
|
||||
GS1,
|
||||
ISO15434,
|
||||
UnknownECI,
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum AIFlag {
|
||||
None,
|
||||
GS1,
|
||||
AIM,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct SymbologyIdentifier {
|
||||
//char code = 0, modifier = 0, eciModifierOffset = 0;
|
||||
pub code: u8,
|
||||
pub modifier: u8,
|
||||
pub eciModifierOffset: u8,
|
||||
pub aiFlag: AIFlag,
|
||||
// AIFlag aiFlag = AIFlag::None;
|
||||
|
||||
// std::string toString(bool hasECI = false) const
|
||||
// {
|
||||
// return code ? ']' + std::string(1, code) + static_cast<char>(modifier + eciModifierOffset * hasECI) : std::string();
|
||||
// }
|
||||
}
|
||||
|
||||
impl Default for SymbologyIdentifier {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
code: 0,
|
||||
modifier: 0,
|
||||
eciModifierOffset: 0,
|
||||
aiFlag: AIFlag::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
203
src/qrcode/cpp_port/bitmatrix_parser.rs
Normal file
203
src/qrcode/cpp_port/bitmatrix_parser.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
// /*
|
||||
// * Copyright 2016 Nu-book Inc.
|
||||
// * Copyright 2016 ZXing authors
|
||||
// */
|
||||
// // SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
common::{BitMatrix, Result},
|
||||
qrcode::decoder::{FormatInformation, Version, VersionRef},
|
||||
Exceptions,
|
||||
};
|
||||
|
||||
pub fn getBit(bitMatrix: &BitMatrix, x: u32, y: u32, mirrored: Option<bool>) -> bool {
|
||||
let mirrored = mirrored.unwrap_or(false);
|
||||
if mirrored {
|
||||
bitMatrix.get(y, x)
|
||||
} else {
|
||||
bitMatrix.get(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hasValidDimension(bitMatrix: &BitMatrix, isMicro: bool) -> bool {
|
||||
let dimension = bitMatrix.height();
|
||||
if (isMicro) {
|
||||
dimension >= 11 && dimension <= 17 && (dimension % 2) == 1
|
||||
} else {
|
||||
dimension >= 21 && dimension <= 177 && (dimension % 4) == 1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ReadVersion(bitMatrix: &BitMatrix) -> Result<VersionRef> {
|
||||
todo!()
|
||||
// int dimension = bitMatrix.height();
|
||||
|
||||
// const Version* version = Version::FromDimension(dimension);
|
||||
|
||||
// if (!version || version->versionNumber() < 7)
|
||||
// return version;
|
||||
|
||||
// for (bool mirror : {false, true}) {
|
||||
// // Read top-right/bottom-left version info: 3 wide by 6 tall (depending on mirrored)
|
||||
// int versionBits = 0;
|
||||
// for (int y = 5; y >= 0; --y)
|
||||
// for (int x = dimension - 9; x >= dimension - 11; --x)
|
||||
// AppendBit(versionBits, getBit(bitMatrix, x, y, mirror));
|
||||
|
||||
// version = Version::DecodeVersionInformation(versionBits);
|
||||
// if (version && version->dimension() == dimension)
|
||||
// return version;
|
||||
// }
|
||||
|
||||
// return nullptr;
|
||||
}
|
||||
|
||||
pub fn ReadFormatInformation(bitMatrix: &BitMatrix, isMicro: bool) -> Result<FormatInformation> {
|
||||
todo!()
|
||||
// if (!hasValidDimension(bitMatrix, isMicro))
|
||||
// return {};
|
||||
|
||||
// if (isMicro) {
|
||||
// // Read top-left format info bits
|
||||
// int formatInfoBits = 0;
|
||||
// for (int x = 1; x < 9; x++)
|
||||
// AppendBit(formatInfoBits, getBit(bitMatrix, x, 8));
|
||||
// for (int y = 7; y >= 1; y--)
|
||||
// AppendBit(formatInfoBits, getBit(bitMatrix, 8, y));
|
||||
|
||||
// return FormatInformation::DecodeMQR(formatInfoBits);
|
||||
// }
|
||||
|
||||
// // Read top-left format info bits
|
||||
// int formatInfoBits1 = 0;
|
||||
// for (int x = 0; x < 6; x++)
|
||||
// AppendBit(formatInfoBits1, getBit(bitMatrix, x, 8));
|
||||
// // .. and skip a bit in the timing pattern ...
|
||||
// AppendBit(formatInfoBits1, getBit(bitMatrix, 7, 8));
|
||||
// AppendBit(formatInfoBits1, getBit(bitMatrix, 8, 8));
|
||||
// AppendBit(formatInfoBits1, getBit(bitMatrix, 8, 7));
|
||||
// // .. and skip a bit in the timing pattern ...
|
||||
// for (int y = 5; y >= 0; y--)
|
||||
// AppendBit(formatInfoBits1, getBit(bitMatrix, 8, y));
|
||||
|
||||
// // Read the top-right/bottom-left pattern including the 'Dark Module' from the bottom-left
|
||||
// // part that has to be considered separately when looking for mirrored symbols.
|
||||
// // See also FormatInformation::DecodeQR
|
||||
// int dimension = bitMatrix.height();
|
||||
// int formatInfoBits2 = 0;
|
||||
// for (int y = dimension - 1; y >= dimension - 8; y--)
|
||||
// AppendBit(formatInfoBits2, getBit(bitMatrix, 8, y));
|
||||
// for (int x = dimension - 8; x < dimension; x++)
|
||||
// AppendBit(formatInfoBits2, getBit(bitMatrix, x, 8));
|
||||
|
||||
// return FormatInformation::DecodeQR(formatInfoBits1, formatInfoBits2);
|
||||
}
|
||||
|
||||
pub fn ReadQRCodewords(
|
||||
bitMatrix: &BitMatrix,
|
||||
version: VersionRef,
|
||||
formatInfo: &FormatInformation,
|
||||
) -> Result<Vec<u8>> {
|
||||
todo!()
|
||||
// BitMatrix functionPattern = version.buildFunctionPattern();
|
||||
|
||||
// ByteArray result;
|
||||
// result.reserve(version.totalCodewords());
|
||||
// uint8_t currentByte = 0;
|
||||
// bool readingUp = true;
|
||||
// int bitsRead = 0;
|
||||
// int dimension = bitMatrix.height();
|
||||
// // Read columns in pairs, from right to left
|
||||
// for (int x = dimension - 1; x > 0; x -= 2) {
|
||||
// // Skip whole column with vertical timing pattern.
|
||||
// if (x == 6)
|
||||
// x--;
|
||||
// // Read alternatingly from bottom to top then top to bottom
|
||||
// for (int row = 0; row < dimension; row++) {
|
||||
// int y = readingUp ? dimension - 1 - row : row;
|
||||
// for (int col = 0; col < 2; col++) {
|
||||
// int xx = x - col;
|
||||
// // Ignore bits covered by the function pattern
|
||||
// if (!functionPattern.get(xx, y)) {
|
||||
// // Read a bit
|
||||
// AppendBit(currentByte,
|
||||
// GetDataMaskBit(formatInfo.dataMask, xx, y) != getBit(bitMatrix, xx, y, formatInfo.isMirrored));
|
||||
// // If we've made a whole byte, save it off
|
||||
// if (++bitsRead % 8 == 0)
|
||||
// result.push_back(std::exchange(currentByte, 0));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// readingUp = !readingUp; // switch directions
|
||||
// }
|
||||
// if (Size(result) != version.totalCodewords())
|
||||
// return {};
|
||||
|
||||
// return result;
|
||||
}
|
||||
|
||||
pub fn ReadMQRCodewords(
|
||||
bitMatrix: &BitMatrix,
|
||||
version: VersionRef,
|
||||
formatInfo: &FormatInformation,
|
||||
) -> Result<Vec<u8>> {
|
||||
todo!()
|
||||
// BitMatrix functionPattern = version.buildFunctionPattern();
|
||||
|
||||
// // D3 in a Version M1 symbol, D11 in a Version M3-L symbol and D9
|
||||
// // in a Version M3-M symbol is a 2x2 square 4-module block.
|
||||
// // See ISO 18004:2006 6.7.3.
|
||||
// bool hasD4mBlock = version.versionNumber() % 2 == 1;
|
||||
// int d4mBlockIndex =
|
||||
// version.versionNumber() == 1 ? 3 : (formatInfo.ecLevel == QRCode::ErrorCorrectionLevel::Low ? 11 : 9);
|
||||
|
||||
// ByteArray result;
|
||||
// result.reserve(version.totalCodewords());
|
||||
// uint8_t currentByte = 0;
|
||||
// bool readingUp = true;
|
||||
// int bitsRead = 0;
|
||||
// int dimension = bitMatrix.height();
|
||||
// // Read columns in pairs, from right to left
|
||||
// for (int x = dimension - 1; x > 0; x -= 2) {
|
||||
// // Read alternatingly from bottom to top then top to bottom
|
||||
// for (int row = 0; row < dimension; row++) {
|
||||
// int y = readingUp ? dimension - 1 - row : row;
|
||||
// for (int col = 0; col < 2; col++) {
|
||||
// int xx = x - col;
|
||||
// // Ignore bits covered by the function pattern
|
||||
// if (!functionPattern.get(xx, y)) {
|
||||
// // Read a bit
|
||||
// AppendBit(currentByte,
|
||||
// GetDataMaskBit(formatInfo.dataMask, xx, y, true) != getBit(bitMatrix, xx, y, formatInfo.isMirrored));
|
||||
// ++bitsRead;
|
||||
// // If we've made a whole byte, save it off; save early if 2x2 data block.
|
||||
// if (bitsRead == 8 || (bitsRead == 4 && hasD4mBlock && Size(result) == d4mBlockIndex - 1)) {
|
||||
// result.push_back(std::exchange(currentByte, 0));
|
||||
// bitsRead = 0;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// readingUp = !readingUp; // switch directions
|
||||
// }
|
||||
// if (Size(result) != version.totalCodewords())
|
||||
// return {};
|
||||
|
||||
// return result;
|
||||
}
|
||||
|
||||
pub fn ReadCodewords(
|
||||
bitMatrix: &BitMatrix,
|
||||
version: VersionRef,
|
||||
formatInfo: &FormatInformation,
|
||||
) -> Result<Vec<u8>> {
|
||||
if (!hasValidDimension(bitMatrix, version.isMicroQRCode())) {
|
||||
return Err(Exceptions::FORMAT);
|
||||
}
|
||||
|
||||
if version.isMicroQRCode() {
|
||||
ReadMQRCodewords(bitMatrix, version, formatInfo)
|
||||
} else {
|
||||
ReadQRCodewords(bitMatrix, version, formatInfo)
|
||||
}
|
||||
}
|
||||
@@ -4,357 +4,450 @@
|
||||
// */
|
||||
// // SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// #include "QRDecoder.h"
|
||||
use crate::common::cpp_essentials::{DecoderResult, StructuredAppendInfo};
|
||||
use crate::common::reedsolomon::{
|
||||
get_predefined_genericgf, PredefinedGenericGF, ReedSolomonDecoder,
|
||||
};
|
||||
use crate::common::{
|
||||
AIFlag, BitMatrix, BitSource, CharacterSet, DecoderRXingResult, ECIStringBuilder, Eci, Result,
|
||||
SymbologyIdentifier,
|
||||
};
|
||||
use crate::qrcode::cpp_port::bitmatrix_parser::{
|
||||
ReadCodewords, ReadFormatInformation, ReadVersion,
|
||||
};
|
||||
use crate::qrcode::decoder::{DataBlock, ErrorCorrectionLevel, Mode, Version};
|
||||
use crate::Exceptions;
|
||||
|
||||
// #include "BitMatrix.h"
|
||||
// #include "BitSource.h"
|
||||
// #include "CharacterSet.h"
|
||||
// #include "DecoderResult.h"
|
||||
// #include "GenericGF.h"
|
||||
// #include "QRBitMatrixParser.h"
|
||||
// #include "QRCodecMode.h"
|
||||
// #include "QRDataBlock.h"
|
||||
// #include "QRFormatInformation.h"
|
||||
// #include "QRVersion.h"
|
||||
// #include "ReedSolomonDecoder.h"
|
||||
// #include "StructuredAppend.h"
|
||||
// #include "ZXAlgorithms.h"
|
||||
// #include "ZXTestSupport.h"
|
||||
/**
|
||||
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
* correct the errors in-place using Reed-Solomon error correction.</p>
|
||||
*
|
||||
* @param codewordBytes data and error correction codewords
|
||||
* @param numDataCodewords number of codewords that are data bytes
|
||||
* @return false if error correction fails
|
||||
*/
|
||||
pub fn CorrectErrors(codewordBytes: &mut [u8], numDataCodewords: u32) -> Result<bool> {
|
||||
// First read into an array of ints
|
||||
// std::vector<int> codewordsInts(codewordBytes.begin(), codewordBytes.end());
|
||||
let mut codewordsInts = codewordBytes.iter().copied().map(|b| b as i32).collect();
|
||||
|
||||
// #include <algorithm>
|
||||
// #include <stdexcept>
|
||||
// #include <utility>
|
||||
// #include <vector>
|
||||
let numECCodewords = ((codewordBytes.len() as u32) - numDataCodewords) as i32;
|
||||
let rs = ReedSolomonDecoder::new(get_predefined_genericgf(
|
||||
PredefinedGenericGF::QrCodeField256,
|
||||
));
|
||||
if rs.decode(&mut codewordsInts, numECCodewords)? == 0
|
||||
// if (!ReedSolomonDecode(GenericGF::QRCodeField256(), codewordsInts, numECCodewords))
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// namespace ZXing::QRCode {
|
||||
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||
// We don't care about errors in the error-correction codewords
|
||||
codewordBytes[..numDataCodewords as usize].copy_from_slice(
|
||||
&codewordsInts[..numDataCodewords as usize]
|
||||
.into_iter()
|
||||
.copied()
|
||||
.map(|i| i as u8)
|
||||
.collect::<Vec<u8>>(),
|
||||
);
|
||||
// std::copy_n(codewordsInts.begin(), numDataCodewords, codewordBytes.begin());
|
||||
|
||||
// /**
|
||||
// * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
// * correct the errors in-place using Reed-Solomon error correction.</p>
|
||||
// *
|
||||
// * @param codewordBytes data and error correction codewords
|
||||
// * @param numDataCodewords number of codewords that are data bytes
|
||||
// * @return false if error correction fails
|
||||
// */
|
||||
// static bool CorrectErrors(ByteArray& codewordBytes, int numDataCodewords)
|
||||
// {
|
||||
// // First read into an array of ints
|
||||
// std::vector<int> codewordsInts(codewordBytes.begin(), codewordBytes.end());
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
// int numECCodewords = Size(codewordBytes) - numDataCodewords;
|
||||
// if (!ReedSolomonDecode(GenericGF::QRCodeField256(), codewordsInts, numECCodewords))
|
||||
// return false;
|
||||
/**
|
||||
* See specification GBT 18284-2000
|
||||
*/
|
||||
pub fn DecodeHanziSegment(
|
||||
bits: &mut BitSource,
|
||||
count: u32,
|
||||
result: &mut ECIStringBuilder,
|
||||
) -> Result<()> {
|
||||
let mut count = count;
|
||||
|
||||
// // Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||
// // We don't care about errors in the error-correction codewords
|
||||
// std::copy_n(codewordsInts.begin(), numDataCodewords, codewordBytes.begin());
|
||||
// return true;
|
||||
// }
|
||||
// Each character will require 2 bytes, decode as GB2312
|
||||
// There is no ECI value for GB2312, use GB18030 which is a superset
|
||||
result.switch_encoding(CharacterSet::GB18030);
|
||||
result.reserve(2 * count as usize);
|
||||
|
||||
while (count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
let twoBytes = bits.readBits(13)?;
|
||||
let mut assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
|
||||
if (assembledTwoBytes < 0x00A00) {
|
||||
// In the 0xA1A1 to 0xAAFE range
|
||||
assembledTwoBytes += 0x0A1A1;
|
||||
} else {
|
||||
// In the 0xB0A1 to 0xFAFE range
|
||||
assembledTwoBytes += 0x0A6A1;
|
||||
}
|
||||
*result += ((assembledTwoBytes >> 8) & 0xFF) as u8;
|
||||
*result += (assembledTwoBytes & 0xFF) as u8;
|
||||
count -= 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// /**
|
||||
// * See specification GBT 18284-2000
|
||||
// */
|
||||
// static void DecodeHanziSegment(BitSource& bits, int count, Content& result)
|
||||
// {
|
||||
// // Each character will require 2 bytes, decode as GB2312
|
||||
// // There is no ECI value for GB2312, use GB18030 which is a superset
|
||||
// result.switchEncoding(CharacterSet::GB18030);
|
||||
// result.reserve(2 * count);
|
||||
pub fn DecodeKanjiSegment(
|
||||
bits: &mut BitSource,
|
||||
count: u32,
|
||||
result: &mut ECIStringBuilder,
|
||||
) -> Result<()> {
|
||||
let mut count = count;
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as Shift_JIS afterwards
|
||||
result.switch_encoding(CharacterSet::Shift_JIS);
|
||||
result.reserve(2 * count as usize);
|
||||
|
||||
// while (count > 0) {
|
||||
// // Each 13 bits encodes a 2-byte character
|
||||
// int twoBytes = bits.readBits(13);
|
||||
// int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
|
||||
// if (assembledTwoBytes < 0x00A00) {
|
||||
// // In the 0xA1A1 to 0xAAFE range
|
||||
// assembledTwoBytes += 0x0A1A1;
|
||||
// } else {
|
||||
// // In the 0xB0A1 to 0xFAFE range
|
||||
// assembledTwoBytes += 0x0A6A1;
|
||||
// }
|
||||
// result += narrow_cast<uint8_t>((assembledTwoBytes >> 8) & 0xFF);
|
||||
// result += narrow_cast<uint8_t>(assembledTwoBytes & 0xFF);
|
||||
// count--;
|
||||
// }
|
||||
// }
|
||||
while (count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
let twoBytes = bits.readBits(13)?;
|
||||
let mut assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
|
||||
if (assembledTwoBytes < 0x01F00) {
|
||||
// In the 0x8140 to 0x9FFC range
|
||||
assembledTwoBytes += 0x08140;
|
||||
} else {
|
||||
// In the 0xE040 to 0xEBBF range
|
||||
assembledTwoBytes += 0x0C140;
|
||||
}
|
||||
*result += (assembledTwoBytes >> 8) as u8;
|
||||
*result += (assembledTwoBytes) as u8;
|
||||
count -= 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// static void DecodeKanjiSegment(BitSource& bits, int count, Content& result)
|
||||
// {
|
||||
// // Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// // and decode as Shift_JIS afterwards
|
||||
// result.switchEncoding(CharacterSet::Shift_JIS);
|
||||
// result.reserve(2 * count);
|
||||
pub fn DecodeByteSegment(
|
||||
bits: &mut BitSource,
|
||||
count: u32,
|
||||
result: &mut ECIStringBuilder,
|
||||
) -> Result<()> {
|
||||
result.switch_encoding(CharacterSet::Unknown);
|
||||
result.reserve(count as usize);
|
||||
|
||||
// while (count > 0) {
|
||||
// // Each 13 bits encodes a 2-byte character
|
||||
// int twoBytes = bits.readBits(13);
|
||||
// int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
|
||||
// if (assembledTwoBytes < 0x01F00) {
|
||||
// // In the 0x8140 to 0x9FFC range
|
||||
// assembledTwoBytes += 0x08140;
|
||||
// } else {
|
||||
// // In the 0xE040 to 0xEBBF range
|
||||
// assembledTwoBytes += 0x0C140;
|
||||
// }
|
||||
// result += narrow_cast<uint8_t>(assembledTwoBytes >> 8);
|
||||
// result += narrow_cast<uint8_t>(assembledTwoBytes);
|
||||
// count--;
|
||||
// }
|
||||
// }
|
||||
for i in 0..count {
|
||||
// for (int i = 0; i < count; i++)
|
||||
*result += (bits.readBits(8)?) as u8;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// static void DecodeByteSegment(BitSource& bits, int count, Content& result)
|
||||
// {
|
||||
// result.switchEncoding(CharacterSet::Unknown);
|
||||
// result.reserve(count);
|
||||
pub fn ToAlphaNumericChar(value: u32) -> Result<char> {
|
||||
let value = value as usize;
|
||||
/**
|
||||
* See ISO 18004:2006, 6.4.4 Table 5
|
||||
*/
|
||||
const ALPHANUMERIC_CHARS: [char; 45] = [
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
' ', '$', '%', '*', '+', '-', '.', '/', ':',
|
||||
];
|
||||
|
||||
// for (int i = 0; i < count; i++)
|
||||
// result += narrow_cast<uint8_t>(bits.readBits(8));
|
||||
// }
|
||||
if (value < 0 || value >= (ALPHANUMERIC_CHARS.len())) {
|
||||
return Err(Exceptions::index_out_of_bounds_with(
|
||||
"oAlphaNumericChar: out of range",
|
||||
));
|
||||
}
|
||||
|
||||
// static char ToAlphaNumericChar(int value)
|
||||
// {
|
||||
// /**
|
||||
// * See ISO 18004:2006, 6.4.4 Table 5
|
||||
// */
|
||||
// static const char ALPHANUMERIC_CHARS[] = {
|
||||
// '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
||||
// 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||
// 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
// ' ', '$', '%', '*', '+', '-', '.', '/', ':'
|
||||
// };
|
||||
Ok(ALPHANUMERIC_CHARS[value])
|
||||
}
|
||||
|
||||
// if (value < 0 || value >= Size(ALPHANUMERIC_CHARS))
|
||||
// throw std::out_of_range("ToAlphaNumericChar: out of range");
|
||||
pub fn DecodeAlphanumericSegment(
|
||||
bits: &mut BitSource,
|
||||
count: u32,
|
||||
result: &mut ECIStringBuilder,
|
||||
) -> Result<()> {
|
||||
let mut count = count;
|
||||
|
||||
// return ALPHANUMERIC_CHARS[value];
|
||||
// }
|
||||
// Read two characters at a time
|
||||
let mut buffer = String::new();
|
||||
|
||||
// static void DecodeAlphanumericSegment(BitSource& bits, int count, Content& result)
|
||||
// {
|
||||
// // Read two characters at a time
|
||||
// std::string buffer;
|
||||
// while (count > 1) {
|
||||
// int nextTwoCharsBits = bits.readBits(11);
|
||||
// buffer += ToAlphaNumericChar(nextTwoCharsBits / 45);
|
||||
// buffer += ToAlphaNumericChar(nextTwoCharsBits % 45);
|
||||
// count -= 2;
|
||||
// }
|
||||
// if (count == 1) {
|
||||
// // special case: one character left
|
||||
// buffer += ToAlphaNumericChar(bits.readBits(6));
|
||||
// }
|
||||
// // See section 6.4.8.1, 6.4.8.2
|
||||
// if (result.symbology.aiFlag != AIFlag::None) {
|
||||
// // We need to massage the result a bit if in an FNC1 mode:
|
||||
// for (size_t i = 0; i < buffer.length(); i++) {
|
||||
// if (buffer[i] == '%') {
|
||||
// if (i < buffer.length() - 1 && buffer[i + 1] == '%') {
|
||||
// // %% is rendered as %
|
||||
// buffer.erase(i + 1);
|
||||
// } else {
|
||||
// // In alpha mode, % should be converted to FNC1 separator 0x1D
|
||||
// buffer[i] = static_cast<char>(0x1D);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
while count > 1 {
|
||||
let nextTwoCharsBits = bits.readBits(11)?;
|
||||
buffer.push(ToAlphaNumericChar(nextTwoCharsBits / 45)?);
|
||||
buffer.push(ToAlphaNumericChar(nextTwoCharsBits % 45)?);
|
||||
count -= 2;
|
||||
}
|
||||
if (count == 1) {
|
||||
// special case: one character left
|
||||
buffer.push(ToAlphaNumericChar(bits.readBits(6)?)?);
|
||||
}
|
||||
// See section 6.4.8.1, 6.4.8.2
|
||||
if (result.symbology.aiFlag != AIFlag::None) {
|
||||
// We need to massage the result a bit if in an FNC1 mode:
|
||||
for i in 0..buffer.len() {
|
||||
// for (size_t i = 0; i < buffer.length(); i++) {
|
||||
if (buffer
|
||||
.chars()
|
||||
.nth(i)
|
||||
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?
|
||||
== '%')
|
||||
{
|
||||
if (i < buffer.len() - 1
|
||||
&& buffer
|
||||
.chars()
|
||||
.nth(i + 1)
|
||||
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?
|
||||
== '%')
|
||||
{
|
||||
// %% is rendered as %
|
||||
buffer.remove(i + 1);
|
||||
// buffer.erase(i + 1);
|
||||
} else {
|
||||
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
||||
buffer.replace_range(i..i, &char::from(0x1D).to_string());
|
||||
// buffer[i] = static_cast<char>(0x1D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// result.switchEncoding(CharacterSet::ISO8859_1);
|
||||
// result += buffer;
|
||||
// }
|
||||
result.switch_encoding(CharacterSet::ISO8859_1);
|
||||
*result += buffer;
|
||||
|
||||
// static void DecodeNumericSegment(BitSource& bits, int count, Content& result)
|
||||
// {
|
||||
// result.switchEncoding(CharacterSet::ISO8859_1);
|
||||
// result.reserve(count);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// while (count) {
|
||||
// int n = std::min(count, 3);
|
||||
// int nDigits = bits.readBits(1 + 3 * n); // read 4, 7 or 10 bits into 1, 2 or 3 digits
|
||||
// result.append(ZXing::ToString(nDigits, n));
|
||||
// count -= n;
|
||||
// }
|
||||
// }
|
||||
pub fn DecodeNumericSegment(
|
||||
bits: &mut BitSource,
|
||||
count: u32,
|
||||
result: &mut ECIStringBuilder,
|
||||
) -> Result<()> {
|
||||
let mut count = count;
|
||||
|
||||
// static ECI ParseECIValue(BitSource& bits)
|
||||
// {
|
||||
// int firstByte = bits.readBits(8);
|
||||
// if ((firstByte & 0x80) == 0) {
|
||||
// // just one byte
|
||||
// return ECI(firstByte & 0x7F);
|
||||
// }
|
||||
// if ((firstByte & 0xC0) == 0x80) {
|
||||
// // two bytes
|
||||
// int secondByte = bits.readBits(8);
|
||||
// return ECI(((firstByte & 0x3F) << 8) | secondByte);
|
||||
// }
|
||||
// if ((firstByte & 0xE0) == 0xC0) {
|
||||
// // three bytes
|
||||
// int secondThirdBytes = bits.readBits(16);
|
||||
// return ECI(((firstByte & 0x1F) << 16) | secondThirdBytes);
|
||||
// }
|
||||
// throw FormatError("ParseECIValue: invalid value");
|
||||
// }
|
||||
result.switch_encoding(CharacterSet::ISO8859_1);
|
||||
result.reserve(count as usize);
|
||||
|
||||
// /**
|
||||
// * QR codes encode mode indicators and terminator codes into a constant bit length of 4.
|
||||
// * Micro QR codes have terminator codes that vary in bit length but are always longer than
|
||||
// * the mode indicators.
|
||||
// * M1 - 0 length mode code, 3 bits terminator code
|
||||
// * M2 - 1 bit mode code, 5 bits terminator code
|
||||
// * M3 - 2 bit mode code, 7 bits terminator code
|
||||
// * M4 - 3 bit mode code, 9 bits terminator code
|
||||
// * IsTerminator peaks into the bit stream to see if the current position is at the start of
|
||||
// * a terminator code. If true, then the decoding can finish. If false, then the decoding
|
||||
// * can read off the next mode code.
|
||||
// *
|
||||
// * See ISO 18004:2015, 7.4.1 Table 2
|
||||
// *
|
||||
// * @param bits the stream of bits that might have a terminator code
|
||||
// * @param version the QR or micro QR code version
|
||||
// */
|
||||
// bool IsEndOfStream(const BitSource& bits, const Version& version)
|
||||
// {
|
||||
// const int bitsRequired = TerminatorBitsLength(version);
|
||||
// const int bitsAvailable = std::min(bits.available(), bitsRequired);
|
||||
// return bitsAvailable == 0 || bits.peakBits(bitsAvailable) == 0;
|
||||
// }
|
||||
while (count > 0) {
|
||||
let n = std::cmp::min(count, 3);
|
||||
let nDigits = bits.readBits(1 + 3 * n as usize)?; // read 4, 7 or 10 bits into 1, 2 or 3 digits
|
||||
result.append_string(&crate::common::cpp_essentials::util::ToString(
|
||||
nDigits as usize,
|
||||
n as usize,
|
||||
)?);
|
||||
count -= n;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||
// * in one QR Code. This method decodes the bits back into text.</p>
|
||||
// *
|
||||
// * <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
|
||||
// */
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ParseECIValue(bits: &mut BitSource) -> Result<Eci> {
|
||||
let firstByte = bits.readBits(8)?;
|
||||
if ((firstByte & 0x80) == 0) {
|
||||
// just one byte
|
||||
return Ok(Eci::from(firstByte & 0x7F));
|
||||
}
|
||||
if ((firstByte & 0xC0) == 0x80) {
|
||||
// two bytes
|
||||
let secondByte = bits.readBits(8)?;
|
||||
return Ok(Eci::from(((firstByte & 0x3F) << 8) | secondByte));
|
||||
}
|
||||
if ((firstByte & 0xE0) == 0xC0) {
|
||||
// three bytes
|
||||
let secondThirdBytes = bits.readBits(16)?;
|
||||
return Ok(Eci::from(((firstByte & 0x1F) << 16) | secondThirdBytes));
|
||||
}
|
||||
Err(Exceptions::format_with("ParseECIValue: invalid value"))
|
||||
}
|
||||
|
||||
/**
|
||||
* QR codes encode mode indicators and terminator codes into a constant bit length of 4.
|
||||
* Micro QR codes have terminator codes that vary in bit length but are always longer than
|
||||
* the mode indicators.
|
||||
* M1 - 0 length mode code, 3 bits terminator code
|
||||
* M2 - 1 bit mode code, 5 bits terminator code
|
||||
* M3 - 2 bit mode code, 7 bits terminator code
|
||||
* M4 - 3 bit mode code, 9 bits terminator code
|
||||
* IsTerminator peaks into the bit stream to see if the current position is at the start of
|
||||
* a terminator code. If true, then the decoding can finish. If false, then the decoding
|
||||
* can read off the next mode code.
|
||||
*
|
||||
* See ISO 18004:2015, 7.4.1 Table 2
|
||||
*
|
||||
* @param bits the stream of bits that might have a terminator code
|
||||
* @param version the QR or micro QR code version
|
||||
*/
|
||||
pub fn IsEndOfStream(bits: &mut BitSource, version: &Version) -> Result<bool> {
|
||||
let bitsRequired = Mode::get_terminator_bit_length(version); //super::qr_codec_mode::TerminatorBitsLength(version);
|
||||
let bitsAvailable = std::cmp::min(bits.available(), bitsRequired as usize);
|
||||
Ok(bitsAvailable == 0 || bits.peak_bits(bitsAvailable)? == 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||
* in one QR Code. This method decodes the bits back into text.</p>
|
||||
*
|
||||
* <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
|
||||
*/
|
||||
// ZXING_EXPORT_TEST_ONLY
|
||||
// DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel)
|
||||
// {
|
||||
// BitSource bits(bytes);
|
||||
// Content result;
|
||||
// Error error;
|
||||
// result.symbology = {'Q', '1', 1};
|
||||
// StructuredAppendInfo structuredAppend;
|
||||
// const int modeBitLength = CodecModeBitsLength(version);
|
||||
pub fn DecodeBitStream(
|
||||
bytes: &[u8],
|
||||
version: &Version,
|
||||
ecLevel: ErrorCorrectionLevel,
|
||||
) -> Result<DecoderResult<bool>> {
|
||||
let mut bits = BitSource::new(bytes.to_vec());
|
||||
let mut result = ECIStringBuilder::default();
|
||||
// Error error;
|
||||
result.symbology = SymbologyIdentifier {
|
||||
code: b'Q',
|
||||
modifier: b'1',
|
||||
eciModifierOffset: 1,
|
||||
aiFlag: AIFlag::None,
|
||||
}; //{'Q', '1', 1};
|
||||
let mut structuredAppend = StructuredAppendInfo::default();
|
||||
let modeBitLength = Mode::get_codec_mode_bits_length(version);
|
||||
|
||||
// try
|
||||
// {
|
||||
// while(!IsEndOfStream(bits, version)) {
|
||||
// CodecMode mode;
|
||||
// if (modeBitLength == 0)
|
||||
// mode = CodecMode::NUMERIC; // MicroQRCode version 1 is always NUMERIC and modeBitLength is 0
|
||||
// else
|
||||
// mode = CodecModeForBits(bits.readBits(modeBitLength), version.isMicroQRCode());
|
||||
let res = (|| {
|
||||
while (!IsEndOfStream(&mut bits, version)?) {
|
||||
let mode: Mode;
|
||||
if (modeBitLength == 0) {
|
||||
mode = Mode::NUMERIC; // MicroQRCode version 1 is always NUMERIC and modeBitLength is 0
|
||||
} else {
|
||||
mode = Mode::CodecModeForBits(
|
||||
bits.readBits(modeBitLength as usize)?,
|
||||
Some(version.isMicroQRCode()),
|
||||
);
|
||||
}
|
||||
|
||||
// switch (mode) {
|
||||
// case CodecMode::FNC1_FIRST_POSITION:
|
||||
// // if (!result.empty()) // uncomment to enforce specification
|
||||
// // throw FormatError("GS1 Indicator (FNC1 in first position) at illegal position");
|
||||
// result.symbology.modifier = '3';
|
||||
// result.symbology.aiFlag = AIFlag::GS1; // In Alphanumeric mode undouble doubled '%' and treat single '%' as <GS>
|
||||
// break;
|
||||
// case CodecMode::FNC1_SECOND_POSITION:
|
||||
// if (!result.empty())
|
||||
// throw FormatError("AIM Application Indicator (FNC1 in second position) at illegal position");
|
||||
// result.symbology.modifier = '5'; // As above
|
||||
// // ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator (FNC1 in second position), "00-99" or "A-Za-z"
|
||||
// if (int appInd = bits.readBits(8); appInd < 100) // "00-09"
|
||||
// result += ZXing::ToString(appInd, 2);
|
||||
// else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222)) // "A-Za-z"
|
||||
// result += narrow_cast<uint8_t>(appInd - 100);
|
||||
// else
|
||||
// throw FormatError("Invalid AIM Application Indicator");
|
||||
// result.symbology.aiFlag = AIFlag::AIM; // see also above
|
||||
// break;
|
||||
// case CodecMode::STRUCTURED_APPEND:
|
||||
// // sequence number and parity is added later to the result metadata
|
||||
// // Read next 4 bits of index, 4 bits of symbol count, and 8 bits of parity data, then continue
|
||||
// structuredAppend.index = bits.readBits(4);
|
||||
// structuredAppend.count = bits.readBits(4) + 1;
|
||||
// structuredAppend.id = std::to_string(bits.readBits(8));
|
||||
// break;
|
||||
// case CodecMode::ECI:
|
||||
// // Count doesn't apply to ECI
|
||||
// result.switchEncoding(ParseECIValue(bits));
|
||||
// break;
|
||||
// case CodecMode::HANZI: {
|
||||
// // First handle Hanzi mode which does not start with character count
|
||||
// // chinese mode contains a sub set indicator right after mode indicator
|
||||
// if (int subset = bits.readBits(4); subset != 1) // GB2312_SUBSET is the only supported one right now
|
||||
// throw FormatError("Unsupported HANZI subset");
|
||||
// int count = bits.readBits(CharacterCountBits(mode, version));
|
||||
// DecodeHanziSegment(bits, count, result);
|
||||
// break;
|
||||
// }
|
||||
// default: {
|
||||
// // "Normal" QR code modes:
|
||||
// // How many characters will follow, encoded in this mode?
|
||||
// int count = bits.readBits(CharacterCountBits(mode, version));
|
||||
// switch (mode) {
|
||||
// case CodecMode::NUMERIC: DecodeNumericSegment(bits, count, result); break;
|
||||
// case CodecMode::ALPHANUMERIC: DecodeAlphanumericSegment(bits, count, result); break;
|
||||
// case CodecMode::BYTE: DecodeByteSegment(bits, count, result); break;
|
||||
// case CodecMode::KANJI: DecodeKanjiSegment(bits, count, result); break;
|
||||
// default: throw FormatError("Invalid CodecMode");
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (std::out_of_range& e) { // see BitSource::readBits
|
||||
// error = FormatError("Truncated bit stream");
|
||||
// } catch (Error e) {
|
||||
// error = std::move(e);
|
||||
// }
|
||||
match (mode) {
|
||||
Mode::FNC1_FIRST_POSITION => {
|
||||
// if (!result.empty()) // uncomment to enforce specification
|
||||
// throw FormatError("GS1 Indicator (FNC1 in first position) at illegal position");
|
||||
result.symbology.modifier = b'3';
|
||||
result.symbology.aiFlag = AIFlag::GS1; // In Alphanumeric mode undouble doubled '%' and treat single '%' as <GS>
|
||||
}
|
||||
Mode::FNC1_SECOND_POSITION => {
|
||||
if (!result.is_empty()) {
|
||||
return Err(Exceptions::format_with("AIM Application Indicator (FNC1 in second position) at illegal position"));
|
||||
// throw FormatError("AIM Application Indicator (FNC1 in second position) at illegal position");
|
||||
}
|
||||
result.symbology.modifier = b'5'; // As above
|
||||
// ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator (FNC1 in second position), "00-99" or "A-Za-z"
|
||||
let appInd = bits.readBits(8)?;
|
||||
if (appInd < 100)
|
||||
// "00-09"
|
||||
{
|
||||
result +=
|
||||
crate::common::cpp_essentials::util::ToString(appInd as usize, 2)?;
|
||||
} else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222))
|
||||
// "A-Za-z"
|
||||
{
|
||||
result += (appInd - 100) as u8;
|
||||
} else {
|
||||
return Err(Exceptions::format_with("Invalid AIM Application Indicator"));
|
||||
// throw FormatError("Invalid AIM Application Indicator");
|
||||
}
|
||||
result.symbology.aiFlag = AIFlag::AIM; // see also above
|
||||
}
|
||||
Mode::STRUCTURED_APPEND => {
|
||||
// sequence number and parity is added later to the result metadata
|
||||
// Read next 4 bits of index, 4 bits of symbol count, and 8 bits of parity data, then continue
|
||||
structuredAppend.index = bits.readBits(4)? as i32;
|
||||
structuredAppend.count = bits.readBits(4)? as i32 + 1;
|
||||
structuredAppend.id = (bits.readBits(8)?).to_string(); //std::to_string(bits.readBits(8));
|
||||
}
|
||||
Mode::ECI => {
|
||||
// Count doesn't apply to ECI
|
||||
result.switch_encoding(ParseECIValue(&mut bits)?.into());
|
||||
}
|
||||
Mode::HANZI => {
|
||||
// First handle Hanzi mode which does not start with character count
|
||||
// chinese mode contains a sub set indicator right after mode indicator
|
||||
let subset = bits.readBits(4)?;
|
||||
if (subset != 1)
|
||||
// GB2312_SUBSET is the only supported one right now
|
||||
{
|
||||
return Err(Exceptions::format_with("Unsupported HANZI subset"));
|
||||
// throw FormatError("Unsupported HANZI subset");
|
||||
}
|
||||
let count = bits.readBits(mode.CharacterCountBits(version) as usize)?;
|
||||
DecodeHanziSegment(&mut bits, count, &mut result);
|
||||
}
|
||||
_ => {
|
||||
// "Normal" QR code modes:
|
||||
// How many characters will follow, encoded in this mode?
|
||||
let count = bits.readBits(mode.CharacterCountBits(version) as usize)?;
|
||||
match (mode) {
|
||||
Mode::NUMERIC => DecodeNumericSegment(&mut bits, count, &mut result),
|
||||
Mode::ALPHANUMERIC => {
|
||||
DecodeAlphanumericSegment(&mut bits, count, &mut result)
|
||||
}
|
||||
Mode::BYTE => DecodeByteSegment(&mut bits, count, &mut result),
|
||||
Mode::KANJI => DecodeKanjiSegment(&mut bits, count, &mut result),
|
||||
_ => return Err(Exceptions::format_with("Invalid CodecMode")), //throw FormatError("Invalid CodecMode");
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
// } catch (std::out_of_range& e) { // see BitSource::readBits
|
||||
// error = FormatError("Truncated bit stream");
|
||||
// } catch (Error e) {
|
||||
// error = std::move(e);
|
||||
// }
|
||||
|
||||
// return DecoderResult(std::move(result))
|
||||
// .setError(std::move(error))
|
||||
// .setEcLevel(ToString(ecLevel))
|
||||
// .setVersionNumber(version.versionNumber())
|
||||
// .setStructuredAppend(structuredAppend);
|
||||
// }
|
||||
Ok(DecoderResult::with_eci_string_builder(result)
|
||||
.withEcLevel(ecLevel.to_string())
|
||||
.withVersionNumber(version.getVersionNumber())
|
||||
.withStructuredAppend(structuredAppend))
|
||||
|
||||
// DecoderResult Decode(const BitMatrix& bits)
|
||||
// {
|
||||
// const Version* pversion = ReadVersion(bits);
|
||||
// if (!pversion)
|
||||
// return FormatError("Invalid version");
|
||||
// const Version& version = *pversion;
|
||||
// return DecoderResult(std::move(result))
|
||||
// .setError(std::move(error))
|
||||
// .setEcLevel(ToString(ecLevel))
|
||||
// .setVersionNumber(version.versionNumber())
|
||||
// .setStructuredAppend(structuredAppend);
|
||||
}
|
||||
|
||||
// auto formatInfo = ReadFormatInformation(bits, version.isMicroQRCode());
|
||||
// if (!formatInfo.isValid())
|
||||
// return FormatError("Invalid format information");
|
||||
pub fn Decode(bits: &BitMatrix) -> Result<DecoderResult<bool>> {
|
||||
let Ok(pversion) = ReadVersion(bits) else {
|
||||
return Err(Exceptions::format_with("Invalid version"))
|
||||
};
|
||||
let version = pversion;
|
||||
|
||||
// // Read codewords
|
||||
// ByteArray codewords = ReadCodewords(bits, version, formatInfo);
|
||||
// if (codewords.empty())
|
||||
// return FormatError("Failed to read codewords");
|
||||
let Ok(formatInfo) = ReadFormatInformation(bits, version.isMicroQRCode()) else {
|
||||
return Err(Exceptions::format_with("Invalid format information"))
|
||||
};
|
||||
|
||||
// // Separate into data blocks
|
||||
// std::vector<DataBlock> dataBlocks = DataBlock::GetDataBlocks(codewords, version, formatInfo.ecLevel);
|
||||
// if (dataBlocks.empty())
|
||||
// return FormatError("Failed to get data blocks");
|
||||
// Read codewords
|
||||
let codewords = ReadCodewords(bits, &version, &formatInfo)?;
|
||||
if (codewords.is_empty()) {
|
||||
return Err(Exceptions::format_with("Failed to read codewords"));
|
||||
}
|
||||
|
||||
// // Count total number of data bytes
|
||||
// const auto op = [](auto totalBytes, const auto& dataBlock){ return totalBytes + dataBlock.numDataCodewords();};
|
||||
// const auto totalBytes = std::accumulate(std::begin(dataBlocks), std::end(dataBlocks), int{}, op);
|
||||
// ByteArray resultBytes(totalBytes);
|
||||
// auto resultIterator = resultBytes.begin();
|
||||
// Separate into data blocks
|
||||
let dataBlocks: Vec<DataBlock> =
|
||||
DataBlock::getDataBlocks(&codewords, &version, formatInfo.error_correction_level)?;
|
||||
if (dataBlocks.is_empty()) {
|
||||
return Err(Exceptions::format_with("Failed to get data blocks"));
|
||||
}
|
||||
|
||||
// // Error-correct and copy data blocks together into a stream of bytes
|
||||
// for (auto& dataBlock : dataBlocks)
|
||||
// {
|
||||
// ByteArray& codewordBytes = dataBlock.codewords();
|
||||
// int numDataCodewords = dataBlock.numDataCodewords();
|
||||
// Count total number of data bytes
|
||||
let op = |totalBytes, dataBlock: &DataBlock| totalBytes + dataBlock.getNumDataCodewords();
|
||||
let totalBytes = dataBlocks.iter().fold(0, op); // std::accumulate(std::begin(dataBlocks), std::end(dataBlocks), int{}, op);
|
||||
let mut resultBytes = vec![0u8; totalBytes as usize];
|
||||
let mut resultIterator = 0; //resultBytes.begin();
|
||||
|
||||
// if (!CorrectErrors(codewordBytes, numDataCodewords))
|
||||
// return ChecksumError();
|
||||
// Error-correct and copy data blocks together into a stream of bytes
|
||||
for dataBlock in dataBlocks.iter() {
|
||||
let mut codewordBytes = dataBlock.getCodewords().to_vec();
|
||||
let numDataCodewords = dataBlock.getNumDataCodewords() as usize;
|
||||
|
||||
// resultIterator = std::copy_n(codewordBytes.begin(), numDataCodewords, resultIterator);
|
||||
// }
|
||||
if (!CorrectErrors(&mut codewordBytes, numDataCodewords as u32)?) {
|
||||
return Err(Exceptions::CHECKSUM);
|
||||
}
|
||||
|
||||
// // Decode the contents of that stream of bytes
|
||||
// return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel).setIsMirrored(formatInfo.isMirrored);
|
||||
// }
|
||||
// resultIterator = std::copy_n(codewordBytes.begin(), numDataCodewords, resultIterator);
|
||||
resultBytes[resultIterator..numDataCodewords]
|
||||
.copy_from_slice(&codewordBytes[..numDataCodewords]);
|
||||
resultIterator += numDataCodewords;
|
||||
}
|
||||
|
||||
// Decode the contents of that stream of bytes
|
||||
Ok(
|
||||
DecodeBitStream(&resultBytes, &version, formatInfo.error_correction_level)?
|
||||
.withIsMirrored(formatInfo.isMirrored),
|
||||
)
|
||||
}
|
||||
|
||||
// } // namespace ZXing::QRCode
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
pub mod decoder;
|
||||
pub mod detector;
|
||||
|
||||
mod bitmatrix_parser;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use std::fmt::{self, Display};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::common::Result;
|
||||
@@ -118,3 +119,15 @@ impl FromStr for ErrorCorrectionLevel {
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ErrorCorrectionLevel {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
ErrorCorrectionLevel::L => "L",
|
||||
ErrorCorrectionLevel::M => "M",
|
||||
ErrorCorrectionLevel::Q => "Q",
|
||||
ErrorCorrectionLevel::H => "H",
|
||||
ErrorCorrectionLevel::Invalid => "_",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ pub struct FormatInformation {
|
||||
pub error_correction_level: ErrorCorrectionLevel,
|
||||
pub data_mask: u8,
|
||||
pub microVersion: u32,
|
||||
pub isMirrored: bool,
|
||||
}
|
||||
|
||||
impl Default for FormatInformation {
|
||||
@@ -81,6 +82,7 @@ impl Default for FormatInformation {
|
||||
error_correction_level: ErrorCorrectionLevel::Invalid,
|
||||
data_mask: Default::default(),
|
||||
microVersion: 0,
|
||||
isMirrored: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,6 +98,7 @@ impl FormatInformation {
|
||||
microVersion: 0,
|
||||
error_correction_level: errorCorrectionLevel,
|
||||
data_mask: dataMask,
|
||||
isMirrored: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,32 @@ impl Mode {
|
||||
Mode::HANZI => 0x0D,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn get_terminator_bit_length(version: &Version) -> u8 {
|
||||
todo!()
|
||||
}
|
||||
pub const fn get_codec_mode_bits_length(version: &Version) -> u8 {
|
||||
todo!()
|
||||
}
|
||||
/**
|
||||
* @param bits variable number of bits encoding a QR Code data mode
|
||||
* @param isMicro is this a MicroQRCode
|
||||
* @return Mode encoded by these bits
|
||||
* @throws FormatError if bits do not correspond to a known mode
|
||||
*/
|
||||
pub fn CodecModeForBits(bits: u32, isMicro: Option<bool>) -> Self {
|
||||
let isMicro = isMicro.unwrap_or(false);
|
||||
todo!()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param version version in question
|
||||
* @return number of bits used, in this QR Code symbol {@link Version}, to encode the
|
||||
* count of characters that will follow encoded in this Mode
|
||||
*/
|
||||
pub fn CharacterCountBits(&self, version: &Version) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mode> for u8 {
|
||||
|
||||
Reference in New Issue
Block a user