From 0bbf2ce2ad23a15b737512a007a3a991380ff918 Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Tue, 3 May 2022 21:52:50 +0530 Subject: [PATCH 001/180] cmake: Install pkgconfig file in mingw --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d01ee88b4..b9052cac91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,7 +109,7 @@ if (NOT DEFINED INSTALLDIR) get_filename_component(INSTALLDIR ${INSTALLDIR} ABSOLUTE) endif() -IF (NOT WIN32) +IF (NOT WIN32 OR MINGW) configure_file(zxing.pc.in zxing.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/zxing.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) ENDIF() From d6d13e4da49b231d046a30673de87b3aa71024e7 Mon Sep 17 00:00:00 2001 From: ptc-brainer <69149788+ptc-brainer@users.noreply.github.com> Date: Wed, 4 May 2022 16:18:05 +0200 Subject: [PATCH 002/180] Fixed comparison bug in MultiFormatReader.cpp Changed comparison of x position from lesser-or-equal to lesser which, in some cases, could result in invalid comparison result (i.e. a > b == b > a). Depending on the compiler and platform this can cause a crash if during sort, an object is tried to be moved onto itself. --- core/src/MultiFormatReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index 7f01e33ee7..391bd72584 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -90,7 +90,7 @@ Results MultiFormatReader::readMultiple(const BinaryBitmap& image, int maxSymbol std::sort(res.begin(), res.end(), [](const Result& l, const Result& r) { auto lp = l.position().topLeft(); auto rp = r.position().topLeft(); - return lp.y < rp.y || (lp.y == rp.y && lp.x <= rp.x); + return lp.y < rp.y || (lp.y == rp.y && lp.x < rp.x); }); return res; From bd7a9509075e9531261c65c180119a9c710034bf Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Wed, 4 May 2022 18:11:35 +0200 Subject: [PATCH 003/180] Generate the PDF417 ratio table at compile time This not only avoids the entire computation at runtime, it also moves the required memory from .bss (allocated at runtime per process) to .rodata (shared between all processes and only loaded on demand), cutting down the .bss section size by about 90%. --- core/src/pdf417/PDFCodewordDecoder.cpp | 15 +++++++-------- core/src/pdf417/PDFCodewordDecoder.h | 8 ++++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/core/src/pdf417/PDFCodewordDecoder.cpp b/core/src/pdf417/PDFCodewordDecoder.cpp index ec84e962ac..cddb572723 100644 --- a/core/src/pdf417/PDFCodewordDecoder.cpp +++ b/core/src/pdf417/PDFCodewordDecoder.cpp @@ -27,7 +27,7 @@ namespace ZXing { namespace Pdf417 { -static const int SYMBOL_COUNT = 2787; +static constexpr const int SYMBOL_COUNT = 2787; /** @@ -35,7 +35,7 @@ static const int SYMBOL_COUNT = 2787; * specification. The index of a symbol in this table corresponds to the * index into the codeword table. */ -const std::array SYMBOL_TABLE = { +static constexpr const std::array SYMBOL_TABLE = { 0x1025e, 0x1027a, 0x1029e, 0x102bc, 0x102f2, 0x102f4, 0x1032e, 0x1034e, 0x1035c, 0x10396, 0x103a6, 0x103ac, 0x10422, 0x10428, 0x10436, 0x10442, 0x10444, 0x10448, 0x10450, 0x1045e, 0x10466, 0x1046c, 0x1047a, 0x10482, 0x1049e, 0x104a0, 0x104bc, 0x104c6, 0x104d8, 0x104ee, 0x104f2, 0x104f4, 0x10504, 0x10508, 0x10510, 0x1051e, @@ -274,7 +274,7 @@ const std::array SYMBOL_TABLE = { /** * This table contains to codewords for all symbols. */ -static const std::array CODEWORD_TABLE = { +static constexpr const std::array CODEWORD_TABLE = { 2627, 1819, 2622, 2621, 1813, 1812, 2729, 2724, 2723, 2779, 2774, 2773, 902, 896, 908, 868, 865, 861, 859, 2511, 873, 871, 1780, 835, 2493, 825, 2491, 842, 837, 844, 1764, 1762, 811, 810, 809, 2483, 807, 2482, 806, 2480, 815, 814, 813, 812, 2484, 817, 816, 1745, 1744, 1742, 1746, 2655, 2637, 2635, 2626, 2625, 2623, 2628, 1820, 2752, @@ -423,7 +423,8 @@ using ModuleBitCountType = std::array; static const RatioTableType& GetRatioTable() { - auto initTable = [](RatioTableType& table) -> RatioTableType& { + static constexpr RatioTableType table{[]() constexpr { + RatioTableType table{}; for (int i = 0; i < SYMBOL_COUNT; i++) { int currentSymbol = SYMBOL_TABLE[i]; int currentBit = currentSymbol & 0x1; @@ -438,11 +439,9 @@ static const RatioTableType& GetRatioTable() } } return table; - }; + }()}; - static RatioTableType table; - static const auto& ref = initTable(table); - return ref; + return table; } static ModuleBitCountType SampleBitCounts(const ModuleBitCountType& moduleBitCount) diff --git a/core/src/pdf417/PDFCodewordDecoder.h b/core/src/pdf417/PDFCodewordDecoder.h index 12b63b5de8..5b8dc0a0ec 100644 --- a/core/src/pdf417/PDFCodewordDecoder.h +++ b/core/src/pdf417/PDFCodewordDecoder.h @@ -28,13 +28,13 @@ namespace Pdf417 { class CodewordDecoder { public: - static const int NUMBER_OF_CODEWORDS = 929; + static constexpr const int NUMBER_OF_CODEWORDS = 929; // Maximum Codewords (Data + Error). - static const int MAX_CODEWORDS_IN_BARCODE = NUMBER_OF_CODEWORDS - 1; + static constexpr const int MAX_CODEWORDS_IN_BARCODE = NUMBER_OF_CODEWORDS - 1; // One left row indication column + max 30 data columns + one right row indicator column //public static final int MAX_CODEWORDS_IN_ROW = 32; - static const int MODULES_IN_CODEWORD = 17; - static const int BARS_IN_MODULE = 8; + static constexpr const int MODULES_IN_CODEWORD = 17; + static constexpr const int BARS_IN_MODULE = 8; /** * @param symbol encoded symbol to translate to a codeword From edd19499e1fb5f6a24b16243d68346b0a95adf88 Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Thu, 5 May 2022 18:31:44 +0200 Subject: [PATCH 004/180] Generate the DataMatrix PROD_SYMBOLS table at compilation time Avoids runtime code and moves the needed memory to the .rodata section. --- core/src/datamatrix/DMSymbolInfo.cpp | 2 +- core/src/datamatrix/DMSymbolInfo.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/datamatrix/DMSymbolInfo.cpp b/core/src/datamatrix/DMSymbolInfo.cpp index 344a0d4832..3aac399de0 100644 --- a/core/src/datamatrix/DMSymbolInfo.cpp +++ b/core/src/datamatrix/DMSymbolInfo.cpp @@ -26,7 +26,7 @@ namespace ZXing::DataMatrix { -static const SymbolInfo PROD_SYMBOLS[] = { +static constexpr const SymbolInfo PROD_SYMBOLS[] = { { false, 3, 5, 8, 8, 1 }, { false, 5, 7, 10, 10, 1 }, { true, 5, 7, 16, 6, 1 }, diff --git a/core/src/datamatrix/DMSymbolInfo.h b/core/src/datamatrix/DMSymbolInfo.h index feb5276b67..90756f0d9d 100644 --- a/core/src/datamatrix/DMSymbolInfo.h +++ b/core/src/datamatrix/DMSymbolInfo.h @@ -33,10 +33,10 @@ class SymbolInfo int _rsBlockError; public: - SymbolInfo(bool rectangular, int dataCapacity, int errorCodewords, int matrixWidth, int matrixHeight, int dataRegions) : + constexpr SymbolInfo(bool rectangular, int dataCapacity, int errorCodewords, int matrixWidth, int matrixHeight, int dataRegions) : SymbolInfo(rectangular, dataCapacity, errorCodewords, matrixWidth, matrixHeight, dataRegions, dataCapacity, errorCodewords) {} - SymbolInfo(bool rectangular, int dataCapacity, int errorCodewords, int matrixWidth, int matrixHeight, int dataRegions, int rsBlockData, int rsBlockError) : + constexpr SymbolInfo(bool rectangular, int dataCapacity, int errorCodewords, int matrixWidth, int matrixHeight, int dataRegions, int rsBlockData, int rsBlockError) : _rectangular(rectangular), _dataCapacity(dataCapacity), _errorCodewords(errorCodewords), _matrixWidth(matrixWidth), _matrixHeight(matrixHeight), _dataRegions(dataRegions), _rsBlockData(rsBlockData), _rsBlockError(rsBlockError) From bec670de591e83406159f40ef3c05f820c9b883a Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 6 May 2022 13:38:45 +0200 Subject: [PATCH 005/180] style: experiment with clang-format / random indentation fixes Two goals: a) come closer to what the current code base looks like b) make up my mind about what I prefer This is a WIP and definitively not globally applicable, yet. This partly fixes inconsistencies with what was already established style. --- .clang-format | 12 ++- core/src/BitArray.h | 85 +++++++++---------- core/src/BitMatrix.h | 55 ++++++------ core/src/BitMatrixCursor.h | 2 +- core/src/DecodeHints.h | 39 +++++---- core/src/DecoderResult.h | 3 +- core/src/DetectorResult.h | 4 +- core/src/ImageView.h | 10 ++- core/src/Result.cpp | 24 ++++-- core/src/Result.h | 56 +++--------- core/src/aztec/AZHighLevelEncoder.cpp | 12 +-- core/src/datamatrix/DMDetector.cpp | 6 +- core/src/datamatrix/DMHighLevelEncoder.cpp | 9 +- core/src/datamatrix/DMReader.cpp | 13 ++- core/src/datamatrix/DMSymbolInfo.h | 77 +++++++---------- core/src/oned/ODCode128Writer.cpp | 19 +++-- core/src/oned/ODMultiUPCEANReader.cpp | 27 +++--- .../oned/rss/ODRSSExpandedBinaryDecoder.cpp | 38 +++------ .../src/oned/rss/ODRSSGenericAppIdDecoder.cpp | 6 +- core/src/pdf417/PDFCodewordDecoder.cpp | 4 +- core/src/qrcode/QRDataBlock.h | 12 +-- core/src/qrcode/QRDecoder.cpp | 54 +++++------- core/src/qrcode/QRDetector.cpp | 6 +- core/src/qrcode/QRECB.h | 17 ++-- core/src/qrcode/QREncoder.cpp | 22 ++--- core/src/qrcode/QRErrorCorrectionLevel.cpp | 9 +- core/src/qrcode/QRErrorCorrectionLevel.h | 10 +-- core/src/qrcode/QRFormatInformation.h | 11 +-- core/src/qrcode/QRMaskUtil.cpp | 23 ++--- core/src/qrcode/QRMaskUtil.h | 10 ++- core/src/qrcode/QRMatrixUtil.cpp | 6 +- core/src/qrcode/QRMatrixUtil.h | 3 +- core/src/qrcode/QRReader.cpp | 8 +- core/src/qrcode/QRVersion.cpp | 16 ++-- core/src/qrcode/QRVersion.h | 26 ++---- core/src/qrcode/QRWriter.cpp | 22 +++-- 36 files changed, 330 insertions(+), 426 deletions(-) diff --git a/.clang-format b/.clang-format index 5520e59df9..51ba5e80f4 100644 --- a/.clang-format +++ b/.clang-format @@ -8,22 +8,26 @@ TabWidth: 4 UseTab: ForContinuationAndIndentation # ForIndentation AccessModifierOffset: -4 -BreakBeforeBraces: Mozilla -ColumnLimit: 120 +ColumnLimit: 135 #AlignConsecutiveAssignments: true +AlignConsecutiveBitFields: true AlignEscapedNewlines: DontAlign AlignTrailingComments: true -AllowShortCaseLabelsOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true AllowShortFunctionsOnASingleLine: Inline #AllowShortLambdasOnASingleLine: Inline +AllowShortEnumsOnASingleLine: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakTemplateDeclarations: Yes BreakBeforeBraces: Mozilla +BreakBeforeBinaryOperators: NonAssignment BreakBeforeTernaryOperators: true -ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true FixNamespaceComments: true IncludeBlocks: Regroup KeepEmptyLinesAtTheStartOfBlocks: false diff --git a/core/src/BitArray.h b/core/src/BitArray.h index 2fdb4a567e..490c0d6e6d 100644 --- a/core/src/BitArray.h +++ b/core/src/BitArray.h @@ -36,7 +36,8 @@ namespace ZXing { class ByteArray; template -struct Range { +struct Range +{ Iterator begin, end; explicit operator bool() const { return begin < end; } int size() const { return static_cast(end - begin); } @@ -129,20 +130,24 @@ class BitArray BitArray() = default; - explicit BitArray(int size) : + explicit BitArray(int size) + : #ifdef ZX_FAST_BIT_STORAGE - _bits(size, 0) {} + _bits(size, 0) {} #else - _size(size), _bits((size + 31) / 32, 0) {} + _size(size), + _bits((size + 31) / 32, 0) {} #endif - BitArray(BitArray&& other) noexcept : + BitArray(BitArray&& other) noexcept + : #ifndef ZX_FAST_BIT_STORAGE - _size(other._size), + _size(other._size), #endif - _bits(std::move(other._bits)) {} + _bits(std::move(other._bits)) {} - BitArray& operator=(BitArray&& other) noexcept { + BitArray& operator=(BitArray&& other) noexcept + { #ifndef ZX_FAST_BIT_STORAGE _size = other._size; #endif @@ -150,27 +155,25 @@ class BitArray return *this; } - BitArray copy() const { - return *this; - } + BitArray copy() const { return *this; } - int size() const noexcept { + int size() const noexcept + { #ifdef ZX_FAST_BIT_STORAGE return Size(_bits); #else return _size; #endif } - - int sizeInBytes() const noexcept { - return (size() + 7) / 8; - } + + int sizeInBytes() const noexcept { return (size() + 7) / 8; } /** * @param i bit to get * @return true iff bit i is set */ - bool get(int i) const { + bool get(int i) const + { #ifdef ZX_FAST_BIT_STORAGE return _bits.at(i) != 0; #else @@ -188,7 +191,8 @@ class BitArray Iterator end() const noexcept { return _bits.cend(); } template - static ITER getNextSetTo(ITER begin, ITER end, bool v) noexcept { + static ITER getNextSetTo(ITER begin, ITER end, bool v) noexcept + { while( begin != end && *begin != static_cast(v) ) ++begin; return begin; @@ -198,7 +202,8 @@ class BitArray Iterator begin() const noexcept { return iterAt(0); } Iterator end() const noexcept { return iterAt(_size); } - static Iterator getNextSetTo(Iterator begin, Iterator end, bool v) { + static Iterator getNextSetTo(Iterator begin, Iterator end, bool v) + { auto i = begin; // reconstruct _bits.end() auto bitsEnd = end._mask == 0x1 ? end._value : std::next(end._value); @@ -216,16 +221,15 @@ class BitArray return i; } - static ReverseIterator getNextSetTo(ReverseIterator begin, ReverseIterator end, bool v) { + static ReverseIterator getNextSetTo(ReverseIterator begin, ReverseIterator end, bool v) + { while( begin != end && *begin != v ) ++begin; return begin; } #endif - Iterator getNextSetTo(Iterator i, bool v) const { - return getNextSetTo(i, end(), v); - } + Iterator getNextSetTo(Iterator i, bool v) const { return getNextSetTo(i, end(), v); } Iterator getNextSet(Iterator i) const { return getNextSetTo(i, true); } Iterator getNextUnset(Iterator i) const { return getNextSetTo(i, false); } @@ -238,7 +242,8 @@ class BitArray * * @param i bit to set */ - void set(int i, bool val) { + void set(int i, bool val) + { #ifdef ZX_FAST_BIT_STORAGE _bits.at(i) = val; #else @@ -252,9 +257,7 @@ class BitArray /** * Clears all bits (sets to false). */ - void clearBits() { - std::fill(_bits.begin(), _bits.end(), 0); - } + void clearBits() { std::fill(_bits.begin(), _bits.end(), 0); } /** * Efficient method to check if a range of bits is set, or not set. @@ -266,7 +269,8 @@ class BitArray * @throws IllegalArgumentException if end is less than or equal to start */ #ifdef ZX_FAST_BIT_STORAGE - bool isRange(int start, int end, bool value) const { + bool isRange(int start, int end, bool value) const + { return std::all_of(std::begin(_bits) + start, std::begin(_bits) + end, [value](uint8_t v) { return v == static_cast(value); }); } #else @@ -276,7 +280,8 @@ class BitArray // Little helper method to make common isRange use case more readable. // Pass positive zone size to look for quiet zone after i and negative for zone in front of i. // Set allowClippedZone to false if clipping the zone at the image border is not acceptable. - bool hasQuietZone(Iterator i, int signedZoneSize, bool allowClippedZone = true) const { + bool hasQuietZone(Iterator i, int signedZoneSize, bool allowClippedZone = true) const + { int index = static_cast(i - begin()); if (signedZoneSize > 0) { if (!allowClippedZone && index + signedZoneSize >= size()) @@ -289,7 +294,8 @@ class BitArray } } - bool hasQuietZone(ReverseIterator i, int signedZoneSize, bool allowClippedZone = true) const { + bool hasQuietZone(ReverseIterator i, int signedZoneSize, bool allowClippedZone = true) const + { return hasQuietZone(i.base(), -signedZoneSize, allowClippedZone); } @@ -302,25 +308,20 @@ class BitArray * @param numBits bits from value to append */ #ifdef ZX_FAST_BIT_STORAGE - void appendBits(int value, int numBits) { + void appendBits(int value, int numBits) + { for (; numBits; --numBits) _bits.push_back((value >> (numBits-1)) & 1); } - void appendBit(bool bit) { - _bits.push_back(bit); - } + void appendBit(bool bit) { _bits.push_back(bit); } - void appendBitArray(const BitArray& other) { - _bits.insert(_bits.end(), other.begin(), other.end()); - } + void appendBitArray(const BitArray& other) { _bits.insert(_bits.end(), other.begin(), other.end()); } /** * Reverses all bits in the array. */ - void reverse() { - std::reverse(_bits.begin(), _bits.end()); - } + void reverse() { std::reverse(_bits.begin(), _bits.end()); } #else void appendBits(int value, int numBits); @@ -331,9 +332,7 @@ class BitArray /** * Reverses all bits in the array. */ - void reverse() { - BitHacks::Reverse(_bits, _bits.size() * 32 - _size); - } + void reverse() { BitHacks::Reverse(_bits, _bits.size() * 32 - _size); } #endif void bitwiseXOR(const BitArray& other); diff --git a/core/src/BitMatrix.h b/core/src/BitMatrix.h index 1569e885a5..01c5d51e03 100644 --- a/core/src/BitMatrix.h +++ b/core/src/BitMatrix.h @@ -81,14 +81,19 @@ class BitMatrix BitMatrix(int width, int height) : _width(width), _height(height), _rowSize(width), _bits(width * height, UNSET_V) {} #else - BitMatrix(int width, int height) : _width(width), _height(height), _rowSize((width + 31) / 32), _bits(((width + 31) / 32) * _height, 0) {} + BitMatrix(int width, int height) + : _width(width), _height(height), _rowSize((width + 31) / 32), _bits(((width + 31) / 32) * _height, 0) + {} #endif explicit BitMatrix(int dimension) : BitMatrix(dimension, dimension) {} // Construct a square matrix. - BitMatrix(BitMatrix&& other) noexcept : _width(other._width), _height(other._height), _rowSize(other._rowSize), _bits(std::move(other._bits)) {} + BitMatrix(BitMatrix&& other) noexcept + : _width(other._width), _height(other._height), _rowSize(other._rowSize), _bits(std::move(other._bits)) + {} - BitMatrix& operator=(BitMatrix&& other) noexcept { + BitMatrix& operator=(BitMatrix&& other) noexcept + { _width = other._width; _height = other._height; _rowSize = other._rowSize; @@ -96,13 +101,11 @@ class BitMatrix return *this; } - BitMatrix copy() const { - return *this; - } + BitMatrix copy() const { return *this; } #ifdef ZX_FAST_BIT_STORAGE // experimental iterator based access - template + template struct Row { iterator _begin, _end; @@ -120,7 +123,8 @@ class BitMatrix * @param y The vertical component (i.e. which row) * @return value of given bit in matrix */ - bool get(int x, int y) const { + bool get(int x, int y) const + { #ifdef ZX_FAST_BIT_STORAGE return isSet(get(y * _width + x)); #else @@ -134,7 +138,8 @@ class BitMatrix * @param x The horizontal component (i.e. which column) * @param y The vertical component (i.e. which row) */ - void set(int x, int y) { + void set(int x, int y) + { #ifdef ZX_FAST_BIT_STORAGE get(y * _width + x) = SET_V; #else @@ -142,7 +147,8 @@ class BitMatrix #endif } - void unset(int x, int y) { + void unset(int x, int y) + { #ifdef ZX_FAST_BIT_STORAGE get(y * _width + x) = UNSET_V; #else @@ -150,7 +156,8 @@ class BitMatrix #endif } - void set(int x, int y, bool val) { + void set(int x, int y, bool val) + { #ifdef ZX_FAST_BIT_STORAGE get(y * _width + x) = val ? SET_V : UNSET_V; #else @@ -164,7 +171,8 @@ class BitMatrix * @param x The horizontal component (i.e. which column) * @param y The vertical component (i.e. which row) */ - void flip(int x, int y) { + void flip(int x, int y) + { #ifdef ZX_FAST_BIT_STORAGE auto& v =get(y * _width + x); v = !v; @@ -173,7 +181,8 @@ class BitMatrix #endif } - void flipAll() { + void flipAll() + { for (auto& i : _bits) { i = ~i; } @@ -182,9 +191,7 @@ class BitMatrix /** * Clears all bits (sets to false). */ - void clear() { - std::fill(_bits.begin(), _bits.end(), 0); - } + void clear() { std::fill(_bits.begin(), _bits.end(), 0); } /** *

Sets a square region of the bit matrix to true.

@@ -241,27 +248,19 @@ class BitMatrix /** * @return The width of the matrix */ - int width() const { - return _width; - } + int width() const { return _width; } /** * @return The height of the matrix */ - int height() const { - return _height; - } + int height() const { return _height; } /** * @return The row size of the matrix. That is the number of 32-bits blocks that one row takes. */ - int rowSize() const { - return _rowSize; - } + int rowSize() const { return _rowSize; } - bool empty() const { - return _bits.empty(); - } + bool empty() const { return _bits.empty(); } friend bool operator==(const BitMatrix& a, const BitMatrix& b) { diff --git a/core/src/BitMatrixCursor.h b/core/src/BitMatrixCursor.h index a7a98e1b61..a8f3f0b6b6 100644 --- a/core/src/BitMatrixCursor.h +++ b/core/src/BitMatrixCursor.h @@ -21,7 +21,7 @@ namespace ZXing { -enum class Direction {LEFT = -1, RIGHT = 1}; +enum class Direction { LEFT = -1, RIGHT = 1 }; inline Direction opposite(Direction dir) noexcept { diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index d4ef5e6e32..8014bee4e5 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -49,31 +49,38 @@ enum class EanAddOnSymbol : unsigned char // see above class DecodeHints { - bool _tryHarder : 1; - bool _tryRotate : 1; - bool _tryDownscale : 1; - bool _isPure : 1; - bool _tryCode39ExtendedMode : 1; - bool _validateCode39CheckSum : 1; - bool _validateITFCheckSum : 1; - bool _returnCodabarStartEnd : 1; - Binarizer _binarizer : 2; + bool _tryHarder : 1; + bool _tryRotate : 1; + bool _tryDownscale : 1; + bool _isPure : 1; + bool _tryCode39ExtendedMode : 1; + bool _validateCode39CheckSum : 1; + bool _validateITFCheckSum : 1; + bool _returnCodabarStartEnd : 1; + Binarizer _binarizer : 2; EanAddOnSymbol _eanAddOnSymbol : 2; std::string _characterSet; std::vector _allowedLengths; - BarcodeFormats _formats = BarcodeFormat::None; + BarcodeFormats _formats = BarcodeFormat::None; uint16_t _downscaleThreshold = 500; - uint8_t _downscaleFactor = 3; - uint8_t _minLineCount = 2; - uint8_t _maxNumberOfSymbols = 0xff; + uint8_t _downscaleFactor = 3; + uint8_t _minLineCount = 2; + uint8_t _maxNumberOfSymbols = 0xff; public: // bitfields don't get default initialized to 0. DecodeHints() - : _tryHarder(1), _tryRotate(1), _tryDownscale(1), _isPure(0), _tryCode39ExtendedMode(0), - _validateCode39CheckSum(0), _validateITFCheckSum(0), _returnCodabarStartEnd(0), - _binarizer(Binarizer::LocalAverage), _eanAddOnSymbol(EanAddOnSymbol::Ignore) + : _tryHarder(1), + _tryRotate(1), + _tryDownscale(1), + _isPure(0), + _tryCode39ExtendedMode(0), + _validateCode39CheckSum(0), + _validateITFCheckSum(0), + _returnCodabarStartEnd(0), + _binarizer(Binarizer::LocalAverage), + _eanAddOnSymbol(EanAddOnSymbol::Ignore) {} #define ZX_PROPERTY(TYPE, GETTER, SETTER) \ diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 30abc8c772..ea158eff18 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -56,7 +56,8 @@ class DecoderResult public: DecoderResult(DecodeStatus status) : _status(status) {} - DecoderResult(ByteArray&& rawBytes, std::wstring&& text) : _rawBytes(std::move(rawBytes)), _text(std::move(text)) { + DecoderResult(ByteArray&& rawBytes, std::wstring&& text) : _rawBytes(std::move(rawBytes)), _text(std::move(text)) + { _numBits = 8 * Size(_rawBytes); } diff --git a/core/src/DetectorResult.h b/core/src/DetectorResult.h index a3549c0505..67db3b6b12 100644 --- a/core/src/DetectorResult.h +++ b/core/src/DetectorResult.h @@ -41,9 +41,7 @@ class DetectorResult DetectorResult(DetectorResult&&) = default; DetectorResult& operator=(DetectorResult&&) = default; - DetectorResult(BitMatrix&& bits, QuadrilateralI&& position) - : _bits(std::move(bits)), _position(std::move(position)) - {} + DetectorResult(BitMatrix&& bits, QuadrilateralI&& position) : _bits(std::move(bits)), _position(std::move(position)) {} const BitMatrix& bits() const & { return _bits; } BitMatrix&& bits() && { return std::move(_bits); } diff --git a/core/src/ImageView.h b/core/src/ImageView.h index ec72d18ae2..7d8058d7cd 100644 --- a/core/src/ImageView.h +++ b/core/src/ImageView.h @@ -67,8 +67,12 @@ class ImageView * @param pixStride optional pixel stride in bytes, default is calculated from format */ ImageView(const uint8_t* data, int width, int height, ImageFormat format, int rowStride = 0, int pixStride = 0) - : _data(data), _format(format), _width(width), _height(height), - _pixStride(pixStride ? pixStride : PixStride(format)), _rowStride(rowStride ? rowStride : width * _pixStride) + : _data(data), + _format(format), + _width(width), + _height(height), + _pixStride(pixStride ? pixStride : PixStride(format)), + _rowStride(rowStride ? rowStride : width * _pixStride) {} int width() const { return _width; } @@ -91,7 +95,7 @@ class ImageView ImageView rotated(int degree) const { switch ((degree + 360) % 360) { - case 90: return {data(0, _height - 1), _height, _width, _format, _pixStride, -_rowStride}; + case 90: return {data(0, _height - 1), _height, _width, _format, _pixStride, -_rowStride}; case 180: return {data(_width - 1, _height - 1), _width, _height, _format, -_rowStride, -_pixStride}; case 270: return {data(_width - 1, 0), _height, _width, _format, -_pixStride, _rowStride}; } diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 19ef576fac..0aad42a4ec 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -27,8 +27,13 @@ namespace ZXing { Result::Result(std::wstring&& text, Position&& position, BarcodeFormat format, std::string&& symbologyIdentifier, ByteArray&& rawBytes, StructuredAppendInfo&& sai, const bool readerInit, int lineCount) - : _format(format), _text(std::move(text)), _position(std::move(position)), _rawBytes(std::move(rawBytes)), - _symbologyIdentifier(std::move(symbologyIdentifier)), _sai(std::move(sai)), _readerInit(readerInit), + : _format(format), + _text(std::move(text)), + _position(std::move(position)), + _rawBytes(std::move(rawBytes)), + _symbologyIdentifier(std::move(symbologyIdentifier)), + _sai(std::move(sai)), + _readerInit(readerInit), _lineCount(lineCount) { _numBits = Size(_rawBytes) * 8; @@ -41,13 +46,18 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor {} Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format) - : _status(decodeResult.errorCode()), _format(format), _text(std::move(decodeResult).text()), - _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), - _ecLevel(decodeResult.ecLevel()), _symbologyIdentifier(decodeResult.symbologyIdentifier()), - _sai(decodeResult.structuredAppend()), _isMirrored(decodeResult.isMirrored()), + : _status(decodeResult.errorCode()), + _format(format), + _text(std::move(decodeResult).text()), + _position(std::move(position)), + _rawBytes(std::move(decodeResult).rawBytes()), + _numBits(decodeResult.numBits()), + _ecLevel(decodeResult.ecLevel()), + _symbologyIdentifier(decodeResult.symbologyIdentifier()), + _sai(decodeResult.structuredAppend()), + _isMirrored(decodeResult.isMirrored()), _readerInit(decodeResult.readerInit()) { - // TODO: add type opaque and code specific 'extra data'? (see DecoderResult::extra()) } diff --git a/core/src/Result.h b/core/src/Result.h index c44c95455c..1d90b835e3 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -52,56 +52,34 @@ class Result Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format); - bool isValid() const { - return StatusIsOK(_status); - } + bool isValid() const { return StatusIsOK(_status); } - DecodeStatus status() const { - return _status; - } + DecodeStatus status() const { return _status; } - BarcodeFormat format() const { - return _format; - } + BarcodeFormat format() const { return _format; } - const std::wstring& text() const { - return _text; - } + const std::wstring& text() const { return _text; } - const Position& position() const { - return _position; - } - void setPosition(Position pos) { - _position = pos; - } + const Position& position() const { return _position; } + void setPosition(Position pos) { _position = pos; } int orientation() const; //< orientation of barcode in degree, see also Position::orientation() /** * @brief isMirrored is the symbol mirrored (currently only supported by QRCode and DataMatrix) */ - bool isMirrored() const { - return _isMirrored; - } + bool isMirrored() const { return _isMirrored; } - const ByteArray& rawBytes() const { - return _rawBytes; - } + const ByteArray& rawBytes() const { return _rawBytes; } - int numBits() const { - return _numBits; - } + int numBits() const { return _numBits; } - const std::wstring& ecLevel() const { - return _ecLevel; - } + const std::wstring& ecLevel() const { return _ecLevel; } /** * @brief symbologyIdentifier Symbology identifier "]cm" where "c" is symbology code character, "m" the modifier. */ - const std::string& symbologyIdentifier() const { - return _symbologyIdentifier; - } + const std::string& symbologyIdentifier() const { return _symbologyIdentifier; } /** * @brief sequenceSize number of symbols in a structured append sequence. @@ -132,19 +110,13 @@ class Result /** * @brief readerInit Set if Reader Initialisation/Programming symbol. */ - bool readerInit() const { - return _readerInit; - } + bool readerInit() const { return _readerInit; } /** * @brief How many lines have been detected with this code (applies only to 1D symbologies) */ - int lineCount() const { - return _lineCount; - } - void incrementLineCount() { - ++_lineCount; - } + int lineCount() const { return _lineCount; } + void incrementLineCount() { ++_lineCount; } bool operator==(const Result& o) const; diff --git a/core/src/aztec/AZHighLevelEncoder.cpp b/core/src/aztec/AZHighLevelEncoder.cpp index f27bac3b5f..d307e28229 100644 --- a/core/src/aztec/AZHighLevelEncoder.cpp +++ b/core/src/aztec/AZHighLevelEncoder.cpp @@ -95,18 +95,14 @@ static const std::array, 5>& InitCharMap() charmap[MODE_DIGIT][','] = 12; charmap[MODE_DIGIT]['.'] = 13; const int8_t mixedTable[] = { - 0x00, 0x20, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, - 0x0b, 0x0c, 0x0d, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x40, 0x5c, 0x5e, - 0x5f, 0x60, 0x7c, 0x7d, 0x7f, + 0x00, 0x20, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x40, 0x5c, 0x5e, 0x5f, 0x60, 0x7c, 0x7d, 0x7f, }; for (uint8_t i = 0; i < Size(mixedTable); i++) { charmap[MODE_MIXED][mixedTable[i]] = i; } - const char punctTable[] = { - '\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'', - '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', - '[', ']', '{', '}' - }; + const char punctTable[] = {'\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'', '(', ')', '*', + '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', ']', '{', '}'}; for (uint8_t i = 0; i < Size(punctTable); i++) { if (punctTable[i] > 0) { charmap[MODE_PUNCT][punctTable[i]] = i; diff --git a/core/src/datamatrix/DMDetector.cpp b/core/src/datamatrix/DMDetector.cpp index 68cc5fff5f..10df1a6691 100644 --- a/core/src/datamatrix/DMDetector.cpp +++ b/core/src/datamatrix/DMDetector.cpp @@ -863,9 +863,9 @@ static DetectorResult DetectPure(const BitMatrix& image) auto modSizeY = float(height) / dimR; auto modSize = (modSizeX + modSizeY) / 2; - if (dimT % 2 != 0 || dimR % 2 != 0 || dimT < 10 || dimT > 144 || dimR < 8 || dimR > 144 || - std::abs(modSizeX - modSizeY) > 1 || - !image.isIn(PointF{left + modSizeX / 2 + (dimT - 1) * modSize, top + modSizeY / 2 + (dimR - 1) * modSize})) + if (dimT % 2 != 0 || dimR % 2 != 0 || dimT < 10 || dimT > 144 || dimR < 8 || dimR > 144 + || std::abs(modSizeX - modSizeY) > 1 + || !image.isIn(PointF{left + modSizeX / 2 + (dimT - 1) * modSize, top + modSizeY / 2 + (dimR - 1) * modSize})) return {}; int right = left + width - 1; diff --git a/core/src/datamatrix/DMHighLevelEncoder.cpp b/core/src/datamatrix/DMHighLevelEncoder.cpp index 0b43f6a7eb..627750909e 100644 --- a/core/src/datamatrix/DMHighLevelEncoder.cpp +++ b/core/src/datamatrix/DMHighLevelEncoder.cpp @@ -168,7 +168,8 @@ static int LookAheadTest(const std::string& msg, size_t startpos, int currentMod //step K if ((startpos + charsProcessed) == msg.length()) { int min = std::numeric_limits::max(); - std::transform(charCounts.begin(), charCounts.end(), intCharCounts.begin(), [](float x) { return static_cast(std::ceil(x)); }); + std::transform(charCounts.begin(), charCounts.end(), intCharCounts.begin(), + [](float x) { return static_cast(std::ceil(x)); }); min = FindMinimums(intCharCounts, min, mins); int minCount = Reduce(mins); @@ -260,7 +261,8 @@ static int LookAheadTest(const std::string& msg, size_t startpos, int currentMod //step R if (charsProcessed >= 4) { - std::transform(charCounts.begin(), charCounts.end(), intCharCounts.begin(), [](float x) { return static_cast(std::ceil(x)); }); + std::transform(charCounts.begin(), charCounts.end(), intCharCounts.begin(), + [](float x) { return static_cast(std::ceil(x)); }); FindMinimums(intCharCounts, std::numeric_limits::max(), mins); int minCount = Reduce(mins); @@ -421,7 +423,8 @@ namespace C40Encoder { return len; } - static int BacktrackOneCharacter(EncoderContext& context, std::string& buffer, std::string& removed, int lastCharSize, std::function encodeChar) + static int BacktrackOneCharacter(EncoderContext& context, std::string& buffer, std::string& removed, int lastCharSize, + std::function encodeChar) { buffer.resize(buffer.size() - lastCharSize); context.setCurrentPos(context.currentPos() - 1); diff --git a/core/src/datamatrix/DMReader.cpp b/core/src/datamatrix/DMReader.cpp index d134f778bb..5d6aad0a09 100644 --- a/core/src/datamatrix/DMReader.cpp +++ b/core/src/datamatrix/DMReader.cpp @@ -30,10 +30,11 @@ namespace ZXing::DataMatrix { Reader::Reader(const DecodeHints& hints) - : _tryRotate(hints.tryRotate()), _tryHarder(hints.tryHarder()), _isPure(hints.isPure()), + : _tryRotate(hints.tryRotate()), + _tryHarder(hints.tryHarder()), + _isPure(hints.isPure()), _characterSet(hints.characterSet()) -{ -} +{} /** * Locates and decodes a Data Matrix code in an image. @@ -47,16 +48,14 @@ Result Reader::decode(const BinaryBitmap& image) const { auto binImg = image.getBitMatrix(); - if (binImg == nullptr) { + if (binImg == nullptr) return Result(DecodeStatus::NotFound); - } auto detectorResult = Detect(*binImg, _tryHarder, _tryRotate, _isPure); if (!detectorResult.isValid()) return Result(DecodeStatus::NotFound); - return Result(Decode(detectorResult.bits(), _characterSet), - std::move(detectorResult).position(), BarcodeFormat::DataMatrix); + return Result(Decode(detectorResult.bits(), _characterSet), std::move(detectorResult).position(), BarcodeFormat::DataMatrix); } } // namespace ZXing::DataMatrix diff --git a/core/src/datamatrix/DMSymbolInfo.h b/core/src/datamatrix/DMSymbolInfo.h index 90756f0d9d..e033fbfb4e 100644 --- a/core/src/datamatrix/DMSymbolInfo.h +++ b/core/src/datamatrix/DMSymbolInfo.h @@ -33,15 +33,21 @@ class SymbolInfo int _rsBlockError; public: - constexpr SymbolInfo(bool rectangular, int dataCapacity, int errorCodewords, int matrixWidth, int matrixHeight, int dataRegions) : - SymbolInfo(rectangular, dataCapacity, errorCodewords, matrixWidth, matrixHeight, dataRegions, dataCapacity, errorCodewords) {} - - constexpr SymbolInfo(bool rectangular, int dataCapacity, int errorCodewords, int matrixWidth, int matrixHeight, int dataRegions, int rsBlockData, int rsBlockError) : - _rectangular(rectangular), _dataCapacity(dataCapacity), _errorCodewords(errorCodewords), - _matrixWidth(matrixWidth), _matrixHeight(matrixHeight), _dataRegions(dataRegions), - _rsBlockData(rsBlockData), _rsBlockError(rsBlockError) - { - } + constexpr SymbolInfo(bool rectangular, int dataCapacity, int errorCodewords, int matrixWidth, int matrixHeight, int dataRegions) + : SymbolInfo(rectangular, dataCapacity, errorCodewords, matrixWidth, matrixHeight, dataRegions, dataCapacity, errorCodewords) + {} + + constexpr SymbolInfo(bool rectangular, int dataCapacity, int errorCodewords, int matrixWidth, int matrixHeight, int dataRegions, + int rsBlockData, int rsBlockError) + : _rectangular(rectangular), + _dataCapacity(dataCapacity), + _errorCodewords(errorCodewords), + _matrixWidth(matrixWidth), + _matrixHeight(matrixHeight), + _dataRegions(dataRegions), + _rsBlockData(rsBlockData), + _rsBlockError(rsBlockError) + {} static const SymbolInfo* Lookup(int dataCodewords); static const SymbolInfo* Lookup(int dataCodewords, SymbolShape shape); @@ -52,57 +58,32 @@ class SymbolInfo int verticalDataRegions() const; - int symbolDataWidth() const { - return horizontalDataRegions() * _matrixWidth; - } + int symbolDataWidth() const { return horizontalDataRegions() * _matrixWidth; } - int symbolDataHeight() const { - return verticalDataRegions() * _matrixHeight; - } + int symbolDataHeight() const { return verticalDataRegions() * _matrixHeight; } - int symbolWidth() const { - return symbolDataWidth() + (horizontalDataRegions() * 2); - } + int symbolWidth() const { return symbolDataWidth() + (horizontalDataRegions() * 2); } - int symbolHeight() const { - return symbolDataHeight() + (verticalDataRegions() * 2); - } + int symbolHeight() const { return symbolDataHeight() + (verticalDataRegions() * 2); } - int matrixWidth() const { - return _matrixWidth; - } + int matrixWidth() const { return _matrixWidth; } - int matrixHeight() const { - return _matrixHeight; - } + int matrixHeight() const { return _matrixHeight; } - int codewordCount() const { - return _dataCapacity + _errorCodewords; - } + int codewordCount() const { return _dataCapacity + _errorCodewords; } - int interleavedBlockCount() const { - if (_rsBlockData > 0) - return _dataCapacity / _rsBlockData; - return 10; // Symbol 144 - } + int interleavedBlockCount() const { return _rsBlockData > 0 ? _dataCapacity / _rsBlockData : 10; /* Symbol 144 */ } - int dataCapacity() const { - return _dataCapacity; - } + int dataCapacity() const { return _dataCapacity; } - int errorCodewords() const { - return _errorCodewords; - } + int errorCodewords() const { return _errorCodewords; } - int dataLengthForInterleavedBlock(int index) const { - if (_rsBlockData > 0) - return _rsBlockData; - return index <= 8 ? 156 : 155; // Symbol 144 + int dataLengthForInterleavedBlock(int index) const + { + return _rsBlockData > 0 ? _rsBlockData : (index <= 8 ? 156 : 155); /* Symbol 144 */ } - int errorLengthForInterleavedBlock() const { - return _rsBlockError; - } + int errorLengthForInterleavedBlock() const { return _rsBlockError; } }; } // DataMatrix diff --git a/core/src/oned/ODCode128Writer.cpp b/core/src/oned/ODCode128Writer.cpp index 24a097e3bf..32133eac3b 100644 --- a/core/src/oned/ODCode128Writer.cpp +++ b/core/src/oned/ODCode128Writer.cpp @@ -30,10 +30,10 @@ namespace ZXing::OneD { static const int CODE_START_A = 103; static const int CODE_START_B = 104; static const int CODE_START_C = 105; -static const int CODE_CODE_A = 101; -static const int CODE_CODE_B = 100; -static const int CODE_CODE_C = 99; -static const int CODE_STOP = 106; +static const int CODE_CODE_A = 101; +static const int CODE_CODE_B = 100; +static const int CODE_CODE_C = 99; +static const int CODE_STOP = 106; // Dummy characters used to specify control characters in input static const auto ESCAPE_FNC_1 = L'\u00f1'; @@ -41,14 +41,15 @@ static const auto ESCAPE_FNC_2 = L'\u00f2'; static const auto ESCAPE_FNC_3 = L'\u00f3'; static const auto ESCAPE_FNC_4 = L'\u00f4'; -static const int CODE_FNC_1 = 102; // Code A, Code B, Code C -static const int CODE_FNC_2 = 97; // Code A, Code B -static const int CODE_FNC_3 = 96; // Code A, Code B +static const int CODE_FNC_1 = 102; // Code A, Code B, Code C +static const int CODE_FNC_2 = 97; // Code A, Code B +static const int CODE_FNC_3 = 96; // Code A, Code B static const int CODE_FNC_4_A = 101; // Code A static const int CODE_FNC_4_B = 100; // Code B - // Results of minimal lookahead for code C -enum class CType { +// Results of minimal lookahead for code C +enum class CType +{ UNCODABLE, ONE_DIGIT, TWO_DIGITS, diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index e567a07a84..21783f001d 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -38,15 +38,13 @@ MultiUPCEANReader::~MultiUPCEANReader() = default; constexpr int CHAR_LEN = 4; -constexpr auto END_PATTERN = FixedPattern<3, 3>{1, 1, 1}; -constexpr auto MID_PATTERN = FixedPattern<5, 5>{1, 1, 1, 1, 1}; -constexpr auto UPCE_END_PATTERN = FixedPattern<6, 6>{1, 1, 1, 1, 1, 1}; -constexpr auto EXT_START_PATTERN = FixedPattern<3, 4>{1, 1, 2}; +constexpr auto END_PATTERN = FixedPattern<3, 3>{1, 1, 1}; +constexpr auto MID_PATTERN = FixedPattern<5, 5>{1, 1, 1, 1, 1}; +constexpr auto UPCE_END_PATTERN = FixedPattern<6, 6>{1, 1, 1, 1, 1, 1}; +constexpr auto EXT_START_PATTERN = FixedPattern<3, 4>{1, 1, 2}; constexpr auto EXT_SEPARATOR_PATTERN = FixedPattern<2, 2>{1, 1}; -static const int FIRST_DIGIT_ENCODINGS[] = { - 0x00, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A -}; +static const int FIRST_DIGIT_ENCODINGS[] = {0x00, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A}; // The GS1 specification has the following to say about quiet zones // Type: EAN-13 | EAN-8 | UPC-A | UPC-E | EAN Add-on | UPC Add-on @@ -69,10 +67,9 @@ static bool DecodeDigit(const PatternView& view, std::string& txt, int* lgPatter static constexpr float MAX_AVG_VARIANCE = 0.48f; static constexpr float MAX_INDIVIDUAL_VARIANCE = 0.7f; - int bestMatch = lgPattern ? RowReader::DecodeDigit(view, UPCEANCommon::L_AND_G_PATTERNS, MAX_AVG_VARIANCE, - MAX_INDIVIDUAL_VARIANCE, false) - : RowReader::DecodeDigit(view, UPCEANCommon::L_PATTERNS, MAX_AVG_VARIANCE, - MAX_INDIVIDUAL_VARIANCE, false); + int bestMatch = + lgPattern ? RowReader::DecodeDigit(view, UPCEANCommon::L_AND_G_PATTERNS, MAX_AVG_VARIANCE, MAX_INDIVIDUAL_VARIANCE, false) + : RowReader::DecodeDigit(view, UPCEANCommon::L_PATTERNS, MAX_AVG_VARIANCE, MAX_INDIVIDUAL_VARIANCE, false); txt += '0' + (bestMatch % 10); if (lgPattern) AppendBit(*lgPattern, bestMatch >= 10); @@ -312,9 +309,8 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u auto ext = res.end; PartialResult addOnRes; - if (_hints.eanAddOnSymbol() != EanAddOnSymbol::Ignore && ext.skipSymbol() && - ext.skipSingle(static_cast(begin.sum() * 3.5)) && (AddOn(addOnRes, ext, 5) || AddOn(addOnRes, ext, 2))) { - + if (_hints.eanAddOnSymbol() != EanAddOnSymbol::Ignore && ext.skipSymbol() && ext.skipSingle(static_cast(begin.sum() * 3.5)) + && (AddOn(addOnRes, ext, 5) || AddOn(addOnRes, ext, 2))) { // ISO/IEC 15420:2009 states that the content for "]E3" should be 15 or 18 digits, i.e. converted to EAN-13 // and extended with no separator, and that the content for "]E4" should be 8 digits, i.e. no add-on //TODO: extend position in include extension @@ -329,8 +325,7 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u if (_hints.eanAddOnSymbol() == EanAddOnSymbol::Require && !addOnRes.isValid()) return Result(DecodeStatus::NotFound); - return { - res.txt, rowNumber, begin.pixelsInFront(), res.end.pixelsTillEnd(), res.format, std::move(symbologyIdentifier)}; + return {res.txt, rowNumber, begin.pixelsInFront(), res.end.pixelsTillEnd(), res.format, std::move(symbologyIdentifier)}; } } // namespace ZXing::OneD diff --git a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp index f2785d6be0..315a41b903 100644 --- a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp +++ b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp @@ -37,8 +37,7 @@ namespace ZXing::OneD::DataBar { static const int AI01_GTIN_SIZE = 40; -static void -AI01AppendCheckDigit(std::string& buffer, int currentPos) +static void AI01AppendCheckDigit(std::string& buffer, int currentPos) { int checkDigit = 0; for (int i = 0; i < 13; i++) { @@ -53,8 +52,7 @@ AI01AppendCheckDigit(std::string& buffer, int currentPos) buffer.append(std::to_string(checkDigit)); } -static void -AI01EncodeCompressedGtinWithoutAI(std::string& buffer, const BitArray& bits, int currentPos, int initialBufferPosition) +static void AI01EncodeCompressedGtinWithoutAI(std::string& buffer, const BitArray& bits, int currentPos, int initialBufferPosition) { for (int i = 0; i < 4; ++i) { int currentBlock = ToInt(bits, currentPos + 10 * i, 10); @@ -69,8 +67,7 @@ AI01EncodeCompressedGtinWithoutAI(std::string& buffer, const BitArray& bits, int AI01AppendCheckDigit(buffer, initialBufferPosition); } -static void -AI01EncodeCompressedGtin(std::string& buffer, const BitArray& bits, int currentPos) +static void AI01EncodeCompressedGtin(std::string& buffer, const BitArray& bits, int currentPos) { buffer.append("(01)"); int initialPosition = Size(buffer); @@ -82,7 +79,7 @@ using AddWeightCodeFunc = const std::function; using CheckWeightFunc = const std::function; static void AI01EncodeCompressedWeight(std::string& buffer, const BitArray& bits, int currentPos, int weightSize, - const AddWeightCodeFunc& addWeightCode, const CheckWeightFunc& checkWeight) + const AddWeightCodeFunc& addWeightCode, const CheckWeightFunc& checkWeight) { int originalWeightNumeric = ToInt(bits, currentPos, weightSize); addWeightCode(buffer, originalWeightNumeric); @@ -103,8 +100,7 @@ static void AI01EncodeCompressedWeight(std::string& buffer, const BitArray& bits * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es) * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es) */ -static std::string -DecodeAI01AndOtherAIs(const BitArray& bits) +static std::string DecodeAI01AndOtherAIs(const BitArray& bits) { static const int HEADER_SIZE = 1 + 1 + 2; // first bit encodes the linkage flag, the second one is the encodation // method, and the other two are for the variable length @@ -125,8 +121,7 @@ DecodeAI01AndOtherAIs(const BitArray& bits) return {}; } -static std::string -DecodeAnyAI(const BitArray& bits) +static std::string DecodeAnyAI(const BitArray& bits) { static const int HEADER_SIZE = 2 + 1 + 2; std::string buffer; @@ -136,8 +131,7 @@ DecodeAnyAI(const BitArray& bits) return std::string(); } -static std::string -DecodeAI013103(const BitArray& bits) +static std::string DecodeAI013103(const BitArray& bits) { static const int HEADER_SIZE = 4 + 1; static const int WEIGHT_SIZE = 15; @@ -157,8 +151,7 @@ DecodeAI013103(const BitArray& bits) return buffer; } -static std::string -DecodeAI01320x(const BitArray& bits) +static std::string DecodeAI01320x(const BitArray& bits) { static const int HEADER_SIZE = 4 + 1; static const int WEIGHT_SIZE = 15; @@ -169,7 +162,8 @@ DecodeAI01320x(const BitArray& bits) std::string buffer; AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); - AI01EncodeCompressedWeight(buffer, bits, HEADER_SIZE + AI01_GTIN_SIZE, WEIGHT_SIZE, + AI01EncodeCompressedWeight( + buffer, bits, HEADER_SIZE + AI01_GTIN_SIZE, WEIGHT_SIZE, // addWeightCode [](std::string& buf, int weight) { buf.append(weight < 10000 ? "(3202)" : "(3203)"); }, // checkWeight @@ -178,8 +172,7 @@ DecodeAI01320x(const BitArray& bits) return buffer; } -static std::string -DecodeAI01392x(const BitArray& bits) +static std::string DecodeAI01392x(const BitArray& bits) { static const int HEADER_SIZE = 5 + 1 + 2; static const int LAST_DIGIT_SIZE = 2; @@ -205,8 +198,7 @@ DecodeAI01392x(const BitArray& bits) return std::string(); } -static std::string -DecodeAI01393x(const BitArray& bits) +static std::string DecodeAI01393x(const BitArray& bits) { static const int HEADER_SIZE = 5 + 1 + 2; static const int LAST_DIGIT_SIZE = 2; @@ -243,8 +235,7 @@ DecodeAI01393x(const BitArray& bits) return std::string(); } -static std::string -DecodeAI013x0x1x(const BitArray& bits, const char* firstAIdigits, const char* dateCode) +static std::string DecodeAI013x0x1x(const BitArray& bits, const char* firstAIdigits, const char* dateCode) { static const int HEADER_SIZE = 7 + 1; static const int WEIGHT_SIZE = 20; @@ -299,8 +290,7 @@ DecodeAI013x0x1x(const BitArray& bits, const char* firstAIdigits, const char* da return buffer; } -std::string -DecodeExpandedBits(const BitArray& bits) +std::string DecodeExpandedBits(const BitArray& bits) { if (bits.get(1)) { return DecodeAI01AndOtherAIs(bits); diff --git a/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp b/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp index 6f4881aa68..7e88ec0b9b 100644 --- a/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp +++ b/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp @@ -107,11 +107,7 @@ struct DecodedNumeric : public DecodedValue struct ParsingState { - enum State { - NUMERIC, - ALPHA, - ISO_IEC_646 - }; + enum State { NUMERIC, ALPHA, ISO_IEC_646 }; int position = 0; State encoding = NUMERIC; diff --git a/core/src/pdf417/PDFCodewordDecoder.cpp b/core/src/pdf417/PDFCodewordDecoder.cpp index cddb572723..f3f17bba53 100644 --- a/core/src/pdf417/PDFCodewordDecoder.cpp +++ b/core/src/pdf417/PDFCodewordDecoder.cpp @@ -423,7 +423,7 @@ using ModuleBitCountType = std::array; static const RatioTableType& GetRatioTable() { - static constexpr RatioTableType table{[]() constexpr { + static constexpr RatioTableType table = []() constexpr { RatioTableType table{}; for (int i = 0; i < SYMBOL_COUNT; i++) { int currentSymbol = SYMBOL_TABLE[i]; @@ -439,7 +439,7 @@ static const RatioTableType& GetRatioTable() } } return table; - }()}; + }(); return table; } diff --git a/core/src/qrcode/QRDataBlock.h b/core/src/qrcode/QRDataBlock.h index 73df72b5b5..e63c326dac 100644 --- a/core/src/qrcode/QRDataBlock.h +++ b/core/src/qrcode/QRDataBlock.h @@ -36,17 +36,11 @@ enum class ErrorCorrectionLevel; class DataBlock { public: - int numDataCodewords() const { - return _numDataCodewords; - } + int numDataCodewords() const { return _numDataCodewords; } - const ByteArray& codewords() const { - return _codewords; - } + const ByteArray& codewords() const { return _codewords; } - ByteArray& codewords() { - return _codewords; - } + ByteArray& codewords() { return _codewords; } /** *

When QR Codes use multiple data blocks, they are actually interleaved. diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index cc0899d7c7..8092147adc 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -49,8 +49,7 @@ namespace ZXing::QRCode { * @param numDataCodewords number of codewords that are data bytes * @throws ChecksumException if error correction fails */ -static bool -CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) +static bool CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) { // First read into an array of ints std::vector codewordsInts(codewordBytes.begin(), codewordBytes.end()); @@ -69,8 +68,7 @@ CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) /** * See specification GBT 18284-2000 */ -static DecodeStatus -DecodeHanziSegment(BitSource& bits, int count, std::wstring& result) +static DecodeStatus DecodeHanziSegment(BitSource& bits, int count, std::wstring& result) { // Don't crash trying to read more bits than we have available. if (count * 13 > bits.available()) { @@ -102,8 +100,7 @@ DecodeHanziSegment(BitSource& bits, int count, std::wstring& result) return DecodeStatus::NoError; } -static DecodeStatus -DecodeKanjiSegment(BitSource& bits, int count, std::wstring& result) +static DecodeStatus DecodeKanjiSegment(BitSource& bits, int count, std::wstring& result) { // Don't crash trying to read more bits than we have available. if (count * 13 > bits.available()) { @@ -135,8 +132,8 @@ DecodeKanjiSegment(BitSource& bits, int count, std::wstring& result) return DecodeStatus::NoError; } -static DecodeStatus -DecodeByteSegment(BitSource& bits, int count, CharacterSet currentCharset, const std::string& hintedCharset, std::wstring& result) +static DecodeStatus DecodeByteSegment(BitSource& bits, int count, CharacterSet currentCharset, const std::string& hintedCharset, + std::wstring& result) { // Don't crash trying to read more bits than we have available. if (8 * count > bits.available()) { @@ -166,8 +163,7 @@ DecodeByteSegment(BitSource& bits, int count, CharacterSet currentCharset, const return DecodeStatus::NoError; } -static char -ToAlphaNumericChar(int value) +static char ToAlphaNumericChar(int value) { /** * See ISO 18004:2006, 6.4.4 Table 5 @@ -185,8 +181,7 @@ ToAlphaNumericChar(int value) return ALPHANUMERIC_CHARS[value]; } -static DecodeStatus -DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffect, std::wstring& result) +static DecodeStatus DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffect, std::wstring& result) { // Read two characters at a time std::string buffer; @@ -226,8 +221,7 @@ DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffect, std::wst return DecodeStatus::NoError; } -static DecodeStatus -DecodeNumericSegment(BitSource& bits, int count, std::wstring& result) +static DecodeStatus DecodeNumericSegment(BitSource& bits, int count, std::wstring& result) { // Read three digits at a time std::string buffer; @@ -273,8 +267,7 @@ DecodeNumericSegment(BitSource& bits, int count, std::wstring& result) return DecodeStatus::NoError; } -static DecodeStatus -ParseECIValue(BitSource& bits, int &outValue) +static DecodeStatus ParseECIValue(BitSource& bits, int& outValue) { int firstByte = bits.readBits(8); if ((firstByte & 0x80) == 0) { @@ -303,8 +296,9 @@ ParseECIValue(BitSource& bits, int &outValue) * *

See ISO 18004:2006, 6.4.3 - 6.4.7

*/ -ZXING_EXPORT_TEST_ONLY DecoderResult -DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel, const std::string& hintedCharset) +ZXING_EXPORT_TEST_ONLY +DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel, + const std::string& hintedCharset) { BitSource bits(bytes); std::wstring result; @@ -350,7 +344,7 @@ DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel // 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)); + structuredAppend.id = std::to_string(bits.readBits(8)); break; case CodecMode::ECI: { // Count doesn't apply to ECI @@ -384,20 +378,11 @@ DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel int count = bits.readBits(CharacterCountBits(mode, version)); DecodeStatus status; switch (mode) { - case CodecMode::NUMERIC: - status = DecodeNumericSegment(bits, count, result); - break; - case CodecMode::ALPHANUMERIC: - status = DecodeAlphanumericSegment(bits, count, fc1InEffect, result); - break; - case CodecMode::BYTE: - status = DecodeByteSegment(bits, count, currentCharset, hintedCharset, result); - break; - case CodecMode::KANJI: - status = DecodeKanjiSegment(bits, count, result); - break; - default: - status = DecodeStatus::FormatError; + case CodecMode::NUMERIC: status = DecodeNumericSegment(bits, count, result); break; + case CodecMode::ALPHANUMERIC: status = DecodeAlphanumericSegment(bits, count, fc1InEffect, result); break; + case CodecMode::BYTE: status = DecodeByteSegment(bits, count, currentCharset, hintedCharset, result); break; + case CodecMode::KANJI: status = DecodeKanjiSegment(bits, count, result); break; + default: status = DecodeStatus::FormatError; } if (StatusIsError(status)) { return status; @@ -436,8 +421,7 @@ DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel .setStructuredAppend(structuredAppend); } -static DecoderResult -DoDecode(const BitMatrix& bits, const Version& version, const std::string& hintedCharset, bool mirrored) +static DecoderResult DoDecode(const BitMatrix& bits, const Version& version, const std::string& hintedCharset, bool mirrored) { auto formatInfo = ReadFormatInformation(bits, mirrored); if (!formatInfo.isValid()) diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index e2834481bb..48afd325ae 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -134,8 +134,7 @@ static FinderPatternSets GenerateFinderPatternSets(std::vectorsize + b->size + c->size) / (3 * 7.f)) + 7; + if (auto moduleCount = (std::sqrt(distAB) + std::sqrt(distBC)) / (2 * (a->size + b->size + c->size) / (3 * 7.f)) + 7; moduleCount < 21 * 0.9 || moduleCount > 177 * 1.05) continue; @@ -303,8 +302,7 @@ DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatt // if we did not land on a black pixel or the concentric pattern finder fails, // leave the intersection of the lines as the best guess if (image.get(brCoR)) { - if (auto brCP = - LocateConcentricPattern(image, FixedPattern<3, 3>{1, 1, 1}, brCoR, moduleSize * 3)) + if (auto brCP = LocateConcentricPattern(image, FixedPattern<3, 3>{1, 1, 1}, brCoR, moduleSize * 3)) return sample(*brCP, quad[2] - PointF(3, 3)); } } diff --git a/core/src/qrcode/QRECB.h b/core/src/qrcode/QRECB.h index 5bb8017819..689d44f6f2 100644 --- a/core/src/qrcode/QRECB.h +++ b/core/src/qrcode/QRECB.h @@ -47,22 +47,17 @@ struct ECBlocks int codewordsPerBlock; std::array blocks; - int numBlocks() const { - return blocks[0].count + blocks[1].count; - } + int numBlocks() const { return blocks[0].count + blocks[1].count; } - int totalCodewords() const { - return codewordsPerBlock * numBlocks(); - } + int totalCodewords() const { return codewordsPerBlock * numBlocks(); } - int totalDataCodewords() const { + int totalDataCodewords() const + { return blocks[0].count * (blocks[0].dataCodewords + codewordsPerBlock) - + blocks[1].count * (blocks[1].dataCodewords + codewordsPerBlock); + + blocks[1].count * (blocks[1].dataCodewords + codewordsPerBlock); } - const std::array& blockArray() const { - return blocks; - } + const std::array& blockArray() const { return blocks; } }; diff --git a/core/src/qrcode/QREncoder.cpp b/core/src/qrcode/QREncoder.cpp index 297da96a31..20285e9379 100644 --- a/core/src/qrcode/QREncoder.cpp +++ b/core/src/qrcode/QREncoder.cpp @@ -38,7 +38,7 @@ namespace ZXing::QRCode { static const CharacterSet DEFAULT_BYTE_MODE_ENCODING = CharacterSet::ISO8859_1; // The original table is defined in the table 5 of JISX0510:2004 (p.19). -static const std::array ALPHANUMERIC_TABLE = { +static const std::array ALPHANUMERIC_TABLE = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f @@ -294,7 +294,8 @@ void TerminateBits(int numDataBytes, BitArray& bits) { int capacity = numDataBytes * 8; if (bits.size() > capacity) { - throw std::invalid_argument("data bits cannot fit in the QR Code" + std::to_string(bits.size()) + " > " + std::to_string(capacity)); + throw std::invalid_argument("data bits cannot fit in the QR Code" + std::to_string(bits.size()) + " > " + + std::to_string(capacity)); } for (int i = 0; i < 4 && bits.size() < capacity; ++i) { bits.appendBit(false); @@ -330,7 +331,8 @@ struct BlockPair * JISX0510:2004 (p.30) */ ZXING_EXPORT_TEST_ONLY -void GetNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes, int numRSBlocks, int blockID, int& numDataBytesInBlock, int& numECBytesInBlock) +void GetNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes, int numRSBlocks, int blockID, + int& numDataBytesInBlock, int& numECBytesInBlock) { if (blockID >= numRSBlocks) { throw std::invalid_argument("Block ID too large"); @@ -361,11 +363,9 @@ void GetNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes, throw std::invalid_argument("RS blocks mismatch"); } // 196 = (13 + 26) * 4 + (14 + 26) * 1 - if (numTotalBytes != - ((numDataBytesInGroup1 + numEcBytesInGroup1) * - numRsBlocksInGroup1) + - ((numDataBytesInGroup2 + numEcBytesInGroup2) * - numRsBlocksInGroup2)) { + if (numTotalBytes + != ((numDataBytesInGroup1 + numEcBytesInGroup1) * numRsBlocksInGroup1) + + ((numDataBytesInGroup2 + numEcBytesInGroup2) * numRsBlocksInGroup2)) { throw std::invalid_argument("Total bytes mismatch"); } @@ -387,8 +387,7 @@ void GenerateECBytes(const ByteArray& dataBytes, int numEcBytes, ByteArray& ecBy ReedSolomonEncode(GenericGF::QRCodeField256(), message, numEcBytes); ecBytes.resize(numEcBytes); - std::transform(message.end() - numEcBytes, message.end(), ecBytes.begin(), - [](auto c) { return static_cast(c); }); + std::transform(message.end() - numEcBytes, message.end(), ecBytes.begin(), [](auto c) { return static_cast(c); }); } @@ -447,7 +446,8 @@ BitArray InterleaveWithECBytes(const BitArray& bits, int numTotalBytes, int numD } } if (numTotalBytes != output.sizeInBytes()) { // Should be same. - throw std::invalid_argument("Interleaving error: " + std::to_string(numTotalBytes) + " and " + std::to_string(output.sizeInBytes()) + " differ."); + throw std::invalid_argument("Interleaving error: " + std::to_string(numTotalBytes) + " and " + std::to_string(output.sizeInBytes()) + + " differ."); } return output; } diff --git a/core/src/qrcode/QRErrorCorrectionLevel.cpp b/core/src/qrcode/QRErrorCorrectionLevel.cpp index 7ff749a515..3c9f9c9f86 100644 --- a/core/src/qrcode/QRErrorCorrectionLevel.cpp +++ b/core/src/qrcode/QRErrorCorrectionLevel.cpp @@ -24,7 +24,7 @@ namespace ZXing::QRCode { const wchar_t* ToString(ErrorCorrectionLevel l) { assert(l != ErrorCorrectionLevel::Invalid); - static const wchar_t* const LEVEL_STR[] = { L"L", L"M", L"Q", L"H", nullptr }; + static const wchar_t* const LEVEL_STR[] = {L"L", L"M", L"Q", L"H", nullptr}; return LEVEL_STR[static_cast(l)]; } @@ -35,20 +35,21 @@ ErrorCorrectionLevel ECLevelFromString(const char* str) case 'M': return ErrorCorrectionLevel::Medium; case 'Q': return ErrorCorrectionLevel::Quality; case 'H': return ErrorCorrectionLevel::High; - default: return ErrorCorrectionLevel::Invalid; + default: return ErrorCorrectionLevel::Invalid; } } ErrorCorrectionLevel ECLevelFromBits(int bits) { - static const ErrorCorrectionLevel LEVEL_FOR_BITS[] = { ErrorCorrectionLevel::Medium, ErrorCorrectionLevel::Low, ErrorCorrectionLevel::High, ErrorCorrectionLevel::Quality }; + static const ErrorCorrectionLevel LEVEL_FOR_BITS[] = {ErrorCorrectionLevel::Medium, ErrorCorrectionLevel::Low, + ErrorCorrectionLevel::High, ErrorCorrectionLevel::Quality}; return LEVEL_FOR_BITS[bits & 0x3]; } int BitsFromECLevel(ErrorCorrectionLevel l) { assert(l != ErrorCorrectionLevel::Invalid); - static const int BITS[] = { 1, 0, 3, 2, -1 }; + static const int BITS[] = {1, 0, 3, 2, -1}; return BITS[(int)l]; } diff --git a/core/src/qrcode/QRErrorCorrectionLevel.h b/core/src/qrcode/QRErrorCorrectionLevel.h index 1241b1451b..2000543da0 100644 --- a/core/src/qrcode/QRErrorCorrectionLevel.h +++ b/core/src/qrcode/QRErrorCorrectionLevel.h @@ -27,11 +27,11 @@ namespace QRCode { */ enum class ErrorCorrectionLevel { - Low, // L = ~7 % correction - Medium, // M = ~15% correction - Quality, // Q = ~25% correction - High, // H = ~30% correction - Invalid, // denotes in invalid/unknown value + Low, // L = ~7 % correction + Medium, // M = ~15% correction + Quality, // Q = ~25% correction + High, // H = ~30% correction + Invalid, // denotes in invalid/unknown value }; const wchar_t* ToString(ErrorCorrectionLevel l); diff --git a/core/src/qrcode/QRFormatInformation.h b/core/src/qrcode/QRFormatInformation.h index fbfe967da6..29a3f72e18 100644 --- a/core/src/qrcode/QRFormatInformation.h +++ b/core/src/qrcode/QRFormatInformation.h @@ -38,17 +38,14 @@ class FormatInformation static FormatInformation DecodeFormatInformation(uint32_t formatInfoBits1, uint32_t formatInfoBits2); - ErrorCorrectionLevel errorCorrectionLevel() const { - return _errorCorrectionLevel; - } + ErrorCorrectionLevel errorCorrectionLevel() const { return _errorCorrectionLevel; } - uint8_t dataMask() const { - return _dataMask; - } + uint8_t dataMask() const { return _dataMask; } bool isValid() const { return _errorCorrectionLevel != ErrorCorrectionLevel::Invalid; } - bool operator==(const FormatInformation& other) const { + bool operator==(const FormatInformation& other) const + { return _dataMask == other._dataMask && _errorCorrectionLevel == other._errorCorrectionLevel; } diff --git a/core/src/qrcode/QRMaskUtil.cpp b/core/src/qrcode/QRMaskUtil.cpp index 2f327e4f2a..e94236377a 100644 --- a/core/src/qrcode/QRMaskUtil.cpp +++ b/core/src/qrcode/QRMaskUtil.cpp @@ -35,7 +35,8 @@ static const int N4 = 10; * Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both * vertical and horizontal orders respectively. */ -static int ApplyMaskPenaltyRule1Internal(const TritMatrix& matrix, bool isHorizontal) { +static int ApplyMaskPenaltyRule1Internal(const TritMatrix& matrix, bool isHorizontal) +{ int penalty = 0; int width = matrix.width(); int height = matrix.height(); @@ -64,7 +65,6 @@ static int ApplyMaskPenaltyRule1Internal(const TritMatrix& matrix, bool isHorizo return penalty; } - /** * Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and * give penalty to them. Example: 00000 or 11111. @@ -93,8 +93,9 @@ static int ApplyMaskPenaltyRule2(const TritMatrix& matrix) return N2 * penalty; } -template -static bool HasPatternAt(const std::array& pattern, const Trit* begin, int count, int stride) { +template +static bool HasPatternAt(const std::array& pattern, const Trit* begin, int count, int stride) +{ assert(std::abs(count) <= (int)N); auto end = begin + count * stride; if (count < 0) @@ -124,14 +125,14 @@ static int ApplyMaskPenaltyRule3(const TritMatrix& matrix) for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { auto i = &matrix.get(x, y); - if (x <= width - finderSize && HasPatternAt(finder, i, finderSize, 1) && - (HasPatternAt(white, i, -std::min(x, whiteSize), 1) || - HasPatternAt(white, i + finderSize, std::min(width - x - finderSize, whiteSize), 1))) { + if (x <= width - finderSize && HasPatternAt(finder, i, finderSize, 1) + && (HasPatternAt(white, i, -std::min(x, whiteSize), 1) + || HasPatternAt(white, i + finderSize, std::min(width - x - finderSize, whiteSize), 1))) { numPenalties++; } - if (y <= height - finderSize && HasPatternAt(finder, i, finderSize, width) && - (HasPatternAt(white, i, -std::min(y, whiteSize), width) || - HasPatternAt(white, i + finderSize * width, std::min(height - y - finderSize, whiteSize), width))) { + if (y <= height - finderSize && HasPatternAt(finder, i, finderSize, width) + && (HasPatternAt(white, i, -std::min(y, whiteSize), width) + || HasPatternAt(white, i + finderSize * width, std::min(height - y - finderSize, whiteSize), width))) { numPenalties++; } } @@ -145,7 +146,7 @@ static int ApplyMaskPenaltyRule3(const TritMatrix& matrix) */ static int ApplyMaskPenaltyRule4(const TritMatrix& matrix) { - auto numDarkCells = std::count_if(matrix.begin(), matrix.end(), [](Trit cell){ return cell; }); + auto numDarkCells = std::count_if(matrix.begin(), matrix.end(), [](Trit cell) { return cell; }); auto numTotalCells = matrix.size(); auto fivePercentVariances = std::abs(numDarkCells * 2 - numTotalCells) * 10 / numTotalCells; return static_cast(fivePercentVariances * N4); diff --git a/core/src/qrcode/QRMaskUtil.h b/core/src/qrcode/QRMaskUtil.h index 9e2d4d355d..67d4da08f0 100644 --- a/core/src/qrcode/QRMaskUtil.h +++ b/core/src/qrcode/QRMaskUtil.h @@ -21,7 +21,9 @@ namespace ZXing { namespace QRCode { namespace MaskUtil { - int CalculateMaskPenalty(const TritMatrix& matrix); -} -} // QRCode -} // ZXing + +int CalculateMaskPenalty(const TritMatrix& matrix); + +} // namespace MaskUtil +} // namespace QRCode +} // namespace ZXing diff --git a/core/src/qrcode/QRMatrixUtil.cpp b/core/src/qrcode/QRMatrixUtil.cpp index f937a08f5b..825cd40ba2 100644 --- a/core/src/qrcode/QRMatrixUtil.cpp +++ b/core/src/qrcode/QRMatrixUtil.cpp @@ -151,7 +151,8 @@ static int FindMSBSet(unsigned value) // // Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit // operations. We don't care if cofficients are positive or negative. -static int CalculateBCHCode(int value, int poly) { +static int CalculateBCHCode(int value, int poly) +{ // If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1 // from 13 to make it 12. int msbSetInPoly = FindMSBSet(poly); @@ -306,8 +307,7 @@ static void EmbedDataBits(const BitArray& dataBits, int maskPattern, TritMatrix& // Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On // success, store the result in "matrix" and return true. -void BuildMatrix(const BitArray& dataBits, ErrorCorrectionLevel ecLevel, const Version& version, int maskPattern, - TritMatrix& matrix) +void BuildMatrix(const BitArray& dataBits, ErrorCorrectionLevel ecLevel, const Version& version, int maskPattern, TritMatrix& matrix) { matrix.clear(); // Let's get started with embedding big squares at corners. diff --git a/core/src/qrcode/QRMatrixUtil.h b/core/src/qrcode/QRMatrixUtil.h index 837bf99c28..4ca09a33d6 100644 --- a/core/src/qrcode/QRMatrixUtil.h +++ b/core/src/qrcode/QRMatrixUtil.h @@ -29,8 +29,7 @@ class Version; constexpr int NUM_MASK_PATTERNS = 8; -void BuildMatrix(const BitArray& dataBits, ErrorCorrectionLevel ecLevel, const Version& version, int maskPattern, - TritMatrix& matrix); +void BuildMatrix(const BitArray& dataBits, ErrorCorrectionLevel ecLevel, const Version& version, int maskPattern, TritMatrix& matrix); } // QRCode } // ZXing diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index effd0e44e1..eb7c5c9b93 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -30,13 +30,9 @@ namespace ZXing::QRCode { -Reader::Reader(const DecodeHints& hints) - : _tryHarder(hints.tryHarder()), _isPure(hints.isPure()), _charset(hints.characterSet()) -{ -} +Reader::Reader(const DecodeHints& hints) : _tryHarder(hints.tryHarder()), _isPure(hints.isPure()), _charset(hints.characterSet()) {} -Result -Reader::decode(const BinaryBitmap& image) const +Result Reader::decode(const BinaryBitmap& image) const { #if 1 if (!_isPure) { diff --git a/core/src/qrcode/QRVersion.cpp b/core/src/qrcode/QRVersion.cpp index 248afbc671..96a9f91fe2 100644 --- a/core/src/qrcode/QRVersion.cpp +++ b/core/src/qrcode/QRVersion.cpp @@ -290,16 +290,13 @@ Version::AllVersions() return allVersions; } -Version::Version(int versionNumber, std::initializer_list alignmentPatternCenters, const std::array &ecBlocks) : - _versionNumber(versionNumber), - _alignmentPatternCenters(alignmentPatternCenters), - _ecBlocks(ecBlocks) +Version::Version(int versionNumber, std::initializer_list alignmentPatternCenters, const std::array& ecBlocks) + : _versionNumber(versionNumber), _alignmentPatternCenters(alignmentPatternCenters), _ecBlocks(ecBlocks) { _totalCodewords = ecBlocks[0].totalDataCodewords(); } -const Version * -Version::VersionForNumber(int versionNumber) +const Version* Version::VersionForNumber(int versionNumber) { if (versionNumber < 1 || versionNumber > 40) { //throw std::invalid_argument("Version should be in range [1-40]."); @@ -308,8 +305,7 @@ Version::VersionForNumber(int versionNumber) return &AllVersions()[versionNumber - 1]; } -const Version * -Version::ProvisionalVersionForDimension(int dimension) +const Version* Version::ProvisionalVersionForDimension(int dimension) { if (dimension % 4 != 1) { //throw std::invalid_argument("Unexpected dimension"); @@ -318,9 +314,7 @@ Version::ProvisionalVersionForDimension(int dimension) return VersionForNumber((dimension - 17) / 4); } - -const Version * -Version::DecodeVersionInformation(int versionBits) +const Version* Version::DecodeVersionInformation(int versionBits) { int bestDifference = std::numeric_limits::max(); int bestVersion = 0; diff --git a/core/src/qrcode/QRVersion.h b/core/src/qrcode/QRVersion.h index 1c415fd71b..2de4988e86 100644 --- a/core/src/qrcode/QRVersion.h +++ b/core/src/qrcode/QRVersion.h @@ -37,25 +37,15 @@ namespace QRCode { class Version { public: - int versionNumber() const { - return _versionNumber; - } + int versionNumber() const { return _versionNumber; } - const std::vector& alignmentPatternCenters() const { - return _alignmentPatternCenters; - } - - int totalCodewords() const { - return _totalCodewords; - } - - int dimensionForVersion() const { - return 17 + 4 * _versionNumber; - } - - const ECBlocks & ecBlocksForLevel(ErrorCorrectionLevel ecLevel) const { - return _ecBlocks[(int)ecLevel]; - } + const std::vector& alignmentPatternCenters() const { return _alignmentPatternCenters; } + + int totalCodewords() const { return _totalCodewords; } + + int dimensionForVersion() const { return 17 + 4 * _versionNumber; } + + const ECBlocks& ecBlocksForLevel(ErrorCorrectionLevel ecLevel) const { return _ecBlocks[(int)ecLevel]; } BitMatrix buildFunctionPattern() const; diff --git a/core/src/qrcode/QRWriter.cpp b/core/src/qrcode/QRWriter.cpp index daf97e3b50..52d6f065da 100644 --- a/core/src/qrcode/QRWriter.cpp +++ b/core/src/qrcode/QRWriter.cpp @@ -30,18 +30,16 @@ namespace ZXing::QRCode { static const int QUIET_ZONE_SIZE = 4; -Writer::Writer() : - _margin(QUIET_ZONE_SIZE), - _ecLevel(ErrorCorrectionLevel::Low), - _encoding(CharacterSet::Unknown), - _version(0), - _useGs1Format(false), - _maskPattern(-1) -{ -} - -BitMatrix -Writer::encode(const std::wstring& contents, int width, int height) const +Writer::Writer() + : _margin(QUIET_ZONE_SIZE), + _ecLevel(ErrorCorrectionLevel::Low), + _encoding(CharacterSet::Unknown), + _version(0), + _useGs1Format(false), + _maskPattern(-1) +{} + +BitMatrix Writer::encode(const std::wstring& contents, int width, int height) const { if (contents.empty()) { throw std::invalid_argument("Found empty contents"); From 9b0cc6038737952fb8e40ac6f72879cd7c9f0485 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 6 May 2022 17:54:23 +0200 Subject: [PATCH 006/180] example: minor performance/readability improvements in ZXingQtReader.h --- example/ZXingQtReader.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index 8a316ca206..44ed865b2b 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -167,7 +167,7 @@ inline Result ReadBarcode(const QImage& img, const DecodeHints& hints = {}) {img.bits(), img.width(), img.height(), ImgFmtFromQImg(img), img.bytesPerLine()}, hints)); }; - return ImgFmtFromQImg(img) == ImageFormat::None ? exec(img.convertToFormat(QImage::Format_RGBX8888)) : exec(img); + return ImgFmtFromQImg(img) == ImageFormat::None ? exec(img.convertToFormat(QImage::Format_Grayscale8)) : exec(img); } #ifdef QT_MULTIMEDIA_LIB @@ -256,9 +256,8 @@ inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = { ZXing::ReadBarcode({img.bits() + pixOffset, img.width(), img.height(), fmt, img.bytesPerLine(), pixStride}, hints)); } else { - auto qfmt = QVideoFrame::imageFormatFromPixelFormat(img.pixelFormat()); - if (qfmt != QImage::Format_Invalid) - res = ReadBarcode(QImage(img.bits(), img.width(), img.height(), qfmt), hints); + if (QVideoFrame::imageFormatFromPixelFormat(img.pixelFormat()) != QImage::Format_Invalid) + res = ReadBarcode(img.image(), hints); } img.unmap(); From ec547b349f354049a18bfb495a8f8291633a95b2 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 6 May 2022 22:12:36 +0200 Subject: [PATCH 007/180] ODReader: fix performance and lineCount bug in checkRows handling If the last checkRow returned a valid result, we ended up adding checkRows that have already been processed. --- core/src/oned/ODReader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index eb4d4fbcdb..d84d1d0e65 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -108,6 +108,7 @@ static Results DoDecode(const std::vector>& readers, int rowStepsAboveOrBelow = (i + 1) / 2; bool isAbove = (i & 0x01) == 0; // i.e. is x even? int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow); + bool isCheckRow = false; if (rowNumber < 0 || rowNumber >= height) { // Oops, if we run off the top or bottom, stop break; @@ -118,6 +119,7 @@ static Results DoDecode(const std::vector>& readers, --i; rowNumber = checkRows.back(); checkRows.pop_back(); + isCheckRow = true; if (rowNumber < 0 || rowNumber >= height) continue; } @@ -199,7 +201,7 @@ static Results DoDecode(const std::vector>& readers, // if we found a valid code but have a minLineCount > 1, add additional check rows above and // below the current one - if (checkRows.empty() && minLineCount > 1 && rowStep > 1) { + if (!isCheckRow && minLineCount > 1 && rowStep > 1) { checkRows = {rowNumber - 1, rowNumber + 1}; if (rowStep > 2) checkRows.insert(checkRows.end(), {rowNumber - 2, rowNumber + 2}); From 2ab9fa6e78ae54cbf2d8e017953cf8bc6e77fef0 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 7 May 2022 02:22:46 +0200 Subject: [PATCH 008/180] AZDetector: switch float to double to fix surprising clang-13 regression Switching from clang-12 to clang-13 resulted in a failure of the samples/aztec-1/readerinit-compact-15x15.png test, due to a numerical instability. Seemingly, older clang and also g++-11 and g++-12 internally operate in double precision with the 'ratio' value even though it used to be a float, while clang-13 does no anymore. --- core/src/aztec/AZDetector.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/src/aztec/AZDetector.cpp b/core/src/aztec/AZDetector.cpp index 6ff3089a8e..d0dfc2e5c7 100644 --- a/core/src/aztec/AZDetector.cpp +++ b/core/src/aztec/AZDetector.cpp @@ -340,21 +340,21 @@ static PointI GetFirstDifferent(const BitMatrix& image, const PointI& init, bool */ static void ExpandSquare(std::array& cornerPoints, float oldSide, float newSide) { - float ratio = newSide / (2 * oldSide); - float dx = cornerPoints[0].x() - cornerPoints[2].x(); - float dy = cornerPoints[0].y() - cornerPoints[2].y(); - float centerx = (cornerPoints[0].x() + cornerPoints[2].x()) / 2.0f; - float centery = (cornerPoints[0].y() + cornerPoints[2].y()) / 2.0f; + double ratio = newSide / (2.0 * oldSide); + double dx = cornerPoints[0].x() - cornerPoints[2].x(); + double dy = cornerPoints[0].y() - cornerPoints[2].y(); + double centerx = (cornerPoints[0].x() + cornerPoints[2].x()) / 2.0f; + double centery = (cornerPoints[0].y() + cornerPoints[2].y()) / 2.0f; - cornerPoints[0] = ResultPoint(centerx + ratio * dx, centery + ratio * dy); - cornerPoints[2] = ResultPoint(centerx - ratio * dx, centery - ratio * dy); + cornerPoints[0] = PointF(centerx + ratio * dx, centery + ratio * dy); + cornerPoints[2] = PointF(centerx - ratio * dx, centery - ratio * dy); dx = cornerPoints[1].x() - cornerPoints[3].x(); dy = cornerPoints[1].y() - cornerPoints[3].y(); centerx = (cornerPoints[1].x() + cornerPoints[3].x()) / 2.0f; centery = (cornerPoints[1].y() + cornerPoints[3].y()) / 2.0f; - cornerPoints[1] = ResultPoint(centerx + ratio * dx, centery + ratio * dy); - cornerPoints[3] = ResultPoint(centerx - ratio * dx, centery - ratio * dy); + cornerPoints[1] = PointF(centerx + ratio * dx, centery + ratio * dy); + cornerPoints[3] = PointF(centerx - ratio * dx, centery - ratio * dy); } From f009fe774794c4761dd4c739318b5dea0b16470f Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 9 May 2022 14:23:22 +0200 Subject: [PATCH 009/180] example: limit the list of 'supported' formats to those that actually are --- example/ZXingWriter.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/example/ZXingWriter.cpp b/example/ZXingWriter.cpp index cd6188bcf8..18acc5cd41 100644 --- a/example/ZXingWriter.cpp +++ b/example/ZXingWriter.cpp @@ -40,9 +40,8 @@ static void PrintUsage(const char* exePath) << " -ecc Error correction level, [0-8]\n" << "\n" << "Supported formats are:\n"; - for (auto f : BarcodeFormats::all()) { + for (auto f : BarcodeFormatsFromString("Aztec Codabar Code39 Code93 Code128 DataMatrix EAN8 EAN13 ITF PDF417 QRCode UPCA UPCE")) std::cout << " " << ToString(f) << "\n"; - } std::cout << "Format can be lowercase letters, with or without '-'.\n"; } From a2c64fe7b136b56afe7f6456ce21ef48bd1f50be Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 9 May 2022 14:25:58 +0200 Subject: [PATCH 010/180] example: support writing barcode as SVG --- core/src/BitMatrixIO.cpp | 24 ++++++++++++++++++++++++ core/src/BitMatrixIO.h | 4 ++-- example/ZXingWriter.cpp | 11 +++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/core/src/BitMatrixIO.cpp b/core/src/BitMatrixIO.cpp index be1732326c..dcdd090d9d 100644 --- a/core/src/BitMatrixIO.cpp +++ b/core/src/BitMatrixIO.cpp @@ -20,6 +20,7 @@ #include "BitArray.h" #include +#include namespace ZXing { @@ -44,6 +45,29 @@ std::string ToString(const BitMatrix& matrix, char one, char zero, bool addSpace return result; } +std::string ToSVG(const BitMatrix& matrix) +{ + // see https://stackoverflow.com/questions/10789059/create-qr-code-in-vector-image/60638350#60638350 + + const int width = matrix.width(); + const int height = matrix.height(); + std::ostringstream out; + + out << "\n" + << "\n" + << "\n"; + + return out.str(); +} + BitMatrix ParseBitMatrix(const std::string& str, char one, bool expectSpace) { auto lineLength = str.find('\n'); diff --git a/core/src/BitMatrixIO.h b/core/src/BitMatrixIO.h index 6946b33905..3178ac01a6 100644 --- a/core/src/BitMatrixIO.h +++ b/core/src/BitMatrixIO.h @@ -22,8 +22,8 @@ namespace ZXing { -std::string ToString(const BitMatrix& matrix, char one = 'X', char zero = ' ', bool addSpace = true, - bool printAsCString = false); +std::string ToString(const BitMatrix& matrix, char one = 'X', char zero = ' ', bool addSpace = true, bool printAsCString = false); +std::string ToSVG(const BitMatrix& matrix); BitMatrix ParseBitMatrix(const std::string& str, char one = 'X', bool expectSpace = true); void SaveAsPBM(const BitMatrix& matrix, const std::string filename, int quietZone = 0); diff --git a/example/ZXingWriter.cpp b/example/ZXingWriter.cpp index 18acc5cd41..2cec15e0b1 100644 --- a/example/ZXingWriter.cpp +++ b/example/ZXingWriter.cpp @@ -16,6 +16,7 @@ #include "BarcodeFormat.h" #include "BitMatrix.h" +#include "BitMatrixIO.h" #include "MultiFormatWriter.h" #include "TextUtfEncoding.h" #include "CharacterSetECI.h" @@ -23,6 +24,7 @@ #include #include #include +#include #include #include @@ -42,7 +44,9 @@ static void PrintUsage(const char* exePath) << "Supported formats are:\n"; for (auto f : BarcodeFormatsFromString("Aztec Codabar Code39 Code93 Code128 DataMatrix EAN8 EAN13 ITF PDF417 QRCode UPCA UPCE")) std::cout << " " << ToString(f) << "\n"; - std::cout << "Format can be lowercase letters, with or without '-'.\n"; + + std::cout << "Format can be lowercase letters, with or without '-'.\n" + << "Output format is determined by file name, supported are png, jpg and svg.\n"; } static bool ParseSize(std::string str, int* width, int* height) @@ -128,7 +132,8 @@ int main(int argc, char* argv[]) try { auto writer = MultiFormatWriter(format).setMargin(margin).setEncoding(encoding).setEccLevel(eccLevel); - auto bitmap = ToMatrix(writer.encode(TextUtfEncoding::FromUtf8(text), width, height)); + auto matrix = writer.encode(TextUtfEncoding::FromUtf8(text), width, height); + auto bitmap = ToMatrix(matrix); auto ext = GetExtension(filePath); int success = 0; @@ -136,6 +141,8 @@ int main(int argc, char* argv[]) success = stbi_write_png(filePath.c_str(), bitmap.width(), bitmap.height(), 1, bitmap.data(), 0); } else if (ext == "jpg" || ext == "jpeg") { success = stbi_write_jpg(filePath.c_str(), bitmap.width(), bitmap.height(), 1, bitmap.data(), 0); + } else if (ext == "svg") { + success = (std::ofstream(filePath) << ToSVG(matrix)).good(); } if (!success) { From 7da0382b5cc7815dddc91160ee77fe1dda951ec7 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 16 May 2022 11:08:23 +0200 Subject: [PATCH 011/180] ConcentricFinder: add FindConcentricPatternCorners This adds infrastructure for later MircoQRCode and Aztec additions. --- core/src/ConcentricFinder.cpp | 93 +++++++++++++++++++++++++++++++++++ core/src/ConcentricFinder.h | 3 ++ core/src/Quadrilateral.h | 8 +++ core/src/RegressionLine.h | 27 ++++++++-- 4 files changed, 127 insertions(+), 4 deletions(-) diff --git a/core/src/ConcentricFinder.cpp b/core/src/ConcentricFinder.cpp index 66ece3d655..830e31bd2b 100644 --- a/core/src/ConcentricFinder.cpp +++ b/core/src/ConcentricFinder.cpp @@ -17,6 +17,7 @@ #include "ConcentricFinder.h" #include "LogMatrix.h" +#include "RegressionLine.h" namespace ZXing { @@ -106,4 +107,96 @@ std::optional FinetuneConcentricPatternCenter(const BitMatrix& image, Po return res; } +static std::vector CollectRingPoints(const BitMatrix& image, PointF center, int range, int edgeIndex, bool backup) +{ + PointI centerI(center); + BitMatrixCursorI cur(image, centerI, {0, 1}); + cur.stepToEdge(edgeIndex, range, backup); + cur.turnRight(); // move clock wise and keep edge on the right/left depending on backup + const auto edgeDir = backup ? Direction::LEFT : Direction::RIGHT; + + uint32_t neighbourMask = 0; + auto start = cur.p; + std::vector points; + points.reserve(4 * range); + + do { + log(cur.p, 4); + points.push_back(centered(cur.p)); + + // find out if we come full circle around the center. 8 bits have to be set in the end. + neighbourMask |= (1 << (4 + dot(bresenhamDirection(cur.p - centerI), PointI(1, 3)))); + + if (!cur.stepAlongEdge(edgeDir)) + return {}; + + // use L-inf norm, simply because it is a lot faster than L2-norm and sufficiently accurate + if (maxAbsComponent(cur.p - center) > range || centerI == cur.p || Size(points) > 4 * 2 * range) + return {}; + + } while (cur.p != start); + + if (neighbourMask != 0b111101111) + return {}; + + return points; +} + +static QuadrilateralF FitQadrilateralToPoints(PointF center, std::vector& points) +{ + auto dist2Center = [c = center](auto a, auto b) { return distance(a, c) < distance(b, c); }; + // rotate points such that the first one is the furthest away from the center (hence, a corner) + std::rotate(points.begin(), std::max_element(points.begin(), points.end(), dist2Center), points.end()); + + std::array corners; + corners[0] = &points[0]; + // find the oposite corner by looking for the farthest point near the oposite point + corners[2] = std::max_element(&points[Size(points) * 3 / 8], &points[Size(points) * 5 / 8], dist2Center); + + // find the two in between corners by looking for the points farthest from the long diagonal + auto dist2Diagonal = [l = RegressionLine(*corners[0], *corners[2])](auto a, auto b) { return l.distance(a) < l.distance(b); }; + corners[1] = std::max_element(&points[Size(points) * 1 / 8], &points[Size(points) * 3 / 8], dist2Diagonal); + corners[3] = std::max_element(&points[Size(points) * 5 / 8], &points[Size(points) * 7 / 8], dist2Diagonal); + + std::array lines{RegressionLine{corners[0] + 1, corners[1]}, RegressionLine{corners[1] + 1, corners[2]}, + RegressionLine{corners[2] + 1, corners[3]}, RegressionLine{corners[3] + 1, &(*points.end())}}; + + QuadrilateralF res; + for (int i = 0; i < 4; ++i) + res[i] = intersect(lines[i], lines[(i + 1) % 4]); + + return res; +} + +std::optional FindConcentricPatternCorners(const BitMatrix& image, PointF center, int range, int lineIndex) +{ + auto innerPoints = CollectRingPoints(image, center, range, lineIndex, false); + auto outerPoints = CollectRingPoints(image, center, range, lineIndex + 1, true); + + if (innerPoints.empty() || outerPoints.empty()) + return {}; + + auto innerCorners = FitQadrilateralToPoints(center, innerPoints); + auto outerCorners = FitQadrilateralToPoints(center, outerPoints); + + auto dist2First = [c = innerCorners[0]](auto a, auto b) { return distance(a, c) < distance(b, c); }; + // rotate points such that the the two topLeft points are closest to each other + std::rotate(outerCorners.begin(), std::min_element(outerCorners.begin(), outerCorners.end(), dist2First), outerCorners.end()); + + QuadrilateralF res; + for (int i=0; i<4; ++i) + res[i] = (innerCorners[i] + outerCorners[i]) / 2; + + for (auto p : innerCorners) + log(p, 3); + + for (auto p : outerCorners) + log(p, 3); + + for (auto p : res) + log(p, 3); + + return res; +} + } // ZXing diff --git a/core/src/ConcentricFinder.h b/core/src/ConcentricFinder.h index 1cfbb6c825..64b47d1bbf 100644 --- a/core/src/ConcentricFinder.h +++ b/core/src/ConcentricFinder.h @@ -17,6 +17,7 @@ #include "BitMatrixCursor.h" #include "Pattern.h" +#include "Quadrilateral.h" #include "ZXContainerAlgorithms.h" #include @@ -76,6 +77,8 @@ std::optional CenterOfRing(const BitMatrix& image, PointI center, int ra std::optional FinetuneConcentricPatternCenter(const BitMatrix& image, PointF center, int range, int finderPatternSize); +std::optional FindConcentricPatternCorners(const BitMatrix& image, PointF center, int range, int ringIndex); + struct ConcentricPattern : public PointF { int size = 0; diff --git a/core/src/Quadrilateral.h b/core/src/Quadrilateral.h index 23d7e76a8f..ba2679d6bc 100644 --- a/core/src/Quadrilateral.h +++ b/core/src/Quadrilateral.h @@ -114,6 +114,14 @@ PointT Center(const Quadrilateral& q) return Reduce(q) / Size(q); } +template +Quadrilateral RotatedCorners(const Quadrilateral& q, int n = 1) +{ + Quadrilateral res; + std::rotate_copy(q.begin(), q.begin() + ((n + 4) % 4), q.end(), res.begin()); + return res; +} + template bool IsInside(const PointT& p, const Quadrilateral& q) { diff --git a/core/src/RegressionLine.h b/core/src/RegressionLine.h index 8be690fc72..a0ff880f80 100644 --- a/core/src/RegressionLine.h +++ b/core/src/RegressionLine.h @@ -22,6 +22,10 @@ #include #include +#ifdef PRINT_DEBUG +#include +#endif + namespace ZXing { class RegressionLine @@ -33,12 +37,12 @@ class RegressionLine friend PointF intersect(const RegressionLine& l1, const RegressionLine& l2); - bool evaluate(const std::vector& ps) + template bool evaluate(const PointT* begin, const PointT* end) { - auto mean = std::accumulate(ps.begin(), ps.end(), PointF()) / ps.size(); + auto mean = std::accumulate(begin, end, PointF()) / std::distance(begin, end); PointF::value_t sumXX = 0, sumYY = 0, sumXY = 0; - for (auto& p : ps) { - auto d = p - mean; + for (auto p = begin; p != end; ++p) { + auto d = *p - mean; sumXX += d.x * d.x; sumYY += d.y * d.y; sumXY += d.x * d.y; @@ -60,14 +64,29 @@ class RegressionLine return dot(_directionInward, normal()) > 0.5f; // angle between original and new direction is at most 60 degree } + template bool evaluate(const std::vector>& points) { return evaluate(&points.front(), &points.back() + 1); } + + template static auto distance(PointT a, PointT b) { return ZXing::distance(a, b); } + public: RegressionLine() { _points.reserve(16); } // arbitrary but plausible start size (tiny performance improvement) + template RegressionLine(PointT a, PointT b) + { + evaluate(std::vector{a, b}); + } + + template RegressionLine(const PointT* b, const PointT* e) + { + evaluate(b, e); + } + const auto& points() const { return _points; } int length() const { return _points.size() >= 2 ? int(distance(_points.front(), _points.back())) : 0; } bool isValid() const { return !std::isnan(a); } PointF normal() const { return isValid() ? PointF(a, b) : _directionInward; } auto signedDistance(PointF p) const { return dot(normal(), p) - c; } + template auto distance(PointT p) const { return std::abs(signedDistance(PointF(p))); } PointF project(PointF p) const { return p - signedDistance(p) * normal(); } void reset() From 86fca5ed176f060c221e851e4e85b78efc5e879c Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 16 May 2022 11:09:57 +0200 Subject: [PATCH 012/180] DecodeHints: hasFormat() maps `None` to `Any` --- core/src/DecodeHints.h | 2 +- core/src/oned/ODMultiUPCEANReader.cpp | 9 ++------- core/src/oned/ODMultiUPCEANReader.h | 1 - 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 8014bee4e5..d098e0a9c1 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -155,7 +155,7 @@ class DecodeHints [[deprecated]] bool assumeCode39CheckDigit() const noexcept { return validateCode39CheckSum(); } [[deprecated]] DecodeHints& setAssumeCode39CheckDigit(bool v) { return setValidateCode39CheckSum(v); } - bool hasFormat(BarcodeFormats f) const noexcept { return _formats.testFlags(f); } + bool hasFormat(BarcodeFormats f) const noexcept { return _formats.testFlags(f) || _formats.empty(); } bool hasNoFormat() const noexcept { return _formats.empty(); } }; diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index 21783f001d..06218ac1e1 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -27,12 +27,7 @@ namespace ZXing::OneD { -MultiUPCEANReader::MultiUPCEANReader(const DecodeHints& hints) : _hints(hints) -{ - _canReturnUPCA = _hints.formats().empty() || _hints.hasFormat(BarcodeFormat::UPCA); - if (_hints.formats().empty()) - _hints.setFormats(BarcodeFormat::Any); -} +MultiUPCEANReader::MultiUPCEANReader(const DecodeHints& hints) : _hints(hints) {} MultiUPCEANReader::~MultiUPCEANReader() = default; @@ -297,7 +292,7 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u // If UPC-A was a requested format and we detected a EAN-13 code with a leading '0', then we drop the '0' and call it // a UPC-A code. // TODO: this is questionable - if (_canReturnUPCA && res.format == BarcodeFormat::EAN13 && res.txt.front() == '0') { + if (_hints.hasFormat(BarcodeFormat::UPCA) && res.format == BarcodeFormat::EAN13 && res.txt.front() == '0') { res.txt = res.txt.substr(1); res.format = BarcodeFormat::UPCA; } diff --git a/core/src/oned/ODMultiUPCEANReader.h b/core/src/oned/ODMultiUPCEANReader.h index 9d3154331e..7385001210 100644 --- a/core/src/oned/ODMultiUPCEANReader.h +++ b/core/src/oned/ODMultiUPCEANReader.h @@ -39,7 +39,6 @@ class MultiUPCEANReader : public RowReader Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; private: - bool _canReturnUPCA = false; DecodeHints _hints; }; From 9d71f099c991f8bbac8ce720be1a6929f86940a1 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 17 May 2022 10:13:02 +0200 Subject: [PATCH 013/180] python: remove pip from `requires` in toml file (hopefully fix CI build) --- wrappers/python/pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/wrappers/python/pyproject.toml b/wrappers/python/pyproject.toml index d4b45410cf..dbeaae43ca 100644 --- a/wrappers/python/pyproject.toml +++ b/wrappers/python/pyproject.toml @@ -3,7 +3,6 @@ requires = [ "setuptools>=42", "setuptools_scm", "wheel", - "pip>=21", "cmake>=3.14", ] build-backend = "setuptools.build_meta" From d2031396c853e1013f5752ed94e894c135d34cc9 Mon Sep 17 00:00:00 2001 From: corbers <30796414+corbers@users.noreply.github.com> Date: Tue, 17 May 2022 09:44:29 +0100 Subject: [PATCH 014/180] Add micro qr code detector (#324) This adds support to detect Micro QR Codes. The code was originally based on an implementation ported from Java that was completely separate from the `QR*` files. It has been rewritten such that it merges with the existing `QRCode` functionality, thereby increasing its performance (both detection rate and runtime speed). For the sake of a cleaner git history, the PR has been squashed to prevent around 3k lines of code of going in and out again immediately. Co-authored-by: axxel --- core/src/BarcodeFormat.cpp | 1 + core/src/BarcodeFormat.h | 5 +- core/src/BitSource.cpp | 17 ++- core/src/BitSource.h | 7 + core/src/ConcentricFinder.cpp | 2 +- core/src/MultiFormatReader.cpp | 2 +- core/src/qrcode/QRBitMatrixParser.cpp | 93 ++++++++++-- core/src/qrcode/QRBitMatrixParser.h | 6 +- core/src/qrcode/QRCodecMode.cpp | 34 ++++- core/src/qrcode/QRCodecMode.h | 17 ++- core/src/qrcode/QRDataBlock.cpp | 2 + core/src/qrcode/QRDataMask.h | 16 ++- core/src/qrcode/QRDecoder.cpp | 45 ++++-- core/src/qrcode/QRDecoder.h | 2 +- core/src/qrcode/QRDetector.cpp | 92 +++++++++++- core/src/qrcode/QRDetector.h | 9 +- core/src/qrcode/QRErrorCorrectionLevel.cpp | 12 +- core/src/qrcode/QRErrorCorrectionLevel.h | 2 +- core/src/qrcode/QRFormatInformation.cpp | 102 ++++++++++--- core/src/qrcode/QRFormatInformation.h | 7 +- core/src/qrcode/QRReader.cpp | 78 +++++++--- core/src/qrcode/QRReader.h | 2 +- core/src/qrcode/QRVersion.cpp | 88 ++++++++---- core/src/qrcode/QRVersion.h | 22 ++- example/ZXingQtReader.h | 3 +- test/blackbox/BlackboxTestRunner.cpp | 8 ++ test/samples/microqrcode-1/1.png | Bin 0 -> 8513 bytes test/samples/microqrcode-1/1.txt | 1 + test/samples/microqrcode-1/11.jpg | Bin 0 -> 29581 bytes test/samples/microqrcode-1/11.txt | 1 + test/samples/microqrcode-1/12.jpg | Bin 0 -> 19328 bytes test/samples/microqrcode-1/12.txt | 1 + test/samples/microqrcode-1/3.jpg | Bin 0 -> 27818 bytes test/samples/microqrcode-1/3.txt | 1 + test/samples/microqrcode-1/7.jpg | Bin 0 -> 27342 bytes test/samples/microqrcode-1/7.txt | 1 + test/samples/microqrcode-1/9.jpg | Bin 0 -> 9245 bytes test/samples/microqrcode-1/9.txt | 1 + test/samples/microqrcode-1/M1-Numeric.png | Bin 0 -> 131 bytes test/samples/microqrcode-1/M1-Numeric.txt | 1 + test/samples/microqrcode-1/M2-Alpha.png | Bin 0 -> 146 bytes test/samples/microqrcode-1/M2-Alpha.txt | 1 + test/samples/microqrcode-1/M2-Numeric.png | Bin 0 -> 144 bytes test/samples/microqrcode-1/M2-Numeric.txt | 1 + test/samples/microqrcode-1/M3-Binary.png | Bin 0 -> 152 bytes test/samples/microqrcode-1/M3-Binary.txt | 1 + test/samples/microqrcode-1/M3-Kanji.png | Bin 0 -> 153 bytes test/samples/microqrcode-1/M3-Kanji.txt | 1 + test/samples/microqrcode-1/M3-Mixed.png | Bin 0 -> 150 bytes test/samples/microqrcode-1/M3-Mixed.txt | 1 + test/samples/microqrcode-1/M4-Binary.png | Bin 0 -> 168 bytes test/samples/microqrcode-1/M4-Binary.txt | 1 + test/samples/microqrcode-1/M4-Mixed.png | Bin 0 -> 167 bytes test/samples/microqrcode-1/M4-Mixed.txt | 1 + .../microqrcode-1/MQR-needs-br-update.jpg | Bin 0 -> 2002 bytes .../microqrcode-1/MQR-needs-br-update.txt | 1 + test/samples/microqrcode-1/NoQuietZone.png | Bin 0 -> 112 bytes test/samples/microqrcode-1/NoQuietZone.txt | 1 + test/unit/CMakeLists.txt | 2 + test/unit/qrcode/MQRDecoderTest.cpp | 135 ++++++++++++++++++ test/unit/qrcode/QRBitMatrixParserTest.cpp | 84 +++++++++++ test/unit/qrcode/QRDataMaskTest.cpp | 39 ++++- .../qrcode/QRErrorCorrectionLevelTest.cpp | 23 +++ test/unit/qrcode/QRFormatInformationTest.cpp | 47 +++++- test/unit/qrcode/QRModeTest.cpp | 32 +++++ test/unit/qrcode/QRVersionTest.cpp | 44 +++++- .../zxingcpp/src/main/cpp/BarcodeReader.cpp | 1 + .../main/java/com/zxingcpp/BarcodeReader.kt | 2 +- wrappers/python/zxing.cpp | 1 + wrappers/winrt/BarcodeReader.cpp | 4 + wrappers/winrt/BarcodeReader.h | 1 + 71 files changed, 965 insertions(+), 140 deletions(-) create mode 100644 test/samples/microqrcode-1/1.png create mode 100644 test/samples/microqrcode-1/1.txt create mode 100644 test/samples/microqrcode-1/11.jpg create mode 100644 test/samples/microqrcode-1/11.txt create mode 100644 test/samples/microqrcode-1/12.jpg create mode 100644 test/samples/microqrcode-1/12.txt create mode 100644 test/samples/microqrcode-1/3.jpg create mode 100644 test/samples/microqrcode-1/3.txt create mode 100644 test/samples/microqrcode-1/7.jpg create mode 100644 test/samples/microqrcode-1/7.txt create mode 100644 test/samples/microqrcode-1/9.jpg create mode 100644 test/samples/microqrcode-1/9.txt create mode 100644 test/samples/microqrcode-1/M1-Numeric.png create mode 100644 test/samples/microqrcode-1/M1-Numeric.txt create mode 100644 test/samples/microqrcode-1/M2-Alpha.png create mode 100644 test/samples/microqrcode-1/M2-Alpha.txt create mode 100644 test/samples/microqrcode-1/M2-Numeric.png create mode 100644 test/samples/microqrcode-1/M2-Numeric.txt create mode 100644 test/samples/microqrcode-1/M3-Binary.png create mode 100644 test/samples/microqrcode-1/M3-Binary.txt create mode 100644 test/samples/microqrcode-1/M3-Kanji.png create mode 100644 test/samples/microqrcode-1/M3-Kanji.txt create mode 100644 test/samples/microqrcode-1/M3-Mixed.png create mode 100644 test/samples/microqrcode-1/M3-Mixed.txt create mode 100644 test/samples/microqrcode-1/M4-Binary.png create mode 100644 test/samples/microqrcode-1/M4-Binary.txt create mode 100644 test/samples/microqrcode-1/M4-Mixed.png create mode 100644 test/samples/microqrcode-1/M4-Mixed.txt create mode 100644 test/samples/microqrcode-1/MQR-needs-br-update.jpg create mode 100644 test/samples/microqrcode-1/MQR-needs-br-update.txt create mode 100644 test/samples/microqrcode-1/NoQuietZone.png create mode 100644 test/samples/microqrcode-1/NoQuietZone.txt create mode 100644 test/unit/qrcode/MQRDecoderTest.cpp create mode 100644 test/unit/qrcode/QRBitMatrixParserTest.cpp diff --git a/core/src/BarcodeFormat.cpp b/core/src/BarcodeFormat.cpp index fddb6eface..e83df3ecfb 100644 --- a/core/src/BarcodeFormat.cpp +++ b/core/src/BarcodeFormat.cpp @@ -48,6 +48,7 @@ static BarcodeFormatName NAMES[] = { {BarcodeFormat::EAN13, "EAN-13"}, {BarcodeFormat::ITF, "ITF"}, {BarcodeFormat::MaxiCode, "MaxiCode"}, + {BarcodeFormat::MicroQRCode, "MicroQRCode"}, {BarcodeFormat::PDF417, "PDF417"}, {BarcodeFormat::QRCode, "QRCode"}, {BarcodeFormat::UPCA, "UPC-A"}, diff --git a/core/src/BarcodeFormat.h b/core/src/BarcodeFormat.h index 2858ed57e6..8bb5d3681a 100644 --- a/core/src/BarcodeFormat.h +++ b/core/src/BarcodeFormat.h @@ -47,9 +47,10 @@ enum class BarcodeFormat QRCode = (1 << 13), ///< QR Code (2D) UPCA = (1 << 14), ///< UPC-A (1D) UPCE = (1 << 15), ///< UPC-E (1D) + MicroQRCode = (1 << 16), ///< Micro QR Code (2D) OneDCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, - TwoDCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode, + TwoDCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, Any = OneDCodes | TwoDCodes, // Deprecated names, kept for compatibility at the moment @@ -70,7 +71,7 @@ enum class BarcodeFormat UPC_A [[deprecated]] = UPCA, UPC_E [[deprecated]] = UPCE, - _max = UPCE, ///> implementation detail, don't use + _max = MicroQRCode, ///> implementation detail, don't use }; ZX_DECLARE_FLAGS(BarcodeFormats, BarcodeFormat) diff --git a/core/src/BitSource.cpp b/core/src/BitSource.cpp index 6580bd6649..569014354c 100644 --- a/core/src/BitSource.cpp +++ b/core/src/BitSource.cpp @@ -30,10 +30,9 @@ BitSource::available() const return 8 * (Size(_bytes) - _byteOffset) - _bitOffset; } -int -BitSource::readBits(int numBits) +static int ReadBitsImpl(int numBits, const ByteArray& _bytes, int available, int& _byteOffset, int& _bitOffset) { - if (numBits < 1 || numBits > 32 || numBits > available()) { + if (numBits < 1 || numBits > 32 || numBits > available) { throw std::out_of_range("BitSource::readBits: out of range"); } @@ -74,4 +73,16 @@ BitSource::readBits(int numBits) return result; } +int BitSource::readBits(int numBits) +{ + return ReadBitsImpl(numBits, _bytes, available(), _byteOffset, _bitOffset); +} + +int BitSource::peakBits(int numBits) const +{ + int bitOffset = _bitOffset; + int byteOffset = _byteOffset; + return ReadBitsImpl(numBits, _bytes, available(), byteOffset, bitOffset); +} + } // ZXing diff --git a/core/src/BitSource.h b/core/src/BitSource.h index be55d42158..f9d0c2b064 100644 --- a/core/src/BitSource.h +++ b/core/src/BitSource.h @@ -68,6 +68,13 @@ class BitSource */ int readBits(int numBits); + /** + * @param numBits number of bits to peak + * @return int representing the bits peaked. The bits will appear as the least-significant + * bits of the int + */ + int peakBits(int numBits) const; + /** * @return number of bits that can be read successfully */ diff --git a/core/src/ConcentricFinder.cpp b/core/src/ConcentricFinder.cpp index 830e31bd2b..e53740d595 100644 --- a/core/src/ConcentricFinder.cpp +++ b/core/src/ConcentricFinder.cpp @@ -159,7 +159,7 @@ static QuadrilateralF FitQadrilateralToPoints(PointF center, std::vector corners[3] = std::max_element(&points[Size(points) * 5 / 8], &points[Size(points) * 7 / 8], dist2Diagonal); std::array lines{RegressionLine{corners[0] + 1, corners[1]}, RegressionLine{corners[1] + 1, corners[2]}, - RegressionLine{corners[2] + 1, corners[3]}, RegressionLine{corners[3] + 1, &(*points.end())}}; + RegressionLine{corners[2] + 1, corners[3]}, RegressionLine{corners[3] + 1, &points.back() + 1}}; QuadrilateralF res; for (int i = 0; i < 4; ++i) diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index 391bd72584..3b9a4b2d3f 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -39,7 +39,7 @@ MultiFormatReader::MultiFormatReader(const DecodeHints& hints) if (formats.testFlags(BarcodeFormat::OneDCodes) && !tryHarder) _readers.emplace_back(new OneD::Reader(hints)); - if (formats.testFlag(BarcodeFormat::QRCode)) + if (formats.testFlags(BarcodeFormat::QRCode | BarcodeFormat::MicroQRCode)) _readers.emplace_back(new QRCode::Reader(hints)); if (formats.testFlag(BarcodeFormat::DataMatrix)) _readers.emplace_back(new DataMatrix::Reader(hints)); diff --git a/core/src/qrcode/QRBitMatrixParser.cpp b/core/src/qrcode/QRBitMatrixParser.cpp index ab599a7b67..d54991cbaf 100644 --- a/core/src/qrcode/QRBitMatrixParser.cpp +++ b/core/src/qrcode/QRBitMatrixParser.cpp @@ -33,22 +33,26 @@ static bool getBit(const BitMatrix& bitMatrix, int x, int y, bool mirrored) return mirrored ? bitMatrix.get(y, x) : bitMatrix.get(x, y); } -static bool hasValidDimension(const BitMatrix& bitMatrix) +static bool hasValidDimension(const BitMatrix& bitMatrix, bool isMicro) { int dimension = bitMatrix.height(); - return dimension >= 21 && dimension <= 177 && (dimension % 4) == 1; + if (isMicro) + return dimension >= 11 && dimension <= 17 && (dimension % 2) == 1; + else + return dimension >= 21 && dimension <= 177 && (dimension % 4) == 1; } -const Version* ReadVersion(const BitMatrix& bitMatrix) +const Version* ReadVersion(const BitMatrix& bitMatrix, bool isMicro) { - if (!hasValidDimension(bitMatrix)) + if (!hasValidDimension(bitMatrix, isMicro)) return nullptr; int dimension = bitMatrix.height(); - int provisionalVersion = (dimension - 17) / 4; + int provisionalVersion = (dimension - Version::DimensionOffset(isMicro)) / Version::DimensionStep(isMicro); + if (provisionalVersion <= 6) - return Version::VersionForNumber(provisionalVersion); + return Version::VersionForNumber(provisionalVersion, isMicro); for (bool mirror : {false, true}) { // Read top-right/bottom-left version info: 3 wide by 6 tall (depending on mirrored) @@ -66,11 +70,22 @@ const Version* ReadVersion(const BitMatrix& bitMatrix) return nullptr; } -FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrored) +FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrored, bool isMicro) { - if (!hasValidDimension(bitMatrix)) + 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, mirrored)); + for (int y = 7; y >= 1; y--) + AppendBit(formatInfoBits, getBit(bitMatrix, 8, y, mirrored)); + + return FormatInformation::DecodeFormatInformation(formatInfoBits); + } + // Read top-left format info bits int formatInfoBits1 = 0; for (int x = 0; x < 6; x++) @@ -94,11 +109,8 @@ FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrore return FormatInformation::DecodeFormatInformation(formatInfoBits1, formatInfoBits2); } -ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, int maskIndex, bool mirrored) +static ByteArray ReadQRCodewords(const BitMatrix& bitMatrix, const Version& version, int maskIndex, bool mirrored) { - if (!hasValidDimension(bitMatrix)) - return {}; - BitMatrix functionPattern = version.buildFunctionPattern(); ByteArray result; @@ -135,4 +147,61 @@ ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, int return result; } +static ByteArray ReadMQRCodewords(const BitMatrix& bitMatrix, const QRCode::Version& version, + const FormatInformation& formatInformation, bool mirrored) +{ + 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 : (formatInformation.errorCorrectionLevel() == 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(formatInformation.dataMask(), xx, y, true) != getBit(bitMatrix, xx, y, mirrored)); + ++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; +} + +ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInformation, + bool mirrored) +{ + if (!hasValidDimension(bitMatrix, version.isMicroQRCode())) + return {}; + + return version.isMicroQRCode() ? ReadMQRCodewords(bitMatrix, version, formatInformation, mirrored) + : ReadQRCodewords(bitMatrix, version, formatInformation.dataMask(), mirrored); +} + } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRBitMatrixParser.h b/core/src/qrcode/QRBitMatrixParser.h index 3f0a9d9900..c911389b6f 100644 --- a/core/src/qrcode/QRBitMatrixParser.h +++ b/core/src/qrcode/QRBitMatrixParser.h @@ -30,20 +30,20 @@ class FormatInformation; * @brief Reads version information from the QR Code. * @return {@link Version} encapsulating the QR Code's version, nullptr if neither location can be parsed */ -const Version* ReadVersion(const BitMatrix& bitMatrix); +const Version* ReadVersion(const BitMatrix& bitMatrix, bool isMicro); /** * @brief Reads format information from one of its two locations within the QR Code. * @return {@link FormatInformation} encapsulating the QR Code's format info, result is invalid if both format * information locations cannot be parsed as the valid encoding of format information */ -FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrored); +FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrored, bool isMicro); /** * @brief Reads the codewords from the BitMatrix. * @return bytes encoded within the QR Code or empty array if the exact number of bytes expected is not read */ -ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, int maskIndex, bool mirrored); +ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInformation, bool mirrored); } // QRCode } // ZXing diff --git a/core/src/qrcode/QRCodecMode.cpp b/core/src/qrcode/QRCodecMode.cpp index a64e6bd9c6..f0e635128a 100644 --- a/core/src/qrcode/QRCodecMode.cpp +++ b/core/src/qrcode/QRCodecMode.cpp @@ -18,16 +18,23 @@ #include "QRCodecMode.h" #include "QRVersion.h" +#include "ZXContainerAlgorithms.h" #include #include namespace ZXing::QRCode { -CodecMode CodecModeForBits(int bits) +CodecMode CodecModeForBits(int bits, bool isMirco) { - if ((bits >= 0x00 && bits <= 0x05) || (bits >= 0x07 && bits <= 0x09) || bits == 0x0d) - return static_cast(bits); + if (!isMirco) { + if ((bits >= 0x00 && bits <= 0x05) || (bits >= 0x07 && bits <= 0x09) || bits == 0x0d) + return static_cast(bits); + } else { + constexpr CodecMode Bits2Mode[4] = {CodecMode::NUMERIC, CodecMode::ALPHANUMERIC, CodecMode::BYTE, CodecMode::KANJI}; + if (bits < Size(Bits2Mode)) + return Bits2Mode[bits]; + } throw std::invalid_argument("Invalid mode"); } @@ -35,6 +42,17 @@ CodecMode CodecModeForBits(int bits) int CharacterCountBits(CodecMode mode, const Version& version) { int number = version.versionNumber(); + if (version.isMicroQRCode()) { + switch (mode) { + case CodecMode::NUMERIC: return std::array{3, 4, 5, 6}[number - 1]; + case CodecMode::ALPHANUMERIC: return std::array{3, 4, 5}[number - 2]; + case CodecMode::BYTE: return std::array{4, 5}[number - 3]; + case CodecMode::KANJI: [[fallthrough]]; + case CodecMode::HANZI: return std::array{3, 4}[number - 3]; + default: return 0; + } + } + int i; if (number <= 9) i = 0; @@ -53,4 +71,14 @@ int CharacterCountBits(CodecMode mode, const Version& version) } } +int CodecModeBitsLength(const Version& version) +{ + return version.isMicroQRCode() ? version.versionNumber() - 1 : 4; +} + +int TerminatorBitsLength(const Version& version) +{ + return version.isMicroQRCode() ? version.versionNumber() * 2 + 1 : 4; +} + } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRCodecMode.h b/core/src/qrcode/QRCodecMode.h index 066c143fd4..eca59e12ca 100644 --- a/core/src/qrcode/QRCodecMode.h +++ b/core/src/qrcode/QRCodecMode.h @@ -40,11 +40,12 @@ enum class CodecMode }; /** - * @param bits four bits encoding a QR Code data mode + * @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 IllegalArgumentException if bits do not correspond to a known mode */ -CodecMode CodecModeForBits(int bits); +CodecMode CodecModeForBits(int bits, bool isMirco = false); /** * @param version version in question @@ -53,5 +54,17 @@ CodecMode CodecModeForBits(int bits); */ int CharacterCountBits(CodecMode mode, const Version& version); +/** + * @param version version in question + * @return number of bits used to encode a codec mode. + */ +int CodecModeBitsLength(const Version& version); + +/** + * @param version version in question + * @return number of bits in the Terminator code. + */ +int TerminatorBitsLength(const Version& version); + } // QRCode } // ZXing diff --git a/core/src/qrcode/QRDataBlock.cpp b/core/src/qrcode/QRDataBlock.cpp index 7c04f5e760..2b005aa48f 100644 --- a/core/src/qrcode/QRDataBlock.cpp +++ b/core/src/qrcode/QRDataBlock.cpp @@ -34,6 +34,8 @@ std::vector DataBlock::GetDataBlocks(const ByteArray& rawCodewords, c // First count the total number of data blocks int totalBlocks = ecBlocks.numBlocks(); + if (totalBlocks == 0) + return {}; std::vector result(totalBlocks); // Now establish DataBlocks of the appropriate size and number of data codewords diff --git a/core/src/qrcode/QRDataMask.h b/core/src/qrcode/QRDataMask.h index 50045cda3a..8b43b9ab86 100644 --- a/core/src/qrcode/QRDataMask.h +++ b/core/src/qrcode/QRDataMask.h @@ -18,20 +18,27 @@ #include "BitMatrix.h" +#include #include namespace ZXing { namespace QRCode { /** -*

Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8.

+*

Encapsulates data masks for the data bits in a QR and micro QR code, per ISO 18004:2006 6.8.

* *

Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position * and j is row position. In fact, as the text says, i is row position and j is column position.

*/ -inline bool GetDataMaskBit(int maskIndex, int x, int y) +inline bool GetDataMaskBit(int maskIndex, int x, int y, bool isMicro = false) { + if (isMicro) { + if (maskIndex < 0 || maskIndex >= 4) + throw std::invalid_argument("QRCode maskIndex out of range"); + maskIndex = std::array{1, 4, 6, 7}[maskIndex]; // map from MQR to QR indices + } + switch (maskIndex) { case 0: return (y + x) % 2 == 0; case 1: return y % 2 == 0; @@ -42,12 +49,13 @@ inline bool GetDataMaskBit(int maskIndex, int x, int y) case 6: return ((y * x) % 6) < 3; case 7: return (y + x + ((y * x) % 3)) % 2 == 0; } + throw std::invalid_argument("QRCode maskIndex out of range"); } -inline bool GetMaskedBit(const BitMatrix& bits, int x, int y, int maskIndex) +inline bool GetMaskedBit(const BitMatrix& bits, int x, int y, int maskIndex, bool isMicro = false) { - return GetDataMaskBit(maskIndex, x, y) != bits.get(x, y); + return GetDataMaskBit(maskIndex, x, y, isMicro) != bits.get(x, y); } } // QRCode diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 8092147adc..6e5c259814 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -28,6 +28,7 @@ #include "QRCodecMode.h" #include "QRDataBlock.h" #include "QRFormatInformation.h" +#include "QRVersion.h" #include "ReedSolomonDecoder.h" #include "StructuredAppend.h" #include "TextDecoder.h" @@ -290,6 +291,30 @@ static DecodeStatus ParseECIValue(BitSource& bits, int& outValue) return DecodeStatus::FormatError; } +/** + * 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:2006, 6.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 IsTerminator(const BitSource& bits, const Version& version) +{ + const int bitsRequired = TerminatorBitsLength(version); + const int bitsAvailable = std::min(bits.available(), bitsRequired); + return bits.peakBits(bitsAvailable) == 0; +} + /** *

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.

@@ -306,6 +331,8 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo int appIndValue = -1; // ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator (FNC1 in second position) StructuredAppendInfo structuredAppend; static const int GB2312_SUBSET = 1; + const int modeBitLength = CodecModeBitsLength(version); + const int minimumBitsRequired = modeBitLength + CharacterCountBits(CodecMode::NUMERIC, version); try { @@ -314,12 +341,14 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo CodecMode mode; do { // While still another segment to read... - if (bits.available() < 4) { + if (bits.available() < minimumBitsRequired || IsTerminator(bits, version)) { // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here mode = CodecMode::TERMINATOR; - } - else { - mode = CodecModeForBits(bits.readBits(4)); // mode is encoded by 4 bits + } else if (modeBitLength == 0) { + // MicroQRCode version 1 is always NUMERIC and modeBitLength is 0 + mode = CodecMode::NUMERIC; + } else { + mode = CodecModeForBits(bits.readBits(modeBitLength), version.isMicroQRCode()); } switch (mode) { case CodecMode::TERMINATOR: @@ -423,12 +452,12 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo static DecoderResult DoDecode(const BitMatrix& bits, const Version& version, const std::string& hintedCharset, bool mirrored) { - auto formatInfo = ReadFormatInformation(bits, mirrored); + auto formatInfo = ReadFormatInformation(bits, mirrored, version.isMicroQRCode()); if (!formatInfo.isValid()) return DecodeStatus::FormatError; // Read codewords - ByteArray codewords = ReadCodewords(bits, version, formatInfo.dataMask(), mirrored); + ByteArray codewords = ReadCodewords(bits, version, formatInfo, mirrored); if (codewords.empty()) return DecodeStatus::FormatError; @@ -459,9 +488,9 @@ static DecoderResult DoDecode(const BitMatrix& bits, const Version& version, con return DecodeBitStream(std::move(resultBytes), version, formatInfo.errorCorrectionLevel(), hintedCharset); } -DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset) +DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset, const bool isMicroQRCode) { - const Version* version = ReadVersion(bits); + const Version* version = ReadVersion(bits, isMicroQRCode); if (!version) return DecodeStatus::FormatError; diff --git a/core/src/qrcode/QRDecoder.h b/core/src/qrcode/QRDecoder.h index e66a7c1229..e8171b036b 100644 --- a/core/src/qrcode/QRDecoder.h +++ b/core/src/qrcode/QRDecoder.h @@ -28,7 +28,7 @@ namespace QRCode { /** * @brief Decodes a QR Code from the BitMatrix and the hinted charset. */ -DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset); +DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset, const bool isMicroQRCode = false); } // QRCode } // ZXing diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 48afd325ae..a50202c97a 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -18,13 +18,15 @@ #include "QRDetector.h" +#include "BitArray.h" #include "BitMatrix.h" #include "BitMatrixCursor.h" #include "ConcentricFinder.h" -#include "DetectorResult.h" #include "GridSampler.h" #include "LogMatrix.h" #include "Pattern.h" +#include "QRFormatInformation.h" +#include "QRVersion.h" #include "Quadrilateral.h" #include "RegressionLine.h" @@ -41,10 +43,8 @@ namespace ZXing::QRCode { constexpr auto PATTERN = FixedPattern<5, 7>{1, 1, 3, 1, 1}; -constexpr int MIN_MODULES = 1 * 4 + 17; // version 1 -constexpr int MAX_MODULES = 40 * 4 + 17; // version 40 -static auto FindFinderPatterns(const BitMatrix& image, bool tryHarder) +std::vector FindFinderPatterns(const BitMatrix& image, bool tryHarder) { constexpr int MIN_SKIP = 3; // 1 pixel/module times 3 modules/center constexpr int MAX_MODULES_FAST = 20 * 4 + 17; // support up to version 20 for mobile clients @@ -93,7 +93,7 @@ static auto FindFinderPatterns(const BitMatrix& image, bool tryHarder) * @param patterns list of ConcentricPattern objects, i.e. found finder pattern squares * @return list of plausible finder pattern sets, sorted by decreasing plausibility */ -static FinderPatternSets GenerateFinderPatternSets(std::vector&& patterns) +FinderPatternSets GenerateFinderPatternSets(FinderPatterns&& patterns) { std::sort(patterns.begin(), patterns.end(), [](const auto& a, const auto& b) { return a.size < b.size; }); @@ -324,7 +324,7 @@ DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatt * around it. This is a specialized method that works exceptionally fast in this special * case. */ -static DetectorResult DetectPure(const BitMatrix& image) +DetectorResult DetectPure(const BitMatrix& image) { using Pattern = std::array; @@ -332,6 +332,9 @@ static DetectorResult DetectPure(const BitMatrix& image) SaveAsPBM(image, "weg.pbm"); #endif + constexpr int MIN_MODULES = Version::DimensionOfVersion(1, false); + constexpr int MAX_MODULES = Version::DimensionOfVersion(40, false); + int left, top, width, height; if (!image.findBoundingBox(left, top, width, height, MIN_MODULES) || std::abs(width - height) > 1) return {}; @@ -369,6 +372,46 @@ static DetectorResult DetectPure(const BitMatrix& image) {{left, top}, {right, top}, {right, bottom}, {left, bottom}}}; } +DetectorResult DetectPureMicroQR(const BitMatrix& image) +{ + using Pattern = std::array; + + constexpr int MIN_MODULES = Version::DimensionOfVersion(1, true); + constexpr int MAX_MODULES = Version::DimensionOfVersion(4, true); + + int left, top, width, height; + if (!image.findBoundingBox(left, top, width, height, MIN_MODULES) || std::abs(width - height) > 1) + return {}; + int right = left + width - 1; + int bottom = top + height - 1; + + // allow corners be moved one pixel inside to accommodate for possible aliasing artifacts + auto diagonal = BitMatrixCursorI(image, {left, top}, {1, 1}).readPatternFromBlack(1); + if (!IsPattern(diagonal, PATTERN)) + return {}; + + auto fpWidth = Reduce(diagonal); + float moduleSize = float(fpWidth) / 7; + auto dimension = width / moduleSize; + + if (dimension < MIN_MODULES || dimension > MAX_MODULES || + !image.isIn(PointF{left + moduleSize / 2 + (dimension - 1) * moduleSize, + top + moduleSize / 2 + (dimension - 1) * moduleSize})) + return {}; + +#ifdef PRINT_DEBUG + LogMatrix log; + LogMatrixWriter lmw(log, image, 5, "grid2.pnm"); + for (int y = 0; y < dimension; y++) + for (int x = 0; x < dimension; x++) + log(PointF(left + (x + .5f) * moduleSize, top + (y + .5f) * moduleSize)); +#endif + + // Now just read off the bits (this is a crop + subsample) + return {Deflate(image, dimension, dimension, top + moduleSize / 2, left + moduleSize / 2, moduleSize), + {{left, top}, {right, top}, {right, bottom}, {left, bottom}}}; +} + FinderPatternSets FindFinderPatternSets(const BitMatrix& image, bool tryHarder) { return GenerateFinderPatternSets(FindFinderPatterns(image, tryHarder)); @@ -395,4 +438,41 @@ DetectorResult Detect(const BitMatrix& image, bool tryHarder, bool isPure) return SampleAtFinderPatternSet(image, sets[0]); } +DetectorResult SampleAtFinderPattern(const BitMatrix& image, const ConcentricPattern& fp) +{ + auto fpQuad = FindConcentricPatternCorners(image, fp, fp.size, 2); + if (!fpQuad) + return {}; + + auto srcQuad = Rectangle(7, 7, 0.5); + + constexpr PointI FORMAT_INFO_COORDS[] = {{0, 8}, {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {6, 8}, {7, 8}, {8, 8}, + {8, 7}, {8, 6}, {8, 5}, {8, 4}, {8, 3}, {8, 2}, {8, 1}, {8, 0}}; + + for (int i = 0; i < 4; ++i) { + auto mod2Pix = PerspectiveTransform(srcQuad, RotatedCorners(*fpQuad, i)); + + auto check = [&](int i, bool checkOne) { + auto p = mod2Pix(centered(FORMAT_INFO_COORDS[i])); + return image.isIn(p) && (!checkOne || image.get(p)); + }; + + // check that we see both innermost timing pattern modules + if (!check(0, true) || !check(8, false) || !check(16, true)) + continue; + + int formatInfoBits = 0; + for (int i = 1; i <= 15; ++i) + AppendBit(formatInfoBits, image.get(mod2Pix(centered(FORMAT_INFO_COORDS[i])))); + + auto fi = FormatInformation::DecodeFormatInformation(formatInfoBits); + if (fi.isValid() && fi.microVersion()) { + const int dim = Version::DimensionOfVersion(fi.microVersion(), true); + return SampleGrid(image, dim, dim, mod2Pix); + } + } + + return {}; +} + } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRDetector.h b/core/src/qrcode/QRDetector.h index b11975fffa..36f1b90bd1 100644 --- a/core/src/qrcode/QRDetector.h +++ b/core/src/qrcode/QRDetector.h @@ -18,6 +18,7 @@ */ #include "ConcentricFinder.h" +#include "DetectorResult.h" #include @@ -33,10 +34,16 @@ struct FinderPatternSet ConcentricPattern bl, tl, tr; }; +using FinderPatterns = std::vector; using FinderPatternSets = std::vector; -FinderPatternSets FindFinderPatternSets(const BitMatrix& image, bool tryHarder); +FinderPatterns FindFinderPatterns(const BitMatrix& image, bool tryHarder); +FinderPatternSets GenerateFinderPatternSets(FinderPatterns&& patterns); DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatternSet& fp); +DetectorResult SampleAtFinderPattern(const BitMatrix& image, const ConcentricPattern& fp); + +DetectorResult DetectPureMicroQR(const BitMatrix& image); +DetectorResult DetectPure(const BitMatrix& image); /** * @brief Detects a QR Code in an image. diff --git a/core/src/qrcode/QRErrorCorrectionLevel.cpp b/core/src/qrcode/QRErrorCorrectionLevel.cpp index 3c9f9c9f86..2a3b93ff15 100644 --- a/core/src/qrcode/QRErrorCorrectionLevel.cpp +++ b/core/src/qrcode/QRErrorCorrectionLevel.cpp @@ -39,10 +39,16 @@ ErrorCorrectionLevel ECLevelFromString(const char* str) } } -ErrorCorrectionLevel ECLevelFromBits(int bits) +ErrorCorrectionLevel ECLevelFromBits(int bits, const bool isMicro) { - static const ErrorCorrectionLevel LEVEL_FOR_BITS[] = {ErrorCorrectionLevel::Medium, ErrorCorrectionLevel::Low, - ErrorCorrectionLevel::High, ErrorCorrectionLevel::Quality}; + if (isMicro) { + constexpr ErrorCorrectionLevel LEVEL_FOR_BITS[] = { + ErrorCorrectionLevel::Low, ErrorCorrectionLevel::Low, ErrorCorrectionLevel::Medium, ErrorCorrectionLevel::Low, + ErrorCorrectionLevel::Medium, ErrorCorrectionLevel::Low, ErrorCorrectionLevel::Medium, ErrorCorrectionLevel::Quality}; + return LEVEL_FOR_BITS[bits & 0x07]; + } + constexpr ErrorCorrectionLevel LEVEL_FOR_BITS[] = {ErrorCorrectionLevel::Medium, ErrorCorrectionLevel::Low, + ErrorCorrectionLevel::High, ErrorCorrectionLevel::Quality}; return LEVEL_FOR_BITS[bits & 0x3]; } diff --git a/core/src/qrcode/QRErrorCorrectionLevel.h b/core/src/qrcode/QRErrorCorrectionLevel.h index 2000543da0..1993fffdda 100644 --- a/core/src/qrcode/QRErrorCorrectionLevel.h +++ b/core/src/qrcode/QRErrorCorrectionLevel.h @@ -36,7 +36,7 @@ enum class ErrorCorrectionLevel const wchar_t* ToString(ErrorCorrectionLevel l); ErrorCorrectionLevel ECLevelFromString(const char* str); -ErrorCorrectionLevel ECLevelFromBits(int bits); +ErrorCorrectionLevel ECLevelFromBits(int bits, const bool isMicro = false); int BitsFromECLevel(ErrorCorrectionLevel l); } // QRCode diff --git a/core/src/qrcode/QRFormatInformation.cpp b/core/src/qrcode/QRFormatInformation.cpp index bc32436f2d..9bea8c0247 100644 --- a/core/src/qrcode/QRFormatInformation.cpp +++ b/core/src/qrcode/QRFormatInformation.cpp @@ -28,7 +28,7 @@ static const int FORMAT_INFO_MASK_QR = 0x5412; /** * See ISO 18004:2006, Annex C, Table C.1 */ -static const std::array FORMAT_INFO_DECODE_LOOKUP[] = { +static const std::array, 32> FORMAT_INFO_DECODE_LOOKUP = {{ {0x5412, 0x00}, {0x5125, 0x01}, {0x5E7C, 0x02}, @@ -61,14 +61,64 @@ static const std::array FORMAT_INFO_DECODE_LOOKUP[] = { {0x2183, 0x1D}, {0x2EDA, 0x1E}, {0x2BED, 0x1F}, -}; +}}; -FormatInformation::FormatInformation(int formatInfo) +static const std::array, 32> FORMAT_INFO_DECODE_LOOKUP_MICRO = {{ + {0x4445, 0x00}, + {0x4172, 0x01}, + {0x4E2B, 0x02}, + {0x4B1C, 0x03}, + {0x55AE, 0x04}, + {0x5099, 0x05}, + {0x5FC0, 0x06}, + {0x5AF7, 0x07}, + {0x6793, 0x08}, + {0x62A4, 0x09}, + {0x6DFD, 0x0A}, + {0x68CA, 0x0B}, + {0x7678, 0x0C}, + {0x734F, 0x0D}, + {0x7C16, 0x0E}, + {0x7921, 0x0F}, + {0x06DE, 0x10}, + {0x03E9, 0x11}, + {0x0CB0, 0x12}, + {0x0987, 0x13}, + {0x1735, 0x14}, + {0x1202, 0x15}, + {0x1D5B, 0x16}, + {0x186C, 0x17}, + {0x2508, 0x18}, + {0x203F, 0x19}, + {0x2F66, 0x1A}, + {0x2A51, 0x1B}, + {0x34E3, 0x1C}, + {0x31D4, 0x1D}, + {0x3E8D, 0x1E}, + {0x3BBA, 0x1F}, +}}; + +static int FindBestFormatInfo(int mask, const std::array, 32> lookup, const std::vector& bits) { - // Bits 3,4 - _errorCorrectionLevel = ECLevelFromBits((formatInfo >> 3) & 0x03); - // Bottom 3 bits - _dataMask = static_cast(formatInfo & 0x07); + // Find the int in lookup with fewest bits differing + int bestDifference = 32; + int bestFormatInfo = -1; + + // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. + for (auto mask : {0, mask}) + for (uint32_t bits : bits) + for (const auto& [pattern, decodedInfo] : lookup) + if (int bitsDifference = BitHacks::CountBitsSet((bits ^ mask) ^ pattern); bitsDifference < bestDifference) { + bestFormatInfo = decodedInfo; + bestDifference = bitsDifference; + } + + // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits + // differing means we found a match + if (bestDifference <= 3) + return bestFormatInfo; + + return -1; } /** @@ -81,25 +131,31 @@ FormatInformation::FormatInformation(int formatInfo) FormatInformation FormatInformation::DecodeFormatInformation(uint32_t formatInfoBits1, uint32_t formatInfoBits2) { - // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing - int bestDifference = 32; - int bestFormatInfo = -1; + int bestFormatInfo = FindBestFormatInfo(FORMAT_INFO_MASK_QR, FORMAT_INFO_DECODE_LOOKUP, {formatInfoBits1, formatInfoBits2}); + if (bestFormatInfo < 0) + return {}; - // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. - for (auto mask : {0, FORMAT_INFO_MASK_QR}) - for (uint32_t bits : {formatInfoBits1 ^ mask, formatInfoBits2 ^ mask}) - for (auto& [pattern, decodedInfo] : FORMAT_INFO_DECODE_LOOKUP) - if (int bitsDifference = BitHacks::CountBitsSet(bits ^ pattern); bitsDifference < bestDifference) { - bestFormatInfo = decodedInfo; - bestDifference = bitsDifference; - } + // Use bits 3/4 for error correction, and 0-2 for mask. + return {ECLevelFromBits((bestFormatInfo >> 3) & 0x03), static_cast(bestFormatInfo & 0x07)}; +} - // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits - // differing means we found a match - if (bestDifference <= 3) - return {bestFormatInfo}; +/** + * @param formatInfoBits format info indicator, with mask still applied + * @return information about the format it specifies, or {@code null} + * if doesn't seem to match any known pattern + */ +FormatInformation FormatInformation::DecodeFormatInformation(uint32_t formatInfoBits) +{ + // We don't use the additional masking (with 0x4445) to work around potentially non complying MircoQRCode encoders + int bestFormatInfo = FindBestFormatInfo(0, FORMAT_INFO_DECODE_LOOKUP_MICRO, {formatInfoBits}); + if (bestFormatInfo < 0) + return {}; + + constexpr uint8_t BITS_TO_VERSION[] = {1, 2, 2, 3, 3, 4, 4, 4}; - return {}; + // Bits 2/3/4 contain both error correction level and version, 0/1 contain mask. + return {ECLevelFromBits((bestFormatInfo >> 2) & 0x07, true), static_cast(bestFormatInfo & 0x03), + BITS_TO_VERSION[(bestFormatInfo >> 2) & 0x07]}; } } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRFormatInformation.h b/core/src/qrcode/QRFormatInformation.h index 29a3f72e18..a560c62d33 100644 --- a/core/src/qrcode/QRFormatInformation.h +++ b/core/src/qrcode/QRFormatInformation.h @@ -37,10 +37,12 @@ class FormatInformation FormatInformation() = default; static FormatInformation DecodeFormatInformation(uint32_t formatInfoBits1, uint32_t formatInfoBits2); + static FormatInformation DecodeFormatInformation(uint32_t formatInfoBits); ErrorCorrectionLevel errorCorrectionLevel() const { return _errorCorrectionLevel; } uint8_t dataMask() const { return _dataMask; } + uint8_t microVersion() const { return _microVersion; } bool isValid() const { return _errorCorrectionLevel != ErrorCorrectionLevel::Invalid; } @@ -52,8 +54,11 @@ class FormatInformation private: ErrorCorrectionLevel _errorCorrectionLevel = ErrorCorrectionLevel::Invalid; uint8_t _dataMask = 0; + uint8_t _microVersion = 0; - FormatInformation(int formatInfo); + FormatInformation(const ErrorCorrectionLevel& errorCorrectionLevel, uint8_t dataMask, uint8_t microVersion = 0) + : _errorCorrectionLevel(errorCorrectionLevel), _dataMask(dataMask), _microVersion(microVersion) + {} }; } // QRCode diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index eb7c5c9b93..3d30b0f05b 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -1,6 +1,7 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors +* Copyright 2022 Axel Waggershauser * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +19,7 @@ #include "QRReader.h" #include "BinaryBitmap.h" +#include "ConcentricFinder.h" #include "DecodeHints.h" #include "DecoderResult.h" #include "DetectorResult.h" @@ -30,7 +32,13 @@ namespace ZXing::QRCode { -Reader::Reader(const DecodeHints& hints) : _tryHarder(hints.tryHarder()), _isPure(hints.isPure()), _charset(hints.characterSet()) {} +Reader::Reader(const DecodeHints& hints) + : _tryHarder(hints.tryHarder()), + _isPure(hints.isPure()), + _testQR(hints.hasFormat(BarcodeFormat::QRCode)), + _testMQR(hints.hasFormat(BarcodeFormat::MicroQRCode)), + _charset(hints.characterSet()) +{} Result Reader::decode(const BinaryBitmap& image) const { @@ -46,14 +54,22 @@ Result Reader::decode(const BinaryBitmap& image) const return Result(DecodeStatus::NotFound); } - auto detectorResult = Detect(*binImg, _tryHarder, _isPure); + bool isMicro = false; + DetectorResult detectorResult; + if (_testQR) + detectorResult = DetectPure(*binImg); + if (_testMQR && !detectorResult.isValid()) { + isMicro = true; + detectorResult = DetectPureMicroQR(*binImg); + } + if (!detectorResult.isValid()) return Result(DecodeStatus::NotFound); - auto decoderResult = Decode(detectorResult.bits(), _charset); + auto decoderResult = Decode(detectorResult.bits(), _charset, isMicro); auto position = detectorResult.position(); - return Result(std::move(decoderResult), std::move(position), BarcodeFormat::QRCode); + return Result(std::move(decoderResult), std::move(position), isMicro ? BarcodeFormat::MicroQRCode : BarcodeFormat::QRCode); } Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const @@ -66,25 +82,47 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const LogMatrixWriter lmw(log, *binImg, 5, "qr-log.pnm"); #endif - FinderPatternSets allFPSets = FindFinderPatternSets(*binImg, _tryHarder); + auto allFPs = FindFinderPatterns(*binImg, _tryHarder); + auto allFPSets = GenerateFinderPatternSets(std::move(allFPs)); + std::vector usedFPs; Results results; - for(auto& fpSet : allFPSets) { - if (Contains(usedFPs, fpSet.bl) || Contains(usedFPs, fpSet.tl) || Contains(usedFPs, fpSet.tr)) - continue; - - auto detectorResult = SampleAtFinderPatternSet(*binImg, fpSet); - if (detectorResult.isValid()) { - auto decoderResult = Decode(detectorResult.bits(), _charset); - auto position = detectorResult.position(); - if (decoderResult.isValid()) { - usedFPs.push_back(fpSet.bl); - usedFPs.push_back(fpSet.tl); - usedFPs.push_back(fpSet.tr); - results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QRCode); - if (maxSymbols && Size(results) == maxSymbols) - break; + if (_testQR) { + for (auto& fpSet : allFPSets) { + if (Contains(usedFPs, fpSet.bl) || Contains(usedFPs, fpSet.tl) || Contains(usedFPs, fpSet.tr)) + continue; + + auto detectorResult = SampleAtFinderPatternSet(*binImg, fpSet); + if (detectorResult.isValid()) { + auto decoderResult = Decode(detectorResult.bits(), _charset); + auto position = detectorResult.position(); + if (decoderResult.isValid()) { + usedFPs.push_back(fpSet.bl); + usedFPs.push_back(fpSet.tl); + usedFPs.push_back(fpSet.tr); + results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QRCode); + if (maxSymbols && Size(results) == maxSymbols) + break; + } + } + } + } + + if (_testMQR && !(maxSymbols && Size(results) == maxSymbols)) { + for (auto fp : allFPs) { + if (Contains(usedFPs, fp)) + continue; + + auto detectorResult = SampleAtFinderPattern(*binImg, fp); + if (detectorResult.isValid()) { + auto decoderResult = Decode(detectorResult.bits(), _charset, true); + auto position = detectorResult.position(); + if (decoderResult.isValid()) { + results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MicroQRCode); + if (maxSymbols && Size(results) == maxSymbols) + break; + } } } } diff --git a/core/src/qrcode/QRReader.h b/core/src/qrcode/QRReader.h index 1df4e8f2b1..e93a05afad 100644 --- a/core/src/qrcode/QRReader.h +++ b/core/src/qrcode/QRReader.h @@ -40,7 +40,7 @@ class Reader : public ZXing::Reader Results decode(const BinaryBitmap& image, int maxSymbols) const override; private: - bool _tryHarder, _isPure; + bool _tryHarder, _isPure, _testQR, _testMQR; std::string _charset; }; diff --git a/core/src/qrcode/QRVersion.cpp b/core/src/qrcode/QRVersion.cpp index 96a9f91fe2..0833b19340 100644 --- a/core/src/qrcode/QRVersion.cpp +++ b/core/src/qrcode/QRVersion.cpp @@ -290,28 +290,47 @@ Version::AllVersions() return allVersions; } +const Version* Version::AllMicroVersions() +{ + /** + * See ISO 18004:2006 6.5.1 Table 9 + */ + static const Version allVersions[] = { + {1, {2, 1, 3, 0, 0}}, + {2, {5, 1, 5, 0, 0, 6, 1, 4, 0, 0}}, + {3, {6, 1, 11, 0, 0, 8, 1, 9, 0, 0}}, + {4, {8, 1, 16, 0, 0, 10, 1, 14, 0, 0, 14, 1, 10, 0, 0}}}; + return allVersions; +} + Version::Version(int versionNumber, std::initializer_list alignmentPatternCenters, const std::array& ecBlocks) - : _versionNumber(versionNumber), _alignmentPatternCenters(alignmentPatternCenters), _ecBlocks(ecBlocks) + : _versionNumber(versionNumber), _alignmentPatternCenters(alignmentPatternCenters), _ecBlocks(ecBlocks), _isMicro(false) +{ + _totalCodewords = ecBlocks[0].totalDataCodewords(); +} + +Version::Version(int versionNumber, const std::array& ecBlocks) + : _versionNumber(versionNumber), _ecBlocks(ecBlocks), _isMicro(true) { _totalCodewords = ecBlocks[0].totalDataCodewords(); } -const Version* Version::VersionForNumber(int versionNumber) +const Version* Version::VersionForNumber(int versionNumber, bool isMicro) { - if (versionNumber < 1 || versionNumber > 40) { + if (versionNumber < 1 || versionNumber > (isMicro ? 4 : 40)) { //throw std::invalid_argument("Version should be in range [1-40]."); return nullptr; } - return &AllVersions()[versionNumber - 1]; + return &(isMicro ? AllMicroVersions() : AllVersions())[versionNumber - 1]; } -const Version* Version::ProvisionalVersionForDimension(int dimension) +const Version* Version::ProvisionalVersionForDimension(int dimension, bool isMicro) { - if (dimension % 4 != 1) { + if (dimension % DimensionStep(isMicro) != 1) { //throw std::invalid_argument("Unexpected dimension"); return nullptr; } - return VersionForNumber((dimension - 17) / 4); + return VersionForNumber((dimension - DimensionOffset(isMicro)) / DimensionStep(isMicro), isMicro); } const Version* Version::DecodeVersionInformation(int versionBits) @@ -352,34 +371,43 @@ BitMatrix Version::buildFunctionPattern() const // Top left finder pattern + separator + format bitMatrix.setRegion(0, 0, 9, 9); - // Top right finder pattern + separator + format - bitMatrix.setRegion(dimension - 8, 0, 8, 9); - // Bottom left finder pattern + separator + format - bitMatrix.setRegion(0, dimension - 8, 9, 8); - // Alignment patterns - size_t max = _alignmentPatternCenters.size(); - for (size_t x = 0; x < max; ++x) { - int i = _alignmentPatternCenters[x] - 2; - for (size_t y = 0; y < max; ++y) { - if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) { - // No alignment patterns near the three finder patterns - continue; + if (!_isMicro) { + // Top right finder pattern + separator + format + bitMatrix.setRegion(dimension - 8, 0, 8, 9); + // Bottom left finder pattern + separator + format + bitMatrix.setRegion(0, dimension - 8, 9, 8); + + // Alignment patterns + size_t max = _alignmentPatternCenters.size(); + for (size_t x = 0; x < max; ++x) { + int i = _alignmentPatternCenters[x] - 2; + for (size_t y = 0; y < max; ++y) { + if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) { + // No alignment patterns near the three finder patterns + continue; + } + bitMatrix.setRegion(_alignmentPatternCenters[y] - 2, i, 5, 5); } - bitMatrix.setRegion(_alignmentPatternCenters[y] - 2, i, 5, 5); } - } - // Vertical timing pattern - bitMatrix.setRegion(6, 9, 1, dimension - 17); - // Horizontal timing pattern - bitMatrix.setRegion(9, 6, dimension - 17, 1); + // Vertical timing pattern + bitMatrix.setRegion(6, 9, 1, dimension - 17); + // Horizontal timing pattern + bitMatrix.setRegion(9, 6, dimension - 17, 1); + + if (_versionNumber > 6) { + // Version info, top right + bitMatrix.setRegion(dimension - 11, 0, 3, 6); + // Version info, bottom left + bitMatrix.setRegion(0, dimension - 11, 6, 3); + } + } else { + // Vertical timing pattern + bitMatrix.setRegion(9, 0, dimension - 9, 1); - if (_versionNumber > 6) { - // Version info, top right - bitMatrix.setRegion(dimension - 11, 0, 3, 6); - // Version info, bottom left - bitMatrix.setRegion(0, dimension - 11, 6, 3); + // Horizontal timing pattern + bitMatrix.setRegion(0, 9, 1, dimension - 9); } return bitMatrix; diff --git a/core/src/qrcode/QRVersion.h b/core/src/qrcode/QRVersion.h index 2de4988e86..393bcbc0e2 100644 --- a/core/src/qrcode/QRVersion.h +++ b/core/src/qrcode/QRVersion.h @@ -43,33 +43,43 @@ class Version int totalCodewords() const { return _totalCodewords; } - int dimensionForVersion() const { return 17 + 4 * _versionNumber; } + int dimensionForVersion() const { return DimensionOfVersion(_versionNumber, _isMicro); } const ECBlocks& ecBlocksForLevel(ErrorCorrectionLevel ecLevel) const { return _ecBlocks[(int)ecLevel]; } BitMatrix buildFunctionPattern() const; + bool isMicroQRCode() const { return _isMicro; } + + static constexpr int DimensionStep(bool isMicro) { return std::array{4, 2}[isMicro]; } + static constexpr int DimensionOffset(bool isMicro) { return std::array{17, 9}[isMicro]; } + static constexpr int DimensionOfVersion(int version, bool isMicro) + { + return DimensionOffset(isMicro) + DimensionStep(isMicro) * version; + } + /** - *

Deduces version information purely from QR Code dimensions.

+ *

Deduces version information purely from micro QR or QR Code dimensions.

* * @param dimension dimension in modules * @return Version for a QR Code of that dimension - * @throws FormatException if dimension is not 1 mod 4 */ - static const Version* ProvisionalVersionForDimension(int dimension); + static const Version* ProvisionalVersionForDimension(int dimension, bool isMicro = false); - static const Version* VersionForNumber(int versionNumber); + static const Version* VersionForNumber(int versionNumber, bool isMicro = false); static const Version* DecodeVersionInformation(int versionBits); - private: int _versionNumber; std::vector _alignmentPatternCenters; std::array _ecBlocks; int _totalCodewords; + bool _isMicro; Version(int versionNumber, std::initializer_list alignmentPatternCenters, const std::array &ecBlocks); + Version(int versionNumber, const std::array& ecBlocks); static const Version* AllVersions(); + static const Version* AllMicroVersions(); }; } // QRCode diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index 44ed865b2b..05a87bad2b 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -54,9 +54,10 @@ enum class BarcodeFormat QRCode = (1 << 13), ///< QR Code (2D) UPCA = (1 << 14), ///< UPC-A (1D) UPCE = (1 << 15), ///< UPC-E (1D) + MicroQRCode = (1 << 16), ///< Micro QR Code (2D) OneDCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, - TwoDCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode, + TwoDCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, }; enum class DecodeStatus diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 9899cef628..cca643fbd0 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -620,6 +620,14 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set { 1, 1, 0 }, }); + runTests("microqrcode-1", "MicroQRCode", 16, { + { 15, 15, 0 }, + { 15, 15, 90 }, + { 15, 15, 180 }, + { 14, 14, 270 }, + { 9, 0, pure }, + }); + runTests("pdf417-1", "PDF417", 17, { { 16, 16, 0 }, { 1, 1, 90 }, diff --git a/test/samples/microqrcode-1/1.png b/test/samples/microqrcode-1/1.png new file mode 100644 index 0000000000000000000000000000000000000000..35c855d0d795380bf539cd29a05ce54fbe9a16fa GIT binary patch literal 8513 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 literal 0 HcmV?d00001 diff --git a/test/samples/microqrcode-1/1.txt b/test/samples/microqrcode-1/1.txt new file mode 100644 index 0000000000..121eab0377 --- /dev/null +++ b/test/samples/microqrcode-1/1.txt @@ -0,0 +1 @@ +Wikipedia \ No newline at end of file diff --git a/test/samples/microqrcode-1/11.jpg b/test/samples/microqrcode-1/11.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bfff33fbba145c21e8fdf08ec13fe20102b9d204 GIT binary patch literal 29581 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 literal 0 HcmV?d00001 diff --git a/test/samples/microqrcode-1/11.txt b/test/samples/microqrcode-1/11.txt new file mode 100644 index 0000000000..64209c1239 --- /dev/null +++ b/test/samples/microqrcode-1/11.txt @@ -0,0 +1 @@ +Loser \ No newline at end of file diff --git a/test/samples/microqrcode-1/12.jpg b/test/samples/microqrcode-1/12.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4724b6c948f5887d7262cfad1e7191360ab318a7 GIT binary patch literal 19328 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 J#7O@D|JjxW2Jip? literal 0 HcmV?d00001 diff --git a/test/samples/microqrcode-1/12.txt b/test/samples/microqrcode-1/12.txt new file mode 100644 index 0000000000..7d3298dfa5 --- /dev/null +++ b/test/samples/microqrcode-1/12.txt @@ -0,0 +1 @@ +SN888623311 \ No newline at end of file diff --git a/test/samples/microqrcode-1/3.jpg b/test/samples/microqrcode-1/3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9a984ddc0947b00355acb80a17e393f81a60d5f4 GIT binary patch literal 27818 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 literal 0 HcmV?d00001 diff --git a/test/samples/microqrcode-1/7.txt b/test/samples/microqrcode-1/7.txt new file mode 100644 index 0000000000..ae75576b8f --- /dev/null +++ b/test/samples/microqrcode-1/7.txt @@ -0,0 +1 @@ +Victus \ No newline at end of file diff --git a/test/samples/microqrcode-1/9.jpg b/test/samples/microqrcode-1/9.jpg new file mode 100644 index 0000000000000000000000000000000000000000..682f4d11f3a182f5336dc7b412cf67404d58e3f9 GIT binary patch literal 9245 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 literal 0 HcmV?d00001 diff --git a/test/samples/microqrcode-1/M2-Alpha.txt b/test/samples/microqrcode-1/M2-Alpha.txt new file mode 100644 index 0000000000..48b83b862e --- /dev/null +++ b/test/samples/microqrcode-1/M2-Alpha.txt @@ -0,0 +1 @@ +ABC \ No newline at end of file diff --git a/test/samples/microqrcode-1/M2-Numeric.png b/test/samples/microqrcode-1/M2-Numeric.png new file mode 100644 index 0000000000000000000000000000000000000000..ea9810f9fb070c7caa238776a53f4653a33f65d6 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^av;pe3?v!<**pYNYymzYuK)l42QotsU9JOCuAVNA zAsp9jk8I>*Fyvu&XuQq7Ua3I(!>cx(ln)F)om9eQK0M*niki};n7=wdtZQ0GcXEFyLWvXpCwM6RgSpag|w4VYS2^wkNMT*IR7nmcHcXEFyLWvXpC+Ss|?Woag{lfH_r6|KthUPb(y!<)X#?eof= y#Tmwayt<74`hZOx?BgOVm)0P zLpZMc9@)rwz<`J40Q+{P^`91mJ-Eu6$s6Z7!R&#_`w7~7KO}F@oyB|oW`0ENclP^h zjAyHUyt<74`hZOx?BgOVmw_O zLpZMc9x>!$rKl{(#``$VCopfG{`@ zl=pBBKzzM;(DXP@I>^)UPiehZ@2f&9nfQlB6E4BW{Gna-2a1MCrzjnkl&NS>363gd zfuFCkA^>mjHx(feAr! zPi%fb!5QVONUMZ<{m*RZm+TR9AZPrXIjrJ4Zn82GfItBd3In0=uP<;z2%HB%i6TYB zFbd)tM!aYoLF?P6fP%p4l>jv&!|=0S$I$)Ysvf8I`;au@hGYY~;C6`)&|aBs);=K^Bu?RfvJW6gy+kE@Jx zQu-nJM&({bQ~dhd{b^1n**6mrA00z#%YyqdNEZKPqx}1Fu(Zz_%7geOoG*BX7B~o-Cv@h*jo` z&daRo(y%kO5wWgB_K2_N=g~`qVP%SFI~ArIvP)j0*Ll9Bu0Bv&e19n~=QJ~PB*`aM zoRT++#MJNelbjia36h_M5M4QNhFo$nx06iw>9QKh zg0ondP2FfYG4>Pp?NhK%?IUaLlLEGs8U^v+E;!v%?NS`m>FEtPPKhx+`-y#EGg&y( zEi>^t-+x0<*43$nUo>uJ&+nk*Z>!yEEj#p@ZrwE%dOt~0Tkc}I5y3VX81b`@uK{6A z`8m^tMVoPIa?95{%i$!Rl;Sm3Q=LhZyhP5db@`W><1T z2)@I(EP)QGKDjn)yhq}q`W?<}|QDm$geig1O{@6>ch zxW@{F1|D^k9;toB3l#q(K$N;!^L^f>bk#9|+J!@4i7>VQ)ZqwoI?wsIM=qH#;N7mi5vMDs>A>+N>3vY<%0@nO6@4HPn%;^bt0znk?rn(qgt5 zhdUT#lW$*Jghti5W%-9@YnA&=35oYmQUf{#J3bc8**HfN1qv=>+PtO_0X!$?`rBP3 z!icsJ&R4Fz;b8dLN{g;#Uh5nBvZ1!QA<=i%IcuT~H1&?z+LN%x)$O3dIG6n}=wo$# zx9K6yK+@XB;)1Z+VRlkj(thKoc)Q*W-Cutzp~sZjtZ&UX{iBVN^LAL+mpJwS%a-6{ujoN&tzL{j}x z)xq()ou%xVx7-OAGIla~n3_H>OW~)%C<)C{cLhG{ZIk?uD^~|Xg8)7Lh-hmDAnJhyko{CP2(h_0G$z(l-E^e9IkUO~h zVay@`wqgT-F5|o%0D9C>6H<}nvFslj9U;$WTrp4MX%xeagsf;D8LV&X^U!Yipm&5_ z9l{@F0D{R||`og7I(U-sBoh{3y-%J|B%A-#MZuE!e3_3 zbQYLmnkLWSb7A$@oBJF6A!fzP zyKzljQyGCjbM>4U35VSh-R&zcaAcFC3vMb_J8^o`vohb@;7*?`3V=YM|0Mz_5Q9`8 zXxM$#0T46*SVQC!CVIoN@`%-isciYsPSm$buF(CbdVnY-MnS{K=SwBGsR7_$q=jQL Us3OC$@&&u8O5s|128Xl%1V(o;jQ{`u literal 0 HcmV?d00001 diff --git a/test/samples/microqrcode-1/MQR-needs-br-update.txt b/test/samples/microqrcode-1/MQR-needs-br-update.txt new file mode 100644 index 0000000000..222d0af5dc --- /dev/null +++ b/test/samples/microqrcode-1/MQR-needs-br-update.txt @@ -0,0 +1 @@ +Micro QR Code \ No newline at end of file diff --git a/test/samples/microqrcode-1/NoQuietZone.png b/test/samples/microqrcode-1/NoQuietZone.png new file mode 100644 index 0000000000000000000000000000000000000000..f7a917df87544f92e8df693a4db88545e5f77839 GIT binary patch literal 112 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LI=AJH&Asp9zPwnJA;2^+s;NY!) z(Z>$XlV@rjb`qD^7Qx*?d@M!N-Yae-&>9U-synmwuM_UzjN0{L9zXKt_4G L`njxgN@xNAb>Ap^ literal 0 HcmV?d00001 diff --git a/test/samples/microqrcode-1/NoQuietZone.txt b/test/samples/microqrcode-1/NoQuietZone.txt new file mode 100644 index 0000000000..bd41cba781 --- /dev/null +++ b/test/samples/microqrcode-1/NoQuietZone.txt @@ -0,0 +1 @@ +12345 \ No newline at end of file diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 1c33901a39..57f1dfafd0 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -45,6 +45,8 @@ add_executable (UnitTest oned/ODUPCEWriterTest.cpp oned/rss/ODRSSExpandedBinaryDecoderTest.cpp oned/rss/ODRSSFieldParserTest.cpp + qrcode/MQRDecoderTest.cpp + qrcode/QRBitMatrixParserTest.cpp qrcode/QRDataMaskTest.cpp qrcode/QRDecodedBitStreamParserTest.cpp qrcode/QREncoderTest.cpp diff --git a/test/unit/qrcode/MQRDecoderTest.cpp b/test/unit/qrcode/MQRDecoderTest.cpp new file mode 100644 index 0000000000..47ebe2ef73 --- /dev/null +++ b/test/unit/qrcode/MQRDecoderTest.cpp @@ -0,0 +1,135 @@ +/* + * Copyright 2017 Huy Cuong Nguyen + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "qrcode/QRDecoder.h" + +#include "BitMatrix.h" +#include "BitMatrixIO.h" +#include "DecoderResult.h" + +#include "gtest/gtest.h" + +using namespace ZXing; +using namespace ZXing::QRCode; + +TEST(MQRDecoderTest, MQRCodeM3L) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X X X\n" + "X X X X \n" + "X XXX X XXXXXXX\n" + "X XXX X X X XX\n" + "X XXX X X XX\n" + "X X X X X X\n" + "XXXXXXX X XX \n" + " X X X\n" + "XXXXXX X X X\n" + " X XX XXX\n" + "XXX XX XXXX XXX\n" + " X X XXX X \n" + "X XXXXX XXX X X\n" + " X X X XXX \n" + "XXX XX X X XXXX\n", + 88, false); + + const auto result = Decode(bitMatrix, {}, true); + EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); +} + +TEST(MQRDecoderTest, MQRCodeM3M) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X X X\n" + "X X XX\n" + "X XXX X X XX XX\n" + "X XXX X X X \n" + "X XXX X XX XXXX\n" + "X X XX \n" + "XXXXXXX X XXXX\n" + " X XXX \n" + "X XX XX X X\n" + " X X XX \n" + "XX XX XXXXXXX\n" + " X X X\n" + "XX X X X \n" + " X X X \n" + "X X XXXX XXX\n", + 88, false); + + const auto result = Decode(bitMatrix, {}, true); + EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); +} + +TEST(MQRDecoderTest, MQRCodeM1) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X\n" + "X X \n" + "X XXX X XXX\n" + "X XXX X XX\n" + "X XXX X X\n" + "X X XX \n" + "XXXXXXX X \n" + " X \n" + "XX X \n" + " X XXXXX X\n" + "X XXXXXX X\n", + 88, false); + const auto result = Decode(bitMatrix, {}, true); + EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); + EXPECT_EQ(L"123", result.text()); +} + +TEST(MQRDecoderTest, MQRCodeM1Error4Bits) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X\n" + "X X XX\n" + "X XXX X X \n" + "X XXX X XX\n" + "X XXX X X\n" + "X X XX \n" + "XXXXXXX X \n" + " X \n" + "XX X \n" + " X XXXXXX \n" + "X XXXXXXX \n", + 88, false); + const auto result = Decode(bitMatrix, {}, true); + EXPECT_EQ(DecodeStatus::ChecksumError, result.errorCode()); + EXPECT_TRUE(result.text().empty()); +} + +TEST(MQRDecoderTest, MQRCodeM4) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X X X X\n" + "X X XX X XX\n" + "X XXX X X X XX\n" + "X XXX X XX XX XX\n" + "X XXX X X XXXXX\n" + "X X XX X\n" + "XXXXXXX XX X XX\n" + " X XX XX\n" + "X X XXX X XXX\n" + " XX X XX XX X \n" + "XX XXXX X XX XX\n" + " XX XX X XX XX\n" + "XXX XXX XXX XX XX\n" + " X X X XX X\n" + "X X XX XXXXX \n" + " X X X X X \n" + "X XXXXXXX X X X\n", + 88, false); + const auto result = Decode(bitMatrix, {}, true); + EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); +} diff --git a/test/unit/qrcode/QRBitMatrixParserTest.cpp b/test/unit/qrcode/QRBitMatrixParserTest.cpp new file mode 100644 index 0000000000..1eea005bf6 --- /dev/null +++ b/test/unit/qrcode/QRBitMatrixParserTest.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2017 Huy Cuong Nguyen + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BitMatrix.h" +#include "BitMatrixIO.h" +#include "ByteArray.h" +#include "qrcode/QRBitMatrixParser.h" +#include "qrcode/QRFormatInformation.h" +#include "qrcode/QRVersion.h" + +#include "gtest/gtest.h" + +using namespace ZXing; +using namespace ZXing::QRCode; + +TEST(QRBitMatrixParserTest, MQRCodeM3L) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X X X\n" + "X X X X \n" + "X XXX X XXXXXXX\n" + "X XXX X X X XX\n" + "X XXX X X XX\n" + "X X X X X X\n" + "XXXXXXX X XX \n" + " X X X\n" + "XXXXXX X X X\n" + " X XX XXX\n" + "XXX XX XXXX XXX\n" + " X X XXX X \n" + "X XXXXX XXX X X\n" + " X X X XXX \n" + "XXX XX X X XXXX\n", + 88, false); + + const auto version = ReadVersion(bitMatrix, true); + EXPECT_EQ(3, version->versionNumber()); + const auto format = ReadFormatInformation(bitMatrix, false, true); + const auto codewords = ReadCodewords(bitMatrix, *version, format, false); + EXPECT_EQ(17, codewords.size()); + EXPECT_EQ(0x0, codewords[10]); + EXPECT_EQ(0xd1, codewords[11]); +} + +TEST(QRBitMatrixParserTest, MQRCodeM3M) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X X X\n" + "X X XX\n" + "X XXX X X XX XX\n" + "X XXX X X X \n" + "X XXX X XX XXXX\n" + "X X XX \n" + "XXXXXXX X XXXX\n" + " X XXX \n" + "X XX XX X X\n" + " X X XX \n" + "XX XX XXXXXXX\n" + " X X X\n" + "XX X X X \n" + " X X X \n" + "X X XXXX XXX\n", + 88, false); + + const auto version = ReadVersion(bitMatrix, true); + EXPECT_EQ(3, version->versionNumber()); + const auto format = ReadFormatInformation(bitMatrix, false, true); + const auto codewords = ReadCodewords(bitMatrix, *version, format, false); + EXPECT_EQ(17, codewords.size()); + EXPECT_EQ(0x0, codewords[8]); + EXPECT_EQ(0x89, codewords[9]); +} diff --git a/test/unit/qrcode/QRDataMaskTest.cpp b/test/unit/qrcode/QRDataMaskTest.cpp index 4f55545f7f..e71387ce74 100644 --- a/test/unit/qrcode/QRDataMaskTest.cpp +++ b/test/unit/qrcode/QRDataMaskTest.cpp @@ -26,17 +26,28 @@ using namespace ZXing::QRCode; namespace { - void TestMaskAcrossDimensions(int maskIndex, std::function condition) { - for (int version = 1; version <= 40; version++) { - int dimension = 17 + 4 * version; + void TestMaskAcrossDimensionsImpl(int maskIndex, bool isMicro, const int versionMax, const int dimensionStart, const int dimensionStep, std::function condition) + { + for (int version = 1; version <= versionMax; version++) { + int dimension = dimensionStart + dimensionStep * version; BitMatrix bits(dimension); for (int i = 0; i < dimension; i++) for (int j = 0; j < dimension; j++) - EXPECT_EQ(GetMaskedBit(bits, j, i, maskIndex), condition(i, j)) << "(" << i << ',' << j << ')'; + EXPECT_EQ(GetMaskedBit(bits, j, i, maskIndex, isMicro), condition(i, j)) << "(" << i << ',' << j << ')'; } } + void TestMaskAcrossDimensions(int maskIndex, std::function condition) + { + TestMaskAcrossDimensionsImpl(maskIndex, false, 40, 17, 4, condition); + } + + void TestMicroMaskAcrossDimensions(int maskIndex, std::function condition) + { + TestMaskAcrossDimensionsImpl(maskIndex, true, 4, 9, 2, condition); + } + } TEST(QRDataMaskTest, Mask0) @@ -78,3 +89,23 @@ TEST(QRDataMaskTest, Mask7) { TestMaskAcrossDimensions(7, [](int i, int j) { return ((i + j) % 2 + (i * j) % 3) % 2 == 0; }); } + +TEST(QRDataMaskTest, MicroMask0) +{ + TestMicroMaskAcrossDimensions(0, [](int i, int) { return i % 2 == 0; }); +} + +TEST(QRDataMaskTest, MicroMask1) +{ + TestMicroMaskAcrossDimensions(1, [](int i, int j) { return (i / 2 + j / 3) % 2 == 0; }); +} + +TEST(QRDataMaskTest, MicroMask2) +{ + TestMicroMaskAcrossDimensions(2, [](int i, int j) { return ((i * j) % 2 + (i * j) % 3) % 2 == 0; }); +} + +TEST(QRDataMaskTest, MicroMask3) +{ + TestMicroMaskAcrossDimensions(3, [](int i, int j) { return ((i + j) % 2 + (i * j) % 3) % 2 == 0; }); +} diff --git a/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp b/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp index 35f8b870fc..c09c226559 100644 --- a/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp +++ b/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp @@ -29,3 +29,26 @@ TEST(QRErrorCorrectionLevelTest, ForBits) EXPECT_EQ(ErrorCorrectionLevel::High, ECLevelFromBits(2)); EXPECT_EQ(ErrorCorrectionLevel::Quality, ECLevelFromBits(3)); } + +TEST(QRErrorCorrectionLevelTest, ForMicroBits) +{ + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(0, true)); + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(1, true)); + EXPECT_EQ(ErrorCorrectionLevel::Medium, ECLevelFromBits(2, true)); + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(3, true)); + EXPECT_EQ(ErrorCorrectionLevel::Medium, ECLevelFromBits(4, true)); + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(5, true)); + EXPECT_EQ(ErrorCorrectionLevel::Medium, ECLevelFromBits(6, true)); + EXPECT_EQ(ErrorCorrectionLevel::Quality, ECLevelFromBits(7, true)); + + EXPECT_EQ(ErrorCorrectionLevel::Quality, ECLevelFromBits(-1, true)); + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(8, true)); +} + +TEST(QRErrorCorrectionLevelTest, ToString) +{ + EXPECT_EQ(L"L", std::wstring(ToString(ErrorCorrectionLevel::Low))); + EXPECT_EQ(L"M", std::wstring(ToString(ErrorCorrectionLevel::Medium))); + EXPECT_EQ(L"Q", std::wstring(ToString(ErrorCorrectionLevel::Quality))); + EXPECT_EQ(L"H", std::wstring(ToString(ErrorCorrectionLevel::High))); +} diff --git a/test/unit/qrcode/QRFormatInformationTest.cpp b/test/unit/qrcode/QRFormatInformationTest.cpp index 62df090171..d4eb47dc87 100644 --- a/test/unit/qrcode/QRFormatInformationTest.cpp +++ b/test/unit/qrcode/QRFormatInformationTest.cpp @@ -22,9 +22,18 @@ using namespace ZXing; using namespace ZXing::QRCode; - static const int MASKED_TEST_FORMAT_INFO = 0x2BED; static const int UNMASKED_TEST_FORMAT_INFO = MASKED_TEST_FORMAT_INFO ^ 0x5412; +static const int MICRO_MASKED_TEST_FORMAT_INFO = 0x3BBA; +static const int MICRO_UNMASKED_TEST_FORMAT_INFO = MICRO_MASKED_TEST_FORMAT_INFO ^ 0x4445; + +static void DoFormatInformationTest(const int formatInfo, const uint8_t expectedMask, const ErrorCorrectionLevel& expectedECL) +{ + FormatInformation parsedFormat = FormatInformation::DecodeFormatInformation(formatInfo); + EXPECT_TRUE(parsedFormat.isValid()); + EXPECT_EQ(expectedMask, parsedFormat.dataMask()); + EXPECT_EQ(expectedECL, parsedFormat.errorCorrectionLevel()); +} TEST(QRFormatInformationTest, Decode) { @@ -52,3 +61,39 @@ TEST(QRFormatInformationTest, DecodeWithMisread) FormatInformation expected = FormatInformation::DecodeFormatInformation(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO); EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x03, MASKED_TEST_FORMAT_INFO ^ 0x0F)); } + +TEST(QRFormatInformationTest, DecodeMicro) +{ + // Normal cases. + DoFormatInformationTest(0x4445, 0x0, ErrorCorrectionLevel::Low); + DoFormatInformationTest(0x4172, 0x1, ErrorCorrectionLevel::Low); + DoFormatInformationTest(0x5fc0, 0x2, ErrorCorrectionLevel::Low); + DoFormatInformationTest(0x5af7, 0x3, ErrorCorrectionLevel::Low); + DoFormatInformationTest(0x6793, 0x0, ErrorCorrectionLevel::Medium); + DoFormatInformationTest(0x62a4, 0x1, ErrorCorrectionLevel::Medium); + DoFormatInformationTest(0x3e8d, 0x2, ErrorCorrectionLevel::Quality); + DoFormatInformationTest(MICRO_MASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Quality); + + // where the code forgot the mask! +// DoFormatInformationTest(MICRO_UNMASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Quality); +} + +// This doesn't work as expected because the implementation of the decode tries with +// and without the mask (0x4445). This effectively adds a tolerance of 5 bits to the Hamming +// distance calculation. +TEST(QRFormatInformationTest, DecodeMicroWithBitDifference) +{ + FormatInformation expected = FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO); + + // 1,2,3 bits difference + EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x01)); + EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x03)); + EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x07)); + + // Bigger bit differences can return valid FormatInformation objects but the data mask and error + // correction levels do not match. +// EXPECT_TRUE(FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).isValid()); +// EXPECT_NE(expected.dataMask(), FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).dataMask()); +// EXPECT_NE(expected.errorCorrectionLevel(), +// FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).errorCorrectionLevel()); +} diff --git a/test/unit/qrcode/QRModeTest.cpp b/test/unit/qrcode/QRModeTest.cpp index 368ce1b07b..77a1611350 100644 --- a/test/unit/qrcode/QRModeTest.cpp +++ b/test/unit/qrcode/QRModeTest.cpp @@ -43,3 +43,35 @@ TEST(QRModeTest, CharacterCount) ASSERT_EQ(8, CharacterCountBits(CodecMode::BYTE, *Version::VersionForNumber(7))); ASSERT_EQ(8, CharacterCountBits(CodecMode::KANJI, *Version::VersionForNumber(8))); } + +TEST(QRModeTest, MicroForBits) +{ + // M1 + ASSERT_EQ(CodecMode::NUMERIC, CodecModeForBits(0x00, true)); + // M2 + ASSERT_EQ(CodecMode::NUMERIC, CodecModeForBits(0x00, true)); + ASSERT_EQ(CodecMode::ALPHANUMERIC, CodecModeForBits(0x01, true)); + // M3 + ASSERT_EQ(CodecMode::NUMERIC, CodecModeForBits(0x00, true)); + ASSERT_EQ(CodecMode::ALPHANUMERIC, CodecModeForBits(0x01, true)); + ASSERT_EQ(CodecMode::BYTE, CodecModeForBits(0x02, true)); + ASSERT_EQ(CodecMode::KANJI, CodecModeForBits(0x03, true)); + // M4 + ASSERT_EQ(CodecMode::NUMERIC, CodecModeForBits(0x00, true)); + ASSERT_EQ(CodecMode::ALPHANUMERIC, CodecModeForBits(0x01, true)); + ASSERT_EQ(CodecMode::BYTE, CodecModeForBits(0x02, true)); + ASSERT_EQ(CodecMode::KANJI, CodecModeForBits(0x03, true)); + + ASSERT_THROW(CodecModeForBits(0x04, true), std::invalid_argument); +} + +TEST(QRModeTest, MicroCharacterCount) +{ + // Spot check a few values + ASSERT_EQ(3, CharacterCountBits(CodecMode::NUMERIC, *Version::VersionForNumber(1, true))); + ASSERT_EQ(4, CharacterCountBits(CodecMode::NUMERIC, *Version::VersionForNumber(2, true))); + ASSERT_EQ(6, CharacterCountBits(CodecMode::NUMERIC, *Version::VersionForNumber(4, true))); + ASSERT_EQ(3, CharacterCountBits(CodecMode::ALPHANUMERIC, *Version::VersionForNumber(2, true))); + ASSERT_EQ(4, CharacterCountBits(CodecMode::BYTE, *Version::VersionForNumber(3, true))); + ASSERT_EQ(4, CharacterCountBits(CodecMode::KANJI, *Version::VersionForNumber(4, true))); +} diff --git a/test/unit/qrcode/QRVersionTest.cpp b/test/unit/qrcode/QRVersionTest.cpp index 5f8e821382..4e6fd8eb0c 100644 --- a/test/unit/qrcode/QRVersionTest.cpp +++ b/test/unit/qrcode/QRVersionTest.cpp @@ -17,6 +17,8 @@ #include "qrcode/QRVersion.h" +#include "BitMatrix.h" + #include "gtest/gtest.h" using namespace ZXing; @@ -27,7 +29,7 @@ namespace { void CheckVersion(const Version* version, int number, int dimension) { ASSERT_NE(version, nullptr); EXPECT_EQ(number, version->versionNumber()); - if (number > 1) { + if (number > 1 && !version->isMicroQRCode()) { EXPECT_FALSE(version->alignmentPatternCenters().empty()); } EXPECT_EQ(dimension, version->dimensionForVersion()); @@ -38,6 +40,7 @@ namespace { ASSERT_NE(version, nullptr); EXPECT_EQ(expectedVersion, version->versionNumber()); } + } TEST(QRVersionTest, VersionForNumber) @@ -71,3 +74,42 @@ TEST(QRVersionTest, DecodeVersionInformation) DoTestVersion(32, 0x209D5); } +TEST(QRVersionTest, MicroVersionForNumber) +{ + auto version = Version::VersionForNumber(0, true); + EXPECT_EQ(version, nullptr) << "There is version with number 0"; + + for (int i = 1; i <= 4; i++) { + CheckVersion(Version::VersionForNumber(i, true), i, 2 * i + 9); + } +} + +TEST(QRVersionTest, GetProvisionalMicroVersionForDimension) +{ + for (int i = 1; i <= 4; i++) { + auto prov = Version::ProvisionalVersionForDimension(2 * i + 9, true); + ASSERT_NE(prov, nullptr); + EXPECT_EQ(i, prov->versionNumber()); + } +} + +TEST(QRVersionTest, FunctionPattern) +{ + auto testFinderPatternRegion = [](const BitMatrix& bitMatrix) { + for (int row = 0; row < 9; row++) + for (int col = 0; col < 9; col++) + EXPECT_TRUE(bitMatrix.get(col, row)); + }; + for (int i = 1; i <= 4; i++) { + const auto version = Version::VersionForNumber(i, true); + const auto functionPattern = version->buildFunctionPattern(); + testFinderPatternRegion(functionPattern); + + // Check timing pattern areas. + const auto dimension = version->dimensionForVersion(); + for (int row = dimension; row < functionPattern.height(); row++) + EXPECT_TRUE(functionPattern.get(0, row)); + for (int col = dimension; col < functionPattern.width(); col++) + EXPECT_TRUE(functionPattern.get(col, 0)); + } +} diff --git a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp index 965ed6e2a1..32994d5934 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp +++ b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp @@ -41,6 +41,7 @@ static const char* JavaBarcodeFormatName(BarcodeFormat format) case BarcodeFormat::MaxiCode: return "MAXICODE"; case BarcodeFormat::PDF417: return "PDF_417"; case BarcodeFormat::QRCode: return "QR_CODE"; + case BarcodeFormat::MicroQRCode: return "MICRO_QR_CODE"; case BarcodeFormat::DataBar: return "DATA_BAR"; case BarcodeFormat::DataBarExpanded: return "DATA_BAR_EXPANDED"; case BarcodeFormat::UPCA: return "UPC_A"; diff --git a/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt b/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt index 52e4b3b380..6dbddecdac 100644 --- a/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt +++ b/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt @@ -30,7 +30,7 @@ class BarcodeReader { // Note that this has to be kept synchronized with native (C++/JNI) side. enum class Format { NONE, AZTEC, CODABAR, CODE_39, CODE_93, CODE_128, DATA_BAR, DATA_BAR_EXPANDED, - DATA_MATRIX, EAN_8, EAN_13, ITF, MAXICODE, PDF_417, QR_CODE, UPC_A, UPC_E, + DATA_MATRIX, EAN_8, EAN_13, ITF, MAXICODE, PDF_417, QR_CODE, MICRO_QR_CODE, UPC_A, UPC_E } data class Options( diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index e7fdfa1425..2756a20f8e 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -136,6 +136,7 @@ PYBIND11_MODULE(zxingcpp, m) .value("MaxiCode", BarcodeFormat::MaxiCode) .value("PDF417", BarcodeFormat::PDF417) .value("QRCode", BarcodeFormat::QRCode) + .value("MircoQRCode", BarcodeFormat::MicroQRCode) .value("DataBar", BarcodeFormat::DataBar) .value("DataBarExpanded", BarcodeFormat::DataBarExpanded) .value("UPCA", BarcodeFormat::UPCA) diff --git a/wrappers/winrt/BarcodeReader.cpp b/wrappers/winrt/BarcodeReader.cpp index 2a57e48df3..9695d26400 100644 --- a/wrappers/winrt/BarcodeReader.cpp +++ b/wrappers/winrt/BarcodeReader.cpp @@ -100,6 +100,8 @@ BarcodeFormat BarcodeReader::ConvertRuntimeToNative(BarcodeType type) return BarcodeFormat::PDF_417; case BarcodeType::QR_CODE: return BarcodeFormat::QR_CODE; + case BarcodeType::MICRO_QR_CODE: + return BarcodeFormat::MicroQRCode; case BarcodeType::RSS_14: return BarcodeFormat::RSS_14; case BarcodeType::RSS_EXPANDED: @@ -141,6 +143,8 @@ BarcodeType BarcodeReader::ConvertNativeToRuntime(BarcodeFormat format) return BarcodeType::PDF_417; case BarcodeFormat::QR_CODE: return BarcodeType::QR_CODE; + case BarcodeFormat::MicroQRCode: + return BarcodeType::MICRO_QR_CODE; case BarcodeFormat::RSS_14: return BarcodeType::RSS_14; case BarcodeFormat::RSS_EXPANDED: diff --git a/wrappers/winrt/BarcodeReader.h b/wrappers/winrt/BarcodeReader.h index 6b5dd8c83c..7455007449 100644 --- a/wrappers/winrt/BarcodeReader.h +++ b/wrappers/winrt/BarcodeReader.h @@ -34,6 +34,7 @@ public enum class BarcodeType : int { MAXICODE, PDF_417, QR_CODE, + MICRO_QR_CODE, RSS_14, RSS_EXPANDED, UPC_A, From 31c21de94187bfad44a82651edcd5190e197505f Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 17 May 2022 12:21:31 +0200 Subject: [PATCH 015/180] README: add info about new MicroQRCode support --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 58a274cf5f..ddaf4a862d 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,11 @@ It was originally ported from the Java [ZXing Library](https://github.com/zxing/ | 1D product | 1D industrial | 2D | ---------- | ----------------- | -------------- | UPC-A | Code 39 | QR Code -| UPC-E | Code 93 | DataMatrix +| UPC-E | Code 93 | Micro QR Code | EAN-8 | Code 128 | Aztec -| EAN-13 | Codabar | PDF417 -| DataBar | ITF | MaxiCode (beta) -| | DataBar Expanded | +| EAN-13 | Codabar | DataMatrix +| DataBar | ITF | PDF417 +| | DataBar Expanded | MaxiCode (beta) Note: DataBar used to be called RSS. DataBar is not supported for writing. From 345bc0450be64557efc0387724715e85fbfbf220 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 17 May 2022 12:46:23 +0200 Subject: [PATCH 016/180] QRCode: dead code removal and function name refactoring --- core/src/qrcode/QRBitMatrixParser.cpp | 4 +-- core/src/qrcode/QRDetector.cpp | 38 ++++---------------- core/src/qrcode/QRDetector.h | 12 +++---- core/src/qrcode/QRFormatInformation.cpp | 6 ++-- core/src/qrcode/QRFormatInformation.h | 4 +-- core/src/qrcode/QRReader.cpp | 9 ++--- test/unit/qrcode/QRFormatInformationTest.cpp | 28 +++++++-------- 7 files changed, 36 insertions(+), 65 deletions(-) diff --git a/core/src/qrcode/QRBitMatrixParser.cpp b/core/src/qrcode/QRBitMatrixParser.cpp index d54991cbaf..c9cc5ebbe0 100644 --- a/core/src/qrcode/QRBitMatrixParser.cpp +++ b/core/src/qrcode/QRBitMatrixParser.cpp @@ -83,7 +83,7 @@ FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrore for (int y = 7; y >= 1; y--) AppendBit(formatInfoBits, getBit(bitMatrix, 8, y, mirrored)); - return FormatInformation::DecodeFormatInformation(formatInfoBits); + return FormatInformation::DecodeMQR(formatInfoBits); } // Read top-left format info bits @@ -106,7 +106,7 @@ FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrore for (int x = dimension - 8; x < dimension; x++) AppendBit(formatInfoBits2, getBit(bitMatrix, x, 8, mirrored)); - return FormatInformation::DecodeFormatInformation(formatInfoBits1, formatInfoBits2); + return FormatInformation::DecodeQR(formatInfoBits1, formatInfoBits2); } static ByteArray ReadQRCodewords(const BitMatrix& bitMatrix, const Version& version, int maskIndex, bool mirrored) diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index a50202c97a..bf218c54d1 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -42,7 +42,7 @@ namespace ZXing::QRCode { -constexpr auto PATTERN = FixedPattern<5, 7>{1, 1, 3, 1, 1}; +constexpr auto PATTERN = FixedPattern<5, 7>{1, 1, 3, 1, 1}; std::vector FindFinderPatterns(const BitMatrix& image, bool tryHarder) { @@ -257,7 +257,7 @@ static double EstimateTilt(const FinderPatternSet& fp) return double(max) / min; } -DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatternSet& fp) +DetectorResult SampleQR(const BitMatrix& image, const FinderPatternSet& fp) { auto top = EstimateDimension(image, fp.tl, fp.tr); auto left = EstimateDimension(image, fp.tl, fp.bl); @@ -324,7 +324,7 @@ DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatt * around it. This is a specialized method that works exceptionally fast in this special * case. */ -DetectorResult DetectPure(const BitMatrix& image) +DetectorResult DetectPureQR(const BitMatrix& image) { using Pattern = std::array; @@ -372,7 +372,7 @@ DetectorResult DetectPure(const BitMatrix& image) {{left, top}, {right, top}, {right, bottom}, {left, bottom}}}; } -DetectorResult DetectPureMicroQR(const BitMatrix& image) +DetectorResult DetectPureMQR(const BitMatrix& image) { using Pattern = std::array; @@ -412,33 +412,7 @@ DetectorResult DetectPureMicroQR(const BitMatrix& image) {{left, top}, {right, top}, {right, bottom}, {left, bottom}}}; } -FinderPatternSets FindFinderPatternSets(const BitMatrix& image, bool tryHarder) -{ - return GenerateFinderPatternSets(FindFinderPatterns(image, tryHarder)); -} - -DetectorResult Detect(const BitMatrix& image, bool tryHarder, bool isPure) -{ -#ifdef PRINT_DEBUG - LogMatrixWriter lmw(log, image, 5, "qr-log.pnm"); -#endif - - if (isPure) - return DetectPure(image); - - auto sets = GenerateFinderPatternSets(FindFinderPatterns(image, tryHarder)); - - if (sets.empty()) - return {}; - -#ifdef PRINT_DEBUG - printf("size of sets: %d\n", Size(sets)); -#endif - - return SampleAtFinderPatternSet(image, sets[0]); -} - -DetectorResult SampleAtFinderPattern(const BitMatrix& image, const ConcentricPattern& fp) +DetectorResult SampleMQR(const BitMatrix& image, const ConcentricPattern& fp) { auto fpQuad = FindConcentricPatternCorners(image, fp, fp.size, 2); if (!fpQuad) @@ -465,7 +439,7 @@ DetectorResult SampleAtFinderPattern(const BitMatrix& image, const ConcentricPat for (int i = 1; i <= 15; ++i) AppendBit(formatInfoBits, image.get(mod2Pix(centered(FORMAT_INFO_COORDS[i])))); - auto fi = FormatInformation::DecodeFormatInformation(formatInfoBits); + auto fi = FormatInformation::DecodeMQR(formatInfoBits); if (fi.isValid() && fi.microVersion()) { const int dim = Version::DimensionOfVersion(fi.microVersion(), true); return SampleGrid(image, dim, dim, mod2Pix); diff --git a/core/src/qrcode/QRDetector.h b/core/src/qrcode/QRDetector.h index 36f1b90bd1..bfb9bc2591 100644 --- a/core/src/qrcode/QRDetector.h +++ b/core/src/qrcode/QRDetector.h @@ -39,16 +39,12 @@ using FinderPatternSets = std::vector; FinderPatterns FindFinderPatterns(const BitMatrix& image, bool tryHarder); FinderPatternSets GenerateFinderPatternSets(FinderPatterns&& patterns); -DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatternSet& fp); -DetectorResult SampleAtFinderPattern(const BitMatrix& image, const ConcentricPattern& fp); -DetectorResult DetectPureMicroQR(const BitMatrix& image); -DetectorResult DetectPure(const BitMatrix& image); +DetectorResult SampleQR(const BitMatrix& image, const FinderPatternSet& fp); +DetectorResult SampleMQR(const BitMatrix& image, const ConcentricPattern& fp); -/** - * @brief Detects a QR Code in an image. - */ -DetectorResult Detect(const BitMatrix& image, bool tryHarder, bool isPure); +DetectorResult DetectPureQR(const BitMatrix& image); +DetectorResult DetectPureMQR(const BitMatrix& image); } // QRCode } // ZXing diff --git a/core/src/qrcode/QRFormatInformation.cpp b/core/src/qrcode/QRFormatInformation.cpp index 9bea8c0247..f5bae8ed83 100644 --- a/core/src/qrcode/QRFormatInformation.cpp +++ b/core/src/qrcode/QRFormatInformation.cpp @@ -105,6 +105,7 @@ static int FindBestFormatInfo(int mask, const std::array, 32 int bestFormatInfo = -1; // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. + // TODO: test for mirrored format for (auto mask : {0, mask}) for (uint32_t bits : bits) for (const auto& [pattern, decodedInfo] : lookup) @@ -128,8 +129,7 @@ static int FindBestFormatInfo(int mask, const std::array, 32 * @return information about the format it specifies, or {@code null} * if doesn't seem to match any known pattern */ -FormatInformation -FormatInformation::DecodeFormatInformation(uint32_t formatInfoBits1, uint32_t formatInfoBits2) +FormatInformation FormatInformation::DecodeQR(uint32_t formatInfoBits1, uint32_t formatInfoBits2) { int bestFormatInfo = FindBestFormatInfo(FORMAT_INFO_MASK_QR, FORMAT_INFO_DECODE_LOOKUP, {formatInfoBits1, formatInfoBits2}); if (bestFormatInfo < 0) @@ -144,7 +144,7 @@ FormatInformation::DecodeFormatInformation(uint32_t formatInfoBits1, uint32_t fo * @return information about the format it specifies, or {@code null} * if doesn't seem to match any known pattern */ -FormatInformation FormatInformation::DecodeFormatInformation(uint32_t formatInfoBits) +FormatInformation FormatInformation::DecodeMQR(uint32_t formatInfoBits) { // We don't use the additional masking (with 0x4445) to work around potentially non complying MircoQRCode encoders int bestFormatInfo = FindBestFormatInfo(0, FORMAT_INFO_DECODE_LOOKUP_MICRO, {formatInfoBits}); diff --git a/core/src/qrcode/QRFormatInformation.h b/core/src/qrcode/QRFormatInformation.h index a560c62d33..a8680b8023 100644 --- a/core/src/qrcode/QRFormatInformation.h +++ b/core/src/qrcode/QRFormatInformation.h @@ -36,8 +36,8 @@ class FormatInformation public: FormatInformation() = default; - static FormatInformation DecodeFormatInformation(uint32_t formatInfoBits1, uint32_t formatInfoBits2); - static FormatInformation DecodeFormatInformation(uint32_t formatInfoBits); + static FormatInformation DecodeQR(uint32_t formatInfoBits1, uint32_t formatInfoBits2); + static FormatInformation DecodeMQR(uint32_t formatInfoBits); ErrorCorrectionLevel errorCorrectionLevel() const { return _errorCorrectionLevel; } diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index 3d30b0f05b..f533e7af12 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -57,10 +57,10 @@ Result Reader::decode(const BinaryBitmap& image) const bool isMicro = false; DetectorResult detectorResult; if (_testQR) - detectorResult = DetectPure(*binImg); + detectorResult = DetectPureQR(*binImg); if (_testMQR && !detectorResult.isValid()) { isMicro = true; - detectorResult = DetectPureMicroQR(*binImg); + detectorResult = DetectPureMQR(*binImg); } if (!detectorResult.isValid()) @@ -93,7 +93,7 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const if (Contains(usedFPs, fpSet.bl) || Contains(usedFPs, fpSet.tl) || Contains(usedFPs, fpSet.tr)) continue; - auto detectorResult = SampleAtFinderPatternSet(*binImg, fpSet); + auto detectorResult = SampleQR(*binImg, fpSet); if (detectorResult.isValid()) { auto decoderResult = Decode(detectorResult.bits(), _charset); auto position = detectorResult.position(); @@ -114,7 +114,7 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const if (Contains(usedFPs, fp)) continue; - auto detectorResult = SampleAtFinderPattern(*binImg, fp); + auto detectorResult = SampleMQR(*binImg, fp); if (detectorResult.isValid()) { auto decoderResult = Decode(detectorResult.bits(), _charset, true); auto position = detectorResult.position(); @@ -123,6 +123,7 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const if (maxSymbols && Size(results) == maxSymbols) break; } + } } } diff --git a/test/unit/qrcode/QRFormatInformationTest.cpp b/test/unit/qrcode/QRFormatInformationTest.cpp index d4eb47dc87..c87184585b 100644 --- a/test/unit/qrcode/QRFormatInformationTest.cpp +++ b/test/unit/qrcode/QRFormatInformationTest.cpp @@ -29,7 +29,7 @@ static const int MICRO_UNMASKED_TEST_FORMAT_INFO = MICRO_MASKED_TEST_FORMAT_INFO static void DoFormatInformationTest(const int formatInfo, const uint8_t expectedMask, const ErrorCorrectionLevel& expectedECL) { - FormatInformation parsedFormat = FormatInformation::DecodeFormatInformation(formatInfo); + FormatInformation parsedFormat = FormatInformation::DecodeMQR(formatInfo); EXPECT_TRUE(parsedFormat.isValid()); EXPECT_EQ(expectedMask, parsedFormat.dataMask()); EXPECT_EQ(expectedECL, parsedFormat.errorCorrectionLevel()); @@ -38,28 +38,28 @@ static void DoFormatInformationTest(const int formatInfo, const uint8_t expected TEST(QRFormatInformationTest, Decode) { // Normal case - FormatInformation expected = FormatInformation::DecodeFormatInformation(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO); + FormatInformation expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO); EXPECT_TRUE(expected.isValid()); EXPECT_EQ(0x07, expected.dataMask()); EXPECT_EQ(ErrorCorrectionLevel::Quality, expected.errorCorrectionLevel()); // where the code forgot the mask! - EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(UNMASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO)); + EXPECT_EQ(expected, FormatInformation::DecodeQR(UNMASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO)); } TEST(QRFormatInformationTest, DecodeWithBitDifference) { - FormatInformation expected = FormatInformation::DecodeFormatInformation(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO); + FormatInformation expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO); // 1,2,3,4 bits difference - EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x01, MASKED_TEST_FORMAT_INFO ^ 0x01)); - EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x03, MASKED_TEST_FORMAT_INFO ^ 0x03)); - EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x07, MASKED_TEST_FORMAT_INFO ^ 0x07)); - EXPECT_TRUE(!FormatInformation::DecodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x0F, MASKED_TEST_FORMAT_INFO ^ 0x0F).isValid()); + EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x01, MASKED_TEST_FORMAT_INFO ^ 0x01)); + EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x03, MASKED_TEST_FORMAT_INFO ^ 0x03)); + EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x07, MASKED_TEST_FORMAT_INFO ^ 0x07)); + EXPECT_TRUE(!FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x0F, MASKED_TEST_FORMAT_INFO ^ 0x0F).isValid()); } TEST(QRFormatInformationTest, DecodeWithMisread) { - FormatInformation expected = FormatInformation::DecodeFormatInformation(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO); - EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x03, MASKED_TEST_FORMAT_INFO ^ 0x0F)); + FormatInformation expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO); + EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x03, MASKED_TEST_FORMAT_INFO ^ 0x0F)); } TEST(QRFormatInformationTest, DecodeMicro) @@ -83,12 +83,12 @@ TEST(QRFormatInformationTest, DecodeMicro) // distance calculation. TEST(QRFormatInformationTest, DecodeMicroWithBitDifference) { - FormatInformation expected = FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO); + FormatInformation expected = FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO); // 1,2,3 bits difference - EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x01)); - EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x03)); - EXPECT_EQ(expected, FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x07)); + EXPECT_EQ(expected, FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x01)); + EXPECT_EQ(expected, FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x03)); + EXPECT_EQ(expected, FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x07)); // Bigger bit differences can return valid FormatInformation objects but the data mask and error // correction levels do not match. From 8566392d25d60a3c1ff5e6f0eec441a269f6bb08 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 17 May 2022 13:03:38 +0200 Subject: [PATCH 017/180] QRCode: simplify Decode API by determining isMicro from symbol size --- core/src/qrcode/QRBitMatrixParser.cpp | 7 ++++--- core/src/qrcode/QRBitMatrixParser.h | 2 +- core/src/qrcode/QRDecoder.cpp | 4 ++-- core/src/qrcode/QRDecoder.h | 2 +- core/src/qrcode/QRReader.cpp | 12 +++++------- test/unit/qrcode/MQRDecoderTest.cpp | 10 +++++----- test/unit/qrcode/QRBitMatrixParserTest.cpp | 4 ++-- 7 files changed, 20 insertions(+), 21 deletions(-) diff --git a/core/src/qrcode/QRBitMatrixParser.cpp b/core/src/qrcode/QRBitMatrixParser.cpp index c9cc5ebbe0..97d484dbdd 100644 --- a/core/src/qrcode/QRBitMatrixParser.cpp +++ b/core/src/qrcode/QRBitMatrixParser.cpp @@ -42,13 +42,14 @@ static bool hasValidDimension(const BitMatrix& bitMatrix, bool isMicro) return dimension >= 21 && dimension <= 177 && (dimension % 4) == 1; } -const Version* ReadVersion(const BitMatrix& bitMatrix, bool isMicro) +const Version* ReadVersion(const BitMatrix& bitMatrix) { + int dimension = bitMatrix.height(); + bool isMicro = dimension < 21; + if (!hasValidDimension(bitMatrix, isMicro)) return nullptr; - int dimension = bitMatrix.height(); - int provisionalVersion = (dimension - Version::DimensionOffset(isMicro)) / Version::DimensionStep(isMicro); if (provisionalVersion <= 6) diff --git a/core/src/qrcode/QRBitMatrixParser.h b/core/src/qrcode/QRBitMatrixParser.h index c911389b6f..b8aa567adf 100644 --- a/core/src/qrcode/QRBitMatrixParser.h +++ b/core/src/qrcode/QRBitMatrixParser.h @@ -30,7 +30,7 @@ class FormatInformation; * @brief Reads version information from the QR Code. * @return {@link Version} encapsulating the QR Code's version, nullptr if neither location can be parsed */ -const Version* ReadVersion(const BitMatrix& bitMatrix, bool isMicro); +const Version* ReadVersion(const BitMatrix& bitMatrix); /** * @brief Reads format information from one of its two locations within the QR Code. diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 6e5c259814..8aaf06ade1 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -488,9 +488,9 @@ static DecoderResult DoDecode(const BitMatrix& bits, const Version& version, con return DecodeBitStream(std::move(resultBytes), version, formatInfo.errorCorrectionLevel(), hintedCharset); } -DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset, const bool isMicroQRCode) +DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset) { - const Version* version = ReadVersion(bits, isMicroQRCode); + const Version* version = ReadVersion(bits); if (!version) return DecodeStatus::FormatError; diff --git a/core/src/qrcode/QRDecoder.h b/core/src/qrcode/QRDecoder.h index e8171b036b..2f10bddc59 100644 --- a/core/src/qrcode/QRDecoder.h +++ b/core/src/qrcode/QRDecoder.h @@ -28,7 +28,7 @@ namespace QRCode { /** * @brief Decodes a QR Code from the BitMatrix and the hinted charset. */ -DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset, const bool isMicroQRCode = false); +DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset = {}); } // QRCode } // ZXing diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index f533e7af12..505ab6ab0d 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -54,22 +54,20 @@ Result Reader::decode(const BinaryBitmap& image) const return Result(DecodeStatus::NotFound); } - bool isMicro = false; DetectorResult detectorResult; if (_testQR) detectorResult = DetectPureQR(*binImg); - if (_testMQR && !detectorResult.isValid()) { - isMicro = true; + if (_testMQR && !detectorResult.isValid()) detectorResult = DetectPureMQR(*binImg); - } if (!detectorResult.isValid()) return Result(DecodeStatus::NotFound); - auto decoderResult = Decode(detectorResult.bits(), _charset, isMicro); + auto decoderResult = Decode(detectorResult.bits(), _charset); auto position = detectorResult.position(); - return Result(std::move(decoderResult), std::move(position), isMicro ? BarcodeFormat::MicroQRCode : BarcodeFormat::QRCode); + return Result(std::move(decoderResult), std::move(position), + detectorResult.bits().width() < 21 ? BarcodeFormat::MicroQRCode : BarcodeFormat::QRCode); } Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const @@ -116,7 +114,7 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const auto detectorResult = SampleMQR(*binImg, fp); if (detectorResult.isValid()) { - auto decoderResult = Decode(detectorResult.bits(), _charset, true); + auto decoderResult = Decode(detectorResult.bits(), _charset); auto position = detectorResult.position(); if (decoderResult.isValid()) { results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MicroQRCode); diff --git a/test/unit/qrcode/MQRDecoderTest.cpp b/test/unit/qrcode/MQRDecoderTest.cpp index 47ebe2ef73..c49f1217d2 100644 --- a/test/unit/qrcode/MQRDecoderTest.cpp +++ b/test/unit/qrcode/MQRDecoderTest.cpp @@ -45,7 +45,7 @@ TEST(MQRDecoderTest, MQRCodeM3L) "XXX XX X X XXXX\n", 88, false); - const auto result = Decode(bitMatrix, {}, true); + const auto result = Decode(bitMatrix); EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); } @@ -68,7 +68,7 @@ TEST(MQRDecoderTest, MQRCodeM3M) "X X XXXX XXX\n", 88, false); - const auto result = Decode(bitMatrix, {}, true); + const auto result = Decode(bitMatrix); EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); } @@ -86,7 +86,7 @@ TEST(MQRDecoderTest, MQRCodeM1) " X XXXXX X\n" "X XXXXXX X\n", 88, false); - const auto result = Decode(bitMatrix, {}, true); + const auto result = Decode(bitMatrix); EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); EXPECT_EQ(L"123", result.text()); } @@ -105,7 +105,7 @@ TEST(MQRDecoderTest, MQRCodeM1Error4Bits) " X XXXXXX \n" "X XXXXXXX \n", 88, false); - const auto result = Decode(bitMatrix, {}, true); + const auto result = Decode(bitMatrix); EXPECT_EQ(DecodeStatus::ChecksumError, result.errorCode()); EXPECT_TRUE(result.text().empty()); } @@ -130,6 +130,6 @@ TEST(MQRDecoderTest, MQRCodeM4) " X X X X X \n" "X XXXXXXX X X X\n", 88, false); - const auto result = Decode(bitMatrix, {}, true); + const auto result = Decode(bitMatrix); EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); } diff --git a/test/unit/qrcode/QRBitMatrixParserTest.cpp b/test/unit/qrcode/QRBitMatrixParserTest.cpp index 1eea005bf6..023c4614d0 100644 --- a/test/unit/qrcode/QRBitMatrixParserTest.cpp +++ b/test/unit/qrcode/QRBitMatrixParserTest.cpp @@ -46,7 +46,7 @@ TEST(QRBitMatrixParserTest, MQRCodeM3L) "XXX XX X X XXXX\n", 88, false); - const auto version = ReadVersion(bitMatrix, true); + const auto version = ReadVersion(bitMatrix); EXPECT_EQ(3, version->versionNumber()); const auto format = ReadFormatInformation(bitMatrix, false, true); const auto codewords = ReadCodewords(bitMatrix, *version, format, false); @@ -74,7 +74,7 @@ TEST(QRBitMatrixParserTest, MQRCodeM3M) "X X XXXX XXX\n", 88, false); - const auto version = ReadVersion(bitMatrix, true); + const auto version = ReadVersion(bitMatrix); EXPECT_EQ(3, version->versionNumber()); const auto format = ReadFormatInformation(bitMatrix, false, true); const auto codewords = ReadCodewords(bitMatrix, *version, format, false); From b4cb588b5de70d195944245f7f2b938a20e06732 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 17 May 2022 13:05:06 +0200 Subject: [PATCH 018/180] BlackboxTestRunner: random clang-format whitespace fixes --- test/blackbox/BlackboxTestRunner.cpp | 31 ++++++++++++---------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index cca643fbd0..ab75e6169e 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -59,9 +59,7 @@ namespace { TC tc[2] = {}; int rotation = 0; // The rotation in degrees clockwise to use for this test. - TestCase(int mntf, int mnts, int mmf, int mms, int r) - : tc{{"fast", mntf, mmf}, {"slow", mnts, mms}}, rotation(r) - {} + TestCase(int mntf, int mnts, int mmf, int mms, int r) : tc{{"fast", mntf, mmf}, {"slow", mnts, mms}}, rotation(r) {} TestCase(int mntf, int mnts, int r) : TestCase(mntf, mnts, 0, 0, r) {} TestCase(int mntp, int mmp, PureTag) : tc{{"pure", mntp, mmp}} {} }; @@ -185,8 +183,7 @@ static void printPositiveTestStats(int imageCount, const TestCase::TC& tc) { int passCount = imageCount - Size(tc.misReadFiles) - Size(tc.notDetectedFiles); - fmt::print(" | {}: {:3} of {:3}, misread {} of {}", tc.name, passCount, tc.minPassCount, Size(tc.misReadFiles), - tc.maxMisreads); + fmt::print(" | {}: {:3} of {:3}, misread {} of {}", tc.name, passCount, tc.minPassCount, Size(tc.misReadFiles), tc.maxMisreads); if (passCount < tc.minPassCount && !tc.notDetectedFiles.empty()) { fmt::print("\nFAILED: Not detected ({}):", tc.name); @@ -217,8 +214,8 @@ static std::vector getImagesInDirectory(const fs::path& directory) return result; } -static void doRunTests( - const fs::path& directory, std::string_view format, int totalTests, const std::vector& tests, DecodeHints hints) +static void doRunTests(const fs::path& directory, std::string_view format, int totalTests, const std::vector& tests, + DecodeHints hints) { auto imgPaths = getImagesInDirectory(directory); auto folderName = directory.stem(); @@ -261,9 +258,8 @@ static Result readMultiple(const std::vector& imgPaths, std::string_vi { std::list allResults; for (const auto& imgPath : imgPaths) { - auto results = - ReadBarcodes(ImageLoader::load(imgPath), - DecodeHints().setFormats(BarcodeFormatFromString(format.data())).setTryDownscale(false)); + auto results = ReadBarcodes(ImageLoader::load(imgPath), + DecodeHints().setFormats(BarcodeFormatFromString(format.data())).setTryDownscale(false)); allResults.insert(allResults.end(), results.begin(), results.end()); } @@ -282,13 +278,12 @@ static Result readMultiple(const std::vector& imgPaths, std::string_vi text.append(r.text()); const auto& first = allResults.front(); - StructuredAppendInfo sai{ first.sequenceIndex(), first.sequenceSize(), first.sequenceId() }; - return Result(std::move(text), {}, first.format(), std::string(first.symbologyIdentifier()), {}, std::move(sai), - first.readerInit()); + StructuredAppendInfo sai{first.sequenceIndex(), first.sequenceSize(), first.sequenceId()}; + return {std::move(text), {}, first.format(), std::string(first.symbologyIdentifier()), {}, std::move(sai), first.readerInit()}; } -static void doRunStructuredAppendTest( - const fs::path& directory, std::string_view format, int totalTests, const std::vector& tests) +static void doRunStructuredAppendTest(const fs::path& directory, std::string_view format, int totalTests, + const std::vector& tests) { auto imgPaths = getImagesInDirectory(directory); auto folderName = directory.stem(); @@ -301,8 +296,7 @@ static void doRunStructuredAppendTest( } if (Size(imageGroups) != totalTests) - fmt::print("TEST {} => Expected number of tests: {}, got: {} => FAILED\n", folderName, totalTests, - imageGroups.size()); + fmt::print("TEST {} => Expected number of tests: {}, got: {} => FAILED\n", folderName, totalTests, imageGroups.size()); for (auto& test : tests) { fmt::print("{:20} @ {:3}, {:3}", folderName.string(), test.rotation, Size(imgPaths)); @@ -339,7 +333,8 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set doRunTests(testPathPrefix / directory, format, total, tests, hints); }; - auto runStructuredAppendTest = [&](std::string_view directory, std::string_view format, int total, const std::vector& tests) { + auto runStructuredAppendTest = [&](std::string_view directory, std::string_view format, int total, + const std::vector& tests) { if (hasTest(directory)) doRunStructuredAppendTest(testPathPrefix / directory, format, total, tests); }; From cff0440d2caffe83ec8a3840c94943582841c5de Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 17 May 2022 13:15:57 +0200 Subject: [PATCH 019/180] QRDecoder: code style fixes --- core/src/qrcode/QRDecoder.cpp | 96 ++++++++++++++--------------------- 1 file changed, 39 insertions(+), 57 deletions(-) diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 8aaf06ade1..24d017a7e3 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -72,9 +72,8 @@ static bool CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) static DecodeStatus DecodeHanziSegment(BitSource& bits, int count, std::wstring& result) { // Don't crash trying to read more bits than we have available. - if (count * 13 > bits.available()) { + if (count * 13 > bits.available()) return DecodeStatus::FormatError; - } // Each character will require 2 bytes. Read the characters as 2-byte pairs // and decode as GB2312 afterwards @@ -87,8 +86,7 @@ static DecodeStatus DecodeHanziSegment(BitSource& bits, int count, std::wstring& if (assembledTwoBytes < 0x00A00) { // In the 0xA1A1 to 0xAAFE range assembledTwoBytes += 0x0A1A1; - } - else { + } else { // In the 0xB0A1 to 0xFAFE range assembledTwoBytes += 0x0A6A1; } @@ -104,9 +102,8 @@ static DecodeStatus DecodeHanziSegment(BitSource& bits, int count, std::wstring& static DecodeStatus DecodeKanjiSegment(BitSource& bits, int count, std::wstring& result) { // Don't crash trying to read more bits than we have available. - if (count * 13 > bits.available()) { + if (count * 13 > bits.available()) return DecodeStatus::FormatError; - } // Each character will require 2 bytes. Read the characters as 2-byte pairs // and decode as Shift_JIS afterwards @@ -119,8 +116,7 @@ static DecodeStatus DecodeKanjiSegment(BitSource& bits, int count, std::wstring& if (assembledTwoBytes < 0x01F00) { // In the 0x8140 to 0x9FFC range assembledTwoBytes += 0x08140; - } - else { + } else { // In the 0xE040 to 0xEBBF range assembledTwoBytes += 0x0C140; } @@ -137,28 +133,24 @@ static DecodeStatus DecodeByteSegment(BitSource& bits, int count, CharacterSet c std::wstring& result) { // Don't crash trying to read more bits than we have available. - if (8 * count > bits.available()) { + if (8 * count > bits.available()) return DecodeStatus::FormatError; - } ByteArray readBytes(count); - for (int i = 0; i < count; i++) { + for (int i = 0; i < count; i++) readBytes[i] = static_cast(bits.readBits(8)); - } - if (currentCharset == CharacterSet::Unknown) { + + if (currentCharset == CharacterSet::Unknown) { // The spec isn't clear on this mode; see // section 6.4.5: t does not say which encoding to assuming // upon decoding. I have seen ISO-8859-1 used as well as // Shift_JIS -- without anything like an ECI designator to // give a hint. if (!hintedCharset.empty()) - { currentCharset = CharacterSetECI::CharsetFromName(hintedCharset.c_str()); - } + if (currentCharset == CharacterSet::Unknown) - { currentCharset = TextDecoder::GuessEncoding(readBytes.data(), Size(readBytes)); - } } TextDecoder::Append(result, readBytes.data(), Size(readBytes), currentCharset); return DecodeStatus::NoError; @@ -176,9 +168,9 @@ static char ToAlphaNumericChar(int value) ' ', '$', '%', '*', '+', '-', '.', '/', ':' }; - if (value < 0 || value >= Size(ALPHANUMERIC_CHARS)) { + if (value < 0 || value >= Size(ALPHANUMERIC_CHARS)) throw std::out_of_range("ToAlphaNumericChar: out of range"); - } + return ALPHANUMERIC_CHARS[value]; } @@ -187,9 +179,8 @@ static DecodeStatus DecodeAlphanumericSegment(BitSource& bits, int count, bool f // Read two characters at a time std::string buffer; while (count > 1) { - if (bits.available() < 11) { + if (bits.available() < 11) return DecodeStatus::FormatError; - } int nextTwoCharsBits = bits.readBits(11); buffer += ToAlphaNumericChar(nextTwoCharsBits / 45); buffer += ToAlphaNumericChar(nextTwoCharsBits % 45); @@ -197,9 +188,8 @@ static DecodeStatus DecodeAlphanumericSegment(BitSource& bits, int count, bool f } if (count == 1) { // special case: one character left - if (bits.available() < 6) { + if (bits.available() < 6) return DecodeStatus::FormatError; - } buffer += ToAlphaNumericChar(bits.readBits(6)); } // See section 6.4.8.1, 6.4.8.2 @@ -210,8 +200,7 @@ static DecodeStatus DecodeAlphanumericSegment(BitSource& bits, int count, bool f if (i < buffer.length() - 1 && buffer[i + 1] == '%') { // %% is rendered as % buffer.erase(i + 1); - } - else { + } else { // In alpha mode, % should be converted to FNC1 separator 0x1D buffer[i] = static_cast(0x1D); } @@ -228,39 +217,39 @@ static DecodeStatus DecodeNumericSegment(BitSource& bits, int count, std::wstrin std::string buffer; while (count >= 3) { // Each 10 bits encodes three digits - if (bits.available() < 10) { + if (bits.available() < 10) return DecodeStatus::FormatError; - } + int threeDigitsBits = bits.readBits(10); - if (threeDigitsBits >= 1000) { + if (threeDigitsBits >= 1000) return DecodeStatus::FormatError; - } + buffer += ToAlphaNumericChar(threeDigitsBits / 100); buffer += ToAlphaNumericChar((threeDigitsBits / 10) % 10); buffer += ToAlphaNumericChar(threeDigitsBits % 10); count -= 3; } + if (count == 2) { // Two digits left over to read, encoded in 7 bits - if (bits.available() < 7) { + if (bits.available() < 7) return DecodeStatus::FormatError; - } + int twoDigitsBits = bits.readBits(7); - if (twoDigitsBits >= 100) { + if (twoDigitsBits >= 100) return DecodeStatus::FormatError; - } + buffer += ToAlphaNumericChar(twoDigitsBits / 10); buffer += ToAlphaNumericChar(twoDigitsBits % 10); - } - else if (count == 1) { + } else if (count == 1) { // One digit left over to read - if (bits.available() < 4) { + if (bits.available() < 4) return DecodeStatus::FormatError; - } + int digitBits = bits.readBits(4); - if (digitBits >= 10) { + if (digitBits >= 10) return DecodeStatus::FormatError; - } + buffer += ToAlphaNumericChar(digitBits); } @@ -366,9 +355,9 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo appIndValue = bits.readBits(8); // Number 00-99 or ASCII value + 100; prefixed to data below break; case CodecMode::STRUCTURED_APPEND: - if (bits.available() < 16) { + if (bits.available() < 16) return DecodeStatus::FormatError; - } + // 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); @@ -379,13 +368,12 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo // Count doesn't apply to ECI int value; auto status = ParseECIValue(bits, value); - if (StatusIsError(status)) { + if (StatusIsError(status)) return status; - } + currentCharset = CharacterSetECI::CharsetFromValue(value); - if (currentCharset == CharacterSet::Unknown) { + if (currentCharset == CharacterSet::Unknown) return DecodeStatus::FormatError; - } break; } case CodecMode::HANZI: { @@ -395,9 +383,8 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo int countHanzi = bits.readBits(CharacterCountBits(mode, version)); if (subset == GB2312_SUBSET) { auto status = DecodeHanziSegment(bits, countHanzi, result); - if (StatusIsError(status)) { + if (StatusIsError(status)) return status; - } } break; } @@ -413,9 +400,8 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo case CodecMode::KANJI: status = DecodeKanjiSegment(bits, count, result); break; default: status = DecodeStatus::FormatError; } - if (StatusIsError(status)) { + if (StatusIsError(status)) return status; - } break; } } @@ -428,18 +414,14 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo } if (appIndValue >= 0) { - if (appIndValue < 10) { // "00-09" + if (appIndValue < 10) // "00-09" result.insert(0, L'0' + std::to_wstring(appIndValue)); - } - else if (appIndValue < 100) { // "10-99" + else if (appIndValue < 100) // "10-99" result.insert(0, std::to_wstring(appIndValue)); - } - else if ((appIndValue >= 165 && appIndValue <= 190) || (appIndValue >= 197 && appIndValue <= 222)) { // "A-Za-z" + else if ((appIndValue >= 165 && appIndValue <= 190) || (appIndValue >= 197 && appIndValue <= 222)) // "A-Za-z" result.insert(0, 1, static_cast(appIndValue - 100)); - } - else { + else return DecodeStatus::FormatError; - } } std::string symbologyIdentifier("]Q" + std::to_string(symbologyIdModifier)); From 3be35e239b5a4f791dab72d2965ca73ef533ef73 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 18 May 2022 02:18:36 +0200 Subject: [PATCH 020/180] QRDecoder: move to consistent exception based error handling The code was inconsistently using error status return values and exceptions. Decoded to consistently switch to exceptions as it leads to cleaner code and is less error prone in its usage. Note: the errors that might happen during decoding of the bit stream are indeed exceptional (not happening in any of the black box sample images) so this is an acceptable performance trade-off. --- core/src/qrcode/QRDecoder.cpp | 101 +++++++++------------------------- 1 file changed, 27 insertions(+), 74 deletions(-) diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 24d017a7e3..66b2a41c11 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -69,12 +69,8 @@ static bool CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) /** * See specification GBT 18284-2000 */ -static DecodeStatus DecodeHanziSegment(BitSource& bits, int count, std::wstring& result) +static void DecodeHanziSegment(BitSource& bits, int count, std::wstring& result) { - // Don't crash trying to read more bits than we have available. - if (count * 13 > bits.available()) - return DecodeStatus::FormatError; - // Each character will require 2 bytes. Read the characters as 2-byte pairs // and decode as GB2312 afterwards ByteArray buffer; @@ -96,15 +92,10 @@ static DecodeStatus DecodeHanziSegment(BitSource& bits, int count, std::wstring& } TextDecoder::Append(result, buffer.data(), Size(buffer), CharacterSet::GB2312); - return DecodeStatus::NoError; } -static DecodeStatus DecodeKanjiSegment(BitSource& bits, int count, std::wstring& result) +static void DecodeKanjiSegment(BitSource& bits, int count, std::wstring& result) { - // Don't crash trying to read more bits than we have available. - if (count * 13 > bits.available()) - return DecodeStatus::FormatError; - // Each character will require 2 bytes. Read the characters as 2-byte pairs // and decode as Shift_JIS afterwards ByteArray buffer; @@ -126,16 +117,11 @@ static DecodeStatus DecodeKanjiSegment(BitSource& bits, int count, std::wstring& } TextDecoder::Append(result, buffer.data(), Size(buffer), CharacterSet::Shift_JIS); - return DecodeStatus::NoError; } -static DecodeStatus DecodeByteSegment(BitSource& bits, int count, CharacterSet currentCharset, const std::string& hintedCharset, - std::wstring& result) +static void DecodeByteSegment(BitSource& bits, int count, CharacterSet currentCharset, const std::string& hintedCharset, + std::wstring& result) { - // Don't crash trying to read more bits than we have available. - if (8 * count > bits.available()) - return DecodeStatus::FormatError; - ByteArray readBytes(count); for (int i = 0; i < count; i++) readBytes[i] = static_cast(bits.readBits(8)); @@ -153,7 +139,6 @@ static DecodeStatus DecodeByteSegment(BitSource& bits, int count, CharacterSet c currentCharset = TextDecoder::GuessEncoding(readBytes.data(), Size(readBytes)); } TextDecoder::Append(result, readBytes.data(), Size(readBytes), currentCharset); - return DecodeStatus::NoError; } static char ToAlphaNumericChar(int value) @@ -174,13 +159,11 @@ static char ToAlphaNumericChar(int value) return ALPHANUMERIC_CHARS[value]; } -static DecodeStatus DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffect, std::wstring& result) +static void DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffect, std::wstring& result) { // Read two characters at a time std::string buffer; while (count > 1) { - if (bits.available() < 11) - return DecodeStatus::FormatError; int nextTwoCharsBits = bits.readBits(11); buffer += ToAlphaNumericChar(nextTwoCharsBits / 45); buffer += ToAlphaNumericChar(nextTwoCharsBits % 45); @@ -188,8 +171,6 @@ static DecodeStatus DecodeAlphanumericSegment(BitSource& bits, int count, bool f } if (count == 1) { // special case: one character left - if (bits.available() < 6) - return DecodeStatus::FormatError; buffer += ToAlphaNumericChar(bits.readBits(6)); } // See section 6.4.8.1, 6.4.8.2 @@ -208,21 +189,17 @@ static DecodeStatus DecodeAlphanumericSegment(BitSource& bits, int count, bool f } } TextDecoder::AppendLatin1(result, buffer); - return DecodeStatus::NoError; } -static DecodeStatus DecodeNumericSegment(BitSource& bits, int count, std::wstring& result) +static void DecodeNumericSegment(BitSource& bits, int count, std::wstring& result) { // Read three digits at a time std::string buffer; while (count >= 3) { // Each 10 bits encodes three digits - if (bits.available() < 10) - return DecodeStatus::FormatError; - int threeDigitsBits = bits.readBits(10); if (threeDigitsBits >= 1000) - return DecodeStatus::FormatError; + throw std::runtime_error("Invalid value in numeric segment"); buffer += ToAlphaNumericChar(threeDigitsBits / 100); buffer += ToAlphaNumericChar((threeDigitsBits / 10) % 10); @@ -232,52 +209,42 @@ static DecodeStatus DecodeNumericSegment(BitSource& bits, int count, std::wstrin if (count == 2) { // Two digits left over to read, encoded in 7 bits - if (bits.available() < 7) - return DecodeStatus::FormatError; - int twoDigitsBits = bits.readBits(7); if (twoDigitsBits >= 100) - return DecodeStatus::FormatError; + throw std::runtime_error("Invalid value in numeric segment"); buffer += ToAlphaNumericChar(twoDigitsBits / 10); buffer += ToAlphaNumericChar(twoDigitsBits % 10); } else if (count == 1) { // One digit left over to read - if (bits.available() < 4) - return DecodeStatus::FormatError; - int digitBits = bits.readBits(4); if (digitBits >= 10) - return DecodeStatus::FormatError; + throw std::runtime_error("Invalid value in numeric segment"); buffer += ToAlphaNumericChar(digitBits); } TextDecoder::AppendLatin1(result, buffer); - return DecodeStatus::NoError; } -static DecodeStatus ParseECIValue(BitSource& bits, int& outValue) +static int ParseECIValue(BitSource& bits) { int firstByte = bits.readBits(8); if ((firstByte & 0x80) == 0) { // just one byte - outValue = firstByte & 0x7F; - return DecodeStatus::NoError; + return firstByte & 0x7F; } if ((firstByte & 0xC0) == 0x80) { // two bytes int secondByte = bits.readBits(8); - outValue = ((firstByte & 0x3F) << 8) | secondByte; - return DecodeStatus::NoError; + return ((firstByte & 0x3F) << 8) | secondByte; } if ((firstByte & 0xE0) == 0xC0) { // three bytes int secondThirdBytes = bits.readBits(16); - outValue = ((firstByte & 0x1F) << 16) | secondThirdBytes; - return DecodeStatus::NoError; + return ((firstByte & 0x1F) << 16) | secondThirdBytes; } - return DecodeStatus::FormatError; + throw std::runtime_error("ParseECIValue: invalid value"); } /** @@ -355,9 +322,6 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo appIndValue = bits.readBits(8); // Number 00-99 or ASCII value + 100; prefixed to data below break; case CodecMode::STRUCTURED_APPEND: - if (bits.available() < 16) - return DecodeStatus::FormatError; - // 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); @@ -366,12 +330,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo break; case CodecMode::ECI: { // Count doesn't apply to ECI - int value; - auto status = ParseECIValue(bits, value); - if (StatusIsError(status)) - return status; - - currentCharset = CharacterSetECI::CharsetFromValue(value); + currentCharset = CharacterSetECI::CharsetFromValue(ParseECIValue(bits)); if (currentCharset == CharacterSet::Unknown) return DecodeStatus::FormatError; break; @@ -381,35 +340,31 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo // chinese mode contains a sub set indicator right after mode indicator int subset = bits.readBits(4); int countHanzi = bits.readBits(CharacterCountBits(mode, version)); - if (subset == GB2312_SUBSET) { - auto status = DecodeHanziSegment(bits, countHanzi, result); - if (StatusIsError(status)) - return status; - } + if (subset == GB2312_SUBSET) + DecodeHanziSegment(bits, countHanzi, result); break; } default: { // "Normal" QR code modes: // How many characters will follow, encoded in this mode? int count = bits.readBits(CharacterCountBits(mode, version)); - DecodeStatus status; switch (mode) { - case CodecMode::NUMERIC: status = DecodeNumericSegment(bits, count, result); break; - case CodecMode::ALPHANUMERIC: status = DecodeAlphanumericSegment(bits, count, fc1InEffect, result); break; - case CodecMode::BYTE: status = DecodeByteSegment(bits, count, currentCharset, hintedCharset, result); break; - case CodecMode::KANJI: status = DecodeKanjiSegment(bits, count, result); break; - default: status = DecodeStatus::FormatError; + case CodecMode::NUMERIC: DecodeNumericSegment(bits, count, result); break; + case CodecMode::ALPHANUMERIC: DecodeAlphanumericSegment(bits, count, fc1InEffect, result); break; + case CodecMode::BYTE: DecodeByteSegment(bits, count, currentCharset, hintedCharset, result); break; + case CodecMode::KANJI: DecodeKanjiSegment(bits, count, result); break; + default: return DecodeStatus::FormatError; } - if (StatusIsError(status)) - return status; break; } } } while (mode != CodecMode::TERMINATOR); } - catch (const std::exception &) + catch (const std::exception& e) { - // from readBits() calls +#ifndef NDEBUG + printf("QRDecoder error: %s\n", e.what()); +#endif return DecodeStatus::FormatError; } @@ -424,11 +379,9 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo return DecodeStatus::FormatError; } - std::string symbologyIdentifier("]Q" + std::to_string(symbologyIdModifier)); - return DecoderResult(std::move(bytes), std::move(result)) .setEcLevel(ToString(ecLevel)) - .setSymbologyIdentifier(std::move(symbologyIdentifier)) + .setSymbologyIdentifier("]Q" + std::to_string(symbologyIdModifier)) .setStructuredAppend(structuredAppend); } From 9bf977c24c938caeb2d6fa1b6e31581473c92549 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 18 May 2022 02:28:19 +0200 Subject: [PATCH 021/180] DMDecoder: move to consistent exception based error handling + cleanup The code was inconsistently using error status return values and exceptions. Decoded to consistently switch to exceptions as it leads to cleaner code and is less error prone in its usage. Note: the errors that might happen during decoding of the bit stream are indeed exceptional (not happening in any of the black box sample images) so this is an acceptable performance trade-off. Furthermore: removed the unnecessary complicated "mode latching" state machine code. --- core/src/datamatrix/DMDecoder.cpp | 271 +++++++++++------------------- 1 file changed, 99 insertions(+), 172 deletions(-) diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index feb469cb38..9320659132 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -51,18 +51,6 @@ namespace ZXing::DataMatrix { */ namespace DecodedBitStreamParser { -enum Mode -{ - FORMAT_ERROR, - DONE, // reached end of code word sequence or a PAD codeword - ASCII_ENCODE, - C40_ENCODE, - TEXT_ENCODE, - ANSIX12_ENCODE, - EDIFACT_ENCODE, - BASE256_ENCODE -}; - /** * See ISO 16022:2006, Annex C Table C.1 * The C40 Basic Character Set (*'s used for placeholders for the shift values) @@ -96,17 +84,6 @@ static const char TEXT_SHIFT3_SET_CHARS[] = { 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', 127 }; -// Decoding state -struct State -{ - CharacterSet encoding; - int symbologyIdModifier = 1; // ECC 200 (ISO 16022:2006 Annex N Table N.1) - struct StructuredAppendInfo sai; - bool readerInit = false; - bool firstCodeword = true; - int firstFNC1Position = 1; -}; - struct Shift128 { bool set = false; @@ -152,97 +129,6 @@ static void ParseStructuredAppend(BitSource& bits, StructuredAppendInfo& sai) sai.id = std::to_string((fileId1 << 8) | fileId2); } -/** -* See ISO 16022:2006, 5.2.3 and Annex C, Table C.2 -*/ -static Mode DecodeAsciiSegment(BitSource& bits, std::string& result, std::string& resultTrailer, - std::wstring& resultEncoded, State& state) -{ - Shift128 upperShift; - - while (bits.available() >= 8) { - int oneByte = bits.readBits(8); - switch (oneByte) { - case 0: - return Mode::FORMAT_ERROR; - case 129: // Pad - return Mode::DONE; - case 230: // Latch to C40 encodation - return Mode::C40_ENCODE; - case 231: // Latch to Base 256 encodation - return Mode::BASE256_ENCODE; - case 232: // FNC1 - if (bits.byteOffset() == state.firstFNC1Position || bits.byteOffset() == state.firstFNC1Position + 1) { - // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using - // modifiers that indicate ECI protocol (ISO 16022:2006 Annex N Table N.1, ISO 21471:2020 Annex G Table G.1) - - // Only recognizing an FNC1 as first/second by codeword position (aka symbol character position), not - // by decoded character position, i.e. not recognizing a C40/Text encoded FNC1 (which requires a latch - // and a shift) - if (bits.byteOffset() == state.firstFNC1Position) - state.symbologyIdModifier = 2; // GS1 - else { - state.symbologyIdModifier = 3; // AIM - // Note no AIM Application Indicator format defined, ISO 16022:2006 11.2 - } - } else - result.push_back((char)29); // translate as ASCII 29 - break; - case 233: // Structured Append - if (state.firstCodeword) { // Must be first ISO 16022:2006 5.6.1 - ParseStructuredAppend(bits, state.sai); - state.firstFNC1Position = 5; - } else - return Mode::FORMAT_ERROR; - break; - case 234: // Reader Programming - if (state.firstCodeword) // Must be first ISO 16022:2006 5.2.4.9 - state.readerInit = true; - else - return Mode::FORMAT_ERROR; - break; - case 235: // Upper Shift (shift to Extended ASCII) - upperShift.set = true; - break; - case 236: // 05 Macro - result.append("[)>\x1E""05\x1D"); - resultTrailer.insert(0, "\x1E\x04"); - break; - case 237: // 06 Macro - result.append("[)>\x1E""06\x1D"); - resultTrailer.insert(0, "\x1E\x04"); - break; - case 238: // Latch to ANSI X12 encodation - return Mode::ANSIX12_ENCODE; - case 239: // Latch to Text encodation - return Mode::TEXT_ENCODE; - case 240: // Latch to EDIFACT encodation - return Mode::EDIFACT_ENCODE; - case 241: // ECI Character - state.encoding = CharacterSetECI::OnChangeAppendReset(ParseECIValue(bits), resultEncoded, result, - state.encoding); - break; - default: - if (oneByte <= 128) { // ASCII data (ASCII value + 1) - result.push_back(upperShift(oneByte) - 1); - } else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130) - int value = oneByte - 130; - if (value < 10) // pad with '0' for single digit values - result.push_back('0'); - result.append(std::to_string(value)); - } else if (oneByte >= 242) { // Not to be used in ASCII encodation - // work around encoders that use unlatch to ASCII as last code word (ask upstream) - if (oneByte == 254 && bits.available() == 0) - break; - return Mode::FORMAT_ERROR; - } - } - state.firstCodeword = false; - } - - return Mode::DONE; -} - std::optional> DecodeNextTriple(BitSource& bits) { // Values are encoded in a 16-bit value as (1600 * C1) + (40 * C2) + C3 + 1 @@ -262,18 +148,20 @@ std::optional> DecodeNextTriple(BitSource& bits) return {{a, b, c}}; } +enum class Mode {C40, TEXT}; + /** * See ISO 16022:2006, 5.2.5 and Annex C, Table C.1 (C40) * See ISO 16022:2006, 5.2.6 and Annex C, Table C.2 (Text) */ -static bool DecodeC40OrTextSegment(BitSource& bits, std::string& result, Mode mode) +static void DecodeC40OrTextSegment(BitSource& bits, std::string& result, Mode mode) { // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time Shift128 upperShift; int shift = 0; - const char* BASIC_SET_CHARS = mode == Mode::C40_ENCODE ? C40_BASIC_SET_CHARS : TEXT_BASIC_SET_CHARS; - const char* SHIFT_SET_CHARS = mode == Mode::C40_ENCODE ? C40_SHIFT2_SET_CHARS : TEXT_SHIFT2_SET_CHARS; + const char* BASIC_SET_CHARS = mode == Mode::C40 ? C40_BASIC_SET_CHARS : TEXT_BASIC_SET_CHARS; + const char* SHIFT_SET_CHARS = mode == Mode::C40 ? C40_SHIFT2_SET_CHARS : TEXT_SHIFT2_SET_CHARS; while (auto triple = DecodeNextTriple(bits)) { for (int cValue : *triple) { @@ -284,7 +172,7 @@ static bool DecodeC40OrTextSegment(BitSource& bits, std::string& result, Mode mo else if (cValue < 40) // Size(BASIC_SET_CHARS) result.push_back(upperShift(BASIC_SET_CHARS[cValue])); else - return false; + throw std::runtime_error("invalid value in C40 or Text segment"); break; case 1: result.push_back(upperShift(cValue)); break; case 2: @@ -293,35 +181,33 @@ static bool DecodeC40OrTextSegment(BitSource& bits, std::string& result, Mode mo else if (cValue == 30) // Upper Shift upperShift.set = true; else - return false; + throw std::runtime_error("invalid value in C40 or Text segment"); break; case 3: - if (mode == Mode::C40_ENCODE) + if (mode == Mode::C40) result.push_back(upperShift(cValue + 96)); else if (cValue < Size(TEXT_SHIFT3_SET_CHARS)) result.push_back(upperShift(TEXT_SHIFT3_SET_CHARS[cValue])); else - return false; + throw std::runtime_error("invalid value in C40 or Text segment"); break; - default: return false; + default: throw std::runtime_error("invalid value in C40 or Text segment"); ; } } } - - return true; } /** * See ISO 16022:2006, 5.2.7 */ -static bool DecodeAnsiX12Segment(BitSource& bits, std::string& result) +static void DecodeAnsiX12Segment(BitSource& bits, std::string& result) { while (auto triple = DecodeNextTriple(bits)) { for (int cValue : *triple) { // X12 segment terminator , separator *, sub-element separator >, space static const char segChars[4] = {'\r', '*', '>', ' '}; if (cValue < 0) - return false; + throw std::runtime_error("invalid value in AnsiX12 segment"); else if (cValue < 4) result.push_back(segChars[cValue]); else if (cValue < 14) // 0 - 9 @@ -329,17 +215,15 @@ static bool DecodeAnsiX12Segment(BitSource& bits, std::string& result) else if (cValue < 40) // A - Z result.push_back((char)(cValue + 51)); else - return false; + throw std::runtime_error("invalid value in AnsiX12 segment"); } } - - return true; } /** * See ISO 16022:2006, 5.2.8 and Annex C Table C.3 */ -static bool DecodeEdifactSegment(BitSource& bits, std::string& result) +static void DecodeEdifactSegment(BitSource& bits, std::string& result) { // If there are less than 3 bytes left then it will be encoded as ASCII while (bits.available() >= 24) { @@ -351,7 +235,7 @@ static bool DecodeEdifactSegment(BitSource& bits, std::string& result) // Read rest of byte, which should be 0, and stop if (bits.bitOffset()) bits.readBits(8 - bits.bitOffset()); - return true; + return; } if ((edifactValue & 0x20) == 0) // no 1 in the leading (6th) bit @@ -359,8 +243,6 @@ static bool DecodeEdifactSegment(BitSource& bits, std::string& result) result.push_back(edifactValue); } } - - return true; } /** @@ -376,7 +258,7 @@ static int Unrandomize255State(int randomizedBase256Codeword, int base256Codewor /** * See ISO 16022:2006, 5.2.9 and Annex B, B.2 */ -static bool DecodeBase256Segment(BitSource& bits, std::string& result) +static void DecodeBase256Segment(BitSource& bits, std::string& result) { // Figure out how long the Base 256 Segment is. int codewordPosition = 1 + bits.byteOffset(); // position is 1-indexed @@ -391,21 +273,16 @@ static bool DecodeBase256Segment(BitSource& bits, std::string& result) // We're seeing NegativeArraySizeException errors from users. if (count < 0) - return false; + throw std::runtime_error("invalid count in Base256 segment"); ByteArray bytes(count); for (int i = 0; i < count; i++) { - // Have seen this particular error in the wild, such as at + // readBits(8) may fail, have seen this particular error in the wild, such as at // http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2 - if (bits.available() < 8) - return false; - bytes[i] = (uint8_t)Unrandomize255State(bits.readBits(8), codewordPosition++); } result.append(reinterpret_cast(bytes.data()), bytes.size()); - - return true; } ZXING_EXPORT_TEST_ONLY @@ -416,46 +293,96 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b result.reserve(100); std::string resultTrailer; std::wstring resultEncoded; - Mode mode = Mode::ASCII_ENCODE; - State state; - state.encoding = CharacterSetECI::InitEncoding(characterSet); - - while (mode != Mode::FORMAT_ERROR && mode != Mode::DONE) { - if (mode == Mode::ASCII_ENCODE) { - mode = DecodeAsciiSegment(bits, result, resultTrailer, resultEncoded, state); - state.firstCodeword = false; - state.firstFNC1Position = -1; // Only recognize in first segment - } else { - bool decodeOK; - switch (mode) { - case C40_ENCODE: [[fallthrough]]; - case TEXT_ENCODE: decodeOK = DecodeC40OrTextSegment(bits, result, mode); break; - case ANSIX12_ENCODE: decodeOK = DecodeAnsiX12Segment(bits, result); break; - case EDIFACT_ENCODE: decodeOK = DecodeEdifactSegment(bits, result); break; - case BASE256_ENCODE: decodeOK = DecodeBase256Segment(bits, result); break; - default: decodeOK = false; break; - } - mode = decodeOK ? Mode::ASCII_ENCODE : Mode::FORMAT_ERROR; - } - } - if (mode == Mode::FORMAT_ERROR) - return DecodeStatus::FormatError; + CharacterSet encoding = CharacterSetECI::InitEncoding(characterSet); + int symbologyIdModifier = 1; // ECC 200 (ISO 16022:2006 Annex N Table N.1) + struct StructuredAppendInfo sai; + bool readerInit = false; + bool firstCodeword = true; + bool done = false; + int firstFNC1Position = 1; + Shift128 upperShift; - if (state.readerInit && state.sai.index > -1) // Not allowed together ISO 16022:2006 5.2.4.9 + // See ISO 16022:2006, 5.2.3 and Annex C, Table C.2 + try { + while (!done && bits.available() >= 8) { + int oneByte = bits.readBits(8); + switch (oneByte) { + case 0: return DecodeStatus::FormatError; + case 129: done = true; break; // Pad -> we are done, ignore the rest of the bits + case 230: DecodeC40OrTextSegment(bits, result, Mode::C40); break; + case 231: DecodeBase256Segment(bits, result); break; + case 232: // FNC1 + // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using + // modifiers that indicate ECI protocol (ISO 16022:2006 Annex N Table N.1, ISO 21471:2020 Annex G Table G.1) + + // Only recognizing an FNC1 as first/second by codeword position (aka symbol character position), not + // by decoded character position, i.e. not recognizing a C40/Text encoded FNC1 (which requires a latch + // and a shift) + if (bits.byteOffset() == firstFNC1Position) + symbologyIdModifier = 2; // GS1 + else if (bits.byteOffset() == firstFNC1Position + 1) + symbologyIdModifier = 3; // AIM, note no AIM Application Indicator format defined, ISO 16022:2006 11.2 + else + result.push_back((char)29); // translate as ASCII 29 + break; + case 233: // Structured Append + if (!firstCodeword) // Must be first ISO 16022:2006 5.6.1 + return DecodeStatus::FormatError; + ParseStructuredAppend(bits, sai); + firstFNC1Position = 5; + break; + case 234: // Reader Programming + if (!firstCodeword) // Must be first ISO 16022:2006 5.2.4.9 + return DecodeStatus::FormatError; + readerInit = true; + break; + case 235: upperShift.set = true; break; // Upper Shift (shift to Extended ASCII) + case 236: // 05 Macro + result.append("[)>\x1E" "05\x1D"); + resultTrailer.insert(0, "\x1E\x04"); + break; + case 237: // 06 Macro + result.append("[)>\x1E" "06\x1D"); + resultTrailer.insert(0, "\x1E\x04"); + break; + case 238: DecodeAnsiX12Segment(bits, result); break; + case 239: DecodeC40OrTextSegment(bits, result, Mode::TEXT); break; + case 240: DecodeEdifactSegment(bits, result); break; + case 241: encoding = CharacterSetECI::OnChangeAppendReset(ParseECIValue(bits), resultEncoded, result, encoding); break; + default: + if (oneByte <= 128) { // ASCII data (ASCII value + 1) + result.push_back(upperShift(oneByte) - 1); + } else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130) + int value = oneByte - 130; + if (value < 10) // pad with '0' for single digit values + result.push_back('0'); + result.append(std::to_string(value)); + } else if (oneByte >= 242) { // Not to be used in ASCII encodation + // work around encoders that use unlatch to ASCII as last code word (ask upstream) + if (oneByte == 254 && bits.available() == 0) + break; + return DecodeStatus::FormatError; + } + } + firstCodeword = false; + } + } catch (const std::exception& e) { +#ifndef NDEBUG + printf("DMDecoder error: %s\n", e.what()); +#endif return DecodeStatus::FormatError; + } if (resultTrailer.length() > 0) result.append(resultTrailer); - TextDecoder::Append(resultEncoded, reinterpret_cast(result.data()), result.size(), state.encoding); - - std::string symbologyIdentifier("]d" + std::to_string(state.symbologyIdModifier + (isDMRE ? 6 : 0))); + TextDecoder::Append(resultEncoded, reinterpret_cast(result.data()), result.size(), encoding); return DecoderResult(std::move(bytes), std::move(resultEncoded)) - .setSymbologyIdentifier(std::move(symbologyIdentifier)) - .setStructuredAppend(state.sai) - .setReaderInit(state.readerInit); + .setSymbologyIdentifier("]d" + std::to_string(symbologyIdModifier + (isDMRE ? 6 : 0))) + .setStructuredAppend(sai) + .setReaderInit(readerInit); } } // namespace DecodedBitStreamParser From 1925bd8a33f0254c900e92388d2de769aed1d711 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 19 May 2022 11:17:13 +0200 Subject: [PATCH 022/180] QRDecoder: simplify logic / increase specification conformance --- core/src/qrcode/QRDecoder.cpp | 63 ++++++++----------- .../qrcode/QRDecodedBitStreamParserTest.cpp | 9 +-- 2 files changed, 31 insertions(+), 41 deletions(-) diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 66b2a41c11..a36c9bbb49 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -264,11 +264,11 @@ static int ParseECIValue(BitSource& bits) * @param bits the stream of bits that might have a terminator code * @param version the QR or micro QR code version */ -bool IsTerminator(const BitSource& bits, const Version& version) +bool IsEndOfStream(const BitSource& bits, const Version& version) { const int bitsRequired = TerminatorBitsLength(version); const int bitsAvailable = std::min(bits.available(), bitsRequired); - return bits.peakBits(bitsAvailable) == 0; + return bitsAvailable == 0 || bits.peakBits(bitsAvailable) == 0; } /** @@ -284,42 +284,43 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo BitSource bits(bytes); std::wstring result; int symbologyIdModifier = 1; // ISO/IEC 18004:2015 Annex F Table F.1 - int appIndValue = -1; // ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator (FNC1 in second position) StructuredAppendInfo structuredAppend; - static const int GB2312_SUBSET = 1; const int modeBitLength = CodecModeBitsLength(version); - const int minimumBitsRequired = modeBitLength + CharacterCountBits(CodecMode::NUMERIC, version); try { CharacterSet currentCharset = CharacterSet::Unknown; bool fc1InEffect = false; - CodecMode mode; - do { - // While still another segment to read... - if (bits.available() < minimumBitsRequired || IsTerminator(bits, version)) { - // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here - mode = CodecMode::TERMINATOR; - } else if (modeBitLength == 0) { - // MicroQRCode version 1 is always NUMERIC and modeBitLength is 0 - mode = CodecMode::NUMERIC; - } else { + 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()); - } + switch (mode) { - case CodecMode::TERMINATOR: - break; case CodecMode::FNC1_FIRST_POSITION: +// if (!result.empty()) // uncomment to enforce specification +// throw std::runtime_error("GS1 Indicator (FNC1 in first position) at illegal position"); fc1InEffect = true; // In Alphanumeric mode undouble doubled percents and treat single percent as // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using // modifiers that indicate ECI protocol (ISO/IEC 18004:2015 Annex F Table F.1) symbologyIdModifier = 3; break; case CodecMode::FNC1_SECOND_POSITION: + if (!result.empty()) + throw std::runtime_error("AIM Application Indicator (FNC1 in second position) at illegal position"); fc1InEffect = true; // As above symbologyIdModifier = 5; // As above - // ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator "00-99" or "A-Za-z" - appIndValue = bits.readBits(8); // Number 00-99 or ASCII value + 100; prefixed to data below + // 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 < 10) // "00-09" + result += L'0' + std::to_wstring(appInd); + else if (appInd < 100) // "10-99" + result += std::to_wstring(appInd); + else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222)) // "A-Za-z" + result += static_cast(appInd - 100); + else + throw std::runtime_error("Invalid AIM Application Indicator"); break; case CodecMode::STRUCTURED_APPEND: // sequence number and parity is added later to the result metadata @@ -328,20 +329,19 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo structuredAppend.count = bits.readBits(4) + 1; structuredAppend.id = std::to_string(bits.readBits(8)); break; - case CodecMode::ECI: { + case CodecMode::ECI: // Count doesn't apply to ECI currentCharset = CharacterSetECI::CharsetFromValue(ParseECIValue(bits)); if (currentCharset == CharacterSet::Unknown) return DecodeStatus::FormatError; 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 int subset = bits.readBits(4); - int countHanzi = bits.readBits(CharacterCountBits(mode, version)); - if (subset == GB2312_SUBSET) - DecodeHanziSegment(bits, countHanzi, result); + int count = bits.readBits(CharacterCountBits(mode, version)); + if (subset == 1 ) // GB2312_SUBSET + DecodeHanziSegment(bits, count, result); break; } default: { @@ -358,7 +358,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo break; } } - } while (mode != CodecMode::TERMINATOR); + } } catch (const std::exception& e) { @@ -368,17 +368,6 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo return DecodeStatus::FormatError; } - if (appIndValue >= 0) { - if (appIndValue < 10) // "00-09" - result.insert(0, L'0' + std::to_wstring(appIndValue)); - else if (appIndValue < 100) // "10-99" - result.insert(0, std::to_wstring(appIndValue)); - else if ((appIndValue >= 165 && appIndValue <= 190) || (appIndValue >= 197 && appIndValue <= 222)) // "A-Za-z" - result.insert(0, 1, static_cast(appIndValue - 100)); - else - return DecodeStatus::FormatError; - } - return DecoderResult(std::move(bytes), std::move(result)) .setEcLevel(ToString(ecLevel)) .setSymbologyIdentifier("]Q" + std::to_string(symbologyIdModifier)) diff --git a/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp b/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp index 4888138a10..5ab4b15589 100644 --- a/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp +++ b/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp @@ -113,7 +113,7 @@ TEST(QRDecodedBitStreamParserTest, SymbologyIdentifier) EXPECT_EQ(result.symbologyIdentifier(), "]Q3"); EXPECT_EQ(result.text(), L"2001"); // "(20)01" - // GS1 "NUM(4) 2001 FNC1(1st) 301" - FNC1(1st) can occur anywhere + // GS1 "NUM(4) 2001 FNC1(1st) 301" - FNC1(1st) can occur anywhere (this actually violates the specification) result = DecodeBitStream({0x10, 0x10, 0xC8, 0x15, 0x10, 0x0D, 0x2D, 0x00}, version, ecLevel, ""); EXPECT_EQ(result.symbologyIdentifier(), "]Q3"); EXPECT_EQ(result.text(), L"2001301"); // "(20)01(30)1" @@ -124,9 +124,10 @@ TEST(QRDecodedBitStreamParserTest, SymbologyIdentifier) EXPECT_EQ(result.text(), L"99A"); // AIM "BYTE(1) A FNC1(2nd) 99 (0x63) BYTE(1) B" - FNC1(2nd) can occur anywhere - result = DecodeBitStream({0x40, 0x14, 0x19, 0x63, 0x40, 0x14, 0x20, 0x00}, version, ecLevel, ""); - EXPECT_EQ(result.symbologyIdentifier(), "]Q5"); - EXPECT_EQ(result.text(), L"99AB"); // Application Indicator prefixed to data + // Disabled this test, since this violates the specification and the code does support it anymore +// result = DecodeBitStream({0x40, 0x14, 0x19, 0x63, 0x40, 0x14, 0x20, 0x00}, version, ecLevel, ""); +// EXPECT_EQ(result.symbologyIdentifier(), "]Q5"); +// EXPECT_EQ(result.text(), L"99AB"); // Application Indicator prefixed to data // AIM "FNC1(2nd) A (100 + 61 = 0xA5) ANUM(1) B" result = DecodeBitStream({0x9A, 0x52, 0x00, 0x96, 0x00}, version, ecLevel, ""); From f302e1e19002a23ece41e9297e7c9e4bbe0b0f64 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 20 May 2022 16:28:02 +0200 Subject: [PATCH 023/180] MCDecoder: code format fixes --- core/src/maxicode/MCDecoder.cpp | 487 ++++++++++++++++---------------- 1 file changed, 238 insertions(+), 249 deletions(-) diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 15b9a7bfa5..922a8a477c 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -52,9 +52,8 @@ static bool CorrectErrors(ByteArray& codewordBytes, int start, int dataCodewords // First read into an array of ints std::vector codewordsInts(codewords / divisor, 0); for (int i = 0; i < codewords; i++) { - if ((mode == ALL) || (i % 2 == (mode - 1))) { + if ((mode == ALL) || (i % 2 == (mode - 1))) codewordsInts[i / divisor] = codewordBytes[i + start]; - } } if (!ReedSolomonDecode(GenericGF::MaxiCodeField64(), codewordsInts, ecCodewords / divisor)) @@ -63,10 +62,10 @@ static bool CorrectErrors(ByteArray& codewordBytes, int start, int dataCodewords // 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 for (int i = 0; i < dataCodewords; i++) { - if ((mode == ALL) || (i % 2 == (mode - 1))) { + if ((mode == ALL) || (i % 2 == (mode - 1))) codewordBytes[i + start] = static_cast(codewordsInts[i / divisor]); - } } + return true; } @@ -77,271 +76,263 @@ static bool CorrectErrors(ByteArray& codewordBytes, int start, int dataCodewords * @author mike32767 * @author Manuel Kasten */ -namespace DecodedBitStreamParser +namespace DecodedBitStreamParser { + +static const short SHI0 = 0x100; +static const short SHI1 = 0x101; +static const short SHI2 = 0x102; +static const short SHI3 = 0x103; +static const short SHI4 = 0x104; +static const short TWSA = 0x105; // two shift A +static const short TRSA = 0x106; // three shift A +static const short LCHA = 0x107; // latch A +static const short LCHB = 0x108; // latch B +static const short LOCK = 0x109; +static const short ECI = 0x10A; +static const short NS = 0x10B; +static const short PAD = 0x10C; + +static const char FS = 0x1C; +static const char GS = 0x1D; +static const char RS = 0x1E; + +// clang-format off +const static std::array CHARSETS[] = { + { // set 0 (A) + '\n', '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', ECI, FS, GS, RS, NS, + ' ', PAD, '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', SHI1, SHI2, SHI3, SHI4, LCHB, + }, + { // set 1 (B) + '`', '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', ECI, FS, GS, RS, NS, + '{', PAD, '}', '~', 0x7F, ';', '<', '=', '>', '?', '[', '\\', ']', '^', '_', ' ', + ',', '.', '/', ':', '@', '!', '|', PAD, TWSA, TRSA, PAD, SHI0, SHI2, SHI3, SHI4, LCHA, + }, + { // set 2 (C) + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, ECI, FS, GS, RS, NS, // Note that in original code in Java, NS is not there, which seems to be a bug + 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xAA, 0xAC, 0xB1, 0xB2, 0xB3, 0xB5, 0xB9, 0xBA, 0xBC, 0xBD, 0xBE, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, LCHA, 0x20, LOCK, SHI3, SHI4, LCHB, + }, + { // set 3 (D) + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, ECI, FS, GS, RS, NS, + 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xA1, 0xA8, 0xAB, 0xAF, 0xB0, 0xB4, 0xB7, 0xB8, 0xBB, 0xBF, 0x8A, + 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, LCHA, 0x20, SHI2, LOCK, SHI4, LCHB, + }, + { // set 4 (E) + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, ECI, PAD, PAD, 0x1B, NS, + FS, GS, RS, 0x1F, 0x9F, 0xA0, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA9, 0xAD, 0xAE, 0xB6, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, LCHA, 0x20, SHI2, SHI3, LOCK, LCHB, + }, +}; +// clang-format on + +static int GetBit(int bit, const ByteArray& bytes) { - static const short SHI0 = 0x100; - static const short SHI1 = 0x101; - static const short SHI2 = 0x102; - static const short SHI3 = 0x103; - static const short SHI4 = 0x104; - static const short TWSA = 0x105; // two shift A - static const short TRSA = 0x106; // three shift A - static const short LCHA = 0x107; // latch A - static const short LCHB = 0x108; // latch B - static const short LOCK = 0x109; - static const short ECI = 0x10A; - static const short NS = 0x10B; - static const short PAD = 0x10C; - - static const char FS = 0x1C; - static const char GS = 0x1D; - static const char RS = 0x1E; - - const static std::array CHARSETS[] = { - { // set 0 (A) - '\n', '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', ECI, FS, GS, RS, NS, - ' ', PAD, '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', SHI1, SHI2, SHI3, SHI4, LCHB, - }, - { // set 1 (B) - '`', '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', ECI, FS, GS, RS, NS, - '{', PAD, '}', '~', 0x7F, ';', '<', '=', '>', '?', '[', '\\', ']', '^', '_', ' ', - ',', '.', '/', ':', '@', '!', '|', PAD, TWSA, TRSA, PAD, SHI0, SHI2, SHI3, SHI4, LCHA, - }, - { // set 2 (C) - 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, - 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, ECI, FS, GS, RS, NS, // Note that in original code in Java, NS is not there, which seems to be a bug - 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xAA, 0xAC, 0xB1, 0xB2, 0xB3, 0xB5, 0xB9, 0xBA, 0xBC, 0xBD, 0xBE, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, LCHA, 0x20, LOCK, SHI3, SHI4, LCHB, - }, - { // set 3 (D) - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, ECI, FS, GS, RS, NS, - 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xA1, 0xA8, 0xAB, 0xAF, 0xB0, 0xB4, 0xB7, 0xB8, 0xBB, 0xBF, 0x8A, - 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, LCHA, 0x20, SHI2, LOCK, SHI4, LCHB, - }, - { // set 4 (E) - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, ECI, PAD, PAD, 0x1B, NS, - FS, GS, RS, 0x1F, 0x9F, 0xA0, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA9, 0xAD, 0xAE, 0xB6, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, LCHA, 0x20, SHI2, SHI3, LOCK, LCHB, - }, - }; + bit--; + return (bytes[bit / 6] & (1 << (5 - (bit % 6)))) == 0 ? 0 : 1; +} - static int GetBit(int bit, const ByteArray& bytes) - { - bit--; - return (bytes[bit / 6] & (1 << (5 - (bit % 6)))) == 0 ? 0 : 1; - } +static int GetInt(const ByteArray& bytes, const ByteArray& x) +{ + int len = Size(x); + int val = 0; + for (int i = 0; i < len; i++) + val += GetBit(x[i], bytes) << (len - i - 1); - static int GetInt(const ByteArray& bytes, const ByteArray& x) - { - int len = Size(x); - int val = 0; - for (int i = 0; i < len; i++) { - val += GetBit(x[i], bytes) << (len - i - 1); - } - return val; - } + return val; +} - static int GetPostCode2(const ByteArray& bytes) - { - return GetInt(bytes, { 33, 34, 35, 36, 25, 26, 27, 28, 29, 30, 19, 20, 21, 22, 23, 24, 13, 14, 15, 16, 17, 18, 7, 8, 9, 10, 11, 12, 1, 2 }); - } +static int GetPostCode2(const ByteArray& bytes) +{ + return GetInt(bytes, + {33, 34, 35, 36, 25, 26, 27, 28, 29, 30, 19, 20, 21, 22, 23, 24, 13, 14, 15, 16, 17, 18, 7, 8, 9, 10, 11, 12, 1, 2}); +} - static int GetPostCode2Length(const ByteArray& bytes) { - return GetInt(bytes, { 39, 40, 41, 42, 31, 32 }); - } - - static std::string GetPostCode3(const ByteArray& bytes) - { - return { - (char) CHARSETS[0].at(GetInt(bytes, { 39, 40, 41, 42, 31, 32 })), - (char) CHARSETS[0].at(GetInt(bytes, { 33, 34, 35, 36, 25, 26 })), - (char) CHARSETS[0].at(GetInt(bytes, { 27, 28, 29, 30, 19, 20 })), - (char) CHARSETS[0].at(GetInt(bytes, { 21, 22, 23, 24, 13, 14 })), - (char) CHARSETS[0].at(GetInt(bytes, { 15, 16, 17, 18, 7, 8 })), - (char) CHARSETS[0].at(GetInt(bytes, { 9, 10, 11, 12, 1, 2 })), - }; - } +static int GetPostCode2Length(const ByteArray& bytes) +{ + return GetInt(bytes, {39, 40, 41, 42, 31, 32}); +} +static std::string GetPostCode3(const ByteArray& bytes) +{ + return { + (char) CHARSETS[0].at(GetInt(bytes, { 39, 40, 41, 42, 31, 32 })), + (char) CHARSETS[0].at(GetInt(bytes, { 33, 34, 35, 36, 25, 26 })), + (char) CHARSETS[0].at(GetInt(bytes, { 27, 28, 29, 30, 19, 20 })), + (char) CHARSETS[0].at(GetInt(bytes, { 21, 22, 23, 24, 13, 14 })), + (char) CHARSETS[0].at(GetInt(bytes, { 15, 16, 17, 18, 7, 8 })), + (char) CHARSETS[0].at(GetInt(bytes, { 9, 10, 11, 12, 1, 2 })), + }; +} - static std::string ToString(int x, int width) - { - std::stringstream buf; - buf << std::setw(width) << std::setfill('0') << x; - return buf.str(); - } +static std::string ToString(int x, int width) +{ + std::stringstream buf; + buf << std::setw(width) << std::setfill('0') << x; + return buf.str(); +} - static int GetCountry(const ByteArray& bytes) - { - return GetInt(bytes, { 53, 54, 43, 44, 45, 46, 47, 48, 37, 38 }); - } +static int GetCountry(const ByteArray& bytes) +{ + return GetInt(bytes, {53, 54, 43, 44, 45, 46, 47, 48, 37, 38}); +} - static int GetServiceClass(const ByteArray& bytes) - { - return GetInt(bytes, { 55, 56, 57, 58, 59, 60, 49, 50, 51, 52 }); - } +static int GetServiceClass(const ByteArray& bytes) +{ + return GetInt(bytes, {55, 56, 57, 58, 59, 60, 49, 50, 51, 52}); +} - /** - * See ISO/IEC 16023:2000 Section 4.6 Table 3 - */ - static int ParseECIValue(const ByteArray& bytes, int& i) - { - int firstByte = bytes[++i]; - if ((firstByte & 0x20) == 0) { - return firstByte; - } - int secondByte = bytes[++i]; - if ((firstByte & 0x10) == 0) { - return ((firstByte & 0x0F) << 6) | secondByte; - } - int thirdByte = bytes[++i]; - if ((firstByte & 0x08) == 0) { - return ((firstByte & 0x07) << 12) | (secondByte << 6) | thirdByte; - } - int fourthByte = bytes[++i]; - return ((firstByte & 0x03) << 18) | (secondByte << 12) | (thirdByte << 6) | fourthByte; - } +/** + * See ISO/IEC 16023:2000 Section 4.6 Table 3 + */ +static int ParseECIValue(const ByteArray& bytes, int& i) +{ + int firstByte = bytes[++i]; + if ((firstByte & 0x20) == 0) + return firstByte; - /** - * See ISO/IEC 16023:2000 Section 4.9.1 Table 5 - */ - static void ParseStructuredAppend(const ByteArray& bytes, int& i, StructuredAppendInfo& sai) - { - const int byte = bytes[++i]; - sai.index = (byte >> 3) & 0x07; - sai.count = (byte & 0x07) + 1; - if (sai.count == 1 || sai.count <= sai.index) { // If info doesn't make sense - sai.count = 0; // Choose to mark count as unknown - } - // No id - } + int secondByte = bytes[++i]; + if ((firstByte & 0x10) == 0) + return ((firstByte & 0x0F) << 6) | secondByte; - static std::wstring GetMessage(const ByteArray& bytes, int start, int len, const std::string& characterSet, - StructuredAppendInfo& sai) - { - std::string sb; - std::wstring sbEncoded; - int shift = -1; - int set = 0; - int lastset = 0; - CharacterSet encoding = CharacterSetECI::InitEncoding(characterSet); - - for (int i = start; i < start + len; i++) { - int c = CHARSETS[set].at(bytes[i]); - switch (c) { - case LCHA: - set = 0; - shift = -1; - break; - case LCHB: - set = 1; - shift = -1; - break; - case SHI0: - case SHI1: - case SHI2: - case SHI3: - case SHI4: - lastset = set; - set = c - SHI0; - shift = 1; - break; - case TWSA: - lastset = set; - set = 0; - shift = 2; - break; - case TRSA: - lastset = set; - set = 0; - shift = 3; - break; - case NS: - sb.append(ToString((bytes[i+1] << 24) + (bytes[i+2] << 18) + (bytes[i+3] << 12) + (bytes[i+4] << 6) + bytes[i+5], 9)); - i += 5; - break; - case LOCK: - shift = -1; - break; - case ECI: - encoding = CharacterSetECI::OnChangeAppendReset(ParseECIValue(bytes, i), sbEncoded, sb, encoding); - break; - case PAD: - if (i == start) { - ParseStructuredAppend(bytes, i, sai); - } - shift = -1; - break; - default: - sb.push_back((unsigned char) c); - } - if (shift-- == 0) { - set = lastset; - } - } - TextDecoder::Append(sbEncoded, reinterpret_cast(sb.data()), sb.size(), encoding); - return sbEncoded; - } + int thirdByte = bytes[++i]; + if ((firstByte & 0x08) == 0) + return ((firstByte & 0x07) << 12) | (secondByte << 6) | thirdByte; - ZXING_EXPORT_TEST_ONLY - DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& characterSet) - { - std::wstring result; - result.reserve(144); - StructuredAppendInfo sai; - switch (mode) { - case 2: - case 3: { - auto postcode = mode == 2 ? ToString(GetPostCode2(bytes), GetPostCode2Length(bytes)) : GetPostCode3(bytes); - auto country = ToString(GetCountry(bytes), 3); - auto service = ToString(GetServiceClass(bytes), 3); - result.append(GetMessage(bytes, 10, 84, characterSet, sai)); - if (result.size() >= 9 && result.compare(0, 7, L"[)>\u001E01\u001D") == 0) { // "[)>" + RS + "01" + GS - result.insert(9, TextDecoder::FromLatin1(postcode + GS + country + GS + service + GS)); - } else { - result.insert(0, TextDecoder::FromLatin1(postcode + GS + country + GS + service + GS)); - } - break; - } - case 4: - case 6: result.append(GetMessage(bytes, 1, 93, characterSet, sai)); break; - case 5: result.append(GetMessage(bytes, 1, 77, characterSet, sai)); break; - } + int fourthByte = bytes[++i]; + return ((firstByte & 0x03) << 18) | (secondByte << 12) | (thirdByte << 6) | fourthByte; +} - // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using modifiers - // that indicate ECI protocol (ISO/IEC 16023:2000 Annexe E Table E1) - std::string symbologyIdentifier; - if (mode == 4 || mode == 5) { - symbologyIdentifier = "]U0"; - } - else if (mode == 2 || mode == 3) { - symbologyIdentifier = "]U1"; +/** + * See ISO/IEC 16023:2000 Section 4.9.1 Table 5 + */ +static void ParseStructuredAppend(const ByteArray& bytes, int& i, StructuredAppendInfo& sai) +{ + int byte = bytes[++i]; + sai.index = (byte >> 3) & 0x07; + sai.count = (byte & 0x07) + 1; + if (sai.count == 1 || sai.count <= sai.index) // If info doesn't make sense + sai.count = 0; // Choose to mark count as unknown + // No id +} + +static std::wstring GetMessage(const ByteArray& bytes, int start, int len, const std::string& characterSet, StructuredAppendInfo& sai) +{ + std::string sb; + std::wstring sbEncoded; + int shift = -1; + int set = 0; + int lastset = 0; + CharacterSet encoding = CharacterSetECI::InitEncoding(characterSet); + + for (int i = start; i < start + len; i++) { + int c = CHARSETS[set].at(bytes[i]); + switch (c) { + case LCHA: + set = 0; + shift = -1; + break; + case LCHB: + set = 1; + shift = -1; + break; + case SHI0: + case SHI1: + case SHI2: + case SHI3: + case SHI4: + lastset = set; + set = c - SHI0; + shift = 1; + break; + case TWSA: + lastset = set; + set = 0; + shift = 2; + break; + case TRSA: + lastset = set; + set = 0; + shift = 3; + break; + case NS: + sb.append( + ToString((bytes[i + 1] << 24) + (bytes[i + 2] << 18) + (bytes[i + 3] << 12) + (bytes[i + 4] << 6) + bytes[i + 5], 9)); + i += 5; + break; + case LOCK: shift = -1; break; + case ECI: encoding = CharacterSetECI::OnChangeAppendReset(ParseECIValue(bytes, i), sbEncoded, sb, encoding); break; + case PAD: + if (i == start) + ParseStructuredAppend(bytes, i, sai); + shift = -1; + break; + default: sb.push_back((unsigned char)c); } - // No identifier defined for mode 6 - return DecoderResult(std::move(bytes), std::move(result)) - .setEcLevel(std::to_wstring(mode)) - .setSymbologyIdentifier(std::move(symbologyIdentifier)) - .setStructuredAppend(sai) - .setReaderInit(mode == 6); + if (shift-- == 0) + set = lastset; } + TextDecoder::Append(sbEncoded, reinterpret_cast(sb.data()), sb.size(), encoding); + return sbEncoded; +} + +ZXING_EXPORT_TEST_ONLY +DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& characterSet) +{ + std::wstring result; + result.reserve(144); + StructuredAppendInfo sai; + switch (mode) { + case 2: + case 3: { + auto postcode = mode == 2 ? ToString(GetPostCode2(bytes), GetPostCode2Length(bytes)) : GetPostCode3(bytes); + auto country = ToString(GetCountry(bytes), 3); + auto service = ToString(GetServiceClass(bytes), 3); + result.append(GetMessage(bytes, 10, 84, characterSet, sai)); + if (result.size() >= 9 && result.compare(0, 7, L"[)>\u001E01\u001D") == 0) // "[)>" + RS + "01" + GS + result.insert(9, TextDecoder::FromLatin1(postcode + GS + country + GS + service + GS)); + else + result.insert(0, TextDecoder::FromLatin1(postcode + GS + country + GS + service + GS)); + break; + } + case 4: + case 6: result.append(GetMessage(bytes, 1, 93, characterSet, sai)); break; + case 5: result.append(GetMessage(bytes, 1, 77, characterSet, sai)); break; + } + + // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using modifiers + // that indicate ECI protocol (ISO/IEC 16023:2000 Annexe E Table E1) + std::string symbologyIdentifier; + if (mode == 4 || mode == 5) + symbologyIdentifier = "]U0"; + else if (mode == 2 || mode == 3) + symbologyIdentifier = "]U1"; + // No identifier defined for mode 6 + + return DecoderResult(std::move(bytes), std::move(result)) + .setEcLevel(std::to_wstring(mode)) + .setSymbologyIdentifier(std::move(symbologyIdentifier)) + .setStructuredAppend(sai) + .setReaderInit(mode == 6); +} } // DecodedBitStreamParser -DecoderResult -Decoder::Decode(const BitMatrix& bits, const std::string& characterSet) +DecoderResult Decoder::Decode(const BitMatrix& bits, const std::string& characterSet) { ByteArray codewords = BitMatrixParser::ReadCodewords(bits); - if (!CorrectErrors(codewords, 0, 10, 10, ALL)) { + if (!CorrectErrors(codewords, 0, 10, 10, ALL)) return DecodeStatus::ChecksumError; - } + int mode = codewords[0] & 0x0F; ByteArray datawords; switch (mode) { @@ -349,18 +340,16 @@ Decoder::Decode(const BitMatrix& bits, const std::string& characterSet) case 3: // Structured Carrier Message (alphanumeric postcode) case 4: // Standard Symbol case 6: // Reader Programming - if (CorrectErrors(codewords, 20, 84, 40, EVEN) && CorrectErrors(codewords, 20, 84, 40, ODD)) { + if (CorrectErrors(codewords, 20, 84, 40, EVEN) && CorrectErrors(codewords, 20, 84, 40, ODD)) datawords.resize(94, 0); - } else { + else return DecodeStatus::ChecksumError; - } break; case 5: // Full ECC - if (CorrectErrors(codewords, 20, 68, 56, EVEN) && CorrectErrors(codewords, 20, 68, 56, ODD)) { + if (CorrectErrors(codewords, 20, 68, 56, EVEN) && CorrectErrors(codewords, 20, 68, 56, ODD)) datawords.resize(78, 0); - } else { + else return DecodeStatus::ChecksumError; - } break; default: return DecodeStatus::FormatError; } From ec50173369656b470a73f787041cd1c5f3dd356d Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 21 May 2022 13:23:18 +0200 Subject: [PATCH 024/180] AZDecoder: improve comments (non-functional change) --- core/src/aztec/AZDecoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index 2e196ad3bc..1a122025fd 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -292,8 +292,8 @@ AztecData GetEncodedData(const BitArray& bits, const std::string& characterSet) CharacterSet encoding = CharacterSetECI::InitEncoding(characterSet); // Check for Structured Append - need 4 5-bit words, beginning with ML UL, ending with index and count - bool haveStructuredAppend = Size(bits) > 20 && ToInt(bits, 0, 5) == 29 // ML (UPPER table) - && ToInt(bits, 5, 5) == 29; // UL (MIXED table) + bool haveStructuredAppend = Size(bits) > 20 && ToInt(bits, 0, 5) == 29 // latch to MIXED (from UPPER) + && ToInt(bits, 5, 5) == 29; // latch back to UPPER (from MIXED) bool haveFNC1 = false; auto remBits = bits.range(); From c7e71eba94e038c0cd286e1d43073d4535ac9067 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 21 May 2022 13:53:39 +0200 Subject: [PATCH 025/180] Result: remove one unnecessary constructor (go through DecoderResult) --- core/src/DecoderResult.h | 2 ++ core/src/Result.cpp | 26 +++++++++-------------- core/src/Result.h | 4 ---- core/src/oned/ODDataBarExpandedReader.cpp | 8 ++++--- core/src/oned/ODDataBarReader.cpp | 11 +++++----- test/blackbox/BlackboxTestRunner.cpp | 9 ++++++-- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index ea158eff18..935a884648 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -45,6 +45,7 @@ class DecoderResult std::wstring _ecLevel; int _errorsCorrected = -1; int _erasures = -1; + int _lineCount = -1; std::string _symbologyIdentifier; StructuredAppendInfo _structuredAppend; bool _isMirrored = false; @@ -92,6 +93,7 @@ class DecoderResult ZX_PROPERTY(std::wstring, ecLevel, setEcLevel) ZX_PROPERTY(int, errorsCorrected, setErrorsCorrected) ZX_PROPERTY(int, erasures, setErasures) + ZX_PROPERTY(int, lineCount, setLineCount) ZX_PROPERTY(std::string, symbologyIdentifier, setSymbologyIdentifier) ZX_PROPERTY(StructuredAppendInfo, structuredAppend, setStructuredAppend) ZX_PROPERTY(bool, isMirrored, setIsMirrored) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 0aad42a4ec..5a9e6d4563 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -25,24 +25,17 @@ namespace ZXing { -Result::Result(std::wstring&& text, Position&& position, BarcodeFormat format, std::string&& symbologyIdentifier, - ByteArray&& rawBytes, StructuredAppendInfo&& sai, const bool readerInit, int lineCount) - : _format(format), - _text(std::move(text)), - _position(std::move(position)), +Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, + std::string&& symbologyIdentifier, ByteArray&& rawBytes, const bool readerInit) + : + _format(format), + _text(TextDecoder::FromLatin1(text)), + _position(Line(y, xStart, xStop)), _rawBytes(std::move(rawBytes)), + _numBits(Size(_rawBytes) * 8), _symbologyIdentifier(std::move(symbologyIdentifier)), - _sai(std::move(sai)), _readerInit(readerInit), - _lineCount(lineCount) -{ - _numBits = Size(_rawBytes) * 8; -} - -Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, - std::string&& symbologyIdentifier, ByteArray&& rawBytes, const bool readerInit) - : Result(TextDecoder::FromLatin1(text), Line(y, xStart, xStop), format, std::move(symbologyIdentifier), - std::move(rawBytes), {}, readerInit) + _lineCount(0) {} Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format) @@ -56,7 +49,8 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat _symbologyIdentifier(decodeResult.symbologyIdentifier()), _sai(decodeResult.structuredAppend()), _isMirrored(decodeResult.isMirrored()), - _readerInit(decodeResult.readerInit()) + _readerInit(decodeResult.readerInit()), + _lineCount(decodeResult.lineCount()) { // TODO: add type opaque and code specific 'extra data'? (see DecoderResult::extra()) } diff --git a/core/src/Result.h b/core/src/Result.h index 1d90b835e3..a0cdeb0e5d 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -42,10 +42,6 @@ class Result public: explicit Result(DecodeStatus status) : _status(status) {} - Result(std::wstring&& text, Position&& position, BarcodeFormat format, std::string&& symbologyIdentifier = "", - ByteArray&& rawBytes = {}, StructuredAppendInfo&& sai = {}, const bool readerInit = false, - int lineCount = 0); - // 1D convenience constructor Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, std::string&& symbologyIdentifier = "", ByteArray&& rawBytes = {}, const bool readerInit = false); diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index 769b279648..9421a4b7be 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -19,6 +19,7 @@ #include "ODDataBarExpandedReader.h" #include "BarcodeFormat.h" +#include "DecoderResult.h" #include "ODDataBarCommon.h" #include "Result.h" #include "TextDecoder.h" @@ -388,9 +389,10 @@ Result DataBarExpandedReader::decodePattern(int rowNumber, PatternView& view, // TODO: EstimatePosition misses part of the symbol in the stacked case where the last row contains less pairs than // the first - return {TextDecoder::FromLatin1(txt), EstimatePosition(pairs.front(), pairs.back()), - BarcodeFormat::DataBarExpanded, std::move(symbologyIdentifier), {}, {}, false, - EstimateLineCount(pairs.front(), pairs.back())}; + return {DecoderResult({}, TextDecoder::FromLatin1(txt)) + .setSymbologyIdentifier("]e0") + .setLineCount(EstimateLineCount(pairs.front(), pairs.back())), + EstimatePosition(pairs.front(), pairs.back()), BarcodeFormat::DataBarExpanded}; } } // namespace ZXing::OneD diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index f418fd64e8..e45ae3e8c9 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -19,6 +19,7 @@ #include "ODDataBarReader.h" #include "BarcodeFormat.h" +#include "DecoderResult.h" #include "GTIN.h" #include "ODDataBarCommon.h" #include "Result.h" @@ -208,12 +209,10 @@ Result DataBarReader::decodePattern(int rowNumber, PatternView& next, for (const auto& rightPair : prevState->rightPairs) if (ChecksumIsValid(leftPair, rightPair)) { // Symbology identifier ISO/IEC 24724:2011 Section 9 and GS1 General Specifications 5.1.3 Figure 5.1.3-2 - std::string symbologyIdentifier("]e0"); - - Result res{TextDecoder::FromLatin1(ConstructText(leftPair, rightPair)), - EstimatePosition(leftPair, rightPair), BarcodeFormat::DataBar, - std::move(symbologyIdentifier), {}, {}, false, - EstimateLineCount(leftPair, rightPair)}; + Result res{DecoderResult({}, TextDecoder::FromLatin1(ConstructText(leftPair, rightPair))) + .setSymbologyIdentifier("]e0") + .setLineCount(EstimateLineCount(leftPair, rightPair)), + EstimatePosition(leftPair, rightPair), BarcodeFormat::DataBar}; prevState->leftPairs.erase(leftPair); prevState->rightPairs.erase(rightPair); diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index ab75e6169e..fce059884f 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -17,6 +17,7 @@ #include "BlackboxTestRunner.h" +#include "DecoderResult.h" #include "ImageLoader.h" #include "ReadBarcode.h" #include "TextUtfEncoding.h" @@ -278,8 +279,12 @@ static Result readMultiple(const std::vector& imgPaths, std::string_vi text.append(r.text()); const auto& first = allResults.front(); - StructuredAppendInfo sai{first.sequenceIndex(), first.sequenceSize(), first.sequenceId()}; - return {std::move(text), {}, first.format(), std::string(first.symbologyIdentifier()), {}, std::move(sai), first.readerInit()}; + return {DecoderResult({}, std::move(text)) + .setStructuredAppend({first.sequenceIndex(), first.sequenceSize(), first.sequenceId()}) + .setSymbologyIdentifier(first.symbologyIdentifier()) + .setReaderInit(first.readerInit()), + {}, + first.format()}; } static void doRunStructuredAppendTest(const fs::path& directory, std::string_view format, int totalTests, From a93de020ae8ea87270fc26beadbe2c34360233fd Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 21 May 2022 15:42:56 +0200 Subject: [PATCH 026/180] CharacterSetECI: function renaming for better self-documentation --- core/src/CharacterSetECI.cpp | 6 +++--- core/src/CharacterSetECI.h | 4 ++-- core/src/pdf417/PDFHighLevelEncoder.cpp | 2 +- core/src/qrcode/QRDecoder.cpp | 2 +- core/src/qrcode/QREncoder.cpp | 2 +- test/unit/CharacterSetECITest.cpp | 14 +++++++------- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/src/CharacterSetECI.cpp b/core/src/CharacterSetECI.cpp index c9142f3891..86cd507042 100644 --- a/core/src/CharacterSetECI.cpp +++ b/core/src/CharacterSetECI.cpp @@ -134,7 +134,7 @@ static const std::map ECI_NAME_TO_CHA {"BINARY", CharacterSet::BINARY}, }; -CharacterSet CharsetFromValue(int value) +CharacterSet ECI2CharacterSet(int value) { auto it = ECI_VALUE_TO_CHARSET.find(value); if (it != ECI_VALUE_TO_CHARSET.end()) { @@ -143,7 +143,7 @@ CharacterSet CharsetFromValue(int value) return CharacterSet::Unknown; } -int ValueForCharset(CharacterSet charset) +int Charset2ECI(CharacterSet charset) { // Special case ISO8859_1 to avoid obsolete ECI 1 if (charset == CharacterSet::ISO8859_1) { @@ -183,7 +183,7 @@ CharacterSet OnChangeAppendReset(const int eci, std::wstring& encoded, std::stri { // Character set ECIs only if (eci >= 0 && eci <= 899) { - auto encodingNew = CharacterSetECI::CharsetFromValue(eci); + auto encodingNew = CharacterSetECI::ECI2CharacterSet(eci); if (encodingNew != CharacterSet::Unknown && encodingNew != encoding) { // Encode data so far in current encoding and reset TextDecoder::Append(encoded, reinterpret_cast(data.data()), data.size(), encoding); diff --git a/core/src/CharacterSetECI.h b/core/src/CharacterSetECI.h index ee66d55f21..03d52cc236 100644 --- a/core/src/CharacterSetECI.h +++ b/core/src/CharacterSetECI.h @@ -33,13 +33,13 @@ namespace CharacterSetECI { * @param value character set ECI value * @return {@code CharacterSet} representing ECI of given value, or {@code CharacterSet::Unknown} if it is unsupported */ -CharacterSet CharsetFromValue(int value); +CharacterSet ECI2CharacterSet(int value); /** * @param charset {@code CharacterSet} representing ECI * @return ECI of given {@code CharacterSet}, or -1 if it is unsupported */ -int ValueForCharset(CharacterSet charset); +int Charset2ECI(CharacterSet charset); /** * @param name character set ECI encoding name diff --git a/core/src/pdf417/PDFHighLevelEncoder.cpp b/core/src/pdf417/PDFHighLevelEncoder.cpp index c37b41c11b..2066d78c0b 100644 --- a/core/src/pdf417/PDFHighLevelEncoder.cpp +++ b/core/src/pdf417/PDFHighLevelEncoder.cpp @@ -514,7 +514,7 @@ HighLevelEncoder::EncodeHighLevel(const std::wstring& msg, Compaction compaction //the codewords 0..928 are encoded as Unicode characters if (encoding != CharacterSet::ISO8859_1) { - EncodingECI(CharacterSetECI::ValueForCharset(encoding), highLevel); + EncodingECI(CharacterSetECI::Charset2ECI(encoding), highLevel); } int len = Size(msg); diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index a36c9bbb49..cd59127196 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -331,7 +331,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo break; case CodecMode::ECI: // Count doesn't apply to ECI - currentCharset = CharacterSetECI::CharsetFromValue(ParseECIValue(bits)); + currentCharset = CharacterSetECI::ECI2CharacterSet(ParseECIValue(bits)); if (currentCharset == CharacterSet::Unknown) return DecodeStatus::FormatError; break; diff --git a/core/src/qrcode/QREncoder.cpp b/core/src/qrcode/QREncoder.cpp index 20285e9379..1601623196 100644 --- a/core/src/qrcode/QREncoder.cpp +++ b/core/src/qrcode/QREncoder.cpp @@ -114,7 +114,7 @@ CodecMode ChooseMode(const std::wstring& content, CharacterSet encoding) */ static void AppendECI(CharacterSet eci, BitArray& bits) { - int eciValue = CharacterSetECI::ValueForCharset(eci); + int eciValue = CharacterSetECI::Charset2ECI(eci); if (eciValue >= 0 && eciValue <= 999999) { bits.appendBits(static_cast(CodecMode::ECI), 4); if (eciValue <= 127) { diff --git a/test/unit/CharacterSetECITest.cpp b/test/unit/CharacterSetECITest.cpp index be587237d4..a5ced0a1ee 100644 --- a/test/unit/CharacterSetECITest.cpp +++ b/test/unit/CharacterSetECITest.cpp @@ -23,14 +23,14 @@ using namespace ZXing; using namespace ZXing::CharacterSetECI; using namespace testing; -TEST(CharacterSetECITest, ValueForCharset) +TEST(CharacterSetECITest, Charset2ECI) { - EXPECT_EQ(ValueForCharset(CharacterSet::ISO8859_1), 3); - EXPECT_EQ(ValueForCharset(CharacterSet::ISO8859_2), 4); - EXPECT_EQ(ValueForCharset(CharacterSet::ASCII), 27); - EXPECT_EQ(ValueForCharset(CharacterSet::EUC_KR), 30); - EXPECT_EQ(ValueForCharset(CharacterSet::BINARY), 899); - EXPECT_EQ(ValueForCharset(CharacterSet::Unknown), -1); + EXPECT_EQ(Charset2ECI(CharacterSet::ISO8859_1), 3); + EXPECT_EQ(Charset2ECI(CharacterSet::ISO8859_2), 4); + EXPECT_EQ(Charset2ECI(CharacterSet::ASCII), 27); + EXPECT_EQ(Charset2ECI(CharacterSet::EUC_KR), 30); + EXPECT_EQ(Charset2ECI(CharacterSet::BINARY), 899); + EXPECT_EQ(Charset2ECI(CharacterSet::Unknown), -1); } TEST(CharacterSetECITest, InitEncoding) From f5c6c62a05c46b55d6aa4bcb6abb07308421f43c Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 21 May 2022 16:21:44 +0200 Subject: [PATCH 027/180] Improve binary data support (1 of N) This is part of a somewhat larger effort to improve the out of the box support for reading barcode symbols that contain binary data. A lengthy discussion about the goals and design decisions can be found in #334. From the user facing API, this manifests in the new property `ByteArray Result::binary()`. This is still a WIP and may change in the future. --- core/CMakeLists.txt | 2 + core/src/ByteArray.h | 15 +++ core/src/Content.cpp | 92 +++++++++++++++ core/src/Content.h | 53 +++++++++ core/src/DecoderResult.h | 12 +- core/src/Result.cpp | 2 + core/src/Result.h | 4 + core/src/datamatrix/DMDecoder.cpp | 29 ++--- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 5 +- core/src/qrcode/QRDecoder.cpp | 108 ++++++++---------- example/ZXingReader.cpp | 1 + test/blackbox/BlackboxTestRunner.cpp | 14 ++- 12 files changed, 252 insertions(+), 85 deletions(-) create mode 100644 core/src/Content.cpp create mode 100644 core/src/Content.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 0d4bff9f0c..461b0832f3 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -84,6 +84,8 @@ if (BUILD_READERS) src/BinaryBitmap.cpp src/BitSource.h src/BitSource.cpp + src/Content.h + src/Content.cpp src/DecodeHints.h src/DecodeHints.cpp src/DecodeStatus.h diff --git a/core/src/ByteArray.h b/core/src/ByteArray.h index efbd3072c1..7dc5f96cea 100644 --- a/core/src/ByteArray.h +++ b/core/src/ByteArray.h @@ -16,6 +16,8 @@ */ #include +#include +#include #include namespace ZXing { @@ -29,6 +31,19 @@ class ByteArray : public std::vector ByteArray() = default; ByteArray(std::initializer_list list) : std::vector(list) {} explicit ByteArray(int len) : std::vector(len, 0) {} + explicit ByteArray(const std::string& str) : std::vector(str.begin(), str.end()) {} + + void append(const ByteArray& other) { insert(end(), other.begin(), other.end()); } }; +inline std::string ToHex(const ByteArray& bytes) +{ + std::string res(bytes.size() * 3, ' '); + + for (size_t i = 0; i < bytes.size(); ++i) + sprintf(&res[i * 3], "%02X ", bytes[i]); + + return res.substr(0, res.size()-1); +} + } // ZXing diff --git a/core/src/Content.cpp b/core/src/Content.cpp new file mode 100644 index 0000000000..4e2e983db0 --- /dev/null +++ b/core/src/Content.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2022 Axel Waggershauser + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Content.h" + +#include "CharacterSetECI.h" +#include "TextDecoder.h" +#include "ZXContainerAlgorithms.h" + +namespace ZXing { + +void Content::switchEncoding(CharacterSet cs, bool isECI) +{ + if (isECI || !hasECI) { + if (encodings.back().second == Size(binary)) + encodings.back().first = cs; // no point in recording 0 length segments + else + encodings.emplace_back(cs, Size(binary)); + } + hasECI |= isECI; +} + +std::wstring Content::text() const +{ + auto fallbackCS = CharacterSetECI::CharsetFromName(hintedCharset.c_str()); + if (!hasECI && fallbackCS == CharacterSet::Unknown) + fallbackCS = guessEncoding(); + + std::wstring wstr; + for (int i = 0; i < Size(encodings); ++i) { + auto [cs, start] = encodings[i]; + int end = i + 1 == Size(encodings) ? Size(binary) : encodings[i + 1].second; + + if (cs == CharacterSet::Unknown) + cs = fallbackCS; + + TextDecoder::Append(wstr, binary.data() + start, end - start, cs); + } + return wstr; +} + +CharacterSet Content::guessEncoding() const +{ + // assemble all blocks with unknown encoding + ByteArray input; + for (int i = 0; i < Size(encodings); ++i) { + auto [cs, start] = encodings[i]; + int end = i + 1 == Size(encodings) ? Size(binary) : encodings[i + 1].second; + if (cs == CharacterSet::Unknown) + input.insert(input.end(), binary.begin() + start, binary.begin() + end); + } + + if (input.empty()) + return CharacterSet::Unknown; + + return TextDecoder::GuessEncoding(input.data(), input.size(), CharacterSet::BINARY); +} + +ContentType Content::type() const +{ + auto isBinary = [](Encoding e) { return e.first == CharacterSet::BINARY || e.first == CharacterSet::Unknown; }; + + if (hasECI) { + if (std::none_of(encodings.begin(), encodings.end(), isBinary)) + return ContentType::Text; + if (std::all_of(encodings.begin(), encodings.end(), isBinary)) + return ContentType::Binary; + } else { + if (std::none_of(encodings.begin(), encodings.end(), isBinary)) + return ContentType::Text; + auto cs = guessEncoding(); + if (cs == CharacterSet::BINARY) + return ContentType::Binary; + } + + return ContentType::Mixed; +} + +} // namespace ZXing diff --git a/core/src/Content.h b/core/src/Content.h new file mode 100644 index 0000000000..e6407ed2ea --- /dev/null +++ b/core/src/Content.h @@ -0,0 +1,53 @@ +#pragma once +/* +* Copyright 2022 Axel Waggershauser +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "ByteArray.h" +#include "CharacterSet.h" + +namespace ZXing { + +enum class ContentType { Text, Binary, Mixed }; + +class Content +{ + bool hasECI = false; + +public: + ByteArray binary; + using Encoding = std::pair; + std::vector encodings = {{CharacterSet::Unknown, 0}}; + std::string hintedCharset; + + void switchEncoding(CharacterSet cs, bool isECI = false); + + void reserve(int count) { binary.reserve(binary.size() + count); } + + void push_back(uint8_t val) { binary.push_back(val); } + void append(const std::string& str) { binary.insert(binary.end(), str.begin(), str.end()); } + void append(const ByteArray& bytes) { binary.insert(binary.end(), bytes.begin(), bytes.end()); } + + void operator+=(char val) { push_back(val); } + void operator+=(const std::string& str) { append(str); } + + bool empty() const { return binary.empty(); } + + std::wstring text() const; + CharacterSet guessEncoding() const; + ContentType type() const; +}; + +} // ZXing diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 935a884648..81fc060479 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -17,6 +17,7 @@ */ #include "ByteArray.h" +#include "Content.h" #include "DecodeStatus.h" #include "StructuredAppend.h" #include "ZXContainerAlgorithms.h" @@ -40,6 +41,7 @@ class DecoderResult { DecodeStatus _status = DecodeStatus::NoError; ByteArray _rawBytes; + Content _content; int _numBits = 0; std::wstring _text; std::wstring _ecLevel; @@ -57,9 +59,15 @@ class DecoderResult public: DecoderResult(DecodeStatus status) : _status(status) {} - DecoderResult(ByteArray&& rawBytes, std::wstring&& text) : _rawBytes(std::move(rawBytes)), _text(std::move(text)) + DecoderResult(ByteArray&& rawBytes, std::wstring&& text, Content&& binary = {}) + : _rawBytes(std::move(rawBytes)), _content(std::move(binary)), _text(std::move(text)) { _numBits = 8 * Size(_rawBytes); + if (_text.empty()) + _text = _content.text(); + // provide some best guess fallback for barcodes not, yet supporting the content info + if (_content.empty() && std::all_of(_text.begin(), _text.end(), [](auto c) { return c < 256; })) + std::for_each(_text.begin(), _text.end(), [this](wchar_t c) { _content += static_cast(c); }); } DecoderResult() = default; @@ -73,6 +81,8 @@ class DecoderResult ByteArray&& rawBytes() && { return std::move(_rawBytes); } const std::wstring& text() const & { return _text; } std::wstring&& text() && { return std::move(_text); } + const ByteArray& binary() const & { return _content.binary; } + ByteArray&& binary() && { return std::move(_content.binary); } // 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 diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 5a9e6d4563..2f0c4be75c 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -30,6 +30,7 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor : _format(format), _text(TextDecoder::FromLatin1(text)), + _binary(text), _position(Line(y, xStart, xStop)), _rawBytes(std::move(rawBytes)), _numBits(Size(_rawBytes) * 8), @@ -42,6 +43,7 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat : _status(decodeResult.errorCode()), _format(format), _text(std::move(decodeResult).text()), + _binary(std::move(decodeResult).binary()), _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), diff --git a/core/src/Result.h b/core/src/Result.h index a0cdeb0e5d..3f7ee54f09 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -56,6 +56,9 @@ class Result const std::wstring& text() const { return _text; } + // WARNING: this is an experimental API and may change/disappear + const ByteArray& binary() const { return _binary; } + const Position& position() const { return _position; } void setPosition(Position pos) { _position = pos; } @@ -120,6 +123,7 @@ class Result DecodeStatus _status = DecodeStatus::NoError; BarcodeFormat _format = BarcodeFormat::None; std::wstring _text; + ByteArray _binary; Position _position; ByteArray _rawBytes; int _numBits = 0; diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 9320659132..c74be87345 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -154,7 +154,7 @@ enum class Mode {C40, TEXT}; * See ISO 16022:2006, 5.2.5 and Annex C, Table C.1 (C40) * See ISO 16022:2006, 5.2.6 and Annex C, Table C.2 (Text) */ -static void DecodeC40OrTextSegment(BitSource& bits, std::string& result, Mode mode) +static void DecodeC40OrTextSegment(BitSource& bits, Content& result, Mode mode) { // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time Shift128 upperShift; @@ -200,7 +200,7 @@ static void DecodeC40OrTextSegment(BitSource& bits, std::string& result, Mode mo /** * See ISO 16022:2006, 5.2.7 */ -static void DecodeAnsiX12Segment(BitSource& bits, std::string& result) +static void DecodeAnsiX12Segment(BitSource& bits, Content& result) { while (auto triple = DecodeNextTriple(bits)) { for (int cValue : *triple) { @@ -223,7 +223,7 @@ static void DecodeAnsiX12Segment(BitSource& bits, std::string& result) /** * See ISO 16022:2006, 5.2.8 and Annex C Table C.3 */ -static void DecodeEdifactSegment(BitSource& bits, std::string& result) +static void DecodeEdifactSegment(BitSource& bits, Content& result) { // If there are less than 3 bytes left then it will be encoded as ASCII while (bits.available() >= 24) { @@ -258,7 +258,7 @@ static int Unrandomize255State(int randomizedBase256Codeword, int base256Codewor /** * See ISO 16022:2006, 5.2.9 and Annex B, B.2 */ -static void DecodeBase256Segment(BitSource& bits, std::string& result) +static void DecodeBase256Segment(BitSource& bits, Content& result) { // Figure out how long the Base 256 Segment is. int codewordPosition = 1 + bits.byteOffset(); // position is 1-indexed @@ -275,26 +275,22 @@ static void DecodeBase256Segment(BitSource& bits, std::string& result) if (count < 0) throw std::runtime_error("invalid count in Base256 segment"); - ByteArray bytes(count); + result.reserve(count); for (int i = 0; i < count; i++) { // readBits(8) may fail, have seen this particular error in the wild, such as at // http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2 - bytes[i] = (uint8_t)Unrandomize255State(bits.readBits(8), codewordPosition++); + result += static_cast(Unrandomize255State(bits.readBits(8), codewordPosition++)); } - - result.append(reinterpret_cast(bytes.data()), bytes.size()); } ZXING_EXPORT_TEST_ONLY DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const bool isDMRE) { BitSource bits(bytes); - std::string result; - result.reserve(100); + Content result; + result.hintedCharset = characterSet; std::string resultTrailer; - std::wstring resultEncoded; - CharacterSet encoding = CharacterSetECI::InitEncoding(characterSet); int symbologyIdModifier = 1; // ECC 200 (ISO 16022:2006 Annex N Table N.1) struct StructuredAppendInfo sai; bool readerInit = false; @@ -349,7 +345,7 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b case 238: DecodeAnsiX12Segment(bits, result); break; case 239: DecodeC40OrTextSegment(bits, result, Mode::TEXT); break; case 240: DecodeEdifactSegment(bits, result); break; - case 241: encoding = CharacterSetECI::OnChangeAppendReset(ParseECIValue(bits), resultEncoded, result, encoding); break; + case 241: result.switchEncoding(CharacterSetECI::ECI2CharacterSet(ParseECIValue(bits)), true); break; default: if (oneByte <= 128) { // ASCII data (ASCII value + 1) result.push_back(upperShift(oneByte) - 1); @@ -374,12 +370,9 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b return DecodeStatus::FormatError; } - if (resultTrailer.length() > 0) - result.append(resultTrailer); - - TextDecoder::Append(resultEncoded, reinterpret_cast(result.data()), result.size(), encoding); + result.append(resultTrailer); - return DecoderResult(std::move(bytes), std::move(resultEncoded)) + return DecoderResult(std::move(bytes), {}, std::move(result)) .setSymbologyIdentifier("]d" + std::to_string(symbologyIdModifier + (isDMRE ? 6 : 0))) .setStructuredAppend(sai) .setReaderInit(readerInit); diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index ab61c4c6c8..af57d17999 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -852,7 +852,10 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel, c sai.id = resultMetadata->fileId(); } - return DecoderResult(ByteArray(), std::move(resultEncoded)) + Content content; + content.append(result); + + return DecoderResult(ByteArray(), std::move(resultEncoded), std::move(content)) .setEcLevel(std::to_wstring(ecLevel)) // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using modifier // that indicates ECI protocol (ISO/IEC 15438:2015 Annex L Table L.1) diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index cd59127196..5065401fda 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -69,12 +69,12 @@ static bool CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) /** * See specification GBT 18284-2000 */ -static void DecodeHanziSegment(BitSource& bits, int count, std::wstring& result) +static void DecodeHanziSegment(BitSource& bits, int count, Content& result) { - // Each character will require 2 bytes. Read the characters as 2-byte pairs - // and decode as GB2312 afterwards - ByteArray buffer; - buffer.reserve(2 * count); + // Each character will require 2 bytes, decode as GB2312 + result.switchEncoding(CharacterSet::GB2312); + result.reserve(2 * count); + while (count > 0) { // Each 13 bits encodes a 2-byte character int twoBytes = bits.readBits(13); @@ -86,20 +86,19 @@ static void DecodeHanziSegment(BitSource& bits, int count, std::wstring& result) // In the 0xB0A1 to 0xFAFE range assembledTwoBytes += 0x0A6A1; } - buffer.push_back(static_cast((assembledTwoBytes >> 8) & 0xFF)); - buffer.push_back(static_cast(assembledTwoBytes & 0xFF)); + result += static_cast((assembledTwoBytes >> 8) & 0xFF); + result += static_cast(assembledTwoBytes & 0xFF); count--; } - - TextDecoder::Append(result, buffer.data(), Size(buffer), CharacterSet::GB2312); } -static void DecodeKanjiSegment(BitSource& bits, int count, std::wstring& result) +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 - ByteArray buffer; - buffer.reserve(2 * count); + result.switchEncoding(CharacterSet::Shift_JIS); + result.reserve(2 * count); + while (count > 0) { // Each 13 bits encodes a 2-byte character int twoBytes = bits.readBits(13); @@ -111,34 +110,19 @@ static void DecodeKanjiSegment(BitSource& bits, int count, std::wstring& result) // In the 0xE040 to 0xEBBF range assembledTwoBytes += 0x0C140; } - buffer.push_back(static_cast(assembledTwoBytes >> 8)); - buffer.push_back(static_cast(assembledTwoBytes)); + result += static_cast(assembledTwoBytes >> 8); + result += static_cast(assembledTwoBytes); count--; } - - TextDecoder::Append(result, buffer.data(), Size(buffer), CharacterSet::Shift_JIS); } -static void DecodeByteSegment(BitSource& bits, int count, CharacterSet currentCharset, const std::string& hintedCharset, - std::wstring& result) +static void DecodeByteSegment(BitSource& bits, int count, Content& result) { - ByteArray readBytes(count); + result.switchEncoding(CharacterSet::Unknown); + result.reserve(count); + for (int i = 0; i < count; i++) - readBytes[i] = static_cast(bits.readBits(8)); - - if (currentCharset == CharacterSet::Unknown) { - // The spec isn't clear on this mode; see - // section 6.4.5: t does not say which encoding to assuming - // upon decoding. I have seen ISO-8859-1 used as well as - // Shift_JIS -- without anything like an ECI designator to - // give a hint. - if (!hintedCharset.empty()) - currentCharset = CharacterSetECI::CharsetFromName(hintedCharset.c_str()); - - if (currentCharset == CharacterSet::Unknown) - currentCharset = TextDecoder::GuessEncoding(readBytes.data(), Size(readBytes)); - } - TextDecoder::Append(result, readBytes.data(), Size(readBytes), currentCharset); + result += static_cast(bits.readBits(8)); } static char ToAlphaNumericChar(int value) @@ -159,7 +143,7 @@ static char ToAlphaNumericChar(int value) return ALPHANUMERIC_CHARS[value]; } -static void DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffect, std::wstring& result) +static void DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffect, Content& result) { // Read two characters at a time std::string buffer; @@ -188,22 +172,26 @@ static void DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffe } } } - TextDecoder::AppendLatin1(result, buffer); + + result.switchEncoding(CharacterSet::ASCII); + result += buffer; } -static void DecodeNumericSegment(BitSource& bits, int count, std::wstring& result) +static void DecodeNumericSegment(BitSource& bits, int count, Content& result) { + result.switchEncoding(CharacterSet::ASCII); + result.reserve(count); + // Read three digits at a time - std::string buffer; while (count >= 3) { // Each 10 bits encodes three digits int threeDigitsBits = bits.readBits(10); if (threeDigitsBits >= 1000) throw std::runtime_error("Invalid value in numeric segment"); - buffer += ToAlphaNumericChar(threeDigitsBits / 100); - buffer += ToAlphaNumericChar((threeDigitsBits / 10) % 10); - buffer += ToAlphaNumericChar(threeDigitsBits % 10); + result += ToAlphaNumericChar(threeDigitsBits / 100); + result += ToAlphaNumericChar((threeDigitsBits / 10) % 10); + result += ToAlphaNumericChar(threeDigitsBits % 10); count -= 3; } @@ -213,18 +201,16 @@ static void DecodeNumericSegment(BitSource& bits, int count, std::wstring& resul if (twoDigitsBits >= 100) throw std::runtime_error("Invalid value in numeric segment"); - buffer += ToAlphaNumericChar(twoDigitsBits / 10); - buffer += ToAlphaNumericChar(twoDigitsBits % 10); + result += ToAlphaNumericChar(twoDigitsBits / 10); + result += ToAlphaNumericChar(twoDigitsBits % 10); } else if (count == 1) { // One digit left over to read int digitBits = bits.readBits(4); if (digitBits >= 10) throw std::runtime_error("Invalid value in numeric segment"); - buffer += ToAlphaNumericChar(digitBits); + result += ToAlphaNumericChar(digitBits); } - - TextDecoder::AppendLatin1(result, buffer); } static int ParseECIValue(BitSource& bits) @@ -282,15 +268,15 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo const std::string& hintedCharset) { BitSource bits(bytes); - std::wstring result; + Content result; + result.hintedCharset = hintedCharset.empty() ? "Auto" : hintedCharset; int symbologyIdModifier = 1; // ISO/IEC 18004:2015 Annex F Table F.1 StructuredAppendInfo structuredAppend; const int modeBitLength = CodecModeBitsLength(version); + bool fc1InEffect = false; try { - CharacterSet currentCharset = CharacterSet::Unknown; - bool fc1InEffect = false; while(!IsEndOfStream(bits, version)) { CodecMode mode; if (modeBitLength == 0) @@ -314,11 +300,11 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo symbologyIdModifier = 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 < 10) // "00-09" - result += L'0' + std::to_wstring(appInd); + result += '0' + std::to_string(appInd); else if (appInd < 100) // "10-99" - result += std::to_wstring(appInd); + result += std::to_string(appInd); else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222)) // "A-Za-z" - result += static_cast(appInd - 100); + result += static_cast(appInd - 100); else throw std::runtime_error("Invalid AIM Application Indicator"); break; @@ -329,19 +315,21 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo structuredAppend.count = bits.readBits(4) + 1; structuredAppend.id = std::to_string(bits.readBits(8)); break; - case CodecMode::ECI: + case CodecMode::ECI: { // Count doesn't apply to ECI - currentCharset = CharacterSetECI::ECI2CharacterSet(ParseECIValue(bits)); - if (currentCharset == CharacterSet::Unknown) + auto charset = CharacterSetECI::ECI2CharacterSet(ParseECIValue(bits)); + if (charset == CharacterSet::Unknown) return DecodeStatus::FormatError; + result.switchEncoding(charset, true); 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 - int subset = bits.readBits(4); + if (int subset = bits.readBits(4); subset != 1) // GB2312_SUBSET is the only supported one right now + return DecodeStatus::FormatError; int count = bits.readBits(CharacterCountBits(mode, version)); - if (subset == 1 ) // GB2312_SUBSET - DecodeHanziSegment(bits, count, result); + DecodeHanziSegment(bits, count, result); break; } default: { @@ -351,7 +339,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo switch (mode) { case CodecMode::NUMERIC: DecodeNumericSegment(bits, count, result); break; case CodecMode::ALPHANUMERIC: DecodeAlphanumericSegment(bits, count, fc1InEffect, result); break; - case CodecMode::BYTE: DecodeByteSegment(bits, count, currentCharset, hintedCharset, result); break; + case CodecMode::BYTE: DecodeByteSegment(bits, count, result); break; case CodecMode::KANJI: DecodeKanjiSegment(bits, count, result); break; default: return DecodeStatus::FormatError; } @@ -368,7 +356,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo return DecodeStatus::FormatError; } - return DecoderResult(std::move(bytes), std::move(result)) + return DecoderResult(std::move(bytes), {}, std::move(result)) .setEcLevel(ToString(ecLevel)) .setSymbologyIdentifier("]Q" + std::to_string(symbologyIdModifier)) .setStructuredAppend(structuredAppend); diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 15150e2897..844e077403 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -182,6 +182,7 @@ int main(int argc, char* argv[]) firstFile = false; } std::cout << "Text: \"" << ToUtf8(result.text(), angleEscape) << "\"\n" + << "Binary: \"" << ToHex(result.binary()) << "\"\n" << "Format: " << ToString(result.format()) << "\n" << "Identifier: " << result.symbologyIdentifier() << "\n" << "Position: " << result.position() << "\n" diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index fce059884f..ac66da3146 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -153,9 +153,10 @@ static std::string checkResult(const fs::path& imgPath, std::string_view expecte } if (auto expected = readFile(".bin")) { - std::string latin1Result(result.text().length(), '\0'); - std::transform(result.text().begin(), result.text().end(), latin1Result.begin(), [](wchar_t c) { return static_cast(c); }); - return latin1Result != *expected ? fmt::format("Content mismatch: expected '{}' but got '{}'", *expected, latin1Result) : ""; + ByteArray binaryExpected(*expected); + return result.binary() != binaryExpected + ? fmt::format("Content mismatch: expected '{}' but got '{}'", ToHex(binaryExpected), ToHex(result.binary())) + : ""; } return "Error reading file"; @@ -275,11 +276,14 @@ static Result readMultiple(const std::vector& imgPaths, std::string_vi return Result(DecodeStatus::FormatError); std::wstring text; - for (const auto& r : allResults) + Content content; + for (const auto& r : allResults) { text.append(r.text()); + content.append(r.binary()); + } const auto& first = allResults.front(); - return {DecoderResult({}, std::move(text)) + return {DecoderResult({}, std::move(text), std::move(content)) .setStructuredAppend({first.sequenceIndex(), first.sequenceSize(), first.sequenceId()}) .setSymbologyIdentifier(first.symbologyIdentifier()) .setReaderInit(first.readerInit()), From 65b6f5b983f7144268052f1df991f61b9d1fb298 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 21 May 2022 18:58:20 +0200 Subject: [PATCH 028/180] test: replace utf8 encoded dm-c.txt with dm-c.bin for appropriate testing --- test/samples/datamatrix-3/dm-c.bin | Bin 0 -> 84 bytes test/samples/datamatrix-3/dm-c.txt | Bin 111 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/samples/datamatrix-3/dm-c.bin delete mode 100644 test/samples/datamatrix-3/dm-c.txt diff --git a/test/samples/datamatrix-3/dm-c.bin b/test/samples/datamatrix-3/dm-c.bin new file mode 100644 index 0000000000000000000000000000000000000000..18dbeccbc88b2f3582a572dada2558160e68027c GIT binary patch literal 84 zcmZ>9bz~OPWs-aQmthr4SmH_rMg|7{z3!6CGahQ|u(rJ7pE;-Q>%7;lzW?qjoOyEf WQ_+dM43BvXAVoj|6I|ZcwgCXlQyPf? literal 0 HcmV?d00001 diff --git a/test/samples/datamatrix-3/dm-c.txt b/test/samples/datamatrix-3/dm-c.txt deleted file mode 100644 index 427bebc63e5f97cfa07c11e3de3e4850d5a5feeb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111 zcmZ>9bz~OPWs*C*_V8YYL#tTA5)Z9ZU}Rw6KeX3flKIe#!wa=_SP!)vUd4ZC=Ak)t zhqoS@cX+j{@8Nxi=PDfTIlScX)We&K4tL~bcpRF?0M-U1F~Q- Date: Sat, 21 May 2022 19:00:42 +0200 Subject: [PATCH 029/180] ZXingReader: add option to only output binary content --- example/ZXingReader.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 844e077403..84dcff4eb6 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -44,7 +44,8 @@ static void PrintUsage(const char* exePath) << " -format Only detect given format(s) (faster)\n" << " -ispure Assume the image contains only a 'pure'/perfect code (faster)\n" << " -1 Print only file name, text and status on one line per file/barcode\n" - << " -escape Escape non-graphical characters in angle brackets (ignored for -1 option, which always escapes)\n" + << " -escape Escape non-graphical characters in angle brackets (implicit for -1 option, which always escapes)\n" + << " -binary Write (only) the binary content of the symbol(s) to stdout\n" << " -pngout Write a copy of the input image with barcodes outlined by a green line\n" << "\n" << "Supported formats are:\n"; @@ -54,7 +55,7 @@ static void PrintUsage(const char* exePath) std::cout << "Formats can be lowercase, with or without '-', separated by ',' and/or '|'\n"; } -static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLine, bool& angleEscape, +static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLine, bool& angleEscape, bool& binaryOutput, std::vector& filePaths, std::string& outPath) { for (int i = 1; i < argc; ++i) { @@ -80,6 +81,8 @@ static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLi oneLine = true; } else if (strcmp(argv[i], "-escape") == 0) { angleEscape = true; + } else if (strcmp(argv[i], "-binary") == 0) { + binaryOutput = true; } else if (strcmp(argv[i], "-pngout") == 0) { if (++i == argc) return false; @@ -125,10 +128,11 @@ int main(int argc, char* argv[]) std::string outPath; bool oneLine = false; bool angleEscape = false; + bool binaryOutput = false; int ret = 0; - if (!ParseOptions(argc, argv, hints, oneLine, angleEscape, filePaths, outPath)) { + if (!ParseOptions(argc, argv, hints, oneLine, angleEscape, binaryOutput, filePaths, outPath)) { PrintUsage(argv[0]); return -1; } @@ -163,6 +167,11 @@ int main(int argc, char* argv[]) ret |= static_cast(result.status()); + if (binaryOutput) { + std::cout.write(reinterpret_cast(result.binary().data()), result.binary().size()); + continue; + } + if (oneLine) { std::cout << filePath << " " << ToString(result.format()); if (result.isValid()) From e4c6a877c46b4ce01be5b59422d696b6d2ad7e05 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 22 May 2022 01:01:46 +0200 Subject: [PATCH 030/180] Introduce SPDX-License-Identifier into Apache licensed source files Also move `#pragma once` below the first header block, such that the 'auto fold first comment' feature of some IDEs (e.g. QtCreator) can work. Note: unfortunately, the SPDX-License-Identifier string needs to be in its own comment to be fully functional. --- core/src/BarcodeFormat.cpp | 13 +------------ core/src/BarcodeFormat.h | 16 +++------------- core/src/BinaryBitmap.cpp | 13 +------------ core/src/BinaryBitmap.h | 16 +++------------- core/src/BitArray.cpp | 13 +------------ core/src/BitArray.h | 16 +++------------- core/src/BitHacks.h | 16 +++------------- core/src/BitMatrix.cpp | 13 +------------ core/src/BitMatrix.h | 16 +++------------- core/src/BitMatrixCursor.h | 16 +++------------- core/src/BitMatrixIO.cpp | 13 +------------ core/src/BitMatrixIO.h | 16 +++------------- core/src/BitSource.cpp | 13 +------------ core/src/BitSource.h | 16 +++------------- core/src/ByteArray.h | 16 +++------------- core/src/ByteMatrix.h | 16 +++------------- core/src/CharacterSet.h | 16 +++------------- core/src/CharacterSetECI.cpp | 13 +------------ core/src/CharacterSetECI.h | 16 +++------------- core/src/ConcentricFinder.cpp | 13 +------------ core/src/ConcentricFinder.h | 16 +++------------- core/src/Content.cpp | 15 ++------------- core/src/Content.h | 16 +++------------- core/src/CustomData.h | 16 +++------------- core/src/DecodeHints.cpp | 13 +------------ core/src/DecodeHints.h | 16 +++------------- core/src/DecodeStatus.cpp | 13 +------------ core/src/DecodeStatus.h | 16 +++------------- core/src/DecoderResult.h | 16 +++------------- core/src/DetectorResult.h | 16 +++------------- core/src/Flags.h | 16 +++------------- core/src/GTIN.cpp | 13 +------------ core/src/GTIN.h | 16 +++------------- core/src/GenericGF.cpp | 13 +------------ core/src/GenericGF.h | 16 +++------------- core/src/GenericGFPoly.cpp | 13 +------------ core/src/GenericGFPoly.h | 16 +++------------- core/src/GlobalHistogramBinarizer.cpp | 13 +------------ core/src/GlobalHistogramBinarizer.h | 16 +++------------- core/src/GridSampler.cpp | 13 +------------ core/src/GridSampler.h | 16 +++------------- core/src/HybridBinarizer.cpp | 13 +------------ core/src/HybridBinarizer.h | 16 +++------------- core/src/ImageView.h | 16 +++------------- core/src/LogMatrix.h | 16 +++------------- core/src/Matrix.h | 16 +++------------- core/src/MultiFormatReader.cpp | 13 +------------ core/src/MultiFormatReader.h | 16 +++------------- core/src/MultiFormatWriter.cpp | 13 +------------ core/src/MultiFormatWriter.h | 16 +++------------- core/src/Pattern.h | 16 +++------------- core/src/PerspectiveTransform.cpp | 13 +------------ core/src/PerspectiveTransform.h | 16 +++------------- core/src/Point.h | 16 +++------------- core/src/Quadrilateral.h | 16 +++------------- core/src/ReadBarcode.cpp | 13 +------------ core/src/ReadBarcode.h | 16 +++------------- core/src/Reader.h | 16 +++------------- core/src/ReedSolomonDecoder.cpp | 13 +------------ core/src/ReedSolomonDecoder.h | 16 +++------------- core/src/ReedSolomonEncoder.cpp | 13 +------------ core/src/ReedSolomonEncoder.h | 16 +++------------- core/src/RegressionLine.h | 16 +++------------- core/src/Result.cpp | 13 +------------ core/src/Result.h | 16 +++------------- core/src/ResultPoint.cpp | 13 +------------ core/src/ResultPoint.h | 16 +++------------- core/src/Scope.h | 16 +++------------- core/src/StructuredAppend.h | 16 +++------------- core/src/TextDecoder.cpp | 13 +------------ core/src/TextDecoder.h | 16 +++------------- core/src/TextEncoder.cpp | 13 +------------ core/src/TextEncoder.h | 16 +++------------- core/src/TextUtfEncoding.cpp | 13 +------------ core/src/TextUtfEncoding.h | 16 +++------------- core/src/ThresholdBinarizer.h | 16 +++------------- core/src/TritMatrix.h | 16 +++------------- core/src/WhiteRectDetector.cpp | 13 +------------ core/src/WhiteRectDetector.h | 16 +++------------- core/src/ZXBigInteger.cpp | 13 +------------ core/src/ZXBigInteger.h | 16 +++------------- core/src/ZXConfig.h | 16 +++------------- core/src/ZXContainerAlgorithms.h | 16 +++------------- core/src/ZXNullable.h | 16 +++------------- core/src/aztec/AZDecoder.cpp | 13 +------------ core/src/aztec/AZDecoder.h | 16 +++------------- core/src/aztec/AZDetector.cpp | 13 +------------ core/src/aztec/AZDetector.h | 16 +++------------- core/src/aztec/AZDetectorResult.h | 16 +++------------- core/src/aztec/AZEncoder.cpp | 13 +------------ core/src/aztec/AZEncoder.h | 16 +++------------- core/src/aztec/AZEncodingState.h | 16 +++------------- core/src/aztec/AZHighLevelEncoder.cpp | 13 +------------ core/src/aztec/AZHighLevelEncoder.h | 16 +++------------- core/src/aztec/AZReader.cpp | 13 +------------ core/src/aztec/AZReader.h | 16 +++------------- core/src/aztec/AZToken.cpp | 13 +------------ core/src/aztec/AZToken.h | 16 +++------------- core/src/aztec/AZWriter.cpp | 13 +------------ core/src/aztec/AZWriter.h | 16 +++------------- core/src/datamatrix/DMBitLayout.cpp | 13 +------------ core/src/datamatrix/DMBitLayout.h | 16 +++------------- core/src/datamatrix/DMDataBlock.cpp | 13 +------------ core/src/datamatrix/DMDataBlock.h | 16 +++------------- core/src/datamatrix/DMDecoder.cpp | 13 +------------ core/src/datamatrix/DMDecoder.h | 16 +++------------- core/src/datamatrix/DMDetector.cpp | 13 +------------ core/src/datamatrix/DMDetector.h | 16 +++------------- core/src/datamatrix/DMECEncoder.cpp | 13 +------------ core/src/datamatrix/DMECEncoder.h | 16 +++------------- core/src/datamatrix/DMEncoderContext.h | 16 +++------------- core/src/datamatrix/DMHighLevelEncoder.cpp | 13 +------------ core/src/datamatrix/DMHighLevelEncoder.h | 16 +++------------- core/src/datamatrix/DMReader.cpp | 13 +------------ core/src/datamatrix/DMReader.h | 16 +++------------- core/src/datamatrix/DMSymbolInfo.cpp | 13 +------------ core/src/datamatrix/DMSymbolInfo.h | 16 +++------------- core/src/datamatrix/DMSymbolShape.h | 16 +++------------- core/src/datamatrix/DMVersion.cpp | 13 +------------ core/src/datamatrix/DMVersion.h | 16 +++------------- core/src/datamatrix/DMWriter.cpp | 13 +------------ core/src/datamatrix/DMWriter.h | 16 +++------------- core/src/maxicode/MCBitMatrixParser.cpp | 13 +------------ core/src/maxicode/MCBitMatrixParser.h | 16 +++------------- core/src/maxicode/MCDecoder.cpp | 13 +------------ core/src/maxicode/MCDecoder.h | 16 +++------------- core/src/maxicode/MCReader.cpp | 13 +------------ core/src/maxicode/MCReader.h | 16 +++------------- core/src/oned/ODCodabarReader.cpp | 13 +------------ core/src/oned/ODCodabarReader.h | 16 +++------------- core/src/oned/ODCodabarWriter.cpp | 13 +------------ core/src/oned/ODCodabarWriter.h | 16 +++------------- core/src/oned/ODCode128Patterns.cpp | 13 +------------ core/src/oned/ODCode128Patterns.h | 16 +++------------- core/src/oned/ODCode128Reader.cpp | 13 +------------ core/src/oned/ODCode128Reader.h | 16 +++------------- core/src/oned/ODCode128Writer.cpp | 13 +------------ core/src/oned/ODCode128Writer.h | 16 +++------------- core/src/oned/ODCode39Reader.cpp | 13 +------------ core/src/oned/ODCode39Reader.h | 16 +++------------- core/src/oned/ODCode39Writer.cpp | 13 +------------ core/src/oned/ODCode39Writer.h | 16 +++------------- core/src/oned/ODCode93Reader.cpp | 13 +------------ core/src/oned/ODCode93Reader.h | 16 +++------------- core/src/oned/ODCode93Writer.cpp | 13 +------------ core/src/oned/ODCode93Writer.h | 16 +++------------- core/src/oned/ODDataBarCommon.cpp | 13 +------------ core/src/oned/ODDataBarCommon.h | 16 +++------------- core/src/oned/ODDataBarExpandedReader.cpp | 13 +------------ core/src/oned/ODDataBarExpandedReader.h | 16 +++------------- core/src/oned/ODDataBarReader.cpp | 13 +------------ core/src/oned/ODDataBarReader.h | 16 +++------------- core/src/oned/ODEAN13Writer.cpp | 13 +------------ core/src/oned/ODEAN13Writer.h | 16 +++------------- core/src/oned/ODEAN8Writer.cpp | 13 +------------ core/src/oned/ODEAN8Writer.h | 16 +++------------- core/src/oned/ODITFReader.cpp | 13 +------------ core/src/oned/ODITFReader.h | 16 +++------------- core/src/oned/ODITFWriter.cpp | 13 +------------ core/src/oned/ODITFWriter.h | 16 +++------------- core/src/oned/ODMultiUPCEANReader.cpp | 13 +------------ core/src/oned/ODMultiUPCEANReader.h | 16 +++------------- core/src/oned/ODReader.cpp | 13 +------------ core/src/oned/ODReader.h | 16 +++------------- core/src/oned/ODRowReader.cpp | 13 +------------ core/src/oned/ODRowReader.h | 16 +++------------- core/src/oned/ODUPCAWriter.cpp | 13 +------------ core/src/oned/ODUPCAWriter.h | 16 +++------------- core/src/oned/ODUPCEANCommon.cpp | 13 +------------ core/src/oned/ODUPCEANCommon.h | 16 +++------------- core/src/oned/ODUPCEWriter.cpp | 13 +------------ core/src/oned/ODUPCEWriter.h | 16 +++------------- core/src/oned/ODWriterHelper.cpp | 13 +------------ core/src/oned/ODWriterHelper.h | 16 +++------------- core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp | 13 +------------ core/src/oned/rss/ODRSSExpandedBinaryDecoder.h | 16 +++------------- core/src/oned/rss/ODRSSFieldParser.cpp | 13 +------------ core/src/oned/rss/ODRSSFieldParser.h | 16 +++------------- core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp | 13 +------------ core/src/oned/rss/ODRSSGenericAppIdDecoder.h | 16 +++------------- core/src/pdf417/PDFBarcodeMetadata.h | 16 +++------------- core/src/pdf417/PDFBarcodeValue.cpp | 13 +------------ core/src/pdf417/PDFBarcodeValue.h | 16 +++------------- core/src/pdf417/PDFBoundingBox.cpp | 13 +------------ core/src/pdf417/PDFBoundingBox.h | 16 +++------------- core/src/pdf417/PDFCodeword.h | 16 +++------------- core/src/pdf417/PDFCodewordDecoder.cpp | 13 +------------ core/src/pdf417/PDFCodewordDecoder.h | 16 +++------------- core/src/pdf417/PDFCompaction.h | 16 +++------------- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 13 +------------ core/src/pdf417/PDFDecodedBitStreamParser.h | 16 +++------------- core/src/pdf417/PDFDecoderResultExtra.h | 16 +++------------- core/src/pdf417/PDFDetectionResult.cpp | 13 +------------ core/src/pdf417/PDFDetectionResult.h | 16 +++------------- core/src/pdf417/PDFDetectionResultColumn.cpp | 13 +------------ core/src/pdf417/PDFDetectionResultColumn.h | 16 +++------------- core/src/pdf417/PDFDetector.cpp | 13 +------------ core/src/pdf417/PDFDetector.h | 16 +++------------- core/src/pdf417/PDFEncoder.cpp | 13 +------------ core/src/pdf417/PDFEncoder.h | 16 +++------------- core/src/pdf417/PDFHighLevelEncoder.cpp | 13 +------------ core/src/pdf417/PDFHighLevelEncoder.h | 16 +++------------- core/src/pdf417/PDFModulusGF.cpp | 13 +------------ core/src/pdf417/PDFModulusGF.h | 16 +++------------- core/src/pdf417/PDFModulusPoly.cpp | 13 +------------ core/src/pdf417/PDFModulusPoly.h | 16 +++------------- core/src/pdf417/PDFReader.cpp | 13 +------------ core/src/pdf417/PDFReader.h | 16 +++------------- core/src/pdf417/PDFScanningDecoder.cpp | 13 +------------ core/src/pdf417/PDFScanningDecoder.h | 16 +++------------- core/src/pdf417/PDFWriter.cpp | 13 +------------ core/src/pdf417/PDFWriter.h | 16 +++------------- core/src/qrcode/QRBitMatrixParser.cpp | 13 +------------ core/src/qrcode/QRBitMatrixParser.h | 16 +++------------- core/src/qrcode/QRCodecMode.cpp | 13 +------------ core/src/qrcode/QRCodecMode.h | 16 +++------------- core/src/qrcode/QRDataBlock.cpp | 13 +------------ core/src/qrcode/QRDataBlock.h | 16 +++------------- core/src/qrcode/QRDataMask.h | 16 +++------------- core/src/qrcode/QRDecoder.cpp | 13 +------------ core/src/qrcode/QRDecoder.h | 16 +++------------- core/src/qrcode/QRDetector.cpp | 13 +------------ core/src/qrcode/QRDetector.h | 16 +++------------- core/src/qrcode/QRECB.h | 16 +++------------- core/src/qrcode/QREncodeResult.h | 16 +++------------- core/src/qrcode/QREncoder.cpp | 13 +------------ core/src/qrcode/QREncoder.h | 16 +++------------- core/src/qrcode/QRErrorCorrectionLevel.cpp | 13 +------------ core/src/qrcode/QRErrorCorrectionLevel.h | 16 +++------------- core/src/qrcode/QRFormatInformation.cpp | 13 +------------ core/src/qrcode/QRFormatInformation.h | 16 +++------------- core/src/qrcode/QRMaskUtil.cpp | 13 +------------ core/src/qrcode/QRMaskUtil.h | 16 +++------------- core/src/qrcode/QRMatrixUtil.cpp | 13 +------------ core/src/qrcode/QRMatrixUtil.h | 16 +++------------- core/src/qrcode/QRReader.cpp | 13 +------------ core/src/qrcode/QRReader.h | 16 +++------------- core/src/qrcode/QRVersion.cpp | 13 +------------ core/src/qrcode/QRVersion.h | 16 +++------------- core/src/qrcode/QRWriter.cpp | 13 +------------ core/src/qrcode/QRWriter.h | 16 +++------------- example/ZXingOpenCV.cpp | 15 ++------------- example/ZXingQtCamReader.cpp | 15 ++------------- example/ZXingQtReader.cpp | 15 ++------------- example/ZXingReader.cpp | 13 +------------ example/ZXingWriter.cpp | 13 +------------ test/blackbox/BlackboxTestRunner.cpp | 13 +------------ test/blackbox/BlackboxTestRunner.h | 16 +++------------- test/blackbox/ImageLoader.cpp | 13 +------------ test/blackbox/ImageLoader.h | 16 +++------------- test/blackbox/TestReaderMain.cpp | 13 +------------ test/blackbox/TestWriterMain.cpp | 13 +------------ test/blackbox/ZXFilesystem.h | 16 +++------------- test/unit/BarcodeFormatTest.cpp | 13 +------------ test/unit/BitArrayUtility.cpp | 13 +------------ test/unit/BitHacksTest.cpp | 13 +------------ test/unit/CharacterSetECITest.cpp | 13 +------------ test/unit/GTINTest.cpp | 13 +------------ test/unit/ReedSolomonTest.cpp | 13 +------------ test/unit/TextDecoderTest.cpp | 13 +------------ test/unit/TextUtfEncodingTest.cpp | 13 +------------ test/unit/ThresholdBinarizerTest.cpp | 13 +------------ test/unit/aztec/AZDecoderTest.cpp | 13 +------------ test/unit/aztec/AZDetectorTest.cpp | 13 +------------ test/unit/aztec/AZEncodeDecodeTest.cpp | 13 +------------ test/unit/aztec/AZEncoderTest.cpp | 13 +------------ test/unit/aztec/AZHighLevelEncoderTest.cpp | 13 +------------ .../datamatrix/DMDecodedBitStreamParserTest.cpp | 13 +------------ test/unit/datamatrix/DMEncodeDecodeTest.cpp | 13 +------------ test/unit/datamatrix/DMHighLevelEncodeTest.cpp | 13 +------------ test/unit/datamatrix/DMPlacementTest.cpp | 13 +------------ test/unit/datamatrix/DMSymbolInfoTest.cpp | 13 +------------ test/unit/datamatrix/DMWriterTest.cpp | 13 +------------ test/unit/maxicode/MCDecoderTest.cpp | 13 +------------ test/unit/oned/ODCodaBarWriterTest.cpp | 13 +------------ test/unit/oned/ODCode128ReaderTest.cpp | 13 +------------ test/unit/oned/ODCode128WriterTest.cpp | 13 +------------ test/unit/oned/ODCode39ExtendedModeTest.cpp | 13 +------------ test/unit/oned/ODCode39ReaderTest.cpp | 13 +------------ test/unit/oned/ODCode39WriterTest.cpp | 13 +------------ test/unit/oned/ODCode93ReaderTest.cpp | 13 +------------ test/unit/oned/ODCode93WriterTest.cpp | 13 +------------ test/unit/oned/ODEAN13WriterTest.cpp | 13 +------------ test/unit/oned/ODEAN8WriterTest.cpp | 13 +------------ test/unit/oned/ODITFWriterTest.cpp | 13 +------------ test/unit/oned/ODUPCAWriterTest.cpp | 13 +------------ test/unit/oned/ODUPCEWriterTest.cpp | 13 +------------ .../oned/rss/ODRSSExpandedBinaryDecoderTest.cpp | 13 +------------ test/unit/oned/rss/ODRSSFieldParserTest.cpp | 13 +------------ test/unit/pdf417/PDF417DecoderTest.cpp | 13 +------------ test/unit/pdf417/PDF417ErrorCorrectionTest.cpp | 13 +------------ test/unit/pdf417/PDF417HighLevelEncoderTest.cpp | 13 +------------ test/unit/pdf417/PDF417WriterTest.cpp | 13 +------------ test/unit/qrcode/MQRDecoderTest.cpp | 15 ++------------- test/unit/qrcode/QRBitMatrixParserTest.cpp | 15 ++------------- test/unit/qrcode/QRDataMaskTest.cpp | 13 +------------ .../unit/qrcode/QRDecodedBitStreamParserTest.cpp | 13 +------------ test/unit/qrcode/QREncoderTest.cpp | 15 ++------------- test/unit/qrcode/QRErrorCorrectionLevelTest.cpp | 13 +------------ test/unit/qrcode/QRFormatInformationTest.cpp | 13 +------------ test/unit/qrcode/QRModeTest.cpp | 13 +------------ test/unit/qrcode/QRVersionTest.cpp | 13 +------------ test/unit/qrcode/QRWriterTest.cpp | 13 +------------ .../zxingcpp/src/main/cpp/BarcodeReader.cpp | 13 +------------ .../android/zxingcpp/src/main/cpp/JNIUtils.cpp | 13 +------------ .../android/zxingcpp/src/main/cpp/JNIUtils.h | 16 +++------------- wrappers/wasm/BarcodeReader.cpp | 13 +------------ wrappers/wasm/BarcodeWriter.cpp | 13 +------------ wrappers/winrt/BarcodeReader.h | 16 +++------------- wrappers/winrt/ReadResult.h | 16 +++------------- 310 files changed, 605 insertions(+), 3871 deletions(-) diff --git a/core/src/BarcodeFormat.cpp b/core/src/BarcodeFormat.cpp index e83df3ecfb..232bcb854f 100644 --- a/core/src/BarcodeFormat.cpp +++ b/core/src/BarcodeFormat.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BarcodeFormat.h" diff --git a/core/src/BarcodeFormat.h b/core/src/BarcodeFormat.h index 8bb5d3681a..2d82784525 100644 --- a/core/src/BarcodeFormat.h +++ b/core/src/BarcodeFormat.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Flags.h" diff --git a/core/src/BinaryBitmap.cpp b/core/src/BinaryBitmap.cpp index e68cccd2bc..e3676eef6a 100644 --- a/core/src/BinaryBitmap.cpp +++ b/core/src/BinaryBitmap.cpp @@ -1,18 +1,7 @@ /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BinaryBitmap.h" diff --git a/core/src/BinaryBitmap.h b/core/src/BinaryBitmap.h index 896294db73..0337d24abd 100644 --- a/core/src/BinaryBitmap.h +++ b/core/src/BinaryBitmap.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2021 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ReadBarcode.h" diff --git a/core/src/BitArray.cpp b/core/src/BitArray.cpp index 5566c65e47..718d025bac 100644 --- a/core/src/BitArray.cpp +++ b/core/src/BitArray.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitArray.h" diff --git a/core/src/BitArray.h b/core/src/BitArray.h index 490c0d6e6d..b2164da1cb 100644 --- a/core/src/BitArray.h +++ b/core/src/BitArray.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2017 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ZXConfig.h" #include "ZXContainerAlgorithms.h" diff --git a/core/src/BitHacks.h b/core/src/BitHacks.h index 7ee06fc38b..482b181a8b 100644 --- a/core/src/BitHacks.h +++ b/core/src/BitHacks.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2017 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/BitMatrix.cpp b/core/src/BitMatrix.cpp index 2f484f7d1c..d9449cfbe3 100644 --- a/core/src/BitMatrix.cpp +++ b/core/src/BitMatrix.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitMatrix.h" diff --git a/core/src/BitMatrix.h b/core/src/BitMatrix.h index 01c5d51e03..a01f20a4f4 100644 --- a/core/src/BitMatrix.h +++ b/core/src/BitMatrix.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Matrix.h" #include "Point.h" diff --git a/core/src/BitMatrixCursor.h b/core/src/BitMatrixCursor.h index a8f3f0b6b6..7e1f16c440 100644 --- a/core/src/BitMatrixCursor.h +++ b/core/src/BitMatrixCursor.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BitMatrix.h" diff --git a/core/src/BitMatrixIO.cpp b/core/src/BitMatrixIO.cpp index dcdd090d9d..10a7c86306 100644 --- a/core/src/BitMatrixIO.cpp +++ b/core/src/BitMatrixIO.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2017 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitMatrixIO.h" diff --git a/core/src/BitMatrixIO.h b/core/src/BitMatrixIO.h index 3178ac01a6..022b48ec35 100644 --- a/core/src/BitMatrixIO.h +++ b/core/src/BitMatrixIO.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2017 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/BitSource.cpp b/core/src/BitSource.cpp index 569014354c..bc2771a932 100644 --- a/core/src/BitSource.cpp +++ b/core/src/BitSource.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitSource.h" diff --git a/core/src/BitSource.h b/core/src/BitSource.h index f9d0c2b064..1db7c9eeb8 100644 --- a/core/src/BitSource.h +++ b/core/src/BitSource.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/ByteArray.h b/core/src/ByteArray.h index 7dc5f96cea..85712f20e5 100644 --- a/core/src/ByteArray.h +++ b/core/src/ByteArray.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/ByteMatrix.h b/core/src/ByteMatrix.h index 06ffd1ddc3..09dc1eac32 100644 --- a/core/src/ByteMatrix.h +++ b/core/src/ByteMatrix.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Matrix.h" diff --git a/core/src/CharacterSet.h b/core/src/CharacterSet.h index dd34fd4fd0..e078f1fa89 100644 --- a/core/src/CharacterSet.h +++ b/core/src/CharacterSet.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/CharacterSetECI.cpp b/core/src/CharacterSetECI.cpp index 86cd507042..f99adc6b32 100644 --- a/core/src/CharacterSetECI.cpp +++ b/core/src/CharacterSetECI.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "CharacterSetECI.h" #include "TextDecoder.h" diff --git a/core/src/CharacterSetECI.h b/core/src/CharacterSetECI.h index 03d52cc236..1d6c1e6ed7 100644 --- a/core/src/CharacterSetECI.h +++ b/core/src/CharacterSetECI.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "CharacterSet.h" diff --git a/core/src/ConcentricFinder.cpp b/core/src/ConcentricFinder.cpp index e53740d595..57b94f4704 100644 --- a/core/src/ConcentricFinder.cpp +++ b/core/src/ConcentricFinder.cpp @@ -1,18 +1,7 @@ /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ConcentricFinder.h" diff --git a/core/src/ConcentricFinder.h b/core/src/ConcentricFinder.h index 64b47d1bbf..8fe07d2945 100644 --- a/core/src/ConcentricFinder.h +++ b/core/src/ConcentricFinder.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BitMatrixCursor.h" #include "Pattern.h" diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 4e2e983db0..dc17fcc8b9 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -1,18 +1,7 @@ /* * Copyright 2022 Axel Waggershauser - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +*/ +// SPDX-License-Identifier: Apache-2.0 #include "Content.h" diff --git a/core/src/Content.h b/core/src/Content.h index e6407ed2ea..1af33bac05 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2022 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ByteArray.h" #include "CharacterSet.h" diff --git a/core/src/CustomData.h b/core/src/CustomData.h index e80dbf533b..6578af5260 100644 --- a/core/src/CustomData.h +++ b/core/src/CustomData.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/DecodeHints.cpp b/core/src/DecodeHints.cpp index 25f34b717c..72ded6073c 100644 --- a/core/src/DecodeHints.cpp +++ b/core/src/DecodeHints.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DecodeHints.h" diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index d098e0a9c1..68ffe031b9 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BarcodeFormat.h" diff --git a/core/src/DecodeStatus.cpp b/core/src/DecodeStatus.cpp index 9f7ae1a8d2..8abff86b2d 100644 --- a/core/src/DecodeStatus.cpp +++ b/core/src/DecodeStatus.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DecodeStatus.h" diff --git a/core/src/DecodeStatus.h b/core/src/DecodeStatus.h index fe321dc3b2..5c8fba4a47 100644 --- a/core/src/DecodeStatus.h +++ b/core/src/DecodeStatus.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 81fc060479..0bb573bc7e 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ByteArray.h" #include "Content.h" diff --git a/core/src/DetectorResult.h b/core/src/DetectorResult.h index 67db3b6b12..dbeffe0b02 100644 --- a/core/src/DetectorResult.h +++ b/core/src/DetectorResult.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BitMatrix.h" #include "Quadrilateral.h" diff --git a/core/src/Flags.h b/core/src/Flags.h index 616082d85b..c5f2e2a8ab 100644 --- a/core/src/Flags.h +++ b/core/src/Flags.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BitHacks.h" diff --git a/core/src/GTIN.cpp b/core/src/GTIN.cpp index 3dc969a66c..8d498206a8 100644 --- a/core/src/GTIN.cpp +++ b/core/src/GTIN.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "GTIN.h" diff --git a/core/src/GTIN.h b/core/src/GTIN.h index 310da18c84..9637be44ec 100644 --- a/core/src/GTIN.h +++ b/core/src/GTIN.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BarcodeFormat.h" #include "ZXContainerAlgorithms.h" diff --git a/core/src/GenericGF.cpp b/core/src/GenericGF.cpp index 6e28b4fd80..d6c03ab056 100644 --- a/core/src/GenericGF.cpp +++ b/core/src/GenericGF.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "GenericGF.h" diff --git a/core/src/GenericGF.h b/core/src/GenericGF.h index 5c11f22aac..4f25b62564 100644 --- a/core/src/GenericGF.h +++ b/core/src/GenericGF.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "GenericGFPoly.h" #include "ZXConfig.h" diff --git a/core/src/GenericGFPoly.cpp b/core/src/GenericGFPoly.cpp index 4fa4df5464..8abe394e93 100644 --- a/core/src/GenericGFPoly.cpp +++ b/core/src/GenericGFPoly.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2017 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "GenericGFPoly.h" diff --git a/core/src/GenericGFPoly.h b/core/src/GenericGFPoly.h index f87dfda3f4..7aa56b927d 100644 --- a/core/src/GenericGFPoly.h +++ b/core/src/GenericGFPoly.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ZXContainerAlgorithms.h" diff --git a/core/src/GlobalHistogramBinarizer.cpp b/core/src/GlobalHistogramBinarizer.cpp index 59ad786b43..000bbe9182 100644 --- a/core/src/GlobalHistogramBinarizer.cpp +++ b/core/src/GlobalHistogramBinarizer.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "GlobalHistogramBinarizer.h" diff --git a/core/src/GlobalHistogramBinarizer.h b/core/src/GlobalHistogramBinarizer.h index fbeac5d9df..8a0bcfa4e9 100644 --- a/core/src/GlobalHistogramBinarizer.h +++ b/core/src/GlobalHistogramBinarizer.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BinaryBitmap.h" diff --git a/core/src/GridSampler.cpp b/core/src/GridSampler.cpp index 260d763c27..b612f3a5ef 100644 --- a/core/src/GridSampler.cpp +++ b/core/src/GridSampler.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "GridSampler.h" diff --git a/core/src/GridSampler.h b/core/src/GridSampler.h index 01e1eb92f7..8fb4eea6a9 100644 --- a/core/src/GridSampler.h +++ b/core/src/GridSampler.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "DetectorResult.h" #include "PerspectiveTransform.h" diff --git a/core/src/HybridBinarizer.cpp b/core/src/HybridBinarizer.cpp index 2a9bb7aea5..bf14d51d6a 100644 --- a/core/src/HybridBinarizer.cpp +++ b/core/src/HybridBinarizer.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "HybridBinarizer.h" diff --git a/core/src/HybridBinarizer.h b/core/src/HybridBinarizer.h index 53fd8851d8..6e0a8b42d0 100644 --- a/core/src/HybridBinarizer.h +++ b/core/src/HybridBinarizer.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "GlobalHistogramBinarizer.h" diff --git a/core/src/ImageView.h b/core/src/ImageView.h index 7d8058d7cd..bee631b67a 100644 --- a/core/src/ImageView.h +++ b/core/src/ImageView.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2019 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/LogMatrix.h b/core/src/LogMatrix.h index 478545d962..1d366b9ca1 100644 --- a/core/src/LogMatrix.h +++ b/core/src/LogMatrix.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BitMatrix.h" #include "Matrix.h" diff --git a/core/src/Matrix.h b/core/src/Matrix.h index 3709f22738..a85ccf42c1 100644 --- a/core/src/Matrix.h +++ b/core/src/Matrix.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Point.h" #include "ZXContainerAlgorithms.h" diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index 3b9a4b2d3f..563c0397a1 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "MultiFormatReader.h" diff --git a/core/src/MultiFormatReader.h b/core/src/MultiFormatReader.h index defafc4b1f..f418f229e7 100644 --- a/core/src/MultiFormatReader.h +++ b/core/src/MultiFormatReader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Result.h" diff --git a/core/src/MultiFormatWriter.cpp b/core/src/MultiFormatWriter.cpp index 1ed1b8ff21..464dbc8054 100644 --- a/core/src/MultiFormatWriter.cpp +++ b/core/src/MultiFormatWriter.cpp @@ -1,18 +1,7 @@ /* * Copyright 2017 Huy Cuong Nguyen -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "MultiFormatWriter.h" diff --git a/core/src/MultiFormatWriter.h b/core/src/MultiFormatWriter.h index 1ec252aa0f..88b1c61591 100644 --- a/core/src/MultiFormatWriter.h +++ b/core/src/MultiFormatWriter.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2017 Huy Cuong Nguyen -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BarcodeFormat.h" #include "CharacterSet.h" diff --git a/core/src/Pattern.h b/core/src/Pattern.h index c317be3e57..2226169d15 100644 --- a/core/src/Pattern.h +++ b/core/src/Pattern.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ZXContainerAlgorithms.h" diff --git a/core/src/PerspectiveTransform.cpp b/core/src/PerspectiveTransform.cpp index 4aba2f1fd7..f3d1aaa2e9 100644 --- a/core/src/PerspectiveTransform.cpp +++ b/core/src/PerspectiveTransform.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PerspectiveTransform.h" diff --git a/core/src/PerspectiveTransform.h b/core/src/PerspectiveTransform.h index b02ce66fe1..0e99c3c101 100644 --- a/core/src/PerspectiveTransform.h +++ b/core/src/PerspectiveTransform.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Point.h" #include "Quadrilateral.h" diff --git a/core/src/Point.h b/core/src/Point.h index 66b8e41f4e..19e1366099 100644 --- a/core/src/Point.h +++ b/core/src/Point.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/Quadrilateral.h b/core/src/Quadrilateral.h index ba2679d6bc..9d3cea0201 100644 --- a/core/src/Quadrilateral.h +++ b/core/src/Quadrilateral.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Point.h" #include "ZXContainerAlgorithms.h" diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index add15b2db7..4ffc2e8aab 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -1,18 +1,7 @@ /* * Copyright 2019 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ReadBarcode.h" diff --git a/core/src/ReadBarcode.h b/core/src/ReadBarcode.h index 3bbc48f7b4..a76d60c31b 100644 --- a/core/src/ReadBarcode.h +++ b/core/src/ReadBarcode.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2019 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "DecodeHints.h" #include "ImageView.h" diff --git a/core/src/Reader.h b/core/src/Reader.h index 642ac9c35f..99ea56d3ff 100644 --- a/core/src/Reader.h +++ b/core/src/Reader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Result.h" diff --git a/core/src/ReedSolomonDecoder.cpp b/core/src/ReedSolomonDecoder.cpp index ac38fc3f65..e521f4cb7e 100644 --- a/core/src/ReedSolomonDecoder.cpp +++ b/core/src/ReedSolomonDecoder.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2017 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ReedSolomonDecoder.h" diff --git a/core/src/ReedSolomonDecoder.h b/core/src/ReedSolomonDecoder.h index d6f001ffa0..7772adb9a8 100644 --- a/core/src/ReedSolomonDecoder.h +++ b/core/src/ReedSolomonDecoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/ReedSolomonEncoder.cpp b/core/src/ReedSolomonEncoder.cpp index 784d69cd5b..94741f1258 100644 --- a/core/src/ReedSolomonEncoder.cpp +++ b/core/src/ReedSolomonEncoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ReedSolomonEncoder.h" diff --git a/core/src/ReedSolomonEncoder.h b/core/src/ReedSolomonEncoder.h index 15c96e5bf1..ad1feee017 100644 --- a/core/src/ReedSolomonEncoder.h +++ b/core/src/ReedSolomonEncoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "GenericGFPoly.h" diff --git a/core/src/RegressionLine.h b/core/src/RegressionLine.h index a0ff880f80..be3dd0a813 100644 --- a/core/src/RegressionLine.h +++ b/core/src/RegressionLine.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Point.h" diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 2f0c4be75c..f69a8804db 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "Result.h" diff --git a/core/src/Result.h b/core/src/Result.h index 3f7ee54f09..be145a888c 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BarcodeFormat.h" #include "ByteArray.h" diff --git a/core/src/ResultPoint.cpp b/core/src/ResultPoint.cpp index 8ea41a47b3..134216aa01 100644 --- a/core/src/ResultPoint.cpp +++ b/core/src/ResultPoint.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ResultPoint.h" diff --git a/core/src/ResultPoint.h b/core/src/ResultPoint.h index 12cd7bda36..067b648f11 100644 --- a/core/src/ResultPoint.h +++ b/core/src/ResultPoint.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Point.h" diff --git a/core/src/Scope.h b/core/src/Scope.h index 1ba2297b0f..40506fa077 100644 --- a/core/src/Scope.h +++ b/core/src/Scope.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/StructuredAppend.h b/core/src/StructuredAppend.h index 6a778f91b3..105d5ebd65 100644 --- a/core/src/StructuredAppend.h +++ b/core/src/StructuredAppend.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2021 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/TextDecoder.cpp b/core/src/TextDecoder.cpp index 27063549bd..4df8a65f14 100644 --- a/core/src/TextDecoder.cpp +++ b/core/src/TextDecoder.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "TextDecoder.h" diff --git a/core/src/TextDecoder.h b/core/src/TextDecoder.h index 7a9118d8ae..ee35251867 100644 --- a/core/src/TextDecoder.h +++ b/core/src/TextDecoder.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/TextEncoder.cpp b/core/src/TextEncoder.cpp index e3553e4dfa..21af9ee744 100644 --- a/core/src/TextEncoder.cpp +++ b/core/src/TextEncoder.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "TextEncoder.h" diff --git a/core/src/TextEncoder.h b/core/src/TextEncoder.h index 83b7627cf6..d16972f104 100644 --- a/core/src/TextEncoder.h +++ b/core/src/TextEncoder.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/TextUtfEncoding.cpp b/core/src/TextUtfEncoding.cpp index b2e3e1e4e7..500a017975 100644 --- a/core/src/TextUtfEncoding.cpp +++ b/core/src/TextUtfEncoding.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "TextUtfEncoding.h" diff --git a/core/src/TextUtfEncoding.h b/core/src/TextUtfEncoding.h index ebbbca2e97..07ceca38df 100644 --- a/core/src/TextUtfEncoding.h +++ b/core/src/TextUtfEncoding.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/ThresholdBinarizer.h b/core/src/ThresholdBinarizer.h index 160ff5a960..620886a536 100644 --- a/core/src/ThresholdBinarizer.h +++ b/core/src/ThresholdBinarizer.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BinaryBitmap.h" #include "BitMatrix.h" diff --git a/core/src/TritMatrix.h b/core/src/TritMatrix.h index 584677ae30..8e70ff9fb1 100644 --- a/core/src/TritMatrix.h +++ b/core/src/TritMatrix.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Matrix.h" diff --git a/core/src/WhiteRectDetector.cpp b/core/src/WhiteRectDetector.cpp index 3741caaf15..5148e9cfdd 100644 --- a/core/src/WhiteRectDetector.cpp +++ b/core/src/WhiteRectDetector.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "WhiteRectDetector.h" diff --git a/core/src/WhiteRectDetector.h b/core/src/WhiteRectDetector.h index 3d1cf39625..834d5a6db6 100644 --- a/core/src/WhiteRectDetector.h +++ b/core/src/WhiteRectDetector.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/ZXBigInteger.cpp b/core/src/ZXBigInteger.cpp index 1bb9ee7b70..56e67300e9 100644 --- a/core/src/ZXBigInteger.cpp +++ b/core/src/ZXBigInteger.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Huy Cuong Nguyen -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ZXBigInteger.h" diff --git a/core/src/ZXBigInteger.h b/core/src/ZXBigInteger.h index e9dfab0056..92aa21b0f6 100644 --- a/core/src/ZXBigInteger.h +++ b/core/src/ZXBigInteger.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/ZXConfig.h b/core/src/ZXConfig.h index 434680caad..0a1e6a6ea1 100644 --- a/core/src/ZXConfig.h +++ b/core/src/ZXConfig.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #define ZX_HAVE_CONFIG diff --git a/core/src/ZXContainerAlgorithms.h b/core/src/ZXContainerAlgorithms.h index d6e9ad59a6..28a9b89a19 100644 --- a/core/src/ZXContainerAlgorithms.h +++ b/core/src/ZXContainerAlgorithms.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2017 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/ZXNullable.h b/core/src/ZXNullable.h index 05deb694fa..8cd6bd040f 100644 --- a/core/src/ZXNullable.h +++ b/core/src/ZXNullable.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index 1a122025fd..9027a677e1 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "AZDecoder.h" diff --git a/core/src/aztec/AZDecoder.h b/core/src/aztec/AZDecoder.h index d9eb6dd1fd..9db9f80a3e 100644 --- a/core/src/aztec/AZDecoder.h +++ b/core/src/aztec/AZDecoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/aztec/AZDetector.cpp b/core/src/aztec/AZDetector.cpp index d0dfc2e5c7..edfcff0295 100644 --- a/core/src/aztec/AZDetector.cpp +++ b/core/src/aztec/AZDetector.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "AZDetector.h" diff --git a/core/src/aztec/AZDetector.h b/core/src/aztec/AZDetector.h index b8307310e1..08a1edda7e 100644 --- a/core/src/aztec/AZDetector.h +++ b/core/src/aztec/AZDetector.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/aztec/AZDetectorResult.h b/core/src/aztec/AZDetectorResult.h index 478ccf764d..54c6c5524e 100644 --- a/core/src/aztec/AZDetectorResult.h +++ b/core/src/aztec/AZDetectorResult.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "DetectorResult.h" diff --git a/core/src/aztec/AZEncoder.cpp b/core/src/aztec/AZEncoder.cpp index 9bf45cfac9..f45eb000db 100644 --- a/core/src/aztec/AZEncoder.cpp +++ b/core/src/aztec/AZEncoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "AZEncoder.h" diff --git a/core/src/aztec/AZEncoder.h b/core/src/aztec/AZEncoder.h index dae27bc897..3c8d5351a3 100644 --- a/core/src/aztec/AZEncoder.h +++ b/core/src/aztec/AZEncoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BitMatrix.h" diff --git a/core/src/aztec/AZEncodingState.h b/core/src/aztec/AZEncodingState.h index 95c2a996fd..71ee8d3292 100644 --- a/core/src/aztec/AZEncodingState.h +++ b/core/src/aztec/AZEncodingState.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "AZToken.h" diff --git a/core/src/aztec/AZHighLevelEncoder.cpp b/core/src/aztec/AZHighLevelEncoder.cpp index d307e28229..0060050cd6 100644 --- a/core/src/aztec/AZHighLevelEncoder.cpp +++ b/core/src/aztec/AZHighLevelEncoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "AZHighLevelEncoder.h" diff --git a/core/src/aztec/AZHighLevelEncoder.h b/core/src/aztec/AZHighLevelEncoder.h index 62deba6a4c..5152a0938c 100644 --- a/core/src/aztec/AZHighLevelEncoder.h +++ b/core/src/aztec/AZHighLevelEncoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/aztec/AZReader.cpp b/core/src/aztec/AZReader.cpp index 4cd8157778..0f6547ae4d 100644 --- a/core/src/aztec/AZReader.cpp +++ b/core/src/aztec/AZReader.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "AZReader.h" diff --git a/core/src/aztec/AZReader.h b/core/src/aztec/AZReader.h index d21dc6d866..6997b57ad5 100644 --- a/core/src/aztec/AZReader.h +++ b/core/src/aztec/AZReader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Reader.h" diff --git a/core/src/aztec/AZToken.cpp b/core/src/aztec/AZToken.cpp index cb549fa1a9..3ea839ef02 100644 --- a/core/src/aztec/AZToken.cpp +++ b/core/src/aztec/AZToken.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "AZToken.h" diff --git a/core/src/aztec/AZToken.h b/core/src/aztec/AZToken.h index 85385bb052..24b2081c12 100644 --- a/core/src/aztec/AZToken.h +++ b/core/src/aztec/AZToken.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/aztec/AZWriter.cpp b/core/src/aztec/AZWriter.cpp index 8c29c5eab2..b41a15a62a 100644 --- a/core/src/aztec/AZWriter.cpp +++ b/core/src/aztec/AZWriter.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "AZWriter.h" diff --git a/core/src/aztec/AZWriter.h b/core/src/aztec/AZWriter.h index deecff0b4e..1a41f6267e 100644 --- a/core/src/aztec/AZWriter.h +++ b/core/src/aztec/AZWriter.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/datamatrix/DMBitLayout.cpp b/core/src/datamatrix/DMBitLayout.cpp index 99fc86ee85..1acffdb662 100644 --- a/core/src/datamatrix/DMBitLayout.cpp +++ b/core/src/datamatrix/DMBitLayout.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Huy Cuong Nguyen * Copyright 2006 Jeremias Maerki * Copyright 2020 Axel Waggersauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DMBitLayout.h" diff --git a/core/src/datamatrix/DMBitLayout.h b/core/src/datamatrix/DMBitLayout.h index d2b266dbe1..395ba23ffd 100644 --- a/core/src/datamatrix/DMBitLayout.h +++ b/core/src/datamatrix/DMBitLayout.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/datamatrix/DMDataBlock.cpp b/core/src/datamatrix/DMDataBlock.cpp index c5f96b79b7..241a160ac5 100644 --- a/core/src/datamatrix/DMDataBlock.cpp +++ b/core/src/datamatrix/DMDataBlock.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DMDataBlock.h" diff --git a/core/src/datamatrix/DMDataBlock.h b/core/src/datamatrix/DMDataBlock.h index ca13d83777..54dc7fade7 100644 --- a/core/src/datamatrix/DMDataBlock.h +++ b/core/src/datamatrix/DMDataBlock.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ByteArray.h" diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index c74be87345..871ca9422a 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DMDecoder.h" diff --git a/core/src/datamatrix/DMDecoder.h b/core/src/datamatrix/DMDecoder.h index cdd135cd96..b91fa0b47e 100644 --- a/core/src/datamatrix/DMDecoder.h +++ b/core/src/datamatrix/DMDecoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/datamatrix/DMDetector.cpp b/core/src/datamatrix/DMDetector.cpp index 10df1a6691..4a9b6eb57d 100644 --- a/core/src/datamatrix/DMDetector.cpp +++ b/core/src/datamatrix/DMDetector.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2017 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DMDetector.h" diff --git a/core/src/datamatrix/DMDetector.h b/core/src/datamatrix/DMDetector.h index 18d727a28b..e632b08681 100644 --- a/core/src/datamatrix/DMDetector.h +++ b/core/src/datamatrix/DMDetector.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/datamatrix/DMECEncoder.cpp b/core/src/datamatrix/DMECEncoder.cpp index bedf09442d..484a1d6e5a 100644 --- a/core/src/datamatrix/DMECEncoder.cpp +++ b/core/src/datamatrix/DMECEncoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2006 Jeremias Maerki. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DMECEncoder.h" diff --git a/core/src/datamatrix/DMECEncoder.h b/core/src/datamatrix/DMECEncoder.h index a06a298503..aea7d3bce4 100644 --- a/core/src/datamatrix/DMECEncoder.h +++ b/core/src/datamatrix/DMECEncoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2006 Jeremias Maerki. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/datamatrix/DMEncoderContext.h b/core/src/datamatrix/DMEncoderContext.h index 5f19b85727..ac5a81555f 100644 --- a/core/src/datamatrix/DMEncoderContext.h +++ b/core/src/datamatrix/DMEncoderContext.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2006-2007 Jeremias Maerki. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ByteArray.h" #include "DMSymbolInfo.h" diff --git a/core/src/datamatrix/DMHighLevelEncoder.cpp b/core/src/datamatrix/DMHighLevelEncoder.cpp index 627750909e..d030aa05be 100644 --- a/core/src/datamatrix/DMHighLevelEncoder.cpp +++ b/core/src/datamatrix/DMHighLevelEncoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DMHighLevelEncoder.h" diff --git a/core/src/datamatrix/DMHighLevelEncoder.h b/core/src/datamatrix/DMHighLevelEncoder.h index ae5f09abb5..1a17587300 100644 --- a/core/src/datamatrix/DMHighLevelEncoder.h +++ b/core/src/datamatrix/DMHighLevelEncoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2006-2007 Jeremias Maerki. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/datamatrix/DMReader.cpp b/core/src/datamatrix/DMReader.cpp index 5d6aad0a09..a2ac999a84 100644 --- a/core/src/datamatrix/DMReader.cpp +++ b/core/src/datamatrix/DMReader.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DMReader.h" diff --git a/core/src/datamatrix/DMReader.h b/core/src/datamatrix/DMReader.h index 8a3e0070db..571a9c0e46 100644 --- a/core/src/datamatrix/DMReader.h +++ b/core/src/datamatrix/DMReader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Reader.h" #include diff --git a/core/src/datamatrix/DMSymbolInfo.cpp b/core/src/datamatrix/DMSymbolInfo.cpp index 3aac399de0..0a3c0ce848 100644 --- a/core/src/datamatrix/DMSymbolInfo.cpp +++ b/core/src/datamatrix/DMSymbolInfo.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DMSymbolInfo.h" diff --git a/core/src/datamatrix/DMSymbolInfo.h b/core/src/datamatrix/DMSymbolInfo.h index e033fbfb4e..fa366da4a1 100644 --- a/core/src/datamatrix/DMSymbolInfo.h +++ b/core/src/datamatrix/DMSymbolInfo.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { namespace DataMatrix { diff --git a/core/src/datamatrix/DMSymbolShape.h b/core/src/datamatrix/DMSymbolShape.h index 383c1421ca..3c15f5d156 100644 --- a/core/src/datamatrix/DMSymbolShape.h +++ b/core/src/datamatrix/DMSymbolShape.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { namespace DataMatrix { diff --git a/core/src/datamatrix/DMVersion.cpp b/core/src/datamatrix/DMVersion.cpp index f119e82865..79426eeb41 100644 --- a/core/src/datamatrix/DMVersion.cpp +++ b/core/src/datamatrix/DMVersion.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DMVersion.h" diff --git a/core/src/datamatrix/DMVersion.h b/core/src/datamatrix/DMVersion.h index 87e22fa519..ad9837a78b 100644 --- a/core/src/datamatrix/DMVersion.h +++ b/core/src/datamatrix/DMVersion.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing::DataMatrix { diff --git a/core/src/datamatrix/DMWriter.cpp b/core/src/datamatrix/DMWriter.cpp index fe79e603cb..a95f5ad8a2 100644 --- a/core/src/datamatrix/DMWriter.cpp +++ b/core/src/datamatrix/DMWriter.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DMWriter.h" diff --git a/core/src/datamatrix/DMWriter.h b/core/src/datamatrix/DMWriter.h index e90e8bfda7..61b0caba2a 100644 --- a/core/src/datamatrix/DMWriter.h +++ b/core/src/datamatrix/DMWriter.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/maxicode/MCBitMatrixParser.cpp b/core/src/maxicode/MCBitMatrixParser.cpp index 895f2f7544..6b9ee368f7 100644 --- a/core/src/maxicode/MCBitMatrixParser.cpp +++ b/core/src/maxicode/MCBitMatrixParser.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "MCBitMatrixParser.h" diff --git a/core/src/maxicode/MCBitMatrixParser.h b/core/src/maxicode/MCBitMatrixParser.h index 5d405926f9..16c8867cdf 100644 --- a/core/src/maxicode/MCBitMatrixParser.h +++ b/core/src/maxicode/MCBitMatrixParser.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 922a8a477c..5bbeba0e95 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "MCDecoder.h" diff --git a/core/src/maxicode/MCDecoder.h b/core/src/maxicode/MCDecoder.h index cbc8bc6d0b..7a112c64ac 100644 --- a/core/src/maxicode/MCDecoder.h +++ b/core/src/maxicode/MCDecoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/maxicode/MCReader.cpp b/core/src/maxicode/MCReader.cpp index 36c5a53607..dd6f7cc0f3 100644 --- a/core/src/maxicode/MCReader.cpp +++ b/core/src/maxicode/MCReader.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "MCReader.h" diff --git a/core/src/maxicode/MCReader.h b/core/src/maxicode/MCReader.h index 5503c856b9..3dacd59f9c 100644 --- a/core/src/maxicode/MCReader.h +++ b/core/src/maxicode/MCReader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Reader.h" diff --git a/core/src/oned/ODCodabarReader.cpp b/core/src/oned/ODCodabarReader.cpp index 354f068511..53d8dec94b 100644 --- a/core/src/oned/ODCodabarReader.cpp +++ b/core/src/oned/ODCodabarReader.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODCodabarReader.h" diff --git a/core/src/oned/ODCodabarReader.h b/core/src/oned/ODCodabarReader.h index 69945c3d6d..52e9c7a8f8 100644 --- a/core/src/oned/ODCodabarReader.h +++ b/core/src/oned/ODCodabarReader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ODRowReader.h" diff --git a/core/src/oned/ODCodabarWriter.cpp b/core/src/oned/ODCodabarWriter.cpp index 48ad392b05..1d74711299 100644 --- a/core/src/oned/ODCodabarWriter.cpp +++ b/core/src/oned/ODCodabarWriter.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODCodabarWriter.h" diff --git a/core/src/oned/ODCodabarWriter.h b/core/src/oned/ODCodabarWriter.h index c4a3f960e8..7304d49efc 100644 --- a/core/src/oned/ODCodabarWriter.h +++ b/core/src/oned/ODCodabarWriter.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/ODCode128Patterns.cpp b/core/src/oned/ODCode128Patterns.cpp index 1e9a354057..4500398e1e 100644 --- a/core/src/oned/ODCode128Patterns.cpp +++ b/core/src/oned/ODCode128Patterns.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODCode128Patterns.h" diff --git a/core/src/oned/ODCode128Patterns.h b/core/src/oned/ODCode128Patterns.h index aeafd2e3cb..391e9be30d 100644 --- a/core/src/oned/ODCode128Patterns.h +++ b/core/src/oned/ODCode128Patterns.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/ODCode128Reader.cpp b/core/src/oned/ODCode128Reader.cpp index f730999853..5f986a04d4 100644 --- a/core/src/oned/ODCode128Reader.cpp +++ b/core/src/oned/ODCode128Reader.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODCode128Reader.h" diff --git a/core/src/oned/ODCode128Reader.h b/core/src/oned/ODCode128Reader.h index ebdc6bf19f..1a2a8db4d1 100644 --- a/core/src/oned/ODCode128Reader.h +++ b/core/src/oned/ODCode128Reader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ODRowReader.h" diff --git a/core/src/oned/ODCode128Writer.cpp b/core/src/oned/ODCode128Writer.cpp index 32133eac3b..2325376d5e 100644 --- a/core/src/oned/ODCode128Writer.cpp +++ b/core/src/oned/ODCode128Writer.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODCode128Writer.h" diff --git a/core/src/oned/ODCode128Writer.h b/core/src/oned/ODCode128Writer.h index 4efb1c52cd..5e517a66a6 100644 --- a/core/src/oned/ODCode128Writer.h +++ b/core/src/oned/ODCode128Writer.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/ODCode39Reader.cpp b/core/src/oned/ODCode39Reader.cpp index 58c1a70871..3952025a4f 100644 --- a/core/src/oned/ODCode39Reader.cpp +++ b/core/src/oned/ODCode39Reader.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODCode39Reader.h" diff --git a/core/src/oned/ODCode39Reader.h b/core/src/oned/ODCode39Reader.h index 5c5daef9f8..58b4de493a 100644 --- a/core/src/oned/ODCode39Reader.h +++ b/core/src/oned/ODCode39Reader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ODRowReader.h" diff --git a/core/src/oned/ODCode39Writer.cpp b/core/src/oned/ODCode39Writer.cpp index 919e59f250..e05faa661f 100644 --- a/core/src/oned/ODCode39Writer.cpp +++ b/core/src/oned/ODCode39Writer.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODCode39Writer.h" diff --git a/core/src/oned/ODCode39Writer.h b/core/src/oned/ODCode39Writer.h index 6d57e493d5..3342490301 100644 --- a/core/src/oned/ODCode39Writer.h +++ b/core/src/oned/ODCode39Writer.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/ODCode93Reader.cpp b/core/src/oned/ODCode93Reader.cpp index d9f3dd6859..484308aafc 100644 --- a/core/src/oned/ODCode93Reader.cpp +++ b/core/src/oned/ODCode93Reader.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODCode93Reader.h" diff --git a/core/src/oned/ODCode93Reader.h b/core/src/oned/ODCode93Reader.h index 95a385e0ad..86ea8ac593 100644 --- a/core/src/oned/ODCode93Reader.h +++ b/core/src/oned/ODCode93Reader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ODRowReader.h" diff --git a/core/src/oned/ODCode93Writer.cpp b/core/src/oned/ODCode93Writer.cpp index dfc42113aa..f84693ffce 100644 --- a/core/src/oned/ODCode93Writer.cpp +++ b/core/src/oned/ODCode93Writer.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODCode93Writer.h" diff --git a/core/src/oned/ODCode93Writer.h b/core/src/oned/ODCode93Writer.h index 1a6dd09e46..e33f4f512b 100644 --- a/core/src/oned/ODCode93Writer.h +++ b/core/src/oned/ODCode93Writer.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/ODDataBarCommon.cpp b/core/src/oned/ODDataBarCommon.cpp index aba8505311..57773f9dc6 100644 --- a/core/src/oned/ODDataBarCommon.cpp +++ b/core/src/oned/ODDataBarCommon.cpp @@ -1,18 +1,7 @@ /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODDataBarCommon.h" diff --git a/core/src/oned/ODDataBarCommon.h b/core/src/oned/ODDataBarCommon.h index 08669b208e..f1a53a3dcf 100644 --- a/core/src/oned/ODDataBarCommon.h +++ b/core/src/oned/ODDataBarCommon.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ODRowReader.h" #include "Pattern.h" diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index 9421a4b7be..95832f52de 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODDataBarExpandedReader.h" diff --git a/core/src/oned/ODDataBarExpandedReader.h b/core/src/oned/ODDataBarExpandedReader.h index a2e9a306d6..290482470a 100644 --- a/core/src/oned/ODDataBarExpandedReader.h +++ b/core/src/oned/ODDataBarExpandedReader.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "DecodeHints.h" #include "ODRowReader.h" diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index e45ae3e8c9..c516370f4a 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODDataBarReader.h" diff --git a/core/src/oned/ODDataBarReader.h b/core/src/oned/ODDataBarReader.h index 2da0109691..0e10cebc1d 100644 --- a/core/src/oned/ODDataBarReader.h +++ b/core/src/oned/ODDataBarReader.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "DecodeHints.h" #include "ODRowReader.h" diff --git a/core/src/oned/ODEAN13Writer.cpp b/core/src/oned/ODEAN13Writer.cpp index aa36f8e851..156ac51999 100644 --- a/core/src/oned/ODEAN13Writer.cpp +++ b/core/src/oned/ODEAN13Writer.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODEAN13Writer.h" diff --git a/core/src/oned/ODEAN13Writer.h b/core/src/oned/ODEAN13Writer.h index 8ba0c652f9..bfed737bc1 100644 --- a/core/src/oned/ODEAN13Writer.h +++ b/core/src/oned/ODEAN13Writer.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/ODEAN8Writer.cpp b/core/src/oned/ODEAN8Writer.cpp index 1533e94d4d..f659a1cb18 100644 --- a/core/src/oned/ODEAN8Writer.cpp +++ b/core/src/oned/ODEAN8Writer.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODEAN8Writer.h" diff --git a/core/src/oned/ODEAN8Writer.h b/core/src/oned/ODEAN8Writer.h index c1c1044173..2bbabd265a 100644 --- a/core/src/oned/ODEAN8Writer.h +++ b/core/src/oned/ODEAN8Writer.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index e314b99368..704cc7bfaa 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODITFReader.h" diff --git a/core/src/oned/ODITFReader.h b/core/src/oned/ODITFReader.h index 322ee17d20..7f494d76bf 100644 --- a/core/src/oned/ODITFReader.h +++ b/core/src/oned/ODITFReader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ODRowReader.h" diff --git a/core/src/oned/ODITFWriter.cpp b/core/src/oned/ODITFWriter.cpp index 34f49a12d6..27b8f58e58 100644 --- a/core/src/oned/ODITFWriter.cpp +++ b/core/src/oned/ODITFWriter.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODITFWriter.h" diff --git a/core/src/oned/ODITFWriter.h b/core/src/oned/ODITFWriter.h index fcf699d4e2..de837dd3a5 100644 --- a/core/src/oned/ODITFWriter.h +++ b/core/src/oned/ODITFWriter.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index 06218ac1e1..cfa806ea17 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODMultiUPCEANReader.h" diff --git a/core/src/oned/ODMultiUPCEANReader.h b/core/src/oned/ODMultiUPCEANReader.h index 7385001210..999c542290 100644 --- a/core/src/oned/ODMultiUPCEANReader.h +++ b/core/src/oned/ODMultiUPCEANReader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BarcodeFormat.h" #include "DecodeHints.h" diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index d84d1d0e65..59d61ad4e1 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODReader.h" diff --git a/core/src/oned/ODReader.h b/core/src/oned/ODReader.h index e07d8d4e57..bccd417c2e 100644 --- a/core/src/oned/ODReader.h +++ b/core/src/oned/ODReader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Reader.h" diff --git a/core/src/oned/ODRowReader.cpp b/core/src/oned/ODRowReader.cpp index de0febc20e..ef5bb3458f 100644 --- a/core/src/oned/ODRowReader.cpp +++ b/core/src/oned/ODRowReader.cpp @@ -1,18 +1,7 @@ /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODRowReader.h" diff --git a/core/src/oned/ODRowReader.h b/core/src/oned/ODRowReader.h index ec6bc2882e..9bf55fc9d2 100644 --- a/core/src/oned/ODRowReader.h +++ b/core/src/oned/ODRowReader.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BitArray.h" #include "DecodeStatus.h" diff --git a/core/src/oned/ODUPCAWriter.cpp b/core/src/oned/ODUPCAWriter.cpp index c5eedd925c..0a789b6e95 100644 --- a/core/src/oned/ODUPCAWriter.cpp +++ b/core/src/oned/ODUPCAWriter.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODUPCAWriter.h" diff --git a/core/src/oned/ODUPCAWriter.h b/core/src/oned/ODUPCAWriter.h index 8d6bfb799c..467fbf0682 100644 --- a/core/src/oned/ODUPCAWriter.h +++ b/core/src/oned/ODUPCAWriter.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/ODUPCEANCommon.cpp b/core/src/oned/ODUPCEANCommon.cpp index 997249baf1..7a9b4a8daa 100644 --- a/core/src/oned/ODUPCEANCommon.cpp +++ b/core/src/oned/ODUPCEANCommon.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODUPCEANCommon.h" diff --git a/core/src/oned/ODUPCEANCommon.h b/core/src/oned/ODUPCEANCommon.h index f2b8113f31..3c0eb59ac3 100644 --- a/core/src/oned/ODUPCEANCommon.h +++ b/core/src/oned/ODUPCEANCommon.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "GTIN.h" diff --git a/core/src/oned/ODUPCEWriter.cpp b/core/src/oned/ODUPCEWriter.cpp index b76b990080..5adf99cc4f 100644 --- a/core/src/oned/ODUPCEWriter.cpp +++ b/core/src/oned/ODUPCEWriter.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODUPCEWriter.h" diff --git a/core/src/oned/ODUPCEWriter.h b/core/src/oned/ODUPCEWriter.h index b7f6177bfd..6e48975970 100644 --- a/core/src/oned/ODUPCEWriter.h +++ b/core/src/oned/ODUPCEWriter.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/ODWriterHelper.cpp b/core/src/oned/ODWriterHelper.cpp index dc99babea5..e0f0a33bf8 100644 --- a/core/src/oned/ODWriterHelper.cpp +++ b/core/src/oned/ODWriterHelper.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODWriterHelper.h" diff --git a/core/src/oned/ODWriterHelper.h b/core/src/oned/ODWriterHelper.h index 63a2cbb060..f50f644df1 100644 --- a/core/src/oned/ODWriterHelper.h +++ b/core/src/oned/ODWriterHelper.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BitMatrix.h" diff --git a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp index 315a41b903..3a669a049f 100644 --- a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp +++ b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 /* * These authors would like to acknowledge the Spanish Ministry of Industry, diff --git a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.h b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.h index 71900b66fe..2c708b4354 100644 --- a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.h +++ b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/rss/ODRSSFieldParser.cpp b/core/src/oned/rss/ODRSSFieldParser.cpp index ef8552b847..6551cf1acf 100644 --- a/core/src/oned/rss/ODRSSFieldParser.cpp +++ b/core/src/oned/rss/ODRSSFieldParser.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODRSSFieldParser.h" diff --git a/core/src/oned/rss/ODRSSFieldParser.h b/core/src/oned/rss/ODRSSFieldParser.h index 16a37abd62..0d24839429 100644 --- a/core/src/oned/rss/ODRSSFieldParser.h +++ b/core/src/oned/rss/ODRSSFieldParser.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp b/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp index 7e88ec0b9b..5064abfd71 100644 --- a/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp +++ b/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ODRSSGenericAppIdDecoder.h" diff --git a/core/src/oned/rss/ODRSSGenericAppIdDecoder.h b/core/src/oned/rss/ODRSSGenericAppIdDecoder.h index e93ccd2d2b..b6fe69485c 100644 --- a/core/src/oned/rss/ODRSSGenericAppIdDecoder.h +++ b/core/src/oned/rss/ODRSSGenericAppIdDecoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/pdf417/PDFBarcodeMetadata.h b/core/src/pdf417/PDFBarcodeMetadata.h index 33a3785722..335a7eb6f2 100644 --- a/core/src/pdf417/PDFBarcodeMetadata.h +++ b/core/src/pdf417/PDFBarcodeMetadata.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { namespace Pdf417 { diff --git a/core/src/pdf417/PDFBarcodeValue.cpp b/core/src/pdf417/PDFBarcodeValue.cpp index 2d02d0ff2f..da608efb7d 100644 --- a/core/src/pdf417/PDFBarcodeValue.cpp +++ b/core/src/pdf417/PDFBarcodeValue.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFBarcodeValue.h" diff --git a/core/src/pdf417/PDFBarcodeValue.h b/core/src/pdf417/PDFBarcodeValue.h index d7d8ca7e92..c15c0f365c 100644 --- a/core/src/pdf417/PDFBarcodeValue.h +++ b/core/src/pdf417/PDFBarcodeValue.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/pdf417/PDFBoundingBox.cpp b/core/src/pdf417/PDFBoundingBox.cpp index fb07c74df2..bd9ea35cc8 100644 --- a/core/src/pdf417/PDFBoundingBox.cpp +++ b/core/src/pdf417/PDFBoundingBox.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFBoundingBox.h" diff --git a/core/src/pdf417/PDFBoundingBox.h b/core/src/pdf417/PDFBoundingBox.h index a09eab4dd4..fe2f7344e5 100644 --- a/core/src/pdf417/PDFBoundingBox.h +++ b/core/src/pdf417/PDFBoundingBox.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ZXNullable.h" #include "ResultPoint.h" diff --git a/core/src/pdf417/PDFCodeword.h b/core/src/pdf417/PDFCodeword.h index 4a2d68de16..bc351e0c9c 100644 --- a/core/src/pdf417/PDFCodeword.h +++ b/core/src/pdf417/PDFCodeword.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { namespace Pdf417 { diff --git a/core/src/pdf417/PDFCodewordDecoder.cpp b/core/src/pdf417/PDFCodewordDecoder.cpp index f3f17bba53..9318156ad2 100644 --- a/core/src/pdf417/PDFCodewordDecoder.cpp +++ b/core/src/pdf417/PDFCodewordDecoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFCodewordDecoder.h" #include "BitArray.h" diff --git a/core/src/pdf417/PDFCodewordDecoder.h b/core/src/pdf417/PDFCodewordDecoder.h index 5b8dc0a0ec..cb661ff10b 100644 --- a/core/src/pdf417/PDFCodewordDecoder.h +++ b/core/src/pdf417/PDFCodewordDecoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/pdf417/PDFCompaction.h b/core/src/pdf417/PDFCompaction.h index ddaa0f3bee..b8ac5d7b02 100644 --- a/core/src/pdf417/PDFCompaction.h +++ b/core/src/pdf417/PDFCompaction.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { namespace Pdf417 { diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index af57d17999..1d0620ce23 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFDecodedBitStreamParser.h" diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.h b/core/src/pdf417/PDFDecodedBitStreamParser.h index 696d118e13..39b03fed41 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.h +++ b/core/src/pdf417/PDFDecodedBitStreamParser.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/pdf417/PDFDecoderResultExtra.h b/core/src/pdf417/PDFDecoderResultExtra.h index 01284bd2c4..8aaf60a253 100644 --- a/core/src/pdf417/PDFDecoderResultExtra.h +++ b/core/src/pdf417/PDFDecoderResultExtra.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "CustomData.h" diff --git a/core/src/pdf417/PDFDetectionResult.cpp b/core/src/pdf417/PDFDetectionResult.cpp index 07d31aa14f..c145a1be2e 100644 --- a/core/src/pdf417/PDFDetectionResult.cpp +++ b/core/src/pdf417/PDFDetectionResult.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFDetectionResult.h" #include "PDFCodewordDecoder.h" diff --git a/core/src/pdf417/PDFDetectionResult.h b/core/src/pdf417/PDFDetectionResult.h index 97fb116aa6..b5dedc64d9 100644 --- a/core/src/pdf417/PDFDetectionResult.h +++ b/core/src/pdf417/PDFDetectionResult.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "PDFBarcodeMetadata.h" #include "PDFBoundingBox.h" diff --git a/core/src/pdf417/PDFDetectionResultColumn.cpp b/core/src/pdf417/PDFDetectionResultColumn.cpp index 18131bcda7..8c85065889 100644 --- a/core/src/pdf417/PDFDetectionResultColumn.cpp +++ b/core/src/pdf417/PDFDetectionResultColumn.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFDetectionResultColumn.h" #include "PDFBarcodeMetadata.h" diff --git a/core/src/pdf417/PDFDetectionResultColumn.h b/core/src/pdf417/PDFDetectionResultColumn.h index 732d8724c4..0df6d7fc73 100644 --- a/core/src/pdf417/PDFDetectionResultColumn.h +++ b/core/src/pdf417/PDFDetectionResultColumn.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "PDFBoundingBox.h" #include "PDFCodeword.h" diff --git a/core/src/pdf417/PDFDetector.cpp b/core/src/pdf417/PDFDetector.cpp index 5ddf915927..3d29fc59fb 100644 --- a/core/src/pdf417/PDFDetector.cpp +++ b/core/src/pdf417/PDFDetector.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFDetector.h" #include "BinaryBitmap.h" diff --git a/core/src/pdf417/PDFDetector.h b/core/src/pdf417/PDFDetector.h index ce8fbff464..e129d6d2c0 100644 --- a/core/src/pdf417/PDFDetector.h +++ b/core/src/pdf417/PDFDetector.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ResultPoint.h" #include "ZXNullable.h" diff --git a/core/src/pdf417/PDFEncoder.cpp b/core/src/pdf417/PDFEncoder.cpp index c38a35ce2f..e53daeea7c 100644 --- a/core/src/pdf417/PDFEncoder.cpp +++ b/core/src/pdf417/PDFEncoder.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors * Copyright 2006 Jeremias Maerki -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFEncoder.h" #include "PDFHighLevelEncoder.h" diff --git a/core/src/pdf417/PDFEncoder.h b/core/src/pdf417/PDFEncoder.h index 459a8634c0..f58d1279ef 100644 --- a/core/src/pdf417/PDFEncoder.h +++ b/core/src/pdf417/PDFEncoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "CharacterSet.h" #include "PDFCompaction.h" #include "ZXContainerAlgorithms.h" diff --git a/core/src/pdf417/PDFHighLevelEncoder.cpp b/core/src/pdf417/PDFHighLevelEncoder.cpp index 2066d78c0b..c09a8fb536 100644 --- a/core/src/pdf417/PDFHighLevelEncoder.cpp +++ b/core/src/pdf417/PDFHighLevelEncoder.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors * Copyright 2006 Jeremias Maerki in part, and ZXing Authors in part -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFHighLevelEncoder.h" #include "PDFCompaction.h" diff --git a/core/src/pdf417/PDFHighLevelEncoder.h b/core/src/pdf417/PDFHighLevelEncoder.h index 0df789e0be..e0bbe89d28 100644 --- a/core/src/pdf417/PDFHighLevelEncoder.h +++ b/core/src/pdf417/PDFHighLevelEncoder.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors * Copyright 2006 Jeremias Maerki -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/pdf417/PDFModulusGF.cpp b/core/src/pdf417/PDFModulusGF.cpp index eebc090947..3859c1c7e4 100644 --- a/core/src/pdf417/PDFModulusGF.cpp +++ b/core/src/pdf417/PDFModulusGF.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFModulusGF.h" #include diff --git a/core/src/pdf417/PDFModulusGF.h b/core/src/pdf417/PDFModulusGF.h index 12095c9491..3ee80bc050 100644 --- a/core/src/pdf417/PDFModulusGF.h +++ b/core/src/pdf417/PDFModulusGF.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "PDFModulusPoly.h" #include "ZXConfig.h" diff --git a/core/src/pdf417/PDFModulusPoly.cpp b/core/src/pdf417/PDFModulusPoly.cpp index bfd414cb36..9883775f77 100644 --- a/core/src/pdf417/PDFModulusPoly.cpp +++ b/core/src/pdf417/PDFModulusPoly.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFModulusPoly.h" #include "PDFModulusGF.h" diff --git a/core/src/pdf417/PDFModulusPoly.h b/core/src/pdf417/PDFModulusPoly.h index ba33405f74..ed44abe7d3 100644 --- a/core/src/pdf417/PDFModulusPoly.h +++ b/core/src/pdf417/PDFModulusPoly.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ZXContainerAlgorithms.h" diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index dadc456117..7d179b37a7 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFReader.h" #include "PDFDetector.h" diff --git a/core/src/pdf417/PDFReader.h b/core/src/pdf417/PDFReader.h index 138d31ef1e..435a50f252 100644 --- a/core/src/pdf417/PDFReader.h +++ b/core/src/pdf417/PDFReader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Reader.h" diff --git a/core/src/pdf417/PDFScanningDecoder.cpp b/core/src/pdf417/PDFScanningDecoder.cpp index c95f80a1dc..4d4217f31a 100644 --- a/core/src/pdf417/PDFScanningDecoder.cpp +++ b/core/src/pdf417/PDFScanningDecoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFScanningDecoder.h" #include "PDFBoundingBox.h" diff --git a/core/src/pdf417/PDFScanningDecoder.h b/core/src/pdf417/PDFScanningDecoder.h index 7ff7248121..08d795649c 100644 --- a/core/src/pdf417/PDFScanningDecoder.h +++ b/core/src/pdf417/PDFScanningDecoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/pdf417/PDFWriter.cpp b/core/src/pdf417/PDFWriter.cpp index 60d8206bea..19543119d6 100644 --- a/core/src/pdf417/PDFWriter.cpp +++ b/core/src/pdf417/PDFWriter.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PDFWriter.h" #include "PDFEncoder.h" diff --git a/core/src/pdf417/PDFWriter.h b/core/src/pdf417/PDFWriter.h index dad8e360aa..a9e01c943b 100644 --- a/core/src/pdf417/PDFWriter.h +++ b/core/src/pdf417/PDFWriter.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/core/src/qrcode/QRBitMatrixParser.cpp b/core/src/qrcode/QRBitMatrixParser.cpp index 97d484dbdd..ed614fe9a3 100644 --- a/core/src/qrcode/QRBitMatrixParser.cpp +++ b/core/src/qrcode/QRBitMatrixParser.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRBitMatrixParser.h" diff --git a/core/src/qrcode/QRBitMatrixParser.h b/core/src/qrcode/QRBitMatrixParser.h index b8aa567adf..cabb260210 100644 --- a/core/src/qrcode/QRBitMatrixParser.h +++ b/core/src/qrcode/QRBitMatrixParser.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { diff --git a/core/src/qrcode/QRCodecMode.cpp b/core/src/qrcode/QRCodecMode.cpp index f0e635128a..4937b1c858 100644 --- a/core/src/qrcode/QRCodecMode.cpp +++ b/core/src/qrcode/QRCodecMode.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRCodecMode.h" diff --git a/core/src/qrcode/QRCodecMode.h b/core/src/qrcode/QRCodecMode.h index eca59e12ca..f55bc677f6 100644 --- a/core/src/qrcode/QRCodecMode.h +++ b/core/src/qrcode/QRCodecMode.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { namespace QRCode { diff --git a/core/src/qrcode/QRDataBlock.cpp b/core/src/qrcode/QRDataBlock.cpp index 2b005aa48f..0c63a52ef5 100644 --- a/core/src/qrcode/QRDataBlock.cpp +++ b/core/src/qrcode/QRDataBlock.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRDataBlock.h" diff --git a/core/src/qrcode/QRDataBlock.h b/core/src/qrcode/QRDataBlock.h index e63c326dac..d5f9858785 100644 --- a/core/src/qrcode/QRDataBlock.h +++ b/core/src/qrcode/QRDataBlock.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ByteArray.h" diff --git a/core/src/qrcode/QRDataMask.h b/core/src/qrcode/QRDataMask.h index 8b43b9ab86..b735baf9ec 100644 --- a/core/src/qrcode/QRDataMask.h +++ b/core/src/qrcode/QRDataMask.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BitMatrix.h" diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 5065401fda..31ed2bbbec 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRDecoder.h" diff --git a/core/src/qrcode/QRDecoder.h b/core/src/qrcode/QRDecoder.h index 2f10bddc59..32194d8899 100644 --- a/core/src/qrcode/QRDecoder.h +++ b/core/src/qrcode/QRDecoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index bf218c54d1..9c189bb3fe 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRDetector.h" diff --git a/core/src/qrcode/QRDetector.h b/core/src/qrcode/QRDetector.h index bfb9bc2591..77b5914615 100644 --- a/core/src/qrcode/QRDetector.h +++ b/core/src/qrcode/QRDetector.h @@ -1,21 +1,11 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2021 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ConcentricFinder.h" #include "DetectorResult.h" diff --git a/core/src/qrcode/QRECB.h b/core/src/qrcode/QRECB.h index 689d44f6f2..37cefea6df 100644 --- a/core/src/qrcode/QRECB.h +++ b/core/src/qrcode/QRECB.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/qrcode/QREncodeResult.h b/core/src/qrcode/QREncodeResult.h index 366a475215..8898c04dc3 100644 --- a/core/src/qrcode/QREncodeResult.h +++ b/core/src/qrcode/QREncodeResult.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BitMatrix.h" #include "ByteArray.h" diff --git a/core/src/qrcode/QREncoder.cpp b/core/src/qrcode/QREncoder.cpp index 1601623196..db3c90f6fb 100644 --- a/core/src/qrcode/QREncoder.cpp +++ b/core/src/qrcode/QREncoder.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QREncoder.h" diff --git a/core/src/qrcode/QREncoder.h b/core/src/qrcode/QREncoder.h index 95420dee0a..b8e364806a 100644 --- a/core/src/qrcode/QREncoder.h +++ b/core/src/qrcode/QREncoder.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/core/src/qrcode/QRErrorCorrectionLevel.cpp b/core/src/qrcode/QRErrorCorrectionLevel.cpp index 2a3b93ff15..6194e5c7b5 100644 --- a/core/src/qrcode/QRErrorCorrectionLevel.cpp +++ b/core/src/qrcode/QRErrorCorrectionLevel.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRErrorCorrectionLevel.h" diff --git a/core/src/qrcode/QRErrorCorrectionLevel.h b/core/src/qrcode/QRErrorCorrectionLevel.h index 1993fffdda..58d689240f 100644 --- a/core/src/qrcode/QRErrorCorrectionLevel.h +++ b/core/src/qrcode/QRErrorCorrectionLevel.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { namespace QRCode { diff --git a/core/src/qrcode/QRFormatInformation.cpp b/core/src/qrcode/QRFormatInformation.cpp index f5bae8ed83..44db397c06 100644 --- a/core/src/qrcode/QRFormatInformation.cpp +++ b/core/src/qrcode/QRFormatInformation.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRFormatInformation.h" diff --git a/core/src/qrcode/QRFormatInformation.h b/core/src/qrcode/QRFormatInformation.h index a8680b8023..e83775b0fc 100644 --- a/core/src/qrcode/QRFormatInformation.h +++ b/core/src/qrcode/QRFormatInformation.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "QRErrorCorrectionLevel.h" diff --git a/core/src/qrcode/QRMaskUtil.cpp b/core/src/qrcode/QRMaskUtil.cpp index e94236377a..0213730b2d 100644 --- a/core/src/qrcode/QRMaskUtil.cpp +++ b/core/src/qrcode/QRMaskUtil.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRMaskUtil.h" diff --git a/core/src/qrcode/QRMaskUtil.h b/core/src/qrcode/QRMaskUtil.h index 67d4da08f0..f7a0c06ddd 100644 --- a/core/src/qrcode/QRMaskUtil.h +++ b/core/src/qrcode/QRMaskUtil.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "TritMatrix.h" diff --git a/core/src/qrcode/QRMatrixUtil.cpp b/core/src/qrcode/QRMatrixUtil.cpp index 825cd40ba2..4767eebc23 100644 --- a/core/src/qrcode/QRMatrixUtil.cpp +++ b/core/src/qrcode/QRMatrixUtil.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRMatrixUtil.h" diff --git a/core/src/qrcode/QRMatrixUtil.h b/core/src/qrcode/QRMatrixUtil.h index 4ca09a33d6..11f922865f 100644 --- a/core/src/qrcode/QRMatrixUtil.h +++ b/core/src/qrcode/QRMatrixUtil.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "TritMatrix.h" diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index 505ab6ab0d..60007c87a4 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -2,19 +2,8 @@ * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors * Copyright 2022 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRReader.h" diff --git a/core/src/qrcode/QRReader.h b/core/src/qrcode/QRReader.h index e93a05afad..d7e96f935f 100644 --- a/core/src/qrcode/QRReader.h +++ b/core/src/qrcode/QRReader.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "Reader.h" diff --git a/core/src/qrcode/QRVersion.cpp b/core/src/qrcode/QRVersion.cpp index 0833b19340..537bc91fc7 100644 --- a/core/src/qrcode/QRVersion.cpp +++ b/core/src/qrcode/QRVersion.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRVersion.h" diff --git a/core/src/qrcode/QRVersion.h b/core/src/qrcode/QRVersion.h index 393bcbc0e2..024f1a7387 100644 --- a/core/src/qrcode/QRVersion.h +++ b/core/src/qrcode/QRVersion.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "QRECB.h" #include "QRErrorCorrectionLevel.h" diff --git a/core/src/qrcode/QRWriter.cpp b/core/src/qrcode/QRWriter.cpp index 52d6f065da..63a161150e 100644 --- a/core/src/qrcode/QRWriter.cpp +++ b/core/src/qrcode/QRWriter.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "QRWriter.h" diff --git a/core/src/qrcode/QRWriter.h b/core/src/qrcode/QRWriter.h index c5a12aaae3..5aef3b70b8 100644 --- a/core/src/qrcode/QRWriter.h +++ b/core/src/qrcode/QRWriter.h @@ -1,20 +1,10 @@ -#pragma once /* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include diff --git a/example/ZXingOpenCV.cpp b/example/ZXingOpenCV.cpp index 7507068ba7..d4c3e4c06f 100644 --- a/example/ZXingOpenCV.cpp +++ b/example/ZXingOpenCV.cpp @@ -1,18 +1,7 @@ /* * Copyright 2021 Axel Waggershauser - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +*/ +// SPDX-License-Identifier: Apache-2.0 #include "ZXingOpenCV.h" diff --git a/example/ZXingQtCamReader.cpp b/example/ZXingQtCamReader.cpp index db11009b25..102312e0e6 100644 --- a/example/ZXingQtCamReader.cpp +++ b/example/ZXingQtCamReader.cpp @@ -1,18 +1,7 @@ /* * Copyright 2020 Axel Waggershauser - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +*/ +// SPDX-License-Identifier: Apache-2.0 #include #include diff --git a/example/ZXingQtReader.cpp b/example/ZXingQtReader.cpp index dedd621780..2fa90d1a8e 100644 --- a/example/ZXingQtReader.cpp +++ b/example/ZXingQtReader.cpp @@ -1,18 +1,7 @@ /* * Copyright 2020 Axel Waggershauser - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +*/ +// SPDX-License-Identifier: Apache-2.0 #include "ZXingQtReader.h" diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 84dcff4eb6..b3c9958ff8 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ReadBarcode.h" #include "TextUtfEncoding.h" diff --git a/example/ZXingWriter.cpp b/example/ZXingWriter.cpp index 2cec15e0b1..14934d62bf 100644 --- a/example/ZXingWriter.cpp +++ b/example/ZXingWriter.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BarcodeFormat.h" #include "BitMatrix.h" diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index ac66da3146..f03a369b48 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2019 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BlackboxTestRunner.h" diff --git a/test/blackbox/BlackboxTestRunner.h b/test/blackbox/BlackboxTestRunner.h index e1229a4508..a4527b100f 100644 --- a/test/blackbox/BlackboxTestRunner.h +++ b/test/blackbox/BlackboxTestRunner.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ZXFilesystem.h" diff --git a/test/blackbox/ImageLoader.cpp b/test/blackbox/ImageLoader.cpp index 51e0066886..69d1d7c303 100644 --- a/test/blackbox/ImageLoader.cpp +++ b/test/blackbox/ImageLoader.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2019 Axel Waggersauser. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ImageLoader.h" diff --git a/test/blackbox/ImageLoader.h b/test/blackbox/ImageLoader.h index ff1497b690..29469ce989 100644 --- a/test/blackbox/ImageLoader.h +++ b/test/blackbox/ImageLoader.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ReadBarcode.h" #include "ZXFilesystem.h" diff --git a/test/blackbox/TestReaderMain.cpp b/test/blackbox/TestReaderMain.cpp index cabc70267f..4aad4905cc 100644 --- a/test/blackbox/TestReaderMain.cpp +++ b/test/blackbox/TestReaderMain.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2017 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BlackboxTestRunner.h" #include "ImageLoader.h" diff --git a/test/blackbox/TestWriterMain.cpp b/test/blackbox/TestWriterMain.cpp index 8dfc63e841..cb8e728198 100644 --- a/test/blackbox/TestWriterMain.cpp +++ b/test/blackbox/TestWriterMain.cpp @@ -1,19 +1,8 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2019 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitMatrix.h" #include "MultiFormatWriter.h" diff --git a/test/blackbox/ZXFilesystem.h b/test/blackbox/ZXFilesystem.h index add9236e41..45f08d3455 100644 --- a/test/blackbox/ZXFilesystem.h +++ b/test/blackbox/ZXFilesystem.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2019 Axel Waggershauser. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #if __has_include() # include diff --git a/test/unit/BarcodeFormatTest.cpp b/test/unit/BarcodeFormatTest.cpp index 425ce40066..7dfcbf3ab8 100644 --- a/test/unit/BarcodeFormatTest.cpp +++ b/test/unit/BarcodeFormatTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2020 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BarcodeFormat.h" diff --git a/test/unit/BitArrayUtility.cpp b/test/unit/BitArrayUtility.cpp index a5d67f6586..e6ab50deb0 100644 --- a/test/unit/BitArrayUtility.cpp +++ b/test/unit/BitArrayUtility.cpp @@ -1,18 +1,7 @@ /* * Copyright 2017 Huy Cuong Nguyen -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitArrayUtility.h" #include "BitArray.h" diff --git a/test/unit/BitHacksTest.cpp b/test/unit/BitHacksTest.cpp index 554f55f8e7..8edfb96252 100644 --- a/test/unit/BitHacksTest.cpp +++ b/test/unit/BitHacksTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2018 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitHacks.h" diff --git a/test/unit/CharacterSetECITest.cpp b/test/unit/CharacterSetECITest.cpp index a5ced0a1ee..2542fcd7c2 100644 --- a/test/unit/CharacterSetECITest.cpp +++ b/test/unit/CharacterSetECITest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2021 gitlost -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "CharacterSetECI.h" diff --git a/test/unit/GTINTest.cpp b/test/unit/GTINTest.cpp index ddaf02db2b..10f89909e9 100644 --- a/test/unit/GTINTest.cpp +++ b/test/unit/GTINTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2022 gitlost -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "GTIN.h" diff --git a/test/unit/ReedSolomonTest.cpp b/test/unit/ReedSolomonTest.cpp index 9be8b6fca4..870c33b268 100644 --- a/test/unit/ReedSolomonTest.cpp +++ b/test/unit/ReedSolomonTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2013 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "GenericGF.h" #include "PseudoRandom.h" diff --git a/test/unit/TextDecoderTest.cpp b/test/unit/TextDecoderTest.cpp index b782df3638..6d3ec55d09 100644 --- a/test/unit/TextDecoderTest.cpp +++ b/test/unit/TextDecoderTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2021 gitlost -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "CharacterSet.h" #include "TextDecoder.h" diff --git a/test/unit/TextUtfEncodingTest.cpp b/test/unit/TextUtfEncodingTest.cpp index e7c2deaadb..2c6c0773a5 100644 --- a/test/unit/TextUtfEncodingTest.cpp +++ b/test/unit/TextUtfEncodingTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2021 gitlost -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "TextUtfEncoding.h" diff --git a/test/unit/ThresholdBinarizerTest.cpp b/test/unit/ThresholdBinarizerTest.cpp index 0a9e54f189..ae6449f8dd 100644 --- a/test/unit/ThresholdBinarizerTest.cpp +++ b/test/unit/ThresholdBinarizerTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2022 gitlost -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ThresholdBinarizer.h" #include "oned/ODReader.h" diff --git a/test/unit/aztec/AZDecoderTest.cpp b/test/unit/aztec/AZDecoderTest.cpp index 242348897c..56c323a6a6 100644 --- a/test/unit/aztec/AZDecoderTest.cpp +++ b/test/unit/aztec/AZDecoderTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2014 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "aztec/AZDecoder.h" #include "BitArray.h" diff --git a/test/unit/aztec/AZDetectorTest.cpp b/test/unit/aztec/AZDetectorTest.cpp index 42a9885ee9..fd7a4f1191 100644 --- a/test/unit/aztec/AZDetectorTest.cpp +++ b/test/unit/aztec/AZDetectorTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2013 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "aztec/AZDetector.h" diff --git a/test/unit/aztec/AZEncodeDecodeTest.cpp b/test/unit/aztec/AZEncodeDecodeTest.cpp index d09f0bd6f9..d968371719 100644 --- a/test/unit/aztec/AZEncodeDecodeTest.cpp +++ b/test/unit/aztec/AZEncodeDecodeTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2013 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitMatrix.h" #include "CharacterSet.h" diff --git a/test/unit/aztec/AZEncoderTest.cpp b/test/unit/aztec/AZEncoderTest.cpp index 7fa4b69d53..045f15e3f3 100644 --- a/test/unit/aztec/AZEncoderTest.cpp +++ b/test/unit/aztec/AZEncoderTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2013 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "aztec/AZEncoder.h" #include "BitArray.h" diff --git a/test/unit/aztec/AZHighLevelEncoderTest.cpp b/test/unit/aztec/AZHighLevelEncoderTest.cpp index 2b55de86a9..7643f1e9b9 100644 --- a/test/unit/aztec/AZHighLevelEncoderTest.cpp +++ b/test/unit/aztec/AZHighLevelEncoderTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2013 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "aztec/AZHighLevelEncoder.h" #include "BitArray.h" diff --git a/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp b/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp index ad3db44716..13d7f99cd0 100644 --- a/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp +++ b/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2008 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ByteArray.h" #include "DecoderResult.h" diff --git a/test/unit/datamatrix/DMEncodeDecodeTest.cpp b/test/unit/datamatrix/DMEncodeDecodeTest.cpp index bb98ef01e4..10a4f7b98d 100644 --- a/test/unit/datamatrix/DMEncodeDecodeTest.cpp +++ b/test/unit/datamatrix/DMEncodeDecodeTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Axel Waggershauser * Copyright 2013 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitMatrixIO.h" #include "DecoderResult.h" diff --git a/test/unit/datamatrix/DMHighLevelEncodeTest.cpp b/test/unit/datamatrix/DMHighLevelEncodeTest.cpp index 05e368e01e..3c0d437e53 100644 --- a/test/unit/datamatrix/DMHighLevelEncodeTest.cpp +++ b/test/unit/datamatrix/DMHighLevelEncodeTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2006 Jeremias Maerki. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ByteArray.h" #include "ZXContainerAlgorithms.h" diff --git a/test/unit/datamatrix/DMPlacementTest.cpp b/test/unit/datamatrix/DMPlacementTest.cpp index a7ad679936..091d365fbf 100644 --- a/test/unit/datamatrix/DMPlacementTest.cpp +++ b/test/unit/datamatrix/DMPlacementTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2006 Jeremias Maerki -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitMatrixIO.h" #include "ByteArray.h" diff --git a/test/unit/datamatrix/DMSymbolInfoTest.cpp b/test/unit/datamatrix/DMSymbolInfoTest.cpp index 7a5c13562a..bae53e8542 100644 --- a/test/unit/datamatrix/DMSymbolInfoTest.cpp +++ b/test/unit/datamatrix/DMSymbolInfoTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2006 Jeremias Maerki -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "datamatrix/DMSymbolInfo.h" #include "datamatrix/DMSymbolShape.h" diff --git a/test/unit/datamatrix/DMWriterTest.cpp b/test/unit/datamatrix/DMWriterTest.cpp index b60d4818ff..4aa0738e51 100644 --- a/test/unit/datamatrix/DMWriterTest.cpp +++ b/test/unit/datamatrix/DMWriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2008 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "datamatrix/DMWriter.h" #include "BitMatrixIO.h" diff --git a/test/unit/maxicode/MCDecoderTest.cpp b/test/unit/maxicode/MCDecoderTest.cpp index 150f4542ec..21f9447d9e 100644 --- a/test/unit/maxicode/MCDecoderTest.cpp +++ b/test/unit/maxicode/MCDecoderTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2021 gitlost -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ByteArray.h" #include "DecoderResult.h" diff --git a/test/unit/oned/ODCodaBarWriterTest.cpp b/test/unit/oned/ODCodaBarWriterTest.cpp index 5958cc13c9..f4071c33b7 100644 --- a/test/unit/oned/ODCodaBarWriterTest.cpp +++ b/test/unit/oned/ODCodaBarWriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2011 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODCodabarWriter.h" #include "BitArray.h" diff --git a/test/unit/oned/ODCode128ReaderTest.cpp b/test/unit/oned/ODCode128ReaderTest.cpp index 87af89dbfb..ed37bcf79c 100644 --- a/test/unit/oned/ODCode128ReaderTest.cpp +++ b/test/unit/oned/ODCode128ReaderTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2021 gitlost -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODCode128Reader.h" diff --git a/test/unit/oned/ODCode128WriterTest.cpp b/test/unit/oned/ODCode128WriterTest.cpp index 65636d64ec..010854357f 100644 --- a/test/unit/oned/ODCode128WriterTest.cpp +++ b/test/unit/oned/ODCode128WriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2014 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODCode128Writer.h" #include "BitMatrixIO.h" diff --git a/test/unit/oned/ODCode39ExtendedModeTest.cpp b/test/unit/oned/ODCode39ExtendedModeTest.cpp index dd92d53b75..0c256971a4 100644 --- a/test/unit/oned/ODCode39ExtendedModeTest.cpp +++ b/test/unit/oned/ODCode39ExtendedModeTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitArray.h" #include "BitArrayUtility.h" diff --git a/test/unit/oned/ODCode39ReaderTest.cpp b/test/unit/oned/ODCode39ReaderTest.cpp index c92d88202f..42da6c6ce0 100644 --- a/test/unit/oned/ODCode39ReaderTest.cpp +++ b/test/unit/oned/ODCode39ReaderTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2022 gitlost -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODCode39Reader.h" diff --git a/test/unit/oned/ODCode39WriterTest.cpp b/test/unit/oned/ODCode39WriterTest.cpp index 7ab6e8fb47..0d98c4c250 100644 --- a/test/unit/oned/ODCode39WriterTest.cpp +++ b/test/unit/oned/ODCode39WriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODCode39Writer.h" #include "BitMatrixIO.h" diff --git a/test/unit/oned/ODCode93ReaderTest.cpp b/test/unit/oned/ODCode93ReaderTest.cpp index 1fc060c6f4..221d37d957 100644 --- a/test/unit/oned/ODCode93ReaderTest.cpp +++ b/test/unit/oned/ODCode93ReaderTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODCode93Reader.h" #include "BitArray.h" diff --git a/test/unit/oned/ODCode93WriterTest.cpp b/test/unit/oned/ODCode93WriterTest.cpp index f1c560982c..beaf6965b5 100644 --- a/test/unit/oned/ODCode93WriterTest.cpp +++ b/test/unit/oned/ODCode93WriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODCode93Writer.h" #include "BitMatrixIO.h" diff --git a/test/unit/oned/ODEAN13WriterTest.cpp b/test/unit/oned/ODEAN13WriterTest.cpp index 7021bfac1e..22cfd92da9 100644 --- a/test/unit/oned/ODEAN13WriterTest.cpp +++ b/test/unit/oned/ODEAN13WriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2009 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODEAN13Writer.h" #include "BitMatrixIO.h" diff --git a/test/unit/oned/ODEAN8WriterTest.cpp b/test/unit/oned/ODEAN8WriterTest.cpp index 19f77bb615..c2bebf5d54 100644 --- a/test/unit/oned/ODEAN8WriterTest.cpp +++ b/test/unit/oned/ODEAN8WriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2009 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODEAN8Writer.h" #include "BitMatrixIO.h" diff --git a/test/unit/oned/ODITFWriterTest.cpp b/test/unit/oned/ODITFWriterTest.cpp index 89c7ebf04c..34a9d0392a 100644 --- a/test/unit/oned/ODITFWriterTest.cpp +++ b/test/unit/oned/ODITFWriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODITFWriter.h" #include "BitMatrixIO.h" diff --git a/test/unit/oned/ODUPCAWriterTest.cpp b/test/unit/oned/ODUPCAWriterTest.cpp index 1b83a168be..f015c6cfbc 100644 --- a/test/unit/oned/ODUPCAWriterTest.cpp +++ b/test/unit/oned/ODUPCAWriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2010 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODUPCAWriter.h" #include "BitMatrixIO.h" diff --git a/test/unit/oned/ODUPCEWriterTest.cpp b/test/unit/oned/ODUPCEWriterTest.cpp index 1d7970f3d5..9db92ee6cd 100644 --- a/test/unit/oned/ODUPCEWriterTest.cpp +++ b/test/unit/oned/ODUPCEWriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/ODUPCEWriter.h" #include "BitMatrixIO.h" diff --git a/test/unit/oned/rss/ODRSSExpandedBinaryDecoderTest.cpp b/test/unit/oned/rss/ODRSSExpandedBinaryDecoderTest.cpp index 12df29d4bb..c11b7528ef 100644 --- a/test/unit/oned/rss/ODRSSExpandedBinaryDecoderTest.cpp +++ b/test/unit/oned/rss/ODRSSExpandedBinaryDecoderTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2021 gitlost -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitArray.h" #include "BitArrayUtility.h" diff --git a/test/unit/oned/rss/ODRSSFieldParserTest.cpp b/test/unit/oned/rss/ODRSSFieldParserTest.cpp index dab4897508..a6cf3ffd79 100644 --- a/test/unit/oned/rss/ODRSSFieldParserTest.cpp +++ b/test/unit/oned/rss/ODRSSFieldParserTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2022 gitlost -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "oned/rss/ODRSSFieldParser.h" diff --git a/test/unit/pdf417/PDF417DecoderTest.cpp b/test/unit/pdf417/PDF417DecoderTest.cpp index 34055006fd..cf9e4359e2 100644 --- a/test/unit/pdf417/PDF417DecoderTest.cpp +++ b/test/unit/pdf417/PDF417DecoderTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "DecoderResult.h" #include "DecodeStatus.h" diff --git a/test/unit/pdf417/PDF417ErrorCorrectionTest.cpp b/test/unit/pdf417/PDF417ErrorCorrectionTest.cpp index 3b8bd94eee..763d9e4dbf 100644 --- a/test/unit/pdf417/PDF417ErrorCorrectionTest.cpp +++ b/test/unit/pdf417/PDF417ErrorCorrectionTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2012 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "PseudoRandom.h" #include "ZXContainerAlgorithms.h" diff --git a/test/unit/pdf417/PDF417HighLevelEncoderTest.cpp b/test/unit/pdf417/PDF417HighLevelEncoderTest.cpp index 412f6e2586..bbcc492d03 100644 --- a/test/unit/pdf417/PDF417HighLevelEncoderTest.cpp +++ b/test/unit/pdf417/PDF417HighLevelEncoderTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright (C) 2014 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "CharacterSet.h" #include "pdf417/PDFCompaction.h" diff --git a/test/unit/pdf417/PDF417WriterTest.cpp b/test/unit/pdf417/PDF417WriterTest.cpp index c88b732490..747d2c5524 100644 --- a/test/unit/pdf417/PDF417WriterTest.cpp +++ b/test/unit/pdf417/PDF417WriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitMatrixIO.h" #include "pdf417/PDFWriter.h" diff --git a/test/unit/qrcode/MQRDecoderTest.cpp b/test/unit/qrcode/MQRDecoderTest.cpp index c49f1217d2..34bf5add5e 100644 --- a/test/unit/qrcode/MQRDecoderTest.cpp +++ b/test/unit/qrcode/MQRDecoderTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +*/ +// SPDX-License-Identifier: Apache-2.0 #include "qrcode/QRDecoder.h" diff --git a/test/unit/qrcode/QRBitMatrixParserTest.cpp b/test/unit/qrcode/QRBitMatrixParserTest.cpp index 023c4614d0..2cd7a9c5cf 100644 --- a/test/unit/qrcode/QRBitMatrixParserTest.cpp +++ b/test/unit/qrcode/QRBitMatrixParserTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +*/ +// SPDX-License-Identifier: Apache-2.0 #include "BitMatrix.h" #include "BitMatrixIO.h" diff --git a/test/unit/qrcode/QRDataMaskTest.cpp b/test/unit/qrcode/QRDataMaskTest.cpp index e71387ce74..24baa4a417 100644 --- a/test/unit/qrcode/QRDataMaskTest.cpp +++ b/test/unit/qrcode/QRDataMaskTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2007 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "qrcode/QRDataMask.h" #include "BitMatrix.h" diff --git a/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp b/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp index 5ab4b15589..81d5f97c9b 100644 --- a/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp +++ b/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2008 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitArray.h" #include "ByteArray.h" diff --git a/test/unit/qrcode/QREncoderTest.cpp b/test/unit/qrcode/QREncoderTest.cpp index ccd9807fdc..78e2715fc8 100644 --- a/test/unit/qrcode/QREncoderTest.cpp +++ b/test/unit/qrcode/QREncoderTest.cpp @@ -1,18 +1,7 @@ /* * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +*/ +// SPDX-License-Identifier: Apache-2.0 #include "BitArray.h" #include "BitArrayUtility.h" diff --git a/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp b/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp index c09c226559..f3870b4bbe 100644 --- a/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp +++ b/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2008 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "qrcode/QRErrorCorrectionLevel.h" diff --git a/test/unit/qrcode/QRFormatInformationTest.cpp b/test/unit/qrcode/QRFormatInformationTest.cpp index c87184585b..959f5ae42e 100644 --- a/test/unit/qrcode/QRFormatInformationTest.cpp +++ b/test/unit/qrcode/QRFormatInformationTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2007 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "qrcode/QRFormatInformation.h" diff --git a/test/unit/qrcode/QRModeTest.cpp b/test/unit/qrcode/QRModeTest.cpp index 77a1611350..94ab70243f 100644 --- a/test/unit/qrcode/QRModeTest.cpp +++ b/test/unit/qrcode/QRModeTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2008 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "qrcode/QRCodecMode.h" #include "qrcode/QRVersion.h" diff --git a/test/unit/qrcode/QRVersionTest.cpp b/test/unit/qrcode/QRVersionTest.cpp index 4e6fd8eb0c..bf4fbe377b 100644 --- a/test/unit/qrcode/QRVersionTest.cpp +++ b/test/unit/qrcode/QRVersionTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2008 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "qrcode/QRVersion.h" diff --git a/test/unit/qrcode/QRWriterTest.cpp b/test/unit/qrcode/QRWriterTest.cpp index 869d66a94e..bcb63f4443 100644 --- a/test/unit/qrcode/QRWriterTest.cpp +++ b/test/unit/qrcode/QRWriterTest.cpp @@ -1,19 +1,8 @@ /* * Copyright 2017 Huy Cuong Nguyen * Copyright 2008 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BitMatrixIO.h" #include "qrcode/QRWriter.h" diff --git a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp index 32994d5934..ce37a1ecde 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp +++ b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp @@ -1,18 +1,7 @@ /* * Copyright 2021 Axel Waggershauser -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "JNIUtils.h" #include "ReadBarcode.h" diff --git a/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp b/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp index 85dc3827c7..e552c912d8 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp +++ b/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "JNIUtils.h" #include diff --git a/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.h b/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.h index 6400a41289..ed1391e022 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.h +++ b/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include #include diff --git a/wrappers/wasm/BarcodeReader.cpp b/wrappers/wasm/BarcodeReader.cpp index 5dd2dab2bf..eb6e437b5a 100644 --- a/wrappers/wasm/BarcodeReader.cpp +++ b/wrappers/wasm/BarcodeReader.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "ReadBarcode.h" diff --git a/wrappers/wasm/BarcodeWriter.cpp b/wrappers/wasm/BarcodeWriter.cpp index 4b6e23c350..209fec1a89 100644 --- a/wrappers/wasm/BarcodeWriter.cpp +++ b/wrappers/wasm/BarcodeWriter.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #include "BarcodeFormat.h" #include "MultiFormatWriter.h" diff --git a/wrappers/winrt/BarcodeReader.h b/wrappers/winrt/BarcodeReader.h index 7455007449..e68383e0e7 100644 --- a/wrappers/winrt/BarcodeReader.h +++ b/wrappers/winrt/BarcodeReader.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "BarcodeFormat.h" diff --git a/wrappers/winrt/ReadResult.h b/wrappers/winrt/ReadResult.h index 65e8251afe..9361441605 100644 --- a/wrappers/winrt/ReadResult.h +++ b/wrappers/winrt/ReadResult.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once namespace ZXing { From 59e76b00a4dfc09748cec2d43e480a7b0ef65eb5 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 22 May 2022 01:57:41 +0200 Subject: [PATCH 031/180] Fix a few missing/missed license comments --- example/ZXingOpenCV.h | 18 ++++-------------- example/ZXingQtReader.h | 16 +++------------- example/ZXingReader.cpp | 1 + test/fuzz/fuzzDBEDecoder.cpp | 5 +++++ test/fuzz/fuzzDMDecoder.cpp | 5 +++++ test/fuzz/fuzzDMEncoder.cpp | 5 +++++ test/fuzz/fuzzODDecoders.cpp | 5 +++++ wrappers/python/zxing.cpp | 7 +++++++ 8 files changed, 35 insertions(+), 27 deletions(-) diff --git a/example/ZXingOpenCV.h b/example/ZXingOpenCV.h index b096b0d770..bf9cca06e7 100644 --- a/example/ZXingOpenCV.h +++ b/example/ZXingOpenCV.h @@ -1,19 +1,9 @@ -#pragma once /* - * Copyright 2021 Axel Waggershauser - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2020 Axel Waggershauser */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ReadBarcode.h" #include "TextUtfEncoding.h" diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index 05a87bad2b..2a05bbf5d8 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2020 Axel Waggershauser - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once #include "ReadBarcode.h" diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index b3c9958ff8..95cbb3a8c8 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -1,5 +1,6 @@ /* * Copyright 2016 Nu-book Inc. +* Copyright 2019 Axel Waggershauser */ // SPDX-License-Identifier: Apache-2.0 diff --git a/test/fuzz/fuzzDBEDecoder.cpp b/test/fuzz/fuzzDBEDecoder.cpp index 4a14761611..292a7b9ce7 100644 --- a/test/fuzz/fuzzDBEDecoder.cpp +++ b/test/fuzz/fuzzDBEDecoder.cpp @@ -1,3 +1,8 @@ +/* + * Copyright 2021 Axel Waggershauser + */ +// SPDX-License-Identifier: Apache-2.0 + #include #include diff --git a/test/fuzz/fuzzDMDecoder.cpp b/test/fuzz/fuzzDMDecoder.cpp index 455679c70c..f6e7a26d3b 100644 --- a/test/fuzz/fuzzDMDecoder.cpp +++ b/test/fuzz/fuzzDMDecoder.cpp @@ -1,3 +1,8 @@ +/* + * Copyright 2021 Axel Waggershauser + */ +// SPDX-License-Identifier: Apache-2.0 + #include #include diff --git a/test/fuzz/fuzzDMEncoder.cpp b/test/fuzz/fuzzDMEncoder.cpp index 2d9d85c6fd..0a56d9b0fc 100644 --- a/test/fuzz/fuzzDMEncoder.cpp +++ b/test/fuzz/fuzzDMEncoder.cpp @@ -1,3 +1,8 @@ +/* + * Copyright 2021 Axel Waggershauser + */ +// SPDX-License-Identifier: Apache-2.0 + #include #include diff --git a/test/fuzz/fuzzODDecoders.cpp b/test/fuzz/fuzzODDecoders.cpp index df39599041..9fc55dec73 100644 --- a/test/fuzz/fuzzODDecoders.cpp +++ b/test/fuzz/fuzzODDecoders.cpp @@ -1,3 +1,8 @@ +/* + * Copyright 2021 Axel Waggershauser + */ +// SPDX-License-Identifier: Apache-2.0 + #include #include diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index 2756a20f8e..60db5387a2 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -1,3 +1,10 @@ +/* + * Copyright 2019 Tim Rae + * Copyright 2021 Antoine Humbert + * Copyright 2021 Axel Waggershauser + */ +// SPDX-License-Identifier: Apache-2.0 + #include "BarcodeFormat.h" // Reader From 7578181156cd48dca6ea5df642c4be561192c6e5 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 22 May 2022 01:57:59 +0200 Subject: [PATCH 032/180] fuzzer: fix build regressions --- test/fuzz/fuzzDMDecoder.cpp | 4 ++-- test/fuzz/fuzzODDecoders.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/fuzz/fuzzDMDecoder.cpp b/test/fuzz/fuzzDMDecoder.cpp index f6e7a26d3b..d0c07f070c 100644 --- a/test/fuzz/fuzzDMDecoder.cpp +++ b/test/fuzz/fuzzDMDecoder.cpp @@ -12,7 +12,7 @@ using namespace ZXing; namespace ZXing::DataMatrix::DecodedBitStreamParser { -DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet); +DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const bool isDMRE); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) @@ -23,7 +23,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) ByteArray ba; ba.insert(ba.begin(), data, data + size); try { - DataMatrix::DecodedBitStreamParser::Decode(std::move(ba), ""); + DataMatrix::DecodedBitStreamParser::Decode(std::move(ba), "", false); } catch (...) { } diff --git a/test/fuzz/fuzzODDecoders.cpp b/test/fuzz/fuzzODDecoders.cpp index 9fc55dec73..2ffd01392d 100644 --- a/test/fuzz/fuzzODDecoders.cpp +++ b/test/fuzz/fuzzODDecoders.cpp @@ -27,7 +27,7 @@ bool init() readers.emplace_back(new MultiUPCEANReader(hints)); readers.emplace_back(new Code39Reader(hints)); readers.emplace_back(new Code93Reader()); - readers.emplace_back(new Code128Reader(hints)); + readers.emplace_back(new Code128Reader()); readers.emplace_back(new ITFReader(hints)); readers.emplace_back(new CodabarReader(hints)); readers.emplace_back(new DataBarReader(hints)); From 4db362a83d0ead168bcdc0376f5f24a1f7466a85 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 22 May 2022 11:21:32 +0200 Subject: [PATCH 033/180] ODDataBarReader: fix heap overflow regression --- core/src/oned/ODDataBarReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index c516370f4a..b9f4fa9601 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -157,7 +157,7 @@ Result DataBarReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const { #if 0 // non-stacked version - next = next.subView(-1, FULL_PAIR_SIZE); + next = next.subView(-1, FULL_PAIR_SIZE + 1); // +1 reflects the guard pattern on the right, see IsRightPair()); // yes: the first view we test is at index 1 (black bar at 0 would be the guard pattern) while (next.shift(2)) { if (IsLeftPair(next)) { @@ -174,7 +174,7 @@ Result DataBarReader::decodePattern(int rowNumber, PatternView& next, state.reset(new State); auto* prevState = static_cast(state.get()); - next = next.subView(0, FULL_PAIR_SIZE); + next = next.subView(0, FULL_PAIR_SIZE + 1); // +1 reflects the guard pattern on the right, see IsRightPair() // yes: the first view we test is at index 1 (black bar at 0 would be the guard pattern) while (next.shift(1)) { if (IsLeftPair(next)) { From fba9e3c49191d9ac5e98c2569dd7245fc0b10c01 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 24 May 2022 08:42:54 +0200 Subject: [PATCH 034/180] DecoderResult: init _lineCount with 0 instead of -1 --- core/src/DecoderResult.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 0bb573bc7e..0b04d449cb 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -37,7 +37,7 @@ class DecoderResult std::wstring _ecLevel; int _errorsCorrected = -1; int _erasures = -1; - int _lineCount = -1; + int _lineCount = 0; std::string _symbologyIdentifier; StructuredAppendInfo _structuredAppend; bool _isMirrored = false; From edee1f8de3634cfd311f6211dd92af65a599bf2b Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 24 May 2022 08:53:21 +0200 Subject: [PATCH 035/180] Result: implement preview of utf8 based ECI protocol channel See https://github.com/nu-book/zxing-cpp/issues/334#issuecomment-1134493465 for a discussion about this in the context of the potentially upcoming ISO standard regarding barcode scanner interfaces. Also add `applicationIndicator` property conveying the presence of either `GS1` or AIM application indicators (`FNC1` tags). Note: all of this experimental and may change at any time. --- core/src/CharacterSetECI.cpp | 9 ++++ core/src/CharacterSetECI.h | 6 +++ core/src/Content.cpp | 85 ++++++++++++++++++++++++------- core/src/Content.h | 21 ++++++-- core/src/DecoderResult.h | 4 +- core/src/Result.cpp | 4 +- core/src/Result.h | 7 ++- core/src/datamatrix/DMDecoder.cpp | 2 +- core/src/qrcode/QRDecoder.cpp | 26 ++++------ example/ZXingReader.cpp | 2 + 10 files changed, 124 insertions(+), 42 deletions(-) diff --git a/core/src/CharacterSetECI.cpp b/core/src/CharacterSetECI.cpp index f99adc6b32..fe59c044d6 100644 --- a/core/src/CharacterSetECI.cpp +++ b/core/src/CharacterSetECI.cpp @@ -8,7 +8,9 @@ #include "TextDecoder.h" #include +#include #include +#include #include namespace ZXing::CharacterSetECI { @@ -123,6 +125,13 @@ static const std::map ECI_NAME_TO_CHA {"BINARY", CharacterSet::BINARY}, }; +std::string ECI2String(int eci) +{ + std::ostringstream oss; + oss << '\\' << std::setw(6) << std::setfill('0') << eci; + return oss.str(); +} + CharacterSet ECI2CharacterSet(int value) { auto it = ECI_VALUE_TO_CHARSET.find(value); diff --git a/core/src/CharacterSetECI.h b/core/src/CharacterSetECI.h index 1d6c1e6ed7..8c586d7a55 100644 --- a/core/src/CharacterSetECI.h +++ b/core/src/CharacterSetECI.h @@ -19,6 +19,12 @@ namespace ZXing { */ namespace CharacterSetECI { +/** + * @brief ECI2String converts the numerical ECI value to a 7 character string as used in the ECI protocol + * @return e.g. "\000020" + */ +std::string ECI2String(int eci); + /** * @param value character set ECI value * @return {@code CharacterSet} representing ECI of given value, or {@code CharacterSet::Unknown} if it is unsupported diff --git a/core/src/Content.cpp b/core/src/Content.cpp index dc17fcc8b9..3085283e0d 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -7,21 +7,44 @@ #include "CharacterSetECI.h" #include "TextDecoder.h" +#include "TextUtfEncoding.h" #include "ZXContainerAlgorithms.h" namespace ZXing { -void Content::switchEncoding(CharacterSet cs, bool isECI) +constexpr bool ECIIsBinary(int eci) { + return eci < 0 || eci > 32; +} + +template +void Content::ForEachECIBlock(FUNC func) const +{ + for (int i = 0; i < Size(encodings); ++i) { + auto [eci, start] = encodings[i]; + int end = i + 1 == Size(encodings) ? Size(binary) : encodings[i + 1].pos; + + func(eci, start, end); + } +} + +void Content::switchEncoding(int eci, bool isECI) +{ + // TODO: replace non-ECI entries on first ECI entry with default ECI if (isECI || !hasECI) { - if (encodings.back().second == Size(binary)) - encodings.back().first = cs; // no point in recording 0 length segments + if (encodings.back().pos == Size(binary)) + encodings.back().eci = eci; // no point in recording 0 length segments else - encodings.emplace_back(cs, Size(binary)); + encodings.push_back({eci, Size(binary)}); } hasECI |= isECI; } +void Content::switchEncoding(CharacterSet cs) +{ + switchEncoding(CharacterSetECI::Charset2ECI(cs), false); +} + std::wstring Content::text() const { auto fallbackCS = CharacterSetECI::CharsetFromName(hintedCharset.c_str()); @@ -29,28 +52,56 @@ std::wstring Content::text() const fallbackCS = guessEncoding(); std::wstring wstr; - for (int i = 0; i < Size(encodings); ++i) { - auto [cs, start] = encodings[i]; - int end = i + 1 == Size(encodings) ? Size(binary) : encodings[i + 1].second; - + ForEachECIBlock([&](int eci, int begin, int end) { + CharacterSet cs = CharacterSetECI::ECI2CharacterSet(eci); if (cs == CharacterSet::Unknown) cs = fallbackCS; - TextDecoder::Append(wstr, binary.data() + start, end - start, cs); - } + TextDecoder::Append(wstr, binary.data() + begin, end - begin, cs); + }); return wstr; } +std::string Content::utf8Protocol() const +{ + std::wstring res; + int lastECI = -1; + + ForEachECIBlock([&](int eci, int begin, int end) { + if (!hasECI) + eci = CharacterSetECI::Charset2ECI(guessEncoding()); + CharacterSet cs = CharacterSetECI::ECI2CharacterSet(eci); + if (cs == CharacterSet::Unknown) + cs = CharacterSet::BINARY; + if (eci == -1) + eci = 899; // 26 == binary ECI + else if (!ECIIsBinary(eci)) + eci = 26; // 26 == utf8 ECI + + if (lastECI != eci) + TextDecoder::AppendLatin1(res, CharacterSetECI::ECI2String(eci)); + lastECI = eci; + + std::wstring tmp; + TextDecoder::Append(tmp, binary.data() + begin, end - begin, cs); + for (auto c : tmp) { + res += c; + if (c == L'\\') // in the ECI protocol a '\' has to be doubled + res += c; + } + }); + + return TextUtfEncoding::ToUtf8(res); +} + CharacterSet Content::guessEncoding() const { // assemble all blocks with unknown encoding ByteArray input; - for (int i = 0; i < Size(encodings); ++i) { - auto [cs, start] = encodings[i]; - int end = i + 1 == Size(encodings) ? Size(binary) : encodings[i + 1].second; - if (cs == CharacterSet::Unknown) - input.insert(input.end(), binary.begin() + start, binary.begin() + end); - } + ForEachECIBlock([&](int eci, int begin, int end) { + if (eci == -1) + input.insert(input.end(), binary.begin() + begin, binary.begin() + end); + }); if (input.empty()) return CharacterSet::Unknown; @@ -60,7 +111,7 @@ CharacterSet Content::guessEncoding() const ContentType Content::type() const { - auto isBinary = [](Encoding e) { return e.first == CharacterSet::BINARY || e.first == CharacterSet::Unknown; }; + auto isBinary = [](Encoding e) { return ECIIsBinary(e.eci); }; if (hasECI) { if (std::none_of(encodings.begin(), encodings.end(), isBinary)) diff --git a/core/src/Content.h b/core/src/Content.h index 1af33bac05..78b8682734 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -16,13 +16,27 @@ class Content { bool hasECI = false; + template + void ForEachECIBlock(FUNC f) const; + + void switchEncoding(int eci, bool isECI); + public: + struct Encoding + { + int eci, pos; + }; + ByteArray binary; - using Encoding = std::pair; - std::vector encodings = {{CharacterSet::Unknown, 0}}; + std::vector encodings = {{-1, 0}}; std::string hintedCharset; + std::string applicationIndicator; + + Content() = default; + Content(ByteArray&& binary, int defaultECI) : binary(binary), encodings{{defaultECI, 0}} {} - void switchEncoding(CharacterSet cs, bool isECI = false); + void switchEncoding(int eci) { switchEncoding(eci, true); } + void switchEncoding(CharacterSet cs); void reserve(int count) { binary.reserve(binary.size() + count); } @@ -36,6 +50,7 @@ class Content bool empty() const { return binary.empty(); } std::wstring text() const; + std::string utf8Protocol() const; CharacterSet guessEncoding() const; ContentType type() const; }; diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 0b04d449cb..c239d07e03 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -71,8 +71,8 @@ class DecoderResult ByteArray&& rawBytes() && { return std::move(_rawBytes); } const std::wstring& text() const & { return _text; } std::wstring&& text() && { return std::move(_text); } - const ByteArray& binary() const & { return _content.binary; } - ByteArray&& binary() && { return std::move(_content.binary); } + const Content& content() const & { return _content; } + Content&& content() && { return std::move(_content); } // 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 diff --git a/core/src/Result.cpp b/core/src/Result.cpp index f69a8804db..0a6ea4ab5e 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -18,8 +18,8 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor std::string&& symbologyIdentifier, ByteArray&& rawBytes, const bool readerInit) : _format(format), + _content({ByteArray(text), 3}), _text(TextDecoder::FromLatin1(text)), - _binary(text), _position(Line(y, xStart, xStop)), _rawBytes(std::move(rawBytes)), _numBits(Size(_rawBytes) * 8), @@ -31,8 +31,8 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format) : _status(decodeResult.errorCode()), _format(format), + _content(std::move(decodeResult).content()), _text(std::move(decodeResult).text()), - _binary(std::move(decodeResult).binary()), _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), diff --git a/core/src/Result.h b/core/src/Result.h index be145a888c..c869e188c9 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -9,6 +9,7 @@ #include "BarcodeFormat.h" #include "ByteArray.h" +#include "Content.h" #include "DecodeStatus.h" #include "Quadrilateral.h" #include "StructuredAppend.h" @@ -47,7 +48,9 @@ class Result const std::wstring& text() const { return _text; } // WARNING: this is an experimental API and may change/disappear - const ByteArray& binary() const { return _binary; } + const ByteArray& binary() const { return _content.binary; } + const std::string utf8Protocol() const { return _content.utf8Protocol(); } + const std::string applicationIndicator() const { return _content.applicationIndicator; } const Position& position() const { return _position; } void setPosition(Position pos) { _position = pos; } @@ -112,8 +115,8 @@ class Result private: DecodeStatus _status = DecodeStatus::NoError; BarcodeFormat _format = BarcodeFormat::None; + Content _content; std::wstring _text; - ByteArray _binary; Position _position; ByteArray _rawBytes; int _numBits = 0; diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 871ca9422a..c76f208e87 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -334,7 +334,7 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b case 238: DecodeAnsiX12Segment(bits, result); break; case 239: DecodeC40OrTextSegment(bits, result, Mode::TEXT); break; case 240: DecodeEdifactSegment(bits, result); break; - case 241: result.switchEncoding(CharacterSetECI::ECI2CharacterSet(ParseECIValue(bits)), true); break; + case 241: result.switchEncoding(ParseECIValue(bits)); break; default: if (oneByte <= 128) { // ASCII data (ASCII value + 1) result.push_back(upperShift(oneByte) - 1); diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 31ed2bbbec..70476b71cd 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -61,7 +61,8 @@ static bool CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) static void DecodeHanziSegment(BitSource& bits, int count, Content& result) { // Each character will require 2 bytes, decode as GB2312 - result.switchEncoding(CharacterSet::GB2312); + // There is no ECI value for GB2312, use GB18030 which is a superset + result.switchEncoding(CharacterSet::GB18030); result.reserve(2 * count); while (count > 0) { @@ -132,7 +133,7 @@ static char ToAlphaNumericChar(int value) return ALPHANUMERIC_CHARS[value]; } -static void DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffect, Content& result) +static void DecodeAlphanumericSegment(BitSource& bits, int count, Content& result) { // Read two characters at a time std::string buffer; @@ -147,7 +148,7 @@ static void DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffe buffer += ToAlphaNumericChar(bits.readBits(6)); } // See section 6.4.8.1, 6.4.8.2 - if (fc1InEffect) { + if (!result.applicationIndicator.empty()) { // 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] == '%') { @@ -162,13 +163,13 @@ static void DecodeAlphanumericSegment(BitSource& bits, int count, bool fc1InEffe } } - result.switchEncoding(CharacterSet::ASCII); + result.switchEncoding(CharacterSet::ISO8859_1); result += buffer; } static void DecodeNumericSegment(BitSource& bits, int count, Content& result) { - result.switchEncoding(CharacterSet::ASCII); + result.switchEncoding(CharacterSet::ISO8859_1); result.reserve(count); // Read three digits at a time @@ -262,7 +263,6 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo int symbologyIdModifier = 1; // ISO/IEC 18004:2015 Annex F Table F.1 StructuredAppendInfo structuredAppend; const int modeBitLength = CodecModeBitsLength(version); - bool fc1InEffect = false; try { @@ -277,15 +277,14 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo case CodecMode::FNC1_FIRST_POSITION: // if (!result.empty()) // uncomment to enforce specification // throw std::runtime_error("GS1 Indicator (FNC1 in first position) at illegal position"); - fc1InEffect = true; // In Alphanumeric mode undouble doubled percents and treat single percent as // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using // modifiers that indicate ECI protocol (ISO/IEC 18004:2015 Annex F Table F.1) symbologyIdModifier = 3; + result.applicationIndicator = "GS1"; // In Alphanumeric mode undouble doubled percents and treat single percent as break; case CodecMode::FNC1_SECOND_POSITION: if (!result.empty()) throw std::runtime_error("AIM Application Indicator (FNC1 in second position) at illegal position"); - fc1InEffect = true; // As above symbologyIdModifier = 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 < 10) // "00-09" @@ -296,6 +295,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo result += static_cast(appInd - 100); else throw std::runtime_error("Invalid AIM Application Indicator"); + result.applicationIndicator = std::string(result.binary.begin(), result.binary.end()); // see also above break; case CodecMode::STRUCTURED_APPEND: // sequence number and parity is added later to the result metadata @@ -304,14 +304,10 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo structuredAppend.count = bits.readBits(4) + 1; structuredAppend.id = std::to_string(bits.readBits(8)); break; - case CodecMode::ECI: { + case CodecMode::ECI: // Count doesn't apply to ECI - auto charset = CharacterSetECI::ECI2CharacterSet(ParseECIValue(bits)); - if (charset == CharacterSet::Unknown) - return DecodeStatus::FormatError; - result.switchEncoding(charset, true); + 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 @@ -327,7 +323,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo int count = bits.readBits(CharacterCountBits(mode, version)); switch (mode) { case CodecMode::NUMERIC: DecodeNumericSegment(bits, count, result); break; - case CodecMode::ALPHANUMERIC: DecodeAlphanumericSegment(bits, count, fc1InEffect, 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: return DecodeStatus::FormatError; diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 95cbb3a8c8..f4ca2d427f 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -182,6 +182,7 @@ int main(int argc, char* argv[]) } std::cout << "Text: \"" << ToUtf8(result.text(), angleEscape) << "\"\n" << "Binary: \"" << ToHex(result.binary()) << "\"\n" + << "ECI-Proto: \"" << result.utf8Protocol() << "\"\n" << "Format: " << ToString(result.format()) << "\n" << "Identifier: " << result.symbologyIdentifier() << "\n" << "Position: " << result.position() << "\n" @@ -195,6 +196,7 @@ int main(int argc, char* argv[]) }; printOptional("EC Level: ", ToUtf8(result.ecLevel())); + printOptional("App-Ind.: ", result.applicationIndicator()); if (result.lineCount()) std::cout << "Lines: " << result.lineCount() << "\n"; From d498cd8ab58bfcb568808b6c9da05dc755d8b7f4 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 24 May 2022 13:32:19 +0200 Subject: [PATCH 036/180] ECI: introduce type safe abstraction via enum and simplify API naming --- core/CMakeLists.txt | 2 + core/src/CharacterSetECI.cpp | 68 +--------------------- core/src/CharacterSetECI.h | 18 ------ core/src/Content.cpp | 37 ++++++------ core/src/Content.h | 13 +++-- core/src/ECI.cpp | 76 +++++++++++++++++++++++++ core/src/ECI.h | 66 +++++++++++++++++++++ core/src/Result.cpp | 2 +- core/src/datamatrix/DMDecoder.cpp | 8 +-- core/src/pdf417/PDFHighLevelEncoder.cpp | 6 +- core/src/qrcode/QRDecoder.cpp | 8 +-- core/src/qrcode/QREncoder.cpp | 4 +- test/unit/CharacterSetECITest.cpp | 13 +++-- 13 files changed, 191 insertions(+), 130 deletions(-) create mode 100644 core/src/ECI.cpp create mode 100644 core/src/ECI.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 461b0832f3..d52b08570d 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -54,6 +54,8 @@ set (COMMON_FILES src/ConcentricFinder.h src/ConcentricFinder.cpp src/CustomData.h + src/ECI.h + src/ECI.cpp src/Flags.h src/GenericGF.h src/GenericGF.cpp diff --git a/core/src/CharacterSetECI.cpp b/core/src/CharacterSetECI.cpp index fe59c044d6..d5640e3e32 100644 --- a/core/src/CharacterSetECI.cpp +++ b/core/src/CharacterSetECI.cpp @@ -5,6 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 #include "CharacterSetECI.h" + +#include "ECI.h" #include "TextDecoder.h" #include @@ -15,40 +17,6 @@ namespace ZXing::CharacterSetECI { -static const std::map ECI_VALUE_TO_CHARSET = { - {0, CharacterSet::Cp437}, // Obsolete - {1, CharacterSet::ISO8859_1}, // Obsolete - {2, CharacterSet::Cp437}, // Obsolete but still used by PDF417 Macro fields (ISO/IEC 15438:2015 Annex H.2.3) - {3, CharacterSet::ISO8859_1}, - {4, CharacterSet::ISO8859_2}, - {5, CharacterSet::ISO8859_3}, - {6, CharacterSet::ISO8859_4}, - {7, CharacterSet::ISO8859_5}, - {8, CharacterSet::ISO8859_6}, - {9, CharacterSet::ISO8859_7}, - {10, CharacterSet::ISO8859_8}, - {11, CharacterSet::ISO8859_9}, - {12, CharacterSet::ISO8859_10}, - {13, CharacterSet::ISO8859_11}, - {15, CharacterSet::ISO8859_13}, - {16, CharacterSet::ISO8859_14}, - {17, CharacterSet::ISO8859_15}, - {18, CharacterSet::ISO8859_16}, - {20, CharacterSet::Shift_JIS}, - {21, CharacterSet::Cp1250}, - {22, CharacterSet::Cp1251}, - {23, CharacterSet::Cp1252}, - {24, CharacterSet::Cp1256}, - {25, CharacterSet::UnicodeBig}, - {26, CharacterSet::UTF8}, - {27, CharacterSet::ASCII}, - {28, CharacterSet::Big5}, - {29, CharacterSet::GB18030}, - {30, CharacterSet::EUC_KR}, - {170, CharacterSet::ASCII}, - {899, CharacterSet::BINARY}, -}; - struct CompareNoCase { bool operator ()(const char* a, const char* b) const { while (*a != '\0' && *b != '\0') { @@ -125,36 +93,6 @@ static const std::map ECI_NAME_TO_CHA {"BINARY", CharacterSet::BINARY}, }; -std::string ECI2String(int eci) -{ - std::ostringstream oss; - oss << '\\' << std::setw(6) << std::setfill('0') << eci; - return oss.str(); -} - -CharacterSet ECI2CharacterSet(int value) -{ - auto it = ECI_VALUE_TO_CHARSET.find(value); - if (it != ECI_VALUE_TO_CHARSET.end()) { - return it->second; - } - return CharacterSet::Unknown; -} - -int Charset2ECI(CharacterSet charset) -{ - // Special case ISO8859_1 to avoid obsolete ECI 1 - if (charset == CharacterSet::ISO8859_1) { - return 3; - } - for (auto& [key, value] : ECI_VALUE_TO_CHARSET) { - if (value == charset) { - return key; - } - } - return -1; -} - CharacterSet CharsetFromName(const char* name) { auto it = ECI_NAME_TO_CHARSET.find(name); @@ -181,7 +119,7 @@ CharacterSet OnChangeAppendReset(const int eci, std::wstring& encoded, std::stri { // Character set ECIs only if (eci >= 0 && eci <= 899) { - auto encodingNew = CharacterSetECI::ECI2CharacterSet(eci); + auto encodingNew = ToCharacterSet(ECI(eci)); if (encodingNew != CharacterSet::Unknown && encodingNew != encoding) { // Encode data so far in current encoding and reset TextDecoder::Append(encoded, reinterpret_cast(data.data()), data.size(), encoding); diff --git a/core/src/CharacterSetECI.h b/core/src/CharacterSetECI.h index 8c586d7a55..e665f920dd 100644 --- a/core/src/CharacterSetECI.h +++ b/core/src/CharacterSetECI.h @@ -19,24 +19,6 @@ namespace ZXing { */ namespace CharacterSetECI { -/** - * @brief ECI2String converts the numerical ECI value to a 7 character string as used in the ECI protocol - * @return e.g. "\000020" - */ -std::string ECI2String(int eci); - -/** - * @param value character set ECI value - * @return {@code CharacterSet} representing ECI of given value, or {@code CharacterSet::Unknown} if it is unsupported - */ -CharacterSet ECI2CharacterSet(int value); - -/** - * @param charset {@code CharacterSet} representing ECI - * @return ECI of given {@code CharacterSet}, or -1 if it is unsupported - */ -int Charset2ECI(CharacterSet charset); - /** * @param name character set ECI encoding name * @return {@code CharacterSet} representing ECI of given value, or {@code CharacterSet::Unknown} if it is diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 3085283e0d..be17d65eb0 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -12,11 +12,6 @@ namespace ZXing { -constexpr bool ECIIsBinary(int eci) -{ - return eci < 0 || eci > 32; -} - template void Content::ForEachECIBlock(FUNC func) const { @@ -28,7 +23,7 @@ void Content::ForEachECIBlock(FUNC func) const } } -void Content::switchEncoding(int eci, bool isECI) +void Content::switchEncoding(ECI eci, bool isECI) { // TODO: replace non-ECI entries on first ECI entry with default ECI if (isECI || !hasECI) { @@ -42,7 +37,7 @@ void Content::switchEncoding(int eci, bool isECI) void Content::switchEncoding(CharacterSet cs) { - switchEncoding(CharacterSetECI::Charset2ECI(cs), false); + switchEncoding(ToECI(cs), false); } std::wstring Content::text() const @@ -52,8 +47,8 @@ std::wstring Content::text() const fallbackCS = guessEncoding(); std::wstring wstr; - ForEachECIBlock([&](int eci, int begin, int end) { - CharacterSet cs = CharacterSetECI::ECI2CharacterSet(eci); + ForEachECIBlock([&](ECI eci, int begin, int end) { + CharacterSet cs = ToCharacterSet(eci); if (cs == CharacterSet::Unknown) cs = fallbackCS; @@ -65,21 +60,21 @@ std::wstring Content::text() const std::string Content::utf8Protocol() const { std::wstring res; - int lastECI = -1; + ECI lastECI = ECI::Unknown; - ForEachECIBlock([&](int eci, int begin, int end) { + ForEachECIBlock([&](ECI eci, int begin, int end) { if (!hasECI) - eci = CharacterSetECI::Charset2ECI(guessEncoding()); - CharacterSet cs = CharacterSetECI::ECI2CharacterSet(eci); + eci = ToECI(guessEncoding()); + CharacterSet cs = ToCharacterSet(eci); if (cs == CharacterSet::Unknown) cs = CharacterSet::BINARY; - if (eci == -1) - eci = 899; // 26 == binary ECI - else if (!ECIIsBinary(eci)) - eci = 26; // 26 == utf8 ECI + if (eci == ECI::Unknown) + eci = ECI::Binary; + else if (IsText(eci)) + eci = ECI::UTF8; if (lastECI != eci) - TextDecoder::AppendLatin1(res, CharacterSetECI::ECI2String(eci)); + TextDecoder::AppendLatin1(res, ToString(eci)); lastECI = eci; std::wstring tmp; @@ -98,8 +93,8 @@ CharacterSet Content::guessEncoding() const { // assemble all blocks with unknown encoding ByteArray input; - ForEachECIBlock([&](int eci, int begin, int end) { - if (eci == -1) + ForEachECIBlock([&](ECI eci, int begin, int end) { + if (eci == ECI::Unknown) input.insert(input.end(), binary.begin() + begin, binary.begin() + end); }); @@ -111,7 +106,7 @@ CharacterSet Content::guessEncoding() const ContentType Content::type() const { - auto isBinary = [](Encoding e) { return ECIIsBinary(e.eci); }; + auto isBinary = [](Encoding e) { return !IsText(e.eci); }; if (hasECI) { if (std::none_of(encodings.begin(), encodings.end(), isBinary)) diff --git a/core/src/Content.h b/core/src/Content.h index 78b8682734..7b7dce5af1 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -6,7 +6,7 @@ #pragma once #include "ByteArray.h" -#include "CharacterSet.h" +#include "ECI.h" namespace ZXing { @@ -19,23 +19,24 @@ class Content template void ForEachECIBlock(FUNC f) const; - void switchEncoding(int eci, bool isECI); + void switchEncoding(ECI eci, bool isECI); public: struct Encoding { - int eci, pos; + ECI eci; + int pos; }; ByteArray binary; - std::vector encodings = {{-1, 0}}; + std::vector encodings = {{ECI::Unknown, 0}}; std::string hintedCharset; std::string applicationIndicator; Content() = default; - Content(ByteArray&& binary, int defaultECI) : binary(binary), encodings{{defaultECI, 0}} {} + Content(ByteArray&& binary, ECI defaultECI) : binary(binary), encodings{{defaultECI, 0}} {} - void switchEncoding(int eci) { switchEncoding(eci, true); } + void switchEncoding(ECI eci) { switchEncoding(eci, true); } void switchEncoding(CharacterSet cs); void reserve(int count) { binary.reserve(binary.size() + count); } diff --git a/core/src/ECI.cpp b/core/src/ECI.cpp new file mode 100644 index 0000000000..a8ee347009 --- /dev/null +++ b/core/src/ECI.cpp @@ -0,0 +1,76 @@ +/* +* Copyright 2022 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "ECI.h" + +#include +#include +#include + +namespace ZXing { + +static const std::map ECI_TO_CHARSET = { + {ECI(0), CharacterSet::Cp437}, // Obsolete + {ECI(1), CharacterSet::ISO8859_1}, // Obsolete + {ECI(2), CharacterSet::Cp437}, // Obsolete but still used by PDF417 Macro fields (ISO/IEC 15438:2015 Annex H.2.3) + {ECI::ISO8859_1, CharacterSet::ISO8859_1}, + {ECI::ISO8859_2, CharacterSet::ISO8859_2}, + {ECI::ISO8859_3, CharacterSet::ISO8859_3}, + {ECI::ISO8859_4, CharacterSet::ISO8859_4}, + {ECI::ISO8859_5, CharacterSet::ISO8859_5}, + {ECI::ISO8859_6, CharacterSet::ISO8859_6}, + {ECI::ISO8859_7, CharacterSet::ISO8859_7}, + {ECI::ISO8859_8, CharacterSet::ISO8859_8}, + {ECI::ISO8859_9, CharacterSet::ISO8859_9}, + {ECI::ISO8859_10, CharacterSet::ISO8859_10}, + {ECI::ISO8859_11, CharacterSet::ISO8859_11}, + {ECI::ISO8859_13, CharacterSet::ISO8859_13}, + {ECI::ISO8859_14, CharacterSet::ISO8859_14}, + {ECI::ISO8859_15, CharacterSet::ISO8859_15}, + {ECI::ISO8859_16, CharacterSet::ISO8859_16}, + {ECI::Shift_JIS, CharacterSet::Shift_JIS}, + {ECI::Cp1250, CharacterSet::Cp1250}, + {ECI::Cp1251, CharacterSet::Cp1251}, + {ECI::Cp1252, CharacterSet::Cp1252}, + {ECI::Cp1256, CharacterSet::Cp1256}, + {ECI::UTF16, CharacterSet::UnicodeBig}, + {ECI::UTF8, CharacterSet::UTF8}, + {ECI::ASCII, CharacterSet::ASCII}, + {ECI::Big5, CharacterSet::Big5}, + {ECI::GB18030, CharacterSet::GB18030}, + {ECI::EUC_KR, CharacterSet::EUC_KR}, + {ECI(170), CharacterSet::ASCII}, + {ECI::Binary, CharacterSet::BINARY}, +}; + +std::string ToString(ECI eci) +{ + std::ostringstream oss; + oss << '\\' << std::setw(6) << std::setfill('0') << ToInt(eci); + return oss.str(); +} + +CharacterSet ToCharacterSet(ECI eci) +{ + if (auto it = ECI_TO_CHARSET.find(eci); it != ECI_TO_CHARSET.end()) + return it->second; + + return CharacterSet::Unknown; +} + +ECI ToECI(CharacterSet cs) +{ + // Special case ISO8859_1 to avoid obsolete ECI 1 + if (cs == CharacterSet::ISO8859_1) + return ECI::ISO8859_1; + + for (auto& [key, value] : ECI_TO_CHARSET) + if (value == cs) + return key; + + return ECI::Unknown; +} + +} // namespace ZXing diff --git a/core/src/ECI.h b/core/src/ECI.h new file mode 100644 index 0000000000..ac2f65dfb8 --- /dev/null +++ b/core/src/ECI.h @@ -0,0 +1,66 @@ +/* +* Copyright 2022 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "CharacterSet.h" + +#include + +namespace ZXing { + +enum class ECI : int +{ + Unknown = -1, + ISO8859_1 = 3, + ISO8859_2 = 4, + ISO8859_3 = 5, + ISO8859_4 = 6, + ISO8859_5 = 7, + ISO8859_6 = 8, + ISO8859_7 = 9, + ISO8859_8 = 10, + ISO8859_9 = 11, + ISO8859_10 = 12, + ISO8859_11 = 13, + ISO8859_13 = 15, + ISO8859_14 = 16, + ISO8859_15 = 17, + ISO8859_16 = 18, + Shift_JIS = 20, + Cp1250 = 21, + Cp1251 = 22, + Cp1252 = 23, + Cp1256 = 24, + UTF16 = 25, + UTF8 = 26, + ASCII = 27, + Big5 = 28, + GB18030 = 29, + EUC_KR = 30, + Binary = 899 +}; + +inline constexpr int ToInt(ECI eci) +{ + return static_cast(eci); +} + +inline constexpr bool IsText(ECI eci) +{ + return ToInt(eci) >= 0 && ToInt(eci) <= 32; +} + +/** + * @brief ToString converts the numerical ECI value to a 7 character string as used in the ECI protocol + * @return e.g. "\000020" + */ +std::string ToString(ECI eci); + +CharacterSet ToCharacterSet(ECI eci); + +ECI ToECI(CharacterSet cs); + +} // namespace ZXing diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 0a6ea4ab5e..f397219f9b 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -18,7 +18,7 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor std::string&& symbologyIdentifier, ByteArray&& rawBytes, const bool readerInit) : _format(format), - _content({ByteArray(text), 3}), + _content({ByteArray(text), ECI::ISO8859_1}), _text(TextDecoder::FromLatin1(text)), _position(Line(y, xStart, xStop)), _rawBytes(std::move(rawBytes)), diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index c76f208e87..af58169cbf 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -82,19 +82,19 @@ struct Shift128 /** * See ISO 16022:2006, 5.4.1, Table 6 */ -static int ParseECIValue(BitSource& bits) +static ECI ParseECIValue(BitSource& bits) { int firstByte = bits.readBits(8); if (firstByte <= 127) - return firstByte - 1; + return ECI(firstByte - 1); int secondByte = bits.readBits(8); if (firstByte <= 191) - return (firstByte - 128) * 254 + 127 + secondByte - 1; + return ECI((firstByte - 128) * 254 + 127 + secondByte - 1); int thirdByte = bits.readBits(8); - return (firstByte - 192) * 64516 + 16383 + (secondByte - 1) * 254 + thirdByte - 1; + return ECI((firstByte - 192) * 64516 + 16383 + (secondByte - 1) * 254 + thirdByte - 1); } /** diff --git a/core/src/pdf417/PDFHighLevelEncoder.cpp b/core/src/pdf417/PDFHighLevelEncoder.cpp index c09a8fb536..ea394d11c2 100644 --- a/core/src/pdf417/PDFHighLevelEncoder.cpp +++ b/core/src/pdf417/PDFHighLevelEncoder.cpp @@ -8,7 +8,7 @@ #include "PDFHighLevelEncoder.h" #include "PDFCompaction.h" #include "CharacterSet.h" -#include "CharacterSetECI.h" +#include "ECI.h" #include "TextEncoder.h" #include "ZXBigInteger.h" #include "ZXContainerAlgorithms.h" @@ -502,8 +502,8 @@ HighLevelEncoder::EncodeHighLevel(const std::wstring& msg, Compaction compaction highLevel.reserve(highLevel.size() + msg.length()); //the codewords 0..928 are encoded as Unicode characters - if (encoding != CharacterSet::ISO8859_1) { - EncodingECI(CharacterSetECI::Charset2ECI(encoding), highLevel); + if (encoding != CharacterSet::ISO8859_1) { + EncodingECI(ToInt(ToECI(encoding)), highLevel); } int len = Size(msg); diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 70476b71cd..ad0b2b27fb 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -203,22 +203,22 @@ static void DecodeNumericSegment(BitSource& bits, int count, Content& result) } } -static int ParseECIValue(BitSource& bits) +static ECI ParseECIValue(BitSource& bits) { int firstByte = bits.readBits(8); if ((firstByte & 0x80) == 0) { // just one byte - return firstByte & 0x7F; + return ECI(firstByte & 0x7F); } if ((firstByte & 0xC0) == 0x80) { // two bytes int secondByte = bits.readBits(8); - return ((firstByte & 0x3F) << 8) | secondByte; + return ECI(((firstByte & 0x3F) << 8) | secondByte); } if ((firstByte & 0xE0) == 0xC0) { // three bytes int secondThirdBytes = bits.readBits(16); - return ((firstByte & 0x1F) << 16) | secondThirdBytes; + return ECI(((firstByte & 0x1F) << 16) | secondThirdBytes); } throw std::runtime_error("ParseECIValue: invalid value"); } diff --git a/core/src/qrcode/QREncoder.cpp b/core/src/qrcode/QREncoder.cpp index db3c90f6fb..2ce915fd0d 100644 --- a/core/src/qrcode/QREncoder.cpp +++ b/core/src/qrcode/QREncoder.cpp @@ -7,7 +7,7 @@ #include "QREncoder.h" #include "BitArray.h" -#include "CharacterSetECI.h" +#include "ECI.h" #include "GenericGF.h" #include "QREncodeResult.h" #include "QRErrorCorrectionLevel.h" @@ -103,7 +103,7 @@ CodecMode ChooseMode(const std::wstring& content, CharacterSet encoding) */ static void AppendECI(CharacterSet eci, BitArray& bits) { - int eciValue = CharacterSetECI::Charset2ECI(eci); + int eciValue = ToInt(ToECI(eci)); if (eciValue >= 0 && eciValue <= 999999) { bits.appendBits(static_cast(CodecMode::ECI), 4); if (eciValue <= 127) { diff --git a/test/unit/CharacterSetECITest.cpp b/test/unit/CharacterSetECITest.cpp index 2542fcd7c2..d83c208741 100644 --- a/test/unit/CharacterSetECITest.cpp +++ b/test/unit/CharacterSetECITest.cpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "CharacterSetECI.h" +#include "ECI.h" #include "gtest/gtest.h" #include "gmock/gmock.h" @@ -14,12 +15,12 @@ using namespace testing; TEST(CharacterSetECITest, Charset2ECI) { - EXPECT_EQ(Charset2ECI(CharacterSet::ISO8859_1), 3); - EXPECT_EQ(Charset2ECI(CharacterSet::ISO8859_2), 4); - EXPECT_EQ(Charset2ECI(CharacterSet::ASCII), 27); - EXPECT_EQ(Charset2ECI(CharacterSet::EUC_KR), 30); - EXPECT_EQ(Charset2ECI(CharacterSet::BINARY), 899); - EXPECT_EQ(Charset2ECI(CharacterSet::Unknown), -1); + EXPECT_EQ(ToInt(ToECI(CharacterSet::ISO8859_1)), 3); + EXPECT_EQ(ToInt(ToECI(CharacterSet::ISO8859_2)), 4); + EXPECT_EQ(ToInt(ToECI(CharacterSet::ASCII)), 27); + EXPECT_EQ(ToInt(ToECI(CharacterSet::EUC_KR)), 30); + EXPECT_EQ(ToInt(ToECI(CharacterSet::BINARY)), 899); + EXPECT_EQ(ToInt(ToECI(CharacterSet::Unknown)), -1); } TEST(CharacterSetECITest, InitEncoding) From 6f9fc1fa04f426bb2d2b6ab9049a2560267b7c3b Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 25 May 2022 02:38:14 +0200 Subject: [PATCH 037/180] Result: return empty utf8Protocol string if no image barcode was found --- core/src/Content.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index be17d65eb0..a7bdd2ec0e 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -59,6 +59,9 @@ std::wstring Content::text() const std::string Content::utf8Protocol() const { + if (binary.empty()) + return {}; + std::wstring res; ECI lastECI = ECI::Unknown; From b7695b1f112a561c393519afeca739d1bcfb4f1e Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 25 May 2022 03:01:02 +0200 Subject: [PATCH 038/180] QRCode: prevent MicroQRCode detection 'inside' standard QRCode We check for a sufficiently empty quite zone while still accepting 'dirty' symbols. --- core/src/qrcode/QRDetector.cpp | 19 +++++++++++++++--- test/blackbox/BlackboxTestRunner.cpp | 2 +- .../falsepositives-1/MQR-falsepositive.jpg | Bin 0 -> 1025 bytes 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 test/samples/falsepositives-1/MQR-falsepositive.jpg diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 9c189bb3fe..3b0ea99766 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -429,10 +429,23 @@ DetectorResult SampleMQR(const BitMatrix& image, const ConcentricPattern& fp) AppendBit(formatInfoBits, image.get(mod2Pix(centered(FORMAT_INFO_COORDS[i])))); auto fi = FormatInformation::DecodeMQR(formatInfoBits); - if (fi.isValid() && fi.microVersion()) { - const int dim = Version::DimensionOfVersion(fi.microVersion(), true); - return SampleGrid(image, dim, dim, mod2Pix); + if (!fi.isValid()) + continue; + + const int dim = Version::DimensionOfVersion(fi.microVersion(), true); + + // check that we are in fact not looking at a corner of a non-micro QRCode symbol + // we accept at most 1/3rd black pixels in the quite zone (in a QRCode symbol we expect about 1/2). + int blackPixels = 0; + for (int i = 0; i < dim; ++i) { + auto px = mod2Pix(centered(PointI{i, dim})); + auto py = mod2Pix(centered(PointI{dim, i})); + blackPixels += (image.isIn(px) && image.get(px)) + (image.isIn(py) && image.get(py)); } + if (blackPixels > 2 * dim / 3) + continue; + + return SampleGrid(image, dim, dim, mod2Pix); } return {}; diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index f03a369b48..9f16fa2aa3 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -644,7 +644,7 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set { 2, 2, 0 }, }); - runTests("falsepositives-1", "None", 25, { + runTests("falsepositives-1", "None", 26, { { 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 90 }, { 0, 0, 0, 0, 180 }, diff --git a/test/samples/falsepositives-1/MQR-falsepositive.jpg b/test/samples/falsepositives-1/MQR-falsepositive.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fdb661127fbfad5f5c7894bf41dfca27fc8d3e0a GIT binary patch literal 1025 zcmV+c1pfQ~*#F=F5K2Z#MgRc;000310RRC1+Wau05K2%0R#aA00RXF z1poj5000010s{mE1^@>U5)&iR5EK?MQ3xSHR+N>~CNn}qV*lCz2mt{A0Y3oL{JRyZ zl5u~>Af_dYf z5KfM9g#*I#8Z?o}AkSyjp`2kKohUzXW7VOYVIQ3+KXGH#>`pn3UG5g-NxG+MaEBHO zn@fY9Jf2S)smwNB;?lmgowr&Xl>2NTsiyh@v&Z1QW0X}G{_12gDxftIKf}@7@Q@E) z#nL$%k+xQIF{I^DrKQwLvRfiD8d8>kT`2BKb$_z*=qkmdchKyBeUupSkfODPt4(cI z-v*ohlw;&S*!QnPRVqUB&DD+LwA+ryf`oTh1Cq(r2FBP^UQ}5*mD9M0ve|f{3s))# zR-ii5yR(!nDhmn6Vge9{wf4a*GbW83maStc8)!;^wxhM%YWAYB1_~(Va1T^}bvw zE%y0xwXa*lM|X-*#%7_|jN>fKWou$a99@kkD_JMbclf=TzSd#)X}-Ui5=ow@jyc^gr~tN`;X2B5S_@6D z#PPU}yl2971@xB4K;b7_Aab#YME9XwYWxQce@^~Ce&?STSw<5!ZNb0X*Vsc+5Yz~Q znMeEo0N@rKPNL>n&eTbPgSi%+#){CiETzX+(o+7RXw#o(yfx*m({Fvs$x-3fe~Z}m z7h9WW+lJkpm7^@h5qva7d^R{+T%JOLwTj+~d6`Zr8Z$0HqJYVO>&BGlXE7{;Sa{>>R4^o#9*lZ v03*>zR<_3}mz(ZQwUxwJq0vmH0$4!^;S2gWSEIBQ3grA}mwvS#jeq~yVkXWF literal 0 HcmV?d00001 From 9f783473b21733074416e5a7419d770596bd3e0d Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 25 May 2022 11:44:27 +0200 Subject: [PATCH 039/180] Result: fix ECI designator for ALPHA content in utf8Protocol --- core/src/Content.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index a7bdd2ec0e..4ad4ec373f 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -48,9 +48,7 @@ std::wstring Content::text() const std::wstring wstr; ForEachECIBlock([&](ECI eci, int begin, int end) { - CharacterSet cs = ToCharacterSet(eci); - if (cs == CharacterSet::Unknown) - cs = fallbackCS; + CharacterSet cs = eci == ECI::Unknown ? fallbackCS : ToCharacterSet(eci); TextDecoder::Append(wstr, binary.data() + begin, end - begin, cs); }); @@ -64,17 +62,19 @@ std::string Content::utf8Protocol() const std::wstring res; ECI lastECI = ECI::Unknown; + auto fallbackCS = guessEncoding(); ForEachECIBlock([&](ECI eci, int begin, int end) { - if (!hasECI) - eci = ToECI(guessEncoding()); - CharacterSet cs = ToCharacterSet(eci); - if (cs == CharacterSet::Unknown) - cs = CharacterSet::BINARY; - if (eci == ECI::Unknown) - eci = ECI::Binary; - else if (IsText(eci)) + // first determine how to decode the content (choose character set) + // * eci == ECI::Unknown implies !hasECI and we guess + // * if !IsText(eci) the ToCharcterSet(eci) will return Unknown and we decode as binary + CharacterSet cs = eci == ECI::Unknown ? fallbackCS : ToCharacterSet(eci); + + // then find the eci to report back in the ECI designator + if (IsText(ToECI(cs))) // everything decoded as text is reported as utf8 eci = ECI::UTF8; + else if (eci == ECI::Unknown) // implies !hasECI and fallbackCS is Unknown or Binary + eci = ECI::Binary; if (lastECI != eci) TextDecoder::AppendLatin1(res, ToString(eci)); From 7693e4d7b85b2ee3066e3f5538b859a46ffa2c58 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 25 May 2022 11:47:29 +0200 Subject: [PATCH 040/180] Content: minor improvement of binary.empty() check --- core/src/Content.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 4ad4ec373f..c17a4d12ca 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -19,7 +19,8 @@ void Content::ForEachECIBlock(FUNC func) const auto [eci, start] = encodings[i]; int end = i + 1 == Size(encodings) ? Size(binary) : encodings[i + 1].pos; - func(eci, start, end); + if (start != end) + func(eci, start, end); } } @@ -57,9 +58,6 @@ std::wstring Content::text() const std::string Content::utf8Protocol() const { - if (binary.empty()) - return {}; - std::wstring res; ECI lastECI = ECI::Unknown; auto fallbackCS = guessEncoding(); From 80ee48ba4da83af397d7d5ee88f41686dfcdc43a Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 25 May 2022 12:23:37 +0200 Subject: [PATCH 041/180] TextDecoder: improve binary auto-detection Treat all ANSI control characters as binary, except EOT, LF, CR, GS and RS (see ISO/IEC 15434). See also the discussion in https://github.com/nu-book/zxing-cpp/issues/334#issuecomment-1133554551 --- core/src/TextDecoder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/TextDecoder.cpp b/core/src/TextDecoder.cpp index 4df8a65f14..98677d63a4 100644 --- a/core/src/TextDecoder.cpp +++ b/core/src/TextDecoder.cpp @@ -402,6 +402,10 @@ TextDecoder::GuessEncoding(const uint8_t* bytes, size_t length, CharacterSet fal if (value > 0x7F && value < 0xA0) { canBeISO88591 = false; } + // treat all ANSI control characters as binary, except EOT, LF, CR, GS and RS (see ISO/IEC 15434) + else if (value < 0x20 && value != 0x04 && value != 0x0a && value != 0x0d && value != 0x1d && value != 0x1e) { + canBeISO88591 = false; + } else if (value > 0x9F) { if (value < 0xC0 || value == 0xD7 || value == 0xF7) { isoHighOther++; From 665f1a102b1715bb5a577e2e41ff0afd89e8c097 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 25 May 2022 12:59:45 +0200 Subject: [PATCH 042/180] test: remove unnecessary explicit std::wstring and std::string construction --- test/unit/CharacterSetECITest.cpp | 10 ++++---- test/unit/TextDecoderTest.cpp | 38 ++++++++++++++-------------- test/unit/TextUtfEncodingTest.cpp | 42 +++++++++++++++---------------- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/test/unit/CharacterSetECITest.cpp b/test/unit/CharacterSetECITest.cpp index d83c208741..5e695e7c68 100644 --- a/test/unit/CharacterSetECITest.cpp +++ b/test/unit/CharacterSetECITest.cpp @@ -57,7 +57,7 @@ TEST(CharacterSetECITest, OnChangeAppendReset) auto result = OnChangeAppendReset(3, encoded, data, CharacterSet::Unknown); EXPECT_EQ(result, CharacterSet::ISO8859_1); EXPECT_TRUE(data.empty()); - EXPECT_EQ(encoded, std::wstring(L"A\u00E9Z")); + EXPECT_EQ(encoded, L"A\u00E9Z"); } { @@ -68,7 +68,7 @@ TEST(CharacterSetECITest, OnChangeAppendReset) // Encoding same auto result = OnChangeAppendReset(3, encoded, data, CharacterSet::ISO8859_1); EXPECT_EQ(result, CharacterSet::ISO8859_1); - EXPECT_EQ(data, std::string("A\xE9Z")); + EXPECT_EQ(data, "A\xE9Z"); EXPECT_TRUE(encoded.empty()); } @@ -82,7 +82,7 @@ TEST(CharacterSetECITest, OnChangeAppendReset) result = OnChangeAppendReset(20, encoded, data, CharacterSet::ISO8859_5); EXPECT_EQ(result, CharacterSet::Shift_JIS); EXPECT_TRUE(data.empty()); - EXPECT_EQ(encoded, std::wstring(L"A\u00E9ZA\u0449Z")); + EXPECT_EQ(encoded, L"A\u00E9ZA\u0449Z"); static const uint8_t bytes2[] = { 'A', 0x83, 0x65, 'Z' }; std::string data2(reinterpret_cast(&bytes2), sizeof(bytes2)); @@ -91,12 +91,12 @@ TEST(CharacterSetECITest, OnChangeAppendReset) result = OnChangeAppendReset(20, encoded, data2, result); EXPECT_EQ(result, CharacterSet::Shift_JIS); EXPECT_THAT(data2, ElementsAreArray(bytes2, sizeof(bytes2))); - EXPECT_EQ(encoded, std::wstring(L"A\u00E9ZA\u0449Z")); + EXPECT_EQ(encoded, L"A\u00E9ZA\u0449Z"); // Encoding change result = OnChangeAppendReset(4, encoded, data2, result); EXPECT_EQ(result, CharacterSet::ISO8859_2); EXPECT_TRUE(data2.empty()); - EXPECT_EQ(encoded, std::wstring(L"A\u00E9ZA\u0449ZA\u30C6Z")); + EXPECT_EQ(encoded, L"A\u00E9ZA\u0449ZA\u30C6Z"); } } diff --git a/test/unit/TextDecoderTest.cpp b/test/unit/TextDecoderTest.cpp index 6d3ec55d09..5276c4e7f5 100644 --- a/test/unit/TextDecoderTest.cpp +++ b/test/unit/TextDecoderTest.cpp @@ -90,7 +90,7 @@ TEST(TextDecoderTest, AppendShift_JIS) static const uint8_t data[] = { 0x5C }; std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::Shift_JIS); - EXPECT_EQ(str, std::wstring(L"\u005C")); // Would normally be "\u00A5" + EXPECT_EQ(str, L"\u005C"); // Would normally be "\u00A5" EXPECT_EQ(ToUtf8(str), "\\"); // "ÂĄ" ditto } @@ -99,7 +99,7 @@ TEST(TextDecoderTest, AppendShift_JIS) static const uint8_t data[] = { 0x81, 0x5F }; std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::Shift_JIS); - EXPECT_EQ(str, std::wstring(L"\uFF3C")); + EXPECT_EQ(str, L"\uFF3C"); EXPECT_EQ(ToUtf8(str), "\"); } @@ -108,7 +108,7 @@ TEST(TextDecoderTest, AppendShift_JIS) static const uint8_t data[] = { 0xA5 }; std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::Shift_JIS); - EXPECT_EQ(str, std::wstring(L"\uFF65")); + EXPECT_EQ(str, L"\uFF65"); EXPECT_EQ(ToUtf8(str), "・"); } @@ -117,7 +117,7 @@ TEST(TextDecoderTest, AppendShift_JIS) static const uint8_t data[] = { 0x7E }; std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::Shift_JIS); - EXPECT_EQ(str, std::wstring(L"~")); // Would normally be "\u203E" + EXPECT_EQ(str, L"~"); // Would normally be "\u203E" EXPECT_EQ(ToUtf8(str), "~"); // "‾" ditto } @@ -126,7 +126,7 @@ TEST(TextDecoderTest, AppendShift_JIS) 0xE4, 0xAA, 0x83, 0x65 }; std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::Shift_JIS); - EXPECT_EQ(str, std::wstring(L"a\u03B2c\u0416\uFF65\uFF7F\uFF3C\u70B9\u8317\u30C6")); + EXPECT_EQ(str, L"a\u03B2c\u0416\uFF65\uFF7F\uFF3C\u70B9\u8317\u30C6"); EXPECT_EQ(ToUtf8(str), "aβcЖ・ソ\点茗ă†"); } } @@ -137,7 +137,7 @@ TEST(TextDecoderTest, AppendBig5) static const uint8_t data[] = { 0xA1, 0x5A }; // Drawings box light left in Big5-2003; not in original Big5 std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::Big5); - EXPECT_EQ(str, std::wstring(L"\u2574")); + EXPECT_EQ(str, L"\u2574"); EXPECT_EQ(ToUtf8(str), "â•´"); } @@ -145,7 +145,7 @@ TEST(TextDecoderTest, AppendBig5) static const uint8_t data[] = { 0xA1, 0x56 }; // En dash U+2013 in Big5, horizontal bar U+2015 in Big5-2003 std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::Big5); - EXPECT_EQ(str, std::wstring(L"\u2013")); + EXPECT_EQ(str, L"\u2013"); EXPECT_EQ(ToUtf8(str), "–"); } @@ -153,7 +153,7 @@ TEST(TextDecoderTest, AppendBig5) static const uint8_t data[] = { 0x1, ' ', 0xA1, 0x71, '@', 0xC0, 0x40, 0xF9, 0xD5, 0x7F }; std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::Big5); - EXPECT_EQ(str, std::wstring(L"\u0001 \u3008@\u9310\u9F98\u007F")); + EXPECT_EQ(str, L"\u0001 \u3008@\u9310\u9F98\u007F"); EXPECT_EQ(ToUtf8(str), "\x01 ă€@éŚéľ\x7F"); } } @@ -164,7 +164,7 @@ TEST(TextDecoderTest, AppendGB2312) static const uint8_t data[] = { 'a', 0xA6, 0xC2, 'c', 0xA1, 0xA4, 0xA1, 0xAA, 0xA8, 0xA6, 'Z' }; std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::GB2312); - EXPECT_EQ(str, std::wstring(L"a\u03B2c\u00B7\u2014\u00E9Z")); + EXPECT_EQ(str, L"a\u03B2c\u00B7\u2014\u00E9Z"); EXPECT_EQ(ToUtf8(str), "aβc·—éZ"); } } @@ -176,8 +176,8 @@ TEST(TextDecoderTest, AppendGB18030) 0xA8, 0xA6, 'Z' }; std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::GB18030); - EXPECT_EQ(str, std::wstring(L"a\u03B2c\u30FB\u00B7\u2014\u00E9Z")); - EXPECT_EQ(ToUtf8(str), std::string("aβcă»Â·â€”Ă©Z")); + EXPECT_EQ(str, L"a\u03B2c\u30FB\u00B7\u2014\u00E9Z"); + EXPECT_EQ(ToUtf8(str), "aβcă»Â·â€”Ă©Z"); } } @@ -187,16 +187,16 @@ TEST(TextDecoderTest, AppendEUC_KR) static const uint8_t data[] = { 0xA2, 0xE6 }; // Euro sign U+20AC added KS X 1001:1998, not supported std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::EUC_KR); - EXPECT_EQ(str, std::wstring(L"\uFFFD")); - EXPECT_EQ(ToUtf8(str), std::string("\xEF\xBF\xBD")); + EXPECT_EQ(str, L"\uFFFD"); + EXPECT_EQ(ToUtf8(str), "\xEF\xBF\xBD"); } { static const uint8_t data[] = { 'a', 0xA4, 0xA1, 'Z' }; std::wstring str; TextDecoder::Append(str, data, sizeof(data), CharacterSet::EUC_KR); - EXPECT_EQ(str, std::wstring(L"a\u3131Z")); - EXPECT_EQ(ToUtf8(str), std::string("aㄱZ")); + EXPECT_EQ(str, L"a\u3131Z"); + EXPECT_EQ(ToUtf8(str), "aㄱZ"); } } @@ -207,15 +207,15 @@ TEST(TextDecoderTest, AppendUnicodeBig) static const uint8_t data[] = { 0x00, 0x01, 0x00, 0x7F, 0x00, 0x80, 0x00, 0xFF, 0x01, 0xFF, 0x10, 0xFF, 0xFF, 0xFD }; TextDecoder::Append(str, data, sizeof(data), CharacterSet::UnicodeBig); - EXPECT_EQ(str, std::wstring(L"\u0001\u007F\u0080\u00FF\u01FF\u10FF\uFFFD")); - EXPECT_EQ(ToUtf8(str), std::string("\x01\x7F\xC2\x80ÿǿáż\xEF\xBF\xBD")); + EXPECT_EQ(str, L"\u0001\u007F\u0080\u00FF\u01FF\u10FF\uFFFD"); + EXPECT_EQ(ToUtf8(str), "\x01\x7F\xC2\x80ÿǿáż\xEF\xBF\xBD"); } { std::wstring str; static const uint8_t data[] = { 0xD8, 0x00, 0xDC, 0x00 }; // Surrogate pair U+10000 TextDecoder::Append(str, data, sizeof(data), CharacterSet::UnicodeBig); - EXPECT_EQ(str, std::wstring(L"\U00010000")); - EXPECT_EQ(ToUtf8(str), std::string("đ€€")); + EXPECT_EQ(str, L"\U00010000"); + EXPECT_EQ(ToUtf8(str), "đ€€"); } } diff --git a/test/unit/TextUtfEncodingTest.cpp b/test/unit/TextUtfEncodingTest.cpp index 2c6c0773a5..f37e1bfb65 100644 --- a/test/unit/TextUtfEncodingTest.cpp +++ b/test/unit/TextUtfEncodingTest.cpp @@ -19,11 +19,11 @@ TEST(TextUtfEncodingTest, ToUtf8AngleEscape) EXPECT_EQ(ctype_locale, std::string("C")); #ifndef _WIN32 - EXPECT_EQ(ToUtf8(std::wstring(L"\u00B6\u0416"), angleEscape), std::string("")); + EXPECT_EQ(ToUtf8(L"\u00B6\u0416", angleEscape), ""); #else - EXPECT_EQ(ToUtf8(std::wstring(L"\u00B6\u0416"), angleEscape), std::string("¶Ж")); + EXPECT_EQ(ToUtf8(L"\u00B6\u0416", angleEscape), "¶Ж"); #endif - EXPECT_EQ(ToUtf8(std::wstring(L"\u2602"), angleEscape), std::string("")); + EXPECT_EQ(ToUtf8(L"\u2602", angleEscape), ""); #ifndef _WIN32 std::setlocale(LC_CTYPE, "en_US.UTF-8"); @@ -33,33 +33,33 @@ TEST(TextUtfEncodingTest, ToUtf8AngleEscape) EXPECT_TRUE(std::string(std::setlocale(LC_CTYPE, NULL)).find("utf8") != std::string::npos); #endif - EXPECT_EQ(ToUtf8(std::wstring(L"\u00B6\u0416"), angleEscape), std::string("¶Ж")); + EXPECT_EQ(ToUtf8(L"\u00B6\u0416", angleEscape), "¶Ж"); #ifndef _WIN32 - EXPECT_EQ(ToUtf8(std::wstring(L"\u2602"), angleEscape), std::string("â‚")); + EXPECT_EQ(ToUtf8(L"\u2602", angleEscape), "â‚"); #else - EXPECT_EQ(ToUtf8(std::wstring(L"\u2602"), angleEscape), std::string("")); + EXPECT_EQ(ToUtf8(L"\u2602", angleEscape), ""); #endif - EXPECT_EQ(ToUtf8(std::wstring(L"\x01\x1F\x7F"), angleEscape), std::string("")); - EXPECT_EQ(ToUtf8(std::wstring(L"\x80\x9F"), angleEscape), std::string("")); - EXPECT_EQ(ToUtf8(std::wstring(L"\xA0"), angleEscape), std::string("")); // NO-BREAK space (nbsp) - EXPECT_EQ(ToUtf8(std::wstring(L"\x2007"), angleEscape), std::string("")); // NO-BREAK space (numsp) - EXPECT_EQ(ToUtf8(std::wstring(L"\xFFEF"), angleEscape), std::string("")); // Was NO-BREAK space but now isn't (BOM) - EXPECT_EQ(ToUtf8(std::wstring(L"\u0100"), angleEscape), std::string("Ä€")); - EXPECT_EQ(ToUtf8(std::wstring(L"\u1000"), angleEscape), std::string("က")); - EXPECT_EQ(ToUtf8(std::wstring(L"\u2000"), angleEscape), std::string("")); // Space char (nqsp) + EXPECT_EQ(ToUtf8(L"\x01\x1F\x7F", angleEscape), ""); + EXPECT_EQ(ToUtf8(L"\x80\x9F", angleEscape), ""); + EXPECT_EQ(ToUtf8(L"\xA0", angleEscape), ""); // NO-BREAK space (nbsp) + EXPECT_EQ(ToUtf8(L"\x2007", angleEscape), ""); // NO-BREAK space (numsp) + EXPECT_EQ(ToUtf8(L"\xFFEF", angleEscape), ""); // Was NO-BREAK space but now isn't (BOM) + EXPECT_EQ(ToUtf8(L"\u0100", angleEscape), "Ä€"); + EXPECT_EQ(ToUtf8(L"\u1000", angleEscape), "က"); + EXPECT_EQ(ToUtf8(L"\u2000", angleEscape), ""); // Space char (nqsp) #ifndef _WIN32 - EXPECT_EQ(ToUtf8(std::wstring(L"\uFFFD"), angleEscape), std::string("ďż˝")); + EXPECT_EQ(ToUtf8(L"\uFFFD", angleEscape), "ďż˝"); #else - EXPECT_EQ(ToUtf8(std::wstring(L"\uFFFD"), angleEscape), std::string("")); + EXPECT_EQ(ToUtf8(L"\uFFFD", angleEscape), ""); #endif - EXPECT_EQ(ToUtf8(std::wstring(L"\uFFFF"), angleEscape), std::string("")); + EXPECT_EQ(ToUtf8(L"\uFFFF", angleEscape), ""); #ifndef __APPLE__ - EXPECT_EQ(ToUtf8(std::wstring(L"\U00010000"), angleEscape), std::string("đ€€")); + EXPECT_EQ(ToUtf8(L"\U00010000", angleEscape), "đ€€"); #else - EXPECT_EQ(ToUtf8(std::wstring(L"\U00010000"), angleEscape), std::string("")); + EXPECT_EQ(ToUtf8(L"\U00010000", angleEscape), ""); #endif - EXPECT_EQ(ToUtf8(std::wstring(L"\xD800Z"), angleEscape), std::string("Z")); // Unpaired high surrogate - EXPECT_EQ(ToUtf8(std::wstring(L"A\xDC00"), angleEscape), std::string("A")); // Unpaired low surrogate + EXPECT_EQ(ToUtf8(L"\xD800Z", angleEscape), "Z"); // Unpaired high surrogate + EXPECT_EQ(ToUtf8(L"A\xDC00", angleEscape), "A"); // Unpaired low surrogate std::setlocale(LC_CTYPE, ctype_locale.c_str()); } From 984c64b614246b0617a90f340b5ad594048c11b5 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 25 May 2022 13:00:50 +0200 Subject: [PATCH 043/180] android: fix potential issue with ISO8859 std::string to JString conversion --- wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp b/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp index e552c912d8..6ae1333f12 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp +++ b/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp @@ -4,6 +4,8 @@ // SPDX-License-Identifier: Apache-2.0 #include "JNIUtils.h" +#include "TextDecoder.h" + #include #include @@ -53,7 +55,7 @@ jstring C2JString(JNIEnv* env, const std::wstring& str) jstring C2JString(JNIEnv* env, const std::string& str) { - return C2JString(env, std::wstring(str.begin(), str.end())); + return C2JString(env, ZXing::TextDecoder::FromLatin1(str)); } std::string J2CString(JNIEnv* env, jstring str) From 966ddfab66770404c4caa57e0468dda7c3c6d077 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 25 May 2022 13:17:06 +0200 Subject: [PATCH 044/180] DecoderResult: switch internal representation of ecLevel to std::string --- core/src/DecoderResult.h | 4 ++-- core/src/Result.cpp | 2 +- core/src/maxicode/MCDecoder.cpp | 2 +- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 2 +- core/src/qrcode/QRErrorCorrectionLevel.cpp | 4 ++-- core/src/qrcode/QRErrorCorrectionLevel.h | 2 +- test/unit/qrcode/QRErrorCorrectionLevelTest.cpp | 10 ++++++---- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index c239d07e03..6f768cc8ec 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -34,7 +34,7 @@ class DecoderResult Content _content; int _numBits = 0; std::wstring _text; - std::wstring _ecLevel; + std::string _ecLevel; int _errorsCorrected = -1; int _erasures = -1; int _lineCount = 0; @@ -90,7 +90,7 @@ class DecoderResult DecoderResult&& SETTER(TYPE&& v) && { _##GETTER = std::move(v); return std::move(*this); } ZX_PROPERTY(int, numBits, setNumBits) - ZX_PROPERTY(std::wstring, ecLevel, setEcLevel) + ZX_PROPERTY(std::string, ecLevel, setEcLevel) ZX_PROPERTY(int, errorsCorrected, setErrorsCorrected) ZX_PROPERTY(int, erasures, setErasures) ZX_PROPERTY(int, lineCount, setLineCount) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index f397219f9b..13ae8f663d 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -36,7 +36,7 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), - _ecLevel(decodeResult.ecLevel()), + _ecLevel(TextDecoder::FromLatin1(decodeResult.ecLevel())), _symbologyIdentifier(decodeResult.symbologyIdentifier()), _sai(decodeResult.structuredAppend()), _isMirrored(decodeResult.isMirrored()), diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 5bbeba0e95..9a499f9399 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -307,7 +307,7 @@ DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& chara // No identifier defined for mode 6 return DecoderResult(std::move(bytes), std::move(result)) - .setEcLevel(std::to_wstring(mode)) + .setEcLevel(std::to_string(mode)) .setSymbologyIdentifier(std::move(symbologyIdentifier)) .setStructuredAppend(sai) .setReaderInit(mode == 6); diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index 1d0620ce23..e616790f4f 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -845,7 +845,7 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel, c content.append(result); return DecoderResult(ByteArray(), std::move(resultEncoded), std::move(content)) - .setEcLevel(std::to_wstring(ecLevel)) + .setEcLevel(std::to_string(ecLevel)) // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using modifier // that indicates ECI protocol (ISO/IEC 15438:2015 Annex L Table L.1) .setSymbologyIdentifier("]L2") diff --git a/core/src/qrcode/QRErrorCorrectionLevel.cpp b/core/src/qrcode/QRErrorCorrectionLevel.cpp index 6194e5c7b5..0c66b2263c 100644 --- a/core/src/qrcode/QRErrorCorrectionLevel.cpp +++ b/core/src/qrcode/QRErrorCorrectionLevel.cpp @@ -10,10 +10,10 @@ namespace ZXing::QRCode { -const wchar_t* ToString(ErrorCorrectionLevel l) +const char* ToString(ErrorCorrectionLevel l) { assert(l != ErrorCorrectionLevel::Invalid); - static const wchar_t* const LEVEL_STR[] = {L"L", L"M", L"Q", L"H", nullptr}; + static const char* const LEVEL_STR[] = {"L", "M", "Q", "H", nullptr}; return LEVEL_STR[static_cast(l)]; } diff --git a/core/src/qrcode/QRErrorCorrectionLevel.h b/core/src/qrcode/QRErrorCorrectionLevel.h index 58d689240f..704fc86939 100644 --- a/core/src/qrcode/QRErrorCorrectionLevel.h +++ b/core/src/qrcode/QRErrorCorrectionLevel.h @@ -24,7 +24,7 @@ enum class ErrorCorrectionLevel Invalid, // denotes in invalid/unknown value }; -const wchar_t* ToString(ErrorCorrectionLevel l); +const char* ToString(ErrorCorrectionLevel l); ErrorCorrectionLevel ECLevelFromString(const char* str); ErrorCorrectionLevel ECLevelFromBits(int bits, const bool isMicro = false); int BitsFromECLevel(ErrorCorrectionLevel l); diff --git a/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp b/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp index f3870b4bbe..77c103023e 100644 --- a/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp +++ b/test/unit/qrcode/QRErrorCorrectionLevelTest.cpp @@ -36,8 +36,10 @@ TEST(QRErrorCorrectionLevelTest, ForMicroBits) TEST(QRErrorCorrectionLevelTest, ToString) { - EXPECT_EQ(L"L", std::wstring(ToString(ErrorCorrectionLevel::Low))); - EXPECT_EQ(L"M", std::wstring(ToString(ErrorCorrectionLevel::Medium))); - EXPECT_EQ(L"Q", std::wstring(ToString(ErrorCorrectionLevel::Quality))); - EXPECT_EQ(L"H", std::wstring(ToString(ErrorCorrectionLevel::High))); + using namespace std::literals; + + EXPECT_EQ("L"s, ToString(ErrorCorrectionLevel::Low)); + EXPECT_EQ("M"s, ToString(ErrorCorrectionLevel::Medium)); + EXPECT_EQ("Q"s, ToString(ErrorCorrectionLevel::Quality)); + EXPECT_EQ("H"s, ToString(ErrorCorrectionLevel::High)); } From a7944233365ef15d84fba496144f051fd5edffd0 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 25 May 2022 13:26:07 +0200 Subject: [PATCH 045/180] cleanup: cut back on explicit std::string construction --- core/src/GTIN.cpp | 6 +++--- .../oned/rss/ODRSSExpandedBinaryDecoder.cpp | 18 +++++++++--------- test/unit/BarcodeFormatTest.cpp | 8 +++++--- test/unit/CharacterSetECITest.cpp | 16 ++++++++-------- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/core/src/GTIN.cpp b/core/src/GTIN.cpp index 8d498206a8..4231597aa8 100644 --- a/core/src/GTIN.cpp +++ b/core/src/GTIN.cpp @@ -165,7 +165,7 @@ std::string LookupCountryIdentifier(const std::string& GTIN, const BarcodeFormat const std::string::size_type size = space != std::string::npos ? space : GTIN.size(); if (size != 14 && size != 13 && size != 12 && size != 8) - return std::string(); + return {}; // GTIN-14 leading packaging level indicator const int first = size == 14 ? 1 : 0; @@ -176,7 +176,7 @@ std::string LookupCountryIdentifier(const std::string& GTIN, const BarcodeFormat // 0000000 Restricted Circulation Numbers; 0000001-0000099 unused to avoid collision with GTIN-8 int prefix = std::stoi(GTIN.substr(first, 7 - implicitZero)); if (prefix >= 0 && prefix <= 99) - return std::string(); + return {}; // 00001-00009 US prefix = std::stoi(GTIN.substr(first, 5 - implicitZero)); @@ -193,7 +193,7 @@ std::string LookupCountryIdentifier(const std::string& GTIN, const BarcodeFormat // Special case EAN-8 for prefix < 100 (GS1 General Specifications Figure 1.4.3-1) if (size == 8 && format == BarcodeFormat::EAN8 && prefix <= 99) // Restricted Circulation Numbers - return std::string(); + return {}; const auto it = std::lower_bound(std::begin(COUNTRIES), std::end(COUNTRIES), CountryId{0, prefix, nullptr}); diff --git a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp index 3a669a049f..444ecf4982 100644 --- a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp +++ b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp @@ -117,7 +117,7 @@ static std::string DecodeAnyAI(const BitArray& bits) if (StatusIsOK(DecodeAppIdAllCodes(bits, HEADER_SIZE, -1, buffer))) { return buffer; } - return std::string(); + return {}; } static std::string DecodeAI013103(const BitArray& bits) @@ -126,7 +126,7 @@ static std::string DecodeAI013103(const BitArray& bits) static const int WEIGHT_SIZE = 15; if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE) { - return std::string(); + return {}; } std::string buffer; @@ -146,7 +146,7 @@ static std::string DecodeAI01320x(const BitArray& bits) static const int WEIGHT_SIZE = 15; if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE) { - return std::string(); + return {}; } std::string buffer; @@ -167,7 +167,7 @@ static std::string DecodeAI01392x(const BitArray& bits) static const int LAST_DIGIT_SIZE = 2; if (bits.size() < HEADER_SIZE + AI01_GTIN_SIZE) { - return std::string(); + return {}; } std::string buffer; @@ -184,7 +184,7 @@ static std::string DecodeAI01392x(const BitArray& bits) && StatusIsOK(DecodeAppIdAllCodes(bits, pos, remainingValue, buffer))) { return buffer; } - return std::string(); + return {}; } static std::string DecodeAI01393x(const BitArray& bits) @@ -194,7 +194,7 @@ static std::string DecodeAI01393x(const BitArray& bits) static const int FIRST_THREE_DIGITS_SIZE = 10; if (bits.size() < HEADER_SIZE + AI01_GTIN_SIZE) { - return std::string(); + return {}; } std::string buffer; @@ -221,7 +221,7 @@ static std::string DecodeAI01393x(const BitArray& bits) && StatusIsOK(DecodeAppIdAllCodes(bits, pos, remainingValue, buffer))) { return buffer; } - return std::string(); + return {}; } static std::string DecodeAI013x0x1x(const BitArray& bits, const char* firstAIdigits, const char* dateCode) @@ -231,7 +231,7 @@ static std::string DecodeAI013x0x1x(const BitArray& bits, const char* firstAIdig static const int DATE_SIZE = 16; if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE + DATE_SIZE) { - return std::string(); + return {}; } std::string buffer; @@ -313,7 +313,7 @@ std::string DecodeExpandedBits(const BitArray& bits) case 63: return DecodeAI013x0x1x(bits, "320", "17"); } - return std::string(); + return {}; //throw new IllegalStateException("unknown decoder: " + information); } diff --git a/test/unit/BarcodeFormatTest.cpp b/test/unit/BarcodeFormatTest.cpp index 7dfcbf3ab8..9413314067 100644 --- a/test/unit/BarcodeFormatTest.cpp +++ b/test/unit/BarcodeFormatTest.cpp @@ -13,9 +13,11 @@ using namespace ZXing; TEST(BarcodeFormatTest, BarcodeFormat) { - EXPECT_EQ(ToString(BarcodeFormat::QRCode), std::string("QRCode")); - EXPECT_EQ(ToString(BarcodeFormat::None), std::string("None")); - EXPECT_EQ(ToString(BarcodeFormat::DataMatrix | BarcodeFormat::EAN13), std::string("DataMatrix|EAN-13")); + using namespace std::literals; + + EXPECT_EQ(ToString(BarcodeFormat::QRCode), "QRCode"s); + EXPECT_EQ(ToString(BarcodeFormat::None), "None"s); + EXPECT_EQ(ToString(BarcodeFormat::DataMatrix | BarcodeFormat::EAN13), "DataMatrix|EAN-13"); EXPECT_EQ(BarcodeFormat::EAN8, BarcodeFormatFromString("EAN_8")); EXPECT_EQ(BarcodeFormat::EAN8, BarcodeFormatFromString("EAN-8")); diff --git a/test/unit/CharacterSetECITest.cpp b/test/unit/CharacterSetECITest.cpp index 5e695e7c68..3495bc3b7e 100644 --- a/test/unit/CharacterSetECITest.cpp +++ b/test/unit/CharacterSetECITest.cpp @@ -25,14 +25,14 @@ TEST(CharacterSetECITest, Charset2ECI) TEST(CharacterSetECITest, InitEncoding) { - EXPECT_EQ(InitEncoding(std::string()), CharacterSet::ISO8859_1); - EXPECT_EQ(InitEncoding(std::string(), CharacterSet::ISO8859_2), CharacterSet::ISO8859_2); - EXPECT_EQ(InitEncoding(std::string("asdfasdf")), CharacterSet::ISO8859_1); - EXPECT_EQ(InitEncoding(std::string("asdfasdf"), CharacterSet::ISO8859_2), CharacterSet::ISO8859_2); - EXPECT_EQ(InitEncoding(std::string("ISO-8859-1")), CharacterSet::ISO8859_1); - EXPECT_EQ(InitEncoding(std::string("ISO-8859-2")), CharacterSet::ISO8859_2); - EXPECT_EQ(InitEncoding(std::string("UTF-16BE")), CharacterSet::UnicodeBig); - EXPECT_EQ(InitEncoding(std::string(), CharacterSet::Unknown), CharacterSet::Unknown); + EXPECT_EQ(InitEncoding(""), CharacterSet::ISO8859_1); + EXPECT_EQ(InitEncoding("", CharacterSet::ISO8859_2), CharacterSet::ISO8859_2); + EXPECT_EQ(InitEncoding("asdfasdf"), CharacterSet::ISO8859_1); + EXPECT_EQ(InitEncoding("asdfasdf", CharacterSet::ISO8859_2), CharacterSet::ISO8859_2); + EXPECT_EQ(InitEncoding("ISO-8859-1"), CharacterSet::ISO8859_1); + EXPECT_EQ(InitEncoding("ISO-8859-2"), CharacterSet::ISO8859_2); + EXPECT_EQ(InitEncoding("UTF-16BE"), CharacterSet::UnicodeBig); + EXPECT_EQ(InitEncoding("", CharacterSet::Unknown), CharacterSet::Unknown); } TEST(CharacterSetECITest, OnChangeAppendReset) From d47ddec0745d71df7cc7abb58df1843febdf0a33 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 25 May 2022 13:35:24 +0200 Subject: [PATCH 046/180] ECI: replace all non-ECI entries on first ECI entry with default ECI This implements the spec in line 'd' in the table in https://github.com/zxing/zxing/pull/1498#issuecomment-1134578616 --- core/src/Content.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index c17a4d12ca..c38e9c8b1e 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -26,7 +26,9 @@ void Content::ForEachECIBlock(FUNC func) const void Content::switchEncoding(ECI eci, bool isECI) { - // TODO: replace non-ECI entries on first ECI entry with default ECI + // replace all non-ECI entries on first ECI entry with default ECI + if (isECI && !hasECI) + encodings = {{ECI::ISO8859_1, 0}}; if (isECI || !hasECI) { if (encodings.back().pos == Size(binary)) encodings.back().eci = eci; // no point in recording 0 length segments From 24deab4ff17ca58e109f58bc818429697f85284a Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 27 May 2022 08:51:49 +0200 Subject: [PATCH 047/180] example: use std::ios::boolalpha --- example/ZXingReader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index f4ca2d427f..576152408d 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -135,6 +135,8 @@ int main(int argc, char* argv[]) if (angleEscape) std::setlocale(LC_CTYPE, "en_US.UTF-8"); // Needed so `std::iswgraph()` in `ToUtf8(angleEscape)` does not 'swallow' all printable non-ascii utf8 chars + std::cout.setf(std::ios::boolalpha); + for (const auto& filePath : filePaths) { int width, height, channels; std::unique_ptr buffer(stbi_load(filePath.c_str(), &width, &height, &channels, 3), stbi_image_free); @@ -187,7 +189,7 @@ int main(int argc, char* argv[]) << "Identifier: " << result.symbologyIdentifier() << "\n" << "Position: " << result.position() << "\n" << "Rotation: " << result.orientation() << " deg\n" - << "IsMirrored: " << (result.isMirrored() ? "true" : "false") << "\n" + << "IsMirrored: " << result.isMirrored() << "\n" << "Error: " << ToString(result.status()) << "\n"; auto printOptional = [](const char* key, const std::string& v) { From a56ade3d3630da558771fb55a9aac1342ded7749 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 27 May 2022 08:55:55 +0200 Subject: [PATCH 048/180] Result: move ECI encoding specification for 1D codes to Content --- core/src/Content.h | 2 +- core/src/Result.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/Content.h b/core/src/Content.h index 7b7dce5af1..cc671025c2 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -34,7 +34,7 @@ class Content std::string applicationIndicator; Content() = default; - Content(ByteArray&& binary, ECI defaultECI) : binary(binary), encodings{{defaultECI, 0}} {} + Content(ByteArray&& binary) : binary(std::move(binary)), encodings{{ECI::ISO8859_1, 0}} {} void switchEncoding(ECI eci) { switchEncoding(eci, true); } void switchEncoding(CharacterSet cs); diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 13ae8f663d..10b4a52731 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -18,7 +18,7 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor std::string&& symbologyIdentifier, ByteArray&& rawBytes, const bool readerInit) : _format(format), - _content({ByteArray(text), ECI::ISO8859_1}), + _content({ByteArray(text)}), _text(TextDecoder::FromLatin1(text)), _position(Line(y, xStart, xStop)), _rawBytes(std::move(rawBytes)), From 096f44a47ecb920239bdb8b00f7c25e95e1b9ae3 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 27 May 2022 09:27:46 +0200 Subject: [PATCH 049/180] ECI: new SymbologyIdentifier class to support ECI protocol modifiers Not yet implemented for PDF417, Aztec and Maxicode --- core/src/Content.cpp | 8 +++++++- core/src/Content.h | 17 +++++++++++++++-- core/src/DecoderResult.h | 2 ++ core/src/Result.cpp | 4 ++-- core/src/Result.h | 2 +- core/src/datamatrix/DMDecoder.cpp | 11 ++++------- core/src/oned/ODCodabarReader.cpp | 4 ++-- core/src/oned/ODCode128Reader.cpp | 8 ++++---- core/src/oned/ODCode39Reader.cpp | 8 +++----- core/src/oned/ODCode93Reader.cpp | 4 ++-- core/src/oned/ODITFReader.cpp | 6 +++--- core/src/oned/ODMultiUPCEANReader.cpp | 6 +++--- core/src/qrcode/QRDecoder.cpp | 9 +++------ 13 files changed, 51 insertions(+), 38 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index c38e9c8b1e..f4c3a20f10 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -12,6 +12,12 @@ namespace ZXing { +std::string ToString(ContentType type) +{ + const char* t2s[] = {"Text", "Binary", "Mixed"}; + return t2s[static_cast(type)]; +} + template void Content::ForEachECIBlock(FUNC func) const { @@ -60,7 +66,7 @@ std::wstring Content::text() const std::string Content::utf8Protocol() const { - std::wstring res; + std::wstring res = TextDecoder::FromLatin1(symbology.toString(true)); ECI lastECI = ECI::Unknown; auto fallbackCS = guessEncoding(); diff --git a/core/src/Content.h b/core/src/Content.h index cc671025c2..5e48f35406 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -12,10 +12,21 @@ namespace ZXing { enum class ContentType { Text, Binary, Mixed }; -class Content +std::string ToString(ContentType type); + +struct SymbologyIdentifier { - bool hasECI = false; + char code, modifier; + int eciModifierOffset = 0; + std::string toString(bool hasECI = false) const + { + return ']' + std::string(1, code) + static_cast(modifier + eciModifierOffset * hasECI); + } +}; + +class Content +{ template void ForEachECIBlock(FUNC f) const; @@ -32,6 +43,8 @@ class Content std::vector encodings = {{ECI::Unknown, 0}}; std::string hintedCharset; std::string applicationIndicator; + SymbologyIdentifier symbology; + bool hasECI = false; Content() = default; Content(ByteArray&& binary) : binary(std::move(binary)), encodings{{ECI::ISO8859_1, 0}} {} diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 6f768cc8ec..8714396f29 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -58,6 +58,8 @@ class DecoderResult // provide some best guess fallback for barcodes not, yet supporting the content info if (_content.empty() && std::all_of(_text.begin(), _text.end(), [](auto c) { return c < 256; })) std::for_each(_text.begin(), _text.end(), [this](wchar_t c) { _content += static_cast(c); }); + if (_content.symbology.code != 0) + _symbologyIdentifier = _content.symbology.toString(); } DecoderResult() = default; diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 10b4a52731..9d3c417868 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -15,7 +15,7 @@ namespace ZXing { Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, - std::string&& symbologyIdentifier, ByteArray&& rawBytes, const bool readerInit) + SymbologyIdentifier si, ByteArray&& rawBytes, const bool readerInit) : _format(format), _content({ByteArray(text)}), @@ -23,7 +23,7 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor _position(Line(y, xStart, xStop)), _rawBytes(std::move(rawBytes)), _numBits(Size(_rawBytes) * 8), - _symbologyIdentifier(std::move(symbologyIdentifier)), + _symbologyIdentifier(si.toString()), _readerInit(readerInit), _lineCount(0) {} diff --git a/core/src/Result.h b/core/src/Result.h index c869e188c9..bdf767e30b 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -35,7 +35,7 @@ class Result // 1D convenience constructor Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, - std::string&& symbologyIdentifier = "", ByteArray&& rawBytes = {}, const bool readerInit = false); + SymbologyIdentifier si, ByteArray&& rawBytes = {}, const bool readerInit = false); Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format); diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index af58169cbf..87286c8300 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -277,10 +277,10 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b { BitSource bits(bytes); Content result; + result.symbology = {'d', '1', 3}; // ECC 200 (ISO 16022:2006 Annex N Table N.1) result.hintedCharset = characterSet; std::string resultTrailer; - int symbologyIdModifier = 1; // ECC 200 (ISO 16022:2006 Annex N Table N.1) struct StructuredAppendInfo sai; bool readerInit = false; bool firstCodeword = true; @@ -298,16 +298,13 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b case 230: DecodeC40OrTextSegment(bits, result, Mode::C40); break; case 231: DecodeBase256Segment(bits, result); break; case 232: // FNC1 - // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using - // modifiers that indicate ECI protocol (ISO 16022:2006 Annex N Table N.1, ISO 21471:2020 Annex G Table G.1) - // Only recognizing an FNC1 as first/second by codeword position (aka symbol character position), not // by decoded character position, i.e. not recognizing a C40/Text encoded FNC1 (which requires a latch // and a shift) if (bits.byteOffset() == firstFNC1Position) - symbologyIdModifier = 2; // GS1 + result.symbology.modifier = '2'; // GS1 else if (bits.byteOffset() == firstFNC1Position + 1) - symbologyIdModifier = 3; // AIM, note no AIM Application Indicator format defined, ISO 16022:2006 11.2 + result.symbology.modifier = '3'; // AIM, note no AIM Application Indicator format defined, ISO 16022:2006 11.2 else result.push_back((char)29); // translate as ASCII 29 break; @@ -360,9 +357,9 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b } result.append(resultTrailer); + result.symbology.modifier += isDMRE * 6; return DecoderResult(std::move(bytes), {}, std::move(result)) - .setSymbologyIdentifier("]d" + std::to_string(symbologyIdModifier + (isDMRE ? 6 : 0))) .setStructuredAppend(sai) .setReaderInit(readerInit); } diff --git a/core/src/oned/ODCodabarReader.cpp b/core/src/oned/ODCodabarReader.cpp index 53d8dec94b..0ebec78088 100644 --- a/core/src/oned/ODCodabarReader.cpp +++ b/core/src/oned/ODCodabarReader.cpp @@ -94,10 +94,10 @@ CodabarReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr= 'a' && txt[0] <= 'z')))) { // ISO/IEC 15417:2007 Annex B.2 // FNC1 in second position following Code Set C "00-99" or Code Set A/B "A-Za-z" - AIM - _symbologyIdentifier = "]C2"; + _symbologyIdentifier.modifier = '2'; } else { // ISO/IEC 15417:2007 Annex B.3. Otherwise FNC1 is returned as ASCII 29 (GS) @@ -153,7 +153,7 @@ class Raw2TxtDecoder return txt.substr(0, lastTxtSize); } - std::string symbologyIdentifier() const { return _symbologyIdentifier; } + SymbologyIdentifier symbologyIdentifier() const { return _symbologyIdentifier; } bool readerInit() const { return _readerInit; } }; diff --git a/core/src/oned/ODCode39Reader.cpp b/core/src/oned/ODCode39Reader.cpp index 3952025a4f..caf4d76f18 100644 --- a/core/src/oned/ODCode39Reader.cpp +++ b/core/src/oned/ODCode39Reader.cpp @@ -133,13 +133,11 @@ Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique return Result(DecodeStatus::FormatError); // Symbology identifier modifiers ISO/IEC 16388:2007 Annex C Table C.1 - static const int symbologyModifiers[4] = { 0, 3 /*checksum*/, 4 /*extended*/, 7 /*checksum,extended*/ }; - int symbologyIdModifier = symbologyModifiers[(int)_extendedMode * 2 + (int)_validateCheckSum]; - - std::string symbologyIdentifier("]A" + std::to_string(symbologyIdModifier)); + constexpr const char symbologyModifiers[4] = { '0', '3' /*checksum*/, '4' /*extended*/, '7' /*checksum,extended*/ }; + SymbologyIdentifier symbologyIdentifier = {'A', symbologyModifiers[(int)_extendedMode * 2 + (int)_validateCheckSum]}; int xStop = next.pixelsTillEnd(); - return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::Code39, std::move(symbologyIdentifier)); + return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::Code39, symbologyIdentifier); } } // namespace ZXing::OneD diff --git a/core/src/oned/ODCode93Reader.cpp b/core/src/oned/ODCode93Reader.cpp index 484308aafc..7b3552d519 100644 --- a/core/src/oned/ODCode93Reader.cpp +++ b/core/src/oned/ODCode93Reader.cpp @@ -124,10 +124,10 @@ Result Code93Reader::decodePattern(int rowNumber, PatternView& next, std::unique return Result(DecodeStatus::FormatError); // Symbology identifier ISO/IEC 15424:2008 4.4.10 no modifiers - std::string symbologyIdentifier("]G0"); + SymbologyIdentifier symbologyIdentifier = {'G', '0'}; int xStop = next.pixelsTillEnd(); - return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::Code93, std::move(symbologyIdentifier)); + return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::Code93, symbologyIdentifier); } } // namespace ZXing::OneD diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index 704cc7bfaa..3de5d5206c 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -81,13 +81,13 @@ Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_pt // Symbology identifier ISO/IEC 16390:2007 Annex C Table C.1 // See also GS1 General Specifications 5.1.3 Figure 5.1.3-2 - std::string symbologyIdentifier("]I0"); // No check character validation + SymbologyIdentifier symbologyIdentifier = {'I', '0'}; // No check character validation if (_validateCheckSum || (txt.size() == 14 && GTIN::IsCheckDigitValid(txt))) // If no hint test if valid ITF-14 - symbologyIdentifier = "]I1"; // Modulo 10 symbol check character validated and transmitted + symbologyIdentifier.modifier = '1'; // Modulo 10 symbol check character validated and transmitted int xStop = next.pixelsTillEnd(); - return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::ITF, std::move(symbologyIdentifier)); + return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::ITF, symbologyIdentifier); } } // namespace ZXing::OneD diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index cfa806ea17..92b59d379a 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -289,7 +289,7 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u // Symbology identifier modifiers ISO/IEC 15420:2009 Annex B Table B.1 // ISO/IEC 15420:2009 (& GS1 General Specifications 5.1.3) states that the content for "]E0" should be 13 digits, // i.e. converted to EAN-13 if UPC-A/E, but not doing this here to maintain backward compatibility - std::string symbologyIdentifier(res.format == BarcodeFormat::EAN8 ? "]E4" : "]E0"); + SymbologyIdentifier symbologyIdentifier = {'E', res.format == BarcodeFormat::EAN8 ? '4' : '0'}; auto ext = res.end; PartialResult addOnRes; @@ -301,7 +301,7 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u res.txt += " " + addOnRes.txt; if (res.format != BarcodeFormat::EAN8) // Keeping EAN-8 with add-on as "]E4" - symbologyIdentifier = "]E3"; // Combined packet, EAN-13, UPC-A, UPC-E, with add-on + symbologyIdentifier.modifier = '3'; // Combined packet, EAN-13, UPC-A, UPC-E, with add-on } next = res.end; @@ -309,7 +309,7 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u if (_hints.eanAddOnSymbol() == EanAddOnSymbol::Require && !addOnRes.isValid()) return Result(DecodeStatus::NotFound); - return {res.txt, rowNumber, begin.pixelsInFront(), res.end.pixelsTillEnd(), res.format, std::move(symbologyIdentifier)}; + return {res.txt, rowNumber, begin.pixelsInFront(), res.end.pixelsTillEnd(), res.format, symbologyIdentifier}; } } // namespace ZXing::OneD diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index ad0b2b27fb..a5b2c579dd 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -259,8 +259,8 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo { BitSource bits(bytes); Content result; + result.symbology = {'Q', '1', 1}; result.hintedCharset = hintedCharset.empty() ? "Auto" : hintedCharset; - int symbologyIdModifier = 1; // ISO/IEC 18004:2015 Annex F Table F.1 StructuredAppendInfo structuredAppend; const int modeBitLength = CodecModeBitsLength(version); @@ -277,15 +277,13 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo case CodecMode::FNC1_FIRST_POSITION: // if (!result.empty()) // uncomment to enforce specification // throw std::runtime_error("GS1 Indicator (FNC1 in first position) at illegal position"); - // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using - // modifiers that indicate ECI protocol (ISO/IEC 18004:2015 Annex F Table F.1) - symbologyIdModifier = 3; + result.symbology.modifier = '3'; result.applicationIndicator = "GS1"; // In Alphanumeric mode undouble doubled percents and treat single percent as break; case CodecMode::FNC1_SECOND_POSITION: if (!result.empty()) throw std::runtime_error("AIM Application Indicator (FNC1 in second position) at illegal position"); - symbologyIdModifier = 5; // As above + 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 < 10) // "00-09" result += '0' + std::to_string(appInd); @@ -343,7 +341,6 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo return DecoderResult(std::move(bytes), {}, std::move(result)) .setEcLevel(ToString(ecLevel)) - .setSymbologyIdentifier("]Q" + std::to_string(symbologyIdModifier)) .setStructuredAppend(structuredAppend); } From 2c01b06015bb9c2ef0cac5483f70d3b5e909f8d9 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 27 May 2022 09:38:39 +0200 Subject: [PATCH 050/180] ECI: remove dead code --- core/src/oned/ODDataBarExpandedReader.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index 95832f52de..42fed5a6fa 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -373,13 +373,10 @@ Result DataBarExpandedReader::decodePattern(int rowNumber, PatternView& view, RemovePairs(allPairs, pairs); - // Symbology identifier ISO/IEC 24724:2011 Section 9 and GS1 General Specifications 5.1.3 Figure 5.1.3-2 - std::string symbologyIdentifier("]e0"); - // TODO: EstimatePosition misses part of the symbol in the stacked case where the last row contains less pairs than // the first return {DecoderResult({}, TextDecoder::FromLatin1(txt)) - .setSymbologyIdentifier("]e0") + .setSymbologyIdentifier("]e0") // ISO/IEC 24724:2011 Section 9 and GS1 General Specifications 5.1.3 Figure 5.1.3-2 .setLineCount(EstimateLineCount(pairs.front(), pairs.back())), EstimatePosition(pairs.front(), pairs.back()), BarcodeFormat::DataBarExpanded}; } From f7cfbbb4a5e420f3a4b5c2d523a49d098809182d Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 27 May 2022 09:40:01 +0200 Subject: [PATCH 051/180] Result: add contentType and hasECI properties (experimental) --- core/src/Result.h | 5 ++++- example/ZXingReader.cpp | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/Result.h b/core/src/Result.h index bdf767e30b..ffa319a9c7 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -50,7 +50,10 @@ class Result // WARNING: this is an experimental API and may change/disappear const ByteArray& binary() const { return _content.binary; } const std::string utf8Protocol() const { return _content.utf8Protocol(); } - const std::string applicationIndicator() const { return _content.applicationIndicator; } + const std::string& applicationIndicator() const { return _content.applicationIndicator; } + ContentType contentType() const { return _content.type(); } + bool hasECI() const { return _content.hasECI; } + // END WARNING const Position& position() const { return _position; } void setPosition(Position pos) { _position = pos; } diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 576152408d..a368169e40 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -187,6 +187,8 @@ int main(int argc, char* argv[]) << "ECI-Proto: \"" << result.utf8Protocol() << "\"\n" << "Format: " << ToString(result.format()) << "\n" << "Identifier: " << result.symbologyIdentifier() << "\n" + << "Content: " << ToString(result.contentType()) << "\n" + << "HasECI: " << result.hasECI() << "\n" << "Position: " << result.position() << "\n" << "Rotation: " << result.orientation() << " deg\n" << "IsMirrored: " << result.isMirrored() << "\n" From cd09da36f299f143415aaafa68bb9aca69122fb0 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 27 May 2022 12:39:45 +0200 Subject: [PATCH 052/180] ECI: tune ContentType deduction --- core/src/Content.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index f4c3a20f10..771459b6b8 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -117,15 +117,15 @@ ContentType Content::type() const { auto isBinary = [](Encoding e) { return !IsText(e.eci); }; - if (hasECI) { - if (std::none_of(encodings.begin(), encodings.end(), isBinary)) - return ContentType::Text; - if (std::all_of(encodings.begin(), encodings.end(), isBinary)) - return ContentType::Binary; - } else { - if (std::none_of(encodings.begin(), encodings.end(), isBinary)) - return ContentType::Text; + if (std::none_of(encodings.begin(), encodings.end(), isBinary)) + return ContentType::Text; + if (std::all_of(encodings.begin(), encodings.end(), isBinary)) + return ContentType::Binary; + + if (!hasECI) { auto cs = guessEncoding(); + if (IsText(ToECI(cs))) + return ContentType::Text; if (cs == CharacterSet::BINARY) return ContentType::Binary; } From 14cb929a4cb2bcb9e1aa0c3507d7184d1ce5782a Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 28 May 2022 17:32:58 +0200 Subject: [PATCH 053/180] ECI: add `binaryECI` channel to `Result` Still a WIP. See https://github.com/nu-book/zxing-cpp/issues/334#issuecomment-1140271102 --- core/src/Content.cpp | 19 +++++++++++++++++++ core/src/Content.h | 1 + core/src/Result.h | 1 + example/ZXingReader.cpp | 3 ++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 771459b6b8..1a505dfa7e 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -98,6 +98,25 @@ std::string Content::utf8Protocol() const return TextUtfEncoding::ToUtf8(res); } +ByteArray Content::binaryECI() const +{ + std::string res = symbology.toString(true); + + ForEachECIBlock([&](ECI eci, int begin, int end) { + if (hasECI) + res += ToString(eci); + + for (int i = begin; i != end; ++i) { + char c = static_cast(binary[i]); + res += c; + if (c == '\\') // in the ECI protocol a '\' has to be doubled + res += c; + } + }); + + return ByteArray(res); +} + CharacterSet Content::guessEncoding() const { // assemble all blocks with unknown encoding diff --git a/core/src/Content.h b/core/src/Content.h index 5e48f35406..abb8bc873b 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -65,6 +65,7 @@ class Content std::wstring text() const; std::string utf8Protocol() const; + ByteArray binaryECI() const; CharacterSet guessEncoding() const; ContentType type() const; }; diff --git a/core/src/Result.h b/core/src/Result.h index ffa319a9c7..f13afcc233 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -49,6 +49,7 @@ class Result // WARNING: this is an experimental API and may change/disappear const ByteArray& binary() const { return _content.binary; } + const ByteArray binaryECI() const { return _content.binaryECI(); } const std::string utf8Protocol() const { return _content.utf8Protocol(); } const std::string& applicationIndicator() const { return _content.applicationIndicator; } ContentType contentType() const { return _content.type(); } diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index a368169e40..9baf5961f8 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -184,7 +184,8 @@ int main(int argc, char* argv[]) } std::cout << "Text: \"" << ToUtf8(result.text(), angleEscape) << "\"\n" << "Binary: \"" << ToHex(result.binary()) << "\"\n" - << "ECI-Proto: \"" << result.utf8Protocol() << "\"\n" + << "TextECI: \"" << result.utf8Protocol() << "\"\n" + << "BinaryECI: \"" << ToHex(result.binaryECI()) << "\"\n" << "Format: " << ToString(result.format()) << "\n" << "Identifier: " << result.symbologyIdentifier() << "\n" << "Content: " << ToString(result.contentType()) << "\n" From e339392e8c759272d7aa9e19d14cab920cf52916 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 29 May 2022 05:20:12 +0200 Subject: [PATCH 054/180] AZDecoder: fail on illegally truncated message and simplify bounds checks --- core/src/BitArray.h | 6 ++++- core/src/aztec/AZDecoder.cpp | 43 ++++++++++++++---------------------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/core/src/BitArray.h b/core/src/BitArray.h index b2164da1cb..91d60a8b5e 100644 --- a/core/src/BitArray.h +++ b/core/src/BitArray.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -365,8 +366,11 @@ int ToInt(const ARRAY& a) inline int ReadBits(BitArray::Range& bits, int n) { + assert(n <= 32); + if (n > bits.size()) + throw std::out_of_range("ReadBits(BitArray::Range&) out of range."); int res = 0; - for (; n > 0 && bits.size(); --n, bits.begin++) + for (; n > 0; --n, bits.begin++) AppendBit(res, *bits.begin); return res; } diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index 9027a677e1..fa549e3be4 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -286,30 +286,17 @@ AztecData GetEncodedData(const BitArray& bits, const std::string& characterSet) bool haveFNC1 = false; auto remBits = bits.range(); - while (remBits) { + while (remBits.size() >= (shiftTable == Table::DIGIT ? 4 : 5)) { // see ISO/IEC 24778:2008 7.3.1.2 regarding padding bits if (shiftTable == Table::BINARY) { - if (remBits.size() < 5) - break; int length = ReadBits(remBits, 5); - if (length == 0) { - if (remBits.size() < 11) - break; + if (length == 0) length = ReadBits(remBits, 11) + 31; - } - for (int charCount = 0; charCount < length; charCount++) { - if (remBits.size() < 8) { - remBits.begin = remBits.end; // Force outer loop to exit - break; - } - int code = ReadBits(remBits, 8); - text.push_back((char)code); - } + for (int i = 0; i < length; i++) + text.push_back(static_cast(ReadBits(remBits, 8))); // Go back to whatever mode we had been in shiftTable = latchTable; } else { int size = shiftTable == Table::DIGIT ? 4 : 5; - if (remBits.size() < size) - break; int code = ReadBits(remBits, size); const char* str = GetCharacter(shiftTable, code); if (std::strncmp(str, "CTRL_", 5) == 0) { @@ -322,8 +309,6 @@ AztecData GetEncodedData(const BitArray& bits, const std::string& characterSet) if (str[6] == 'L') latchTable = shiftTable; } else if (std::strcmp(str, "FLGN") == 0) { - if (remBits.size() < 3) - break; int flg = ReadBits(remBits, 3); if (flg == 0) { // FNC1 haveFNC1 = true; // Will process first/second FNC1 at end after any Structured Append @@ -382,15 +367,19 @@ DecoderResult Decode(const DetectorResult& detectorResult, const std::string& ch if (!bits.size()) return DecodeStatus::FormatError; - auto data = GetEncodedData(bits, characterSet); - if (data.text.empty()) + try { + auto data = GetEncodedData(bits, characterSet); + if (data.text.empty()) + return DecodeStatus::FormatError; + + return DecoderResult(bits.toBytes(), std::move(data.text)) + .setNumBits(Size(bits)) + .setSymbologyIdentifier(std::move(data.symbologyIdentifier)) + .setStructuredAppend(data.sai) + .setReaderInit(detectorResult.readerInit()); + } catch (const std::out_of_range&) { // see ReadBits() return DecodeStatus::FormatError; - - return DecoderResult(bits.toBytes(), std::move(data.text)) - .setNumBits(Size(bits)) - .setSymbologyIdentifier(std::move(data.symbologyIdentifier)) - .setStructuredAppend(data.sai) - .setReaderInit(detectorResult.readerInit()); + } } } // namespace ZXing::Aztec From 6312ca9e62b29e3a54623e723f9b239180dfd285 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 30 May 2022 10:14:34 +0200 Subject: [PATCH 055/180] style: trivial whitespace fix --- core/src/Content.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 1a505dfa7e..fb3a81424b 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -46,7 +46,7 @@ void Content::switchEncoding(ECI eci, bool isECI) void Content::switchEncoding(CharacterSet cs) { - switchEncoding(ToECI(cs), false); + switchEncoding(ToECI(cs), false); } std::wstring Content::text() const From a7f4722fd7dbb503156246ce2eb73535459b3f44 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 30 May 2022 10:18:58 +0200 Subject: [PATCH 056/180] ECI: transformative ECIs are not supported -> return empty `text` see https://github.com/nu-book/zxing-cpp/commit/d8587545434d533c4e568181e1c12ef04a8e42d9#r74864359 Switch 2 test samples from text to binary. --- core/src/Content.cpp | 11 +++++++++++ core/src/Content.h | 1 + core/src/ECI.h | 6 ++++++ test/samples/aztec-1/mixed-ecis-41x41.bin | Bin 0 -> 29 bytes test/samples/aztec-1/mixed-ecis-41x41.txt | 1 - test/samples/datamatrix-1/eci-mixed.bin | 1 + test/samples/datamatrix-1/eci-mixed.txt | 1 - 7 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 test/samples/aztec-1/mixed-ecis-41x41.bin delete mode 100644 test/samples/aztec-1/mixed-ecis-41x41.txt create mode 100644 test/samples/datamatrix-1/eci-mixed.bin delete mode 100644 test/samples/datamatrix-1/eci-mixed.txt diff --git a/core/src/Content.cpp b/core/src/Content.cpp index fb3a81424b..96f12e491d 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -49,8 +49,16 @@ void Content::switchEncoding(CharacterSet cs) switchEncoding(ToECI(cs), false); } +bool Content::canProcess() const +{ + return std::all_of(encodings.begin(), encodings.end(), [](Encoding e) { return CanProcess(e.eci); }); +} + std::wstring Content::text() const { + if (!canProcess()) + return {}; + auto fallbackCS = CharacterSetECI::CharsetFromName(hintedCharset.c_str()); if (!hasECI && fallbackCS == CharacterSet::Unknown) fallbackCS = guessEncoding(); @@ -66,6 +74,9 @@ std::wstring Content::text() const std::string Content::utf8Protocol() const { + if (!canProcess()) + return {}; + std::wstring res = TextDecoder::FromLatin1(symbology.toString(true)); ECI lastECI = ECI::Unknown; auto fallbackCS = guessEncoding(); diff --git a/core/src/Content.h b/core/src/Content.h index abb8bc873b..e856393ba8 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -62,6 +62,7 @@ class Content void operator+=(const std::string& str) { append(str); } bool empty() const { return binary.empty(); } + bool canProcess() const; std::wstring text() const; std::string utf8Protocol() const; diff --git a/core/src/ECI.h b/core/src/ECI.h index ac2f65dfb8..bb780324b0 100644 --- a/core/src/ECI.h +++ b/core/src/ECI.h @@ -53,6 +53,12 @@ inline constexpr bool IsText(ECI eci) return ToInt(eci) >= 0 && ToInt(eci) <= 32; } +inline constexpr bool CanProcess(ECI eci) +{ + // see https://github.com/nu-book/zxing-cpp/commit/d8587545434d533c4e568181e1c12ef04a8e42d9#r74864359 + return ToInt(eci) <= 899; +} + /** * @brief ToString converts the numerical ECI value to a 7 character string as used in the ECI protocol * @return e.g. "\000020" diff --git a/test/samples/aztec-1/mixed-ecis-41x41.bin b/test/samples/aztec-1/mixed-ecis-41x41.bin new file mode 100644 index 0000000000000000000000000000000000000000..a51ee9f519ddaff94183407aa032013c7c33fb28 GIT binary patch literal 29 kcmaF35Coc2C&x1^{4k+s+s~^%7rxxEaKobS{}wI+026%^Z~y=R literal 0 HcmV?d00001 diff --git a/test/samples/aztec-1/mixed-ecis-41x41.txt b/test/samples/aztec-1/mixed-ecis-41x41.txt deleted file mode 100644 index 6c534d58d7..0000000000 --- a/test/samples/aztec-1/mixed-ecis-41x41.txt +++ /dev/null @@ -1 +0,0 @@ -á¡ĦĐâ€Ä„ă†ç‚ąÂˇđڶéľéľ¤Ă©ę°€ę°é˝„،¢ \ No newline at end of file diff --git a/test/samples/datamatrix-1/eci-mixed.bin b/test/samples/datamatrix-1/eci-mixed.bin new file mode 100644 index 0000000000..7f4b77e09b --- /dev/null +++ b/test/samples/datamatrix-1/eci-mixed.bin @@ -0,0 +1 @@ +ᡡˇˇÍ±đڶ@@@@@@@@@@_ \ No newline at end of file diff --git a/test/samples/datamatrix-1/eci-mixed.txt b/test/samples/datamatrix-1/eci-mixed.txt deleted file mode 100644 index 18e1efa017..0000000000 --- a/test/samples/datamatrix-1/eci-mixed.txt +++ /dev/null @@ -1 +0,0 @@ -á¡¡ĦĦͱđڶ@@@@@@@@@@_ \ No newline at end of file From a9f5f078bd93e4c64acb84ae3e684444306852ea Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 30 May 2022 11:47:13 +0200 Subject: [PATCH 057/180] ECI: switch AZDecoder to new Content class There are a couple open questions/ToDos regarding the handling of symbology identifier, structured append and AIM data. --- core/src/Content.cpp | 8 ++ core/src/Content.h | 2 + core/src/aztec/AZDecoder.cpp | 146 +++++++++------------ test/unit/aztec/AZDecoderTest.cpp | 105 +++++++-------- test/unit/aztec/AZEncodeDecodeTest.cpp | 5 +- test/unit/aztec/AZHighLevelEncoderTest.cpp | 16 +-- 6 files changed, 127 insertions(+), 155 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 96f12e491d..12e560cf1d 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -49,6 +49,14 @@ void Content::switchEncoding(CharacterSet cs) switchEncoding(ToECI(cs), false); } +void Content::erase(int pos, int n) +{ + binary.erase(binary.begin() + pos, binary.begin() + pos + n); + for (auto& e : encodings) + if (e.pos > pos) + pos -= n; +} + bool Content::canProcess() const { return std::all_of(encodings.begin(), encodings.end(), [](Encoding e) { return CanProcess(e.eci); }); diff --git a/core/src/Content.h b/core/src/Content.h index e856393ba8..33e7991ba2 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -61,6 +61,8 @@ class Content void operator+=(char val) { push_back(val); } void operator+=(const std::string& str) { append(str); } + void erase(int pos, int n); + bool empty() const { return binary.empty(); } bool canProcess() const; diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index fa549e3be4..6c295a6593 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -1,6 +1,7 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors +* Copyright 2022 Axel Waggershauser */ // SPDX-License-Identifier: Apache-2.0 @@ -9,21 +10,17 @@ #include "AZDetectorResult.h" #include "BitArray.h" #include "BitMatrix.h" -#include "CharacterSetECI.h" +#include "CharacterSet.h" #include "DecodeStatus.h" #include "DecoderResult.h" #include "GenericGF.h" #include "ReedSolomonDecoder.h" -#include "TextDecoder.h" -#include "TextUtfEncoding.h" #include "ZXTestSupport.h" -#include -#include -#include #include #include #include +#include #include namespace ZXing::Aztec { @@ -215,20 +212,21 @@ static const char* GetCharacter(Table table, int code) /** * See ISO/IEC 24778:2008 Section 10.1 */ -static int ParseECIValue(BitArray::Range& bits, const int flg) +static ECI ParseECIValue(BitArray::Range& bits, const int flg) { int eci = 0; - for (int i = 0; i < flg && bits.size() >= 4; i++) + for (int i = 0; i < flg; i++) eci = 10 * eci + ReadBits(bits, 4) - 2; - return eci; + return ECI(eci); } /** * See ISO/IEC 24778:2008 Section 8 */ -static StructuredAppendInfo ParseStructuredAppend(std::wstring& text) +static StructuredAppendInfo ParseStructuredAppend(ByteArray& binary) { - std::wstring id; + std::string text(binary.begin(), binary.end()); + StructuredAppendInfo sai; std::string::size_type i = 0; if (text[0] == ' ') { // Space-delimited id @@ -236,54 +234,29 @@ static StructuredAppendInfo ParseStructuredAppend(std::wstring& text) if (sp == std::string::npos) return {}; - id = text.substr(1, sp - 1); // Strip space delimiters + sai.id = text.substr(1, sp - 1); // Strip space delimiters i = sp + 1; } if (i + 1 >= text.size() || !std::isupper(text[i]) || !std::isupper(text[i + 1])) return {}; - StructuredAppendInfo sai; sai.index = text[i] - 'A'; sai.count = text[i + 1] - 'A' + 1; if (sai.count == 1 || sai.count <= sai.index) // If info doesn't make sense sai.count = 0; // Choose to mark count as unknown - if (!id.empty()) - TextUtfEncoding::ToUtf8(id, sai.id); - text.erase(0, i + 2); // Remove + binary = ByteArray(text); return sai; } -struct AztecData -{ - std::wstring text; - std::string symbologyIdentifier; - StructuredAppendInfo sai; -}; - -/** -* Gets the string encoded in the aztec code bits -* -* @return the decoded string -*/ -ZXING_EXPORT_TEST_ONLY -AztecData GetEncodedData(const BitArray& bits, const std::string& characterSet) +static void DecodeContent(const BitArray& bits, Content& res) { - AztecData res; Table latchTable = Table::UPPER; // table most recently latched to Table shiftTable = Table::UPPER; // table to use for the next read - std::string text; - text.reserve(20); - int symbologyIdModifier = 0; - CharacterSet encoding = CharacterSetECI::InitEncoding(characterSet); - // Check for Structured Append - need 4 5-bit words, beginning with ML UL, ending with index and count - bool haveStructuredAppend = Size(bits) > 20 && ToInt(bits, 0, 5) == 29 // latch to MIXED (from UPPER) - && ToInt(bits, 5, 5) == 29; // latch back to UPPER (from MIXED) - bool haveFNC1 = false; auto remBits = bits.range(); while (remBits.size() >= (shiftTable == Table::DIGIT ? 4 : 5)) { // see ISO/IEC 24778:2008 7.3.1.2 regarding padding bits @@ -292,7 +265,7 @@ AztecData GetEncodedData(const BitArray& bits, const std::string& characterSet) if (length == 0) length = ReadBits(remBits, 11) + 31; for (int i = 0; i < length; i++) - text.push_back(static_cast(ReadBits(remBits, 8))); + res.push_back(ReadBits(remBits, 8)); // Go back to whatever mode we had been in shiftTable = latchTable; } else { @@ -311,53 +284,70 @@ AztecData GetEncodedData(const BitArray& bits, const std::string& characterSet) } else if (std::strcmp(str, "FLGN") == 0) { int flg = ReadBits(remBits, 3); if (flg == 0) { // FNC1 - haveFNC1 = true; // Will process first/second FNC1 at end after any Structured Append - text.push_back((char)29); // May be removed at end if first/second FNC1 + res.push_back(29); // May be removed at end if first/second FNC1 } else if (flg <= 6) { // FLG(1) to FLG(6) ECI - encoding = - CharacterSetECI::OnChangeAppendReset(ParseECIValue(remBits, flg), res.text, text, encoding); + res.switchEncoding(ParseECIValue(remBits, flg)); } else { // FLG(7) is invalid } shiftTable = latchTable; } else { - text.append(str); + res.append(str); // Go back to whatever mode we had been in shiftTable = latchTable; } } } +} - TextDecoder::Append(res.text, reinterpret_cast(text.data()), text.size(), encoding); - - if (!res.text.empty()) { - if (haveStructuredAppend) - res.sai = ParseStructuredAppend(res.text); - if (haveFNC1) { - // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using - // modifiers that indicate ECI protocol (ISO/IEC 24778:2008 Annex F Table F.1) - if (res.text.front() == 29) { - symbologyIdModifier = 1; // GS1 - res.text.erase(0, 1); // Remove FNC1 - } else if (res.text.size() > 2 && std::isupper(res.text[0]) && res.text[1] == 29) { - // FNC1 following single uppercase letter (the AIM Application Indicator) - symbologyIdModifier = 2; // AIM - res.text.erase(1, 1); // Remove FNC1 - // The AIM Application Indicator character "A"-"Z" is left in the stream (ISO/IEC 24778:2008 16.2) - } else if (res.text.size() > 3 && std::isdigit(res.text[0]) && std::isdigit(res.text[1]) && - res.text[2] == 29) { - // FNC1 following 2 digits (the AIM Application Indicator) - symbologyIdModifier = 2; // AIM - res.text.erase(2, 1); // Remove FNC1 - // The AIM Application Indicator characters "00"-"99" are left in the stream (ISO/IEC 24778:2008 16.2) - } - } +ZXING_EXPORT_TEST_ONLY +DecoderResult Decode(const BitArray& bits, const std::string& characterSet) +{ + Content res; + res.symbology = {'z', '0', 3}; + res.hintedCharset = characterSet; + + try { + DecodeContent(bits, res); + } catch (const std::out_of_range&) { // see ReadBits() + return DecodeStatus::FormatError; } - res.symbologyIdentifier = "]z" + std::to_string(symbologyIdModifier + (res.sai.index > -1 ? 6 : 0)); + if (res.binary.empty()) + return DecodeStatus::FormatError; - return res; + // Check for Structured Append - need 4 5-bit words, beginning with ML UL, ending with index and count + bool haveStructuredAppend = Size(bits) > 20 && ToInt(bits, 0, 5) == 29 // latch to MIXED (from UPPER) + && ToInt(bits, 5, 5) == 29; // latch back to UPPER (from MIXED) + + StructuredAppendInfo sai = haveStructuredAppend ? ParseStructuredAppend(res.binary) : StructuredAppendInfo(); + + // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using + // modifiers that indicate ECI protocol (ISO/IEC 24778:2008 Annex F Table F.1) + if (res.binary[0] == 29) { + res.symbology.modifier = '1'; // GS1 + res.applicationIndicator = "GS1"; + res.erase(0, 1); // Remove FNC1 + } else if (res.binary.size() > 2 && std::isupper(res.binary[0]) && res.binary[1] == 29) { + // FNC1 following single uppercase letter (the AIM Application Indicator) + res.symbology.modifier = '2'; // AIM + // TODO: remove the AI from the content? + res.applicationIndicator = std::string(reinterpret_cast(res.binary.data()), 1); + res.erase(1, 1); // Remove FNC1, + // The AIM Application Indicator character "A"-"Z" is left in the stream (ISO/IEC 24778:2008 16.2) + } else if (res.binary.size() > 3 && std::isdigit(res.binary[0]) && std::isdigit(res.binary[1]) && res.binary[2] == 29) { + // FNC1 following 2 digits (the AIM Application Indicator) + res.symbology.modifier = '2'; // AIM + res.applicationIndicator = std::string(reinterpret_cast(res.binary.data()), 2); + res.erase(2, 1); // Remove FNC1 + // The AIM Application Indicator characters "00"-"99" are left in the stream (ISO/IEC 24778:2008 16.2) + } + + if (sai.index != -1) + res.symbology.modifier += 6; // TODO: this is wrong as long as we remove the sai info from the content in ParseStructuredAppend + + return DecoderResult(bits.toBytes(), {}, std::move(res)).setNumBits(Size(bits)).setStructuredAppend(sai); } DecoderResult Decode(const DetectorResult& detectorResult, const std::string& characterSet) @@ -367,19 +357,7 @@ DecoderResult Decode(const DetectorResult& detectorResult, const std::string& ch if (!bits.size()) return DecodeStatus::FormatError; - try { - auto data = GetEncodedData(bits, characterSet); - if (data.text.empty()) - return DecodeStatus::FormatError; - - return DecoderResult(bits.toBytes(), std::move(data.text)) - .setNumBits(Size(bits)) - .setSymbologyIdentifier(std::move(data.symbologyIdentifier)) - .setStructuredAppend(data.sai) - .setReaderInit(detectorResult.readerInit()); - } catch (const std::out_of_range&) { // see ReadBits() - return DecodeStatus::FormatError; - } + return Decode(bits, characterSet).setReaderInit(detectorResult.readerInit()); } } // namespace ZXing::Aztec diff --git a/test/unit/aztec/AZDecoderTest.cpp b/test/unit/aztec/AZDecoderTest.cpp index 56c323a6a6..523bb8b920 100644 --- a/test/unit/aztec/AZDecoderTest.cpp +++ b/test/unit/aztec/AZDecoderTest.cpp @@ -17,14 +17,7 @@ namespace ZXing::Aztec { -struct AztecData -{ - std::wstring text; - std::string symbologyIdentifier; - StructuredAppendInfo sai; -}; - -AztecData GetEncodedData(const BitArray& bits, const std::string& characterSet = ""); +DecoderResult Decode(const BitArray& bits, const std::string& characterSet = ""); } @@ -148,14 +141,14 @@ TEST(AZDecoderTest, DecodeTooManyErrors2) } // Helper taking bit string to call GetEncodedData() -static Aztec::AztecData getData(std::string_view bitStr) +static DecoderResult getData(std::string_view bitStr) { BitArray bits; for (auto b : bitStr) bits.appendBit(b == '1'); - return Aztec::GetEncodedData(bits); + return Aztec::Decode(bits); } TEST(AZDecoderTest, SymbologyIdentifier) @@ -163,101 +156,101 @@ TEST(AZDecoderTest, SymbologyIdentifier) { // Plain auto data = getData("00010"); - EXPECT_EQ(data.symbologyIdentifier, "]z0"); - EXPECT_EQ(data.text, L"A"); + EXPECT_EQ(data.symbologyIdentifier(), "]z0"); + EXPECT_EQ(data.text(), L"A"); } { // GS1 ("PS FLGN(0) DL (20)01") auto data = getData("0000000000000111100100001000100011"); - EXPECT_EQ(data.symbologyIdentifier, "]z1"); - EXPECT_EQ(data.text, L"2001"); + EXPECT_EQ(data.symbologyIdentifier(), "]z1"); + EXPECT_EQ(data.text(), L"2001"); } { // AIM ("A PS FLGN(0) B") auto data = getData("00010000000000000000011"); - EXPECT_EQ(data.symbologyIdentifier, "]z2"); - EXPECT_EQ(data.text, L"AB"); + EXPECT_EQ(data.symbologyIdentifier(), "]z2"); + EXPECT_EQ(data.text(), L"AB"); } { // AIM ("DL 99 UL PS FLGN(0) B") auto data = getData("11110101110111110000000000000000011"); - EXPECT_EQ(data.symbologyIdentifier, "]z2"); - EXPECT_EQ(data.text, L"99B"); + EXPECT_EQ(data.symbologyIdentifier(), "]z2"); + EXPECT_EQ(data.text(), L"99B"); } { // Structured Append ("UL ML A D A") auto data = getData("1110111101000100010100010"); - EXPECT_EQ(data.symbologyIdentifier, "]z6"); - EXPECT_EQ(data.text, L"A"); - EXPECT_EQ(data.sai.index, 0); - EXPECT_EQ(data.sai.count, 4); + EXPECT_EQ(data.symbologyIdentifier(), "]z6"); + EXPECT_EQ(data.text(), L"A"); + EXPECT_EQ(data.structuredAppend().index, 0); + EXPECT_EQ(data.structuredAppend().count, 4); } { // Structured Append with GS1 ("UL ML A D PS FLGN(0) DL (20)01") auto data = getData("111011110100010001010000000000000111100100001000100011"); - EXPECT_EQ(data.symbologyIdentifier, "]z7"); - EXPECT_EQ(data.text, L"2001"); - EXPECT_EQ(data.sai.index, 0); - EXPECT_EQ(data.sai.count, 4); + EXPECT_EQ(data.symbologyIdentifier(), "]z7"); + EXPECT_EQ(data.text(), L"2001"); + EXPECT_EQ(data.structuredAppend().index, 0); + EXPECT_EQ(data.structuredAppend().count, 4); } { // Structured Append with AIM ("UL ML A D A PS FLGN(0) B") auto data = getData("1110111101000100010100010000000000000000011"); - EXPECT_EQ(data.symbologyIdentifier, "]z8"); - EXPECT_EQ(data.text, L"AB"); - EXPECT_EQ(data.sai.index, 0); - EXPECT_EQ(data.sai.count, 4); + EXPECT_EQ(data.symbologyIdentifier(), "]z8"); + EXPECT_EQ(data.text(), L"AB"); + EXPECT_EQ(data.structuredAppend().index, 0); + EXPECT_EQ(data.structuredAppend().count, 4); } { // Plain with FNC1 not in first/second position ("A B PS FLGN(0) C") auto data = getData("0001000011000000000000000100"); - EXPECT_EQ(data.symbologyIdentifier, "]z0"); - EXPECT_EQ(data.text, L"AB\u001DC"); // "ABC" + EXPECT_EQ(data.symbologyIdentifier(), "]z0"); + EXPECT_EQ(data.text(), L"AB\u001DC"); // "ABC" } { // Plain with FNC1 not in first/second position ("A B C PS FLGN(0) D") auto data = getData("000100001100100000000000000000101"); - EXPECT_EQ(data.symbologyIdentifier, "]z0"); - EXPECT_EQ(data.text, L"ABC\u001DD"); // "ABCD" + EXPECT_EQ(data.symbologyIdentifier(), "]z0"); + EXPECT_EQ(data.text(), L"ABC\u001DD"); // "ABCD" } { // Plain with FNC1 not in first/second position ("DL 1 UL PS FLGN(0) A") auto data = getData("1111000111110000000000000000010"); - EXPECT_EQ(data.symbologyIdentifier, "]z0"); - EXPECT_EQ(data.text, L"1\u001DA"); // "1D" + EXPECT_EQ(data.symbologyIdentifier(), "]z0"); + EXPECT_EQ(data.text(), L"1\u001DA"); // "1D" } } // Helper taking 5-bit word array to call GetEncodedData() -static Aztec::AztecData getData(const ByteArray& bytes) +static DecoderResult getData(const ByteArray& bytes) { BitArray bits; // 5-bit words (assuming no digits/binary) for (auto b : bytes) bits.appendBits(b, 5); - return Aztec::GetEncodedData(bits); + return Aztec::Decode(bits); } // Shorthand to return Structured Append given 5-bit word array static StructuredAppendInfo sai(const ByteArray& bytes) { - return getData(bytes).sai; + return getData(bytes).structuredAppend(); } // Shorthand to return string result given 5-bit word array static std::wstring text(const ByteArray& bytes) { - return getData(bytes).text; + return getData(bytes).text(); } TEST(AZDecoderTest, StructuredAppend) @@ -335,30 +328,30 @@ TEST(AZDecoderTest, StructuredAppend) // Invalid Ids { auto data = getData({29, 29, 1, 10, 5, 2, 5, 2}); // No terminating space - EXPECT_TRUE(data.sai.id.empty()); - EXPECT_EQ(data.sai.index, -1); // Not recognized as sequence - EXPECT_EQ(data.sai.count, -1); - EXPECT_EQ(data.text, L" IDADA"); // Bad ID and sequencing left in result + EXPECT_TRUE(data.structuredAppend().id.empty()); + EXPECT_EQ(data.structuredAppend().index, -1); // Not recognized as sequence + EXPECT_EQ(data.structuredAppend().count, -1); + EXPECT_EQ(data.text(), L" IDADA"); // Bad ID and sequencing left in result } { auto data = getData({29, 29, 1, 1, 2, 5, 2}); // Blank - EXPECT_TRUE(data.sai.id.empty()); - EXPECT_EQ(data.sai.index, 0); // Recognized as sequence - EXPECT_EQ(data.sai.count, 4); - EXPECT_EQ(data.text, L"A"); + EXPECT_TRUE(data.structuredAppend().id.empty()); + EXPECT_EQ(data.structuredAppend().index, 0); // Recognized as sequence + EXPECT_EQ(data.structuredAppend().count, 4); + EXPECT_EQ(data.text(), L"A"); } { auto data = getData({29, 29, 1, 10, 1, 5, 1, 2, 5, 2}); // Space in "I D" - EXPECT_TRUE(data.sai.id.empty()); - EXPECT_EQ(data.sai.index, -1); // Not recognized as sequence as sequence count invalid (space) - EXPECT_EQ(data.sai.count, -1); - EXPECT_EQ(data.text, L" I D ADA"); // Bad ID and sequencing left in result + EXPECT_TRUE(data.structuredAppend().id.empty()); + EXPECT_EQ(data.structuredAppend().index, -1); // Not recognized as sequence as sequence count invalid (space) + EXPECT_EQ(data.structuredAppend().count, -1); + EXPECT_EQ(data.text(), L" I D ADA"); // Bad ID and sequencing left in result } { auto data = getData({29, 29, 1, 10, 1, 2, 5, 1, 2, 5, 2}); // "I AD" (happens to have valid sequencing at end) - EXPECT_EQ(data.sai.id, "I"); - EXPECT_EQ(data.sai.index, 0); - EXPECT_EQ(data.sai.count, 4); - EXPECT_EQ(data.text, L" ADA"); // Trailing space and "real" sequencing left in result + EXPECT_EQ(data.structuredAppend().id, "I"); + EXPECT_EQ(data.structuredAppend().index, 0); + EXPECT_EQ(data.structuredAppend().count, 4); + EXPECT_EQ(data.text(), L" ADA"); // Trailing space and "real" sequencing left in result } } diff --git a/test/unit/aztec/AZEncodeDecodeTest.cpp b/test/unit/aztec/AZEncodeDecodeTest.cpp index d968371719..54c7d2fe7f 100644 --- a/test/unit/aztec/AZEncodeDecodeTest.cpp +++ b/test/unit/aztec/AZEncodeDecodeTest.cpp @@ -86,10 +86,9 @@ namespace { EXPECT_EQ(aztec.matrix, matrix); - std::wstring expectedData = TextDecoder::ToUnicode(textBytes, CharacterSet::ISO8859_1); DecoderResult res = parse(matrix.copy(), aztec.compact, aztec.codeWords, aztec.layers); EXPECT_EQ(res.isValid(), true); - EXPECT_EQ(res.text(), expectedData); + EXPECT_EQ(res.content().binary, ByteArray(textBytes)); // Check error correction by introducing up to eccPercent/2 errors int ecWords = aztec.codeWords * eccPercent / 100 / 2; @@ -106,7 +105,7 @@ namespace { } res = parse(std::move(matrix), aztec.compact, aztec.codeWords, aztec.layers); EXPECT_EQ(res.isValid(), true); - EXPECT_EQ(res.text(), expectedData); + EXPECT_EQ(res.content().binary, ByteArray(textBytes)); } } diff --git a/test/unit/aztec/AZHighLevelEncoderTest.cpp b/test/unit/aztec/AZHighLevelEncoderTest.cpp index 7643f1e9b9..7e80feb770 100644 --- a/test/unit/aztec/AZHighLevelEncoderTest.cpp +++ b/test/unit/aztec/AZHighLevelEncoderTest.cpp @@ -7,6 +7,7 @@ #include "aztec/AZHighLevelEncoder.h" #include "BitArray.h" #include "BitArrayUtility.h" +#include "DecoderResult.h" #include "StructuredAppend.h" #include "TextDecoder.h" @@ -15,14 +16,7 @@ namespace ZXing::Aztec { -struct AztecData -{ - std::wstring text; - std::string symbologyIdentifier; - StructuredAppendInfo sai; -}; - -AztecData GetEncodedData(const BitArray& bits, const std::string& characterSet = ""); +DecoderResult Decode(const BitArray& bits, const std::string& characterSet = ""); } @@ -37,16 +31,14 @@ namespace { void TestHighLevelEncodeString(const std::string& s, const std::string& expectedBits) { BitArray bits = Aztec::HighLevelEncoder::Encode(s); EXPECT_EQ(Utility::ToString(bits), StripSpaces(expectedBits)) << "highLevelEncode() failed for input string: " + s; - EXPECT_EQ(TextDecoder::FromLatin1(s), Aztec::GetEncodedData(bits).text); + EXPECT_EQ(TextDecoder::FromLatin1(s), Aztec::Decode(bits).text()); } void TestHighLevelEncodeString(const std::string& s, int expectedReceivedBits) { BitArray bits = Aztec::HighLevelEncoder::Encode(s); int receivedBitCount = Size(Utility::ToString(bits)); EXPECT_EQ(receivedBitCount, expectedReceivedBits) << "highLevelEncode() failed for input string: " + s; - std::string symbologyIdentifier; - StructuredAppendInfo sai; - EXPECT_EQ(TextDecoder::FromLatin1(s), Aztec::GetEncodedData(bits).text); + EXPECT_EQ(ByteArray(s), Aztec::Decode(bits).content().binary); } } From becb24448e5147e7c9a2338adb45b8ba22499642 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 30 May 2022 11:48:17 +0200 Subject: [PATCH 058/180] ECI: tune Content::type() (again...) --- core/src/Content.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 12e560cf1d..a0ec9951fa 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -154,20 +154,17 @@ CharacterSet Content::guessEncoding() const ContentType Content::type() const { auto isBinary = [](Encoding e) { return !IsText(e.eci); }; + auto es = encodings; - if (std::none_of(encodings.begin(), encodings.end(), isBinary)) + for (auto& e : es) + if (e.eci == ECI::Unknown) + e.eci = ToECI(guessEncoding()); + + if (std::none_of(es.begin(), es.end(), isBinary)) return ContentType::Text; - if (std::all_of(encodings.begin(), encodings.end(), isBinary)) + if (std::all_of(es.begin(), es.end(), isBinary)) return ContentType::Binary; - if (!hasECI) { - auto cs = guessEncoding(); - if (IsText(ToECI(cs))) - return ContentType::Text; - if (cs == CharacterSet::BINARY) - return ContentType::Binary; - } - return ContentType::Mixed; } From 2ec23cb1d2d3cdc5efc3b8f7797782296e99d51c Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 30 May 2022 18:37:34 +0200 Subject: [PATCH 059/180] PDF417: add trivial IsECI() helper function to raise abstraction --- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index e616790f4f..0a760140f6 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -66,6 +66,11 @@ static const char* MIXED_CHARS = "0123456789&\r\t,:#-.$/+%*=^"; static const int NUMBER_OF_SEQUENCE_CODEWORDS = 2; +inline bool IsECI(int code) +{ + return code >= ECI_USER_DEFINED && code <= ECI_CHARSET; +} + /** * Whether a codeword terminates a Compaction mode. * @@ -91,7 +96,7 @@ static bool TerminatesCompaction(int code) static int ProcessECI(const std::vector& codewords, int codeIndex, const int length, const int code, std::wstring& resultEncoded, std::string& result, CharacterSet& encoding) { - if (codeIndex < length && code >= ECI_USER_DEFINED && code <= ECI_CHARSET) { + if (codeIndex < length && IsECI(code)) { if (code == ECI_CHARSET) { encoding = CharacterSetECI::OnChangeAppendReset(codewords[codeIndex++], resultEncoded, result, encoding); } @@ -134,13 +139,13 @@ static void DecodeTextCompaction(const std::vector& textCompactionData, int int subModeCh = textCompactionData[i]; // Note only have ECI and MODE_SHIFT_TO_BYTE_COMPACTION_MODE function codewords in text compaction array - if (subModeCh >= ECI_USER_DEFINED && subModeCh <= ECI_CHARSET) { + if (IsECI(subModeCh)) { i = ProcessECI(textCompactionData, i + 1, length, subModeCh, resultEncoded, result, encoding); continue; } if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) { i++; - while (i < length && textCompactionData[i] >= ECI_USER_DEFINED && textCompactionData[i] <= ECI_CHARSET) { + while (i < length && IsECI(textCompactionData[i])) { i = ProcessECI(textCompactionData, i + 1, length, textCompactionData[i], resultEncoded, result, encoding); } @@ -303,8 +308,7 @@ static int TextCompaction(DecodeStatus& status, const std::vector& codeword // in Text Compaction mode; its use is described in 5.4.2.4. textCompactionData[index++] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE; // 5.5.3.1 allows ECIs anywhere in Text Compaction, including after a Shift to Byte - while (codeIndex < codewords[0] && codewords[codeIndex] >= ECI_USER_DEFINED - && codewords[codeIndex] <= ECI_CHARSET) { + while (codeIndex < codewords[0] && IsECI(codewords[codeIndex])) { codeIndex = ProcessTextECI(textCompactionData, index, codewords, codeIndex + 1, codewords[codeIndex]); } @@ -349,7 +353,7 @@ static int CountByteBatches(DecodeStatus& status, int mode, const std::vector= ECI_USER_DEFINED && code <= ECI_CHARSET) { + if (IsECI(code)) { codeIndex += code == ECI_GENERAL_PURPOSE ? 2 : 1; continue; } @@ -395,7 +399,7 @@ static int ProcessByteECIs(const std::vector& codewords, int codeIndex, while (codeIndex < codewords[0] && codewords[codeIndex] >= TEXT_COMPACTION_MODE_LATCH && !TerminatesCompaction(codewords[codeIndex])) { int code = codewords[codeIndex++]; - if (code >= ECI_USER_DEFINED && code <= ECI_CHARSET) { + if (IsECI(code)) { codeIndex = ProcessECI(codewords, codeIndex, codewords[0], code, resultEncoded, result, encoding); } } @@ -543,7 +547,7 @@ static int NumericCompaction(DecodeStatus& status, const std::vector& codew while (codeIndex < codewords[0] && !end) { int code = codewords[codeIndex++]; if (code >= TEXT_COMPACTION_MODE_LATCH) { - if (code >= ECI_USER_DEFINED && code <= ECI_CHARSET) { + if (IsECI(code)) { // As operating in Basic Channel Mode (i.e. not embedding backslashed ECIs and doubling backslashes) // allow ECIs anywhere in Numeric Compaction (i.e. ISO/IEC 15438:2015 5.5.3.4 doesn't apply). if (count > 0) { From e1c70bf87b5aae96ff2e81a77bd4eda28df24d53 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 31 May 2022 11:35:19 +0200 Subject: [PATCH 060/180] PDF417: clang-format fixes --- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 172 +++++++----------- 1 file changed, 67 insertions(+), 105 deletions(-) diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index 0a760140f6..425016674a 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -24,7 +24,8 @@ namespace ZXing::Pdf417 { -enum class Mode { +enum class Mode +{ ALPHA, LOWER, MIXED, @@ -97,13 +98,10 @@ static int ProcessECI(const std::vector& codewords, int codeIndex, const in std::wstring& resultEncoded, std::string& result, CharacterSet& encoding) { if (codeIndex < length && IsECI(code)) { - if (code == ECI_CHARSET) { + if (code == ECI_CHARSET) encoding = CharacterSetECI::OnChangeAppendReset(codewords[codeIndex++], resultEncoded, result, encoding); - } - else { - // Don't currently handle non-character set ECIs so just ignore - codeIndex += code == ECI_GENERAL_PURPOSE ? 2 : 1; - } + else + codeIndex += code == ECI_GENERAL_PURPOSE ? 2 : 1; // Don't currently handle non-character set ECIs so just ignore } return codeIndex; @@ -145,14 +143,12 @@ static void DecodeTextCompaction(const std::vector& textCompactionData, int } if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) { i++; - while (i < length && IsECI(textCompactionData[i])) { - i = ProcessECI(textCompactionData, i + 1, length, textCompactionData[i], resultEncoded, result, - encoding); - } - if (i < length) { - result.push_back((uint8_t)textCompactionData[i]); - i++; - } + while (i < length && IsECI(textCompactionData[i])) + i = ProcessECI(textCompactionData, i + 1, length, textCompactionData[i], resultEncoded, result, encoding); + + if (i < length) + result.push_back((uint8_t)textCompactionData[i++]); + continue; } @@ -164,19 +160,15 @@ static void DecodeTextCompaction(const std::vector& textCompactionData, int if (subModeCh < 26) { // Upper/lowercase character ch = (char)((subMode == Mode::ALPHA ? 'A' : 'a') + subModeCh); - } - else if (subModeCh == 26) { // Space + } else if (subModeCh == 26) { // Space ch = ' '; - } - else if (subModeCh == 27 && subMode == Mode::ALPHA) { // LL + } else if (subModeCh == 27 && subMode == Mode::ALPHA) { // LL subMode = Mode::LOWER; - } - else if (subModeCh == 27 && subMode == Mode::LOWER) { // AS + } else if (subModeCh == 27 && subMode == Mode::LOWER) { // AS // Shift to alpha priorToShiftMode = subMode; subMode = Mode::ALPHA_SHIFT; - } - else if (subModeCh == 28) { // ML + } else if (subModeCh == 28) { // ML subMode = Mode::MIXED; } // 29 PS - ignore if last or followed by Shift to Byte, 5.4.2.4 (b) (1) @@ -191,17 +183,13 @@ static void DecodeTextCompaction(const std::vector& textCompactionData, int // Mixed (numeric and some punctuation) if (subModeCh < 25) { ch = MIXED_CHARS[subModeCh]; - } - else if (subModeCh == 25) { // PL + } else if (subModeCh == 25) { // PL subMode = Mode::PUNCT; - } - else if (subModeCh == 26) { // Space + } else if (subModeCh == 26) { // Space ch = ' '; - } - else if (subModeCh == 27) { // LL + } else if (subModeCh == 27) { // LL subMode = Mode::LOWER; - } - else if (subModeCh == 28) { // AL + } else if (subModeCh == 28) { // AL subMode = Mode::ALPHA; } // 29 PS - ignore if last or followed by Shift to Byte, 5.4.2.4 (b) (1) @@ -214,41 +202,33 @@ static void DecodeTextCompaction(const std::vector& textCompactionData, int case Mode::PUNCT: // Punctuation - if (subModeCh < 29) { + if (subModeCh < 29) ch = PUNCT_CHARS[subModeCh]; - } - else { // 29 AL - note not ignored if followed by Shift to Byte, 5.4.2.4 (b) (2) + else // 29 AL - note not ignored if followed by Shift to Byte, 5.4.2.4 (b) (2) subMode = Mode::ALPHA; - } break; case Mode::ALPHA_SHIFT: // Restore sub-mode subMode = priorToShiftMode; - if (subModeCh < 26) { + if (subModeCh < 26) ch = (char)('A' + subModeCh); - } - else if (subModeCh == 26) { // Space + else if (subModeCh == 26) // Space ch = ' '; - } // 27 LL, 28 ML, 29 PS used as padding break; case Mode::PUNCT_SHIFT: // Restore sub-mode subMode = priorToShiftMode; - if (subModeCh < 29) { + if (subModeCh < 29) ch = PUNCT_CHARS[subModeCh]; - } - else { // 29 AL + else // 29 AL subMode = Mode::ALPHA; - } break; } - if (ch != 0) { - // Append decoded character to result - result.push_back(ch); - } + if (ch != 0) + result.push_back(ch); // Append decoded character to result i++; } } @@ -256,8 +236,9 @@ static void DecodeTextCompaction(const std::vector& textCompactionData, int /* * Helper to put ECI codewords into Text Compaction array. */ -static int ProcessTextECI(std::vector& textCompactionData, int& index, - const std::vector& codewords, int codeIndex, const int code) { +static int ProcessTextECI(std::vector& textCompactionData, int& index, const std::vector& codewords, int codeIndex, + const int code) +{ textCompactionData[index++] = code; if (codeIndex < codewords[0]) { textCompactionData[index++] = codewords[codeIndex++]; @@ -296,8 +277,7 @@ static int TextCompaction(DecodeStatus& status, const std::vector& codeword textCompactionData[index] = code / 30; textCompactionData[index + 1] = code % 30; index += 2; - } - else { + } else { switch (code) { case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: // The Mode Shift codeword 913 shall cause a temporary @@ -309,12 +289,10 @@ static int TextCompaction(DecodeStatus& status, const std::vector& codeword textCompactionData[index++] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE; // 5.5.3.1 allows ECIs anywhere in Text Compaction, including after a Shift to Byte while (codeIndex < codewords[0] && IsECI(codewords[codeIndex])) { - codeIndex = ProcessTextECI(textCompactionData, index, codewords, codeIndex + 1, - codewords[codeIndex]); + codeIndex = ProcessTextECI(textCompactionData, index, codewords, codeIndex + 1, codewords[codeIndex]); } - if (codeIndex < codewords[0]) { + if (codeIndex < codewords[0]) textCompactionData[index++] = codewords[codeIndex++]; // Byte to shift - } break; case ECI_CHARSET: case ECI_GENERAL_PURPOSE: @@ -340,8 +318,7 @@ static int TextCompaction(DecodeStatus& status, const std::vector& codeword * Helper for Byte Compaction to look ahead and count 5-codeword batches and trailing bytes, with some checking of * format errors. */ -static int CountByteBatches(DecodeStatus& status, int mode, const std::vector& codewords, int codeIndex, - int& trailingCount) +static int CountByteBatches(DecodeStatus& status, int mode, const std::vector& codewords, int codeIndex, int& trailingCount) { int count = 0; trailingCount = 0; @@ -369,9 +346,8 @@ static int CountByteBatches(DecodeStatus& status, int mode, const std::vector& codewords, int codeIndex, while (codeIndex < codewords[0] && codewords[codeIndex] >= TEXT_COMPACTION_MODE_LATCH && !TerminatesCompaction(codewords[codeIndex])) { int code = codewords[codeIndex++]; - if (IsECI(code)) { + if (IsECI(code)) codeIndex = ProcessECI(codewords, codeIndex, codewords[0], code, resultEncoded, result, encoding); - } } return codeIndex; @@ -428,21 +402,20 @@ static int ByteCompaction(DecodeStatus& status, int mode, const std::vector int trailingCount; int batches = CountByteBatches(status, mode, codewords, codeIndex, trailingCount); - if (StatusIsError(status)) { + if (StatusIsError(status)) return codeIndex; - } // Deal with initial ECIs codeIndex = ProcessByteECIs(codewords, codeIndex, resultEncoded, result, encoding); for (int batch = 0; batch < batches; batch++) { int64_t value = 0; - for (int count = 0; count < 5; count++) { + for (int count = 0; count < 5; count++) value = 900 * value + codewords[codeIndex++]; - } - for (int j = 0; j < 6; ++j) { + + for (int j = 0; j < 6; ++j) result.push_back((uint8_t)(value >> (8 * (5 - j)))); - } + // Deal with inter-batch ECIs codeIndex = ProcessByteECIs(codewords, codeIndex, resultEncoded, result, encoding); } @@ -505,18 +478,17 @@ static DecodeStatus DecodeBase900toBase10(const std::vector& codewords, int // Table containing values for the exponent of 900. static const auto EXP900 = []() { std::array table = {1, 900}; - for (size_t i = 2; i < table.size(); ++i) { + for (size_t i = 2; i < table.size(); ++i) table[i] = table[i - 1] * 900; - } return table; }(); assert(count <= 16); BigInteger result; - for (int i = 0; i < count; i++) { + for (int i = 0; i < count; i++) result += EXP900[count - i - 1] * codewords[i]; - } + resultString = result.toString(); if (!resultString.empty() && resultString.front() == '1') { resultString = resultString.substr(1); @@ -553,9 +525,9 @@ static int NumericCompaction(DecodeStatus& status, const std::vector& codew if (count > 0) { std::string tmp; status = DecodeBase900toBase10(numericCodewords, count, tmp); - if (StatusIsError(status)) { + if (StatusIsError(status)) return codeIndex; - } + result += tmp; count = 0; } @@ -584,9 +556,9 @@ static int NumericCompaction(DecodeStatus& status, const std::vector& codew if (count > 0) { std::string tmp; status = DecodeBase900toBase10(numericCodewords, count, tmp); - if (StatusIsError(status)) { + if (StatusIsError(status)) return codeIndex; - } + result += tmp; count = 0; } @@ -640,20 +612,19 @@ ZXING_EXPORT_TEST_ONLY DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, DecoderResultExtra& resultMetadata, int& next) { - if (codeIndex + NUMBER_OF_SEQUENCE_CODEWORDS > codewords[0]) { - // we must have at least two bytes left for the segment index + // we must have at least two bytes left for the segment index + if (codeIndex + NUMBER_OF_SEQUENCE_CODEWORDS > codewords[0]) return DecodeStatus::FormatError; - } + std::vector segmentIndexArray(NUMBER_OF_SEQUENCE_CODEWORDS); - for (int i = 0; i < NUMBER_OF_SEQUENCE_CODEWORDS; i++, codeIndex++) { + for (int i = 0; i < NUMBER_OF_SEQUENCE_CODEWORDS; i++, codeIndex++) segmentIndexArray[i] = codewords[codeIndex]; - } std::string strBuf; DecodeStatus status = DecodeBase900toBase10(segmentIndexArray, NUMBER_OF_SEQUENCE_CODEWORDS, strBuf); - if (StatusIsError(status)) { + if (StatusIsError(status)) return status; - } + resultMetadata.setSegmentIndex(std::stoi(strBuf)); // Decoding the fileId codewords as 0-899 numbers, each 0-filled to width 3. This follows the spec @@ -661,25 +632,23 @@ DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, // the fileId using text compaction, so in those cases the fileId will appear mangled. std::ostringstream fileId; fileId.fill('0'); - for (; codeIndex < codewords[0] && codewords[codeIndex] != MACRO_PDF417_TERMINATOR && - codewords[codeIndex] != BEGIN_MACRO_PDF417_OPTIONAL_FIELD; + for (; codeIndex < codewords[0] && codewords[codeIndex] != MACRO_PDF417_TERMINATOR + && codewords[codeIndex] != BEGIN_MACRO_PDF417_OPTIONAL_FIELD; codeIndex++) { fileId << std::setw(3) << codewords[codeIndex]; } resultMetadata.setFileId(fileId.str()); int optionalFieldsStart = -1; - if (codeIndex < codewords[0] && codewords[codeIndex] == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) { + if (codeIndex < codewords[0] && codewords[codeIndex] == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) optionalFieldsStart = codeIndex + 1; - } while (codeIndex < codewords[0]) { switch (codewords[codeIndex]) { case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: { codeIndex++; - if (codeIndex >= codewords[0]) { + if (codeIndex >= codewords[0]) break; - } switch (codewords[codeIndex]) { case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME: { std::string fileName; @@ -742,10 +711,9 @@ DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, // copy optional fields to additional options if (optionalFieldsStart != -1) { int optionalFieldsLength = codeIndex - optionalFieldsStart; - if (resultMetadata.isLastSegment()) { - // do not include terminator - optionalFieldsLength--; - } + if (resultMetadata.isLastSegment()) + optionalFieldsLength--; // do not include terminator + resultMetadata.setOptionalData(std::vector(codewords.begin() + optionalFieldsStart, codewords.begin() + optionalFieldsStart + optionalFieldsLength)); } @@ -797,20 +765,15 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel, c status = DecodeStatus::FormatError; break; case READER_INIT: - if (codeIndex != 2) { // Must be first codeword after symbol length (ISO/IEC 15438:2015 5.4.1.4) + if (codeIndex != 2) // Must be first codeword after symbol length (ISO/IEC 15438:2015 5.4.1.4) status = DecodeStatus::FormatError; - } - else { + else readerInit = true; - } break; case LINKAGE_EANUCC: - if (codeIndex != 2) { // Must be first codeword after symbol length (GS1 Composite ISO/IEC 24723:2010 4.3) + if (codeIndex != 2) // Must be first codeword after symbol length (GS1 Composite ISO/IEC 24723:2010 4.3) status = DecodeStatus::FormatError; - } - else { - // TODO: handle - } + // TODO: handle else case break; case LINKAGE_OTHER: // Allowed to treat as invalid by ISO/IEC 24723:2010 5.4.1.5 and 5.4.6.1 when in Basic Channel Mode @@ -820,8 +783,7 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel, c if (code >= TEXT_COMPACTION_MODE_LATCH) { // Reserved codewords (all others in switch) // Allowed to treat as invalid by ISO/IEC 24723:2010 5.4.6.1 when in Basic Channel Mode status = DecodeStatus::FormatError; // TODO: add NotSupported error - } - else { + } else { // Default mode is Text Compaction mode Alpha sub-mode (ISO/IEC 15438:2015 5.4.2.1) codeIndex = TextCompaction(status, codewords, codeIndex - 1, resultEncoded, result, encoding); } From 89a0d2b15113fa04e8105e75d13f58fa9a0bc351 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 31 May 2022 12:33:42 +0200 Subject: [PATCH 061/180] ECI: update PDF417 decoder to use new Content class --- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 102 +++++++----------- test/unit/pdf417/PDF417DecoderTest.cpp | 3 +- 2 files changed, 42 insertions(+), 63 deletions(-) diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index 425016674a..e3ebc2a8ff 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -94,12 +94,11 @@ static bool TerminatesCompaction(int code) /** * Helper to process ECIs. **/ -static int ProcessECI(const std::vector& codewords, int codeIndex, const int length, const int code, - std::wstring& resultEncoded, std::string& result, CharacterSet& encoding) +static int ProcessECI(const std::vector& codewords, int codeIndex, const int length, const int code, Content& result) { if (codeIndex < length && IsECI(code)) { if (code == ECI_CHARSET) - encoding = CharacterSetECI::OnChangeAppendReset(codewords[codeIndex++], resultEncoded, result, encoding); + result.switchEncoding(ECI(codewords[codeIndex++])); else codeIndex += code == ECI_GENERAL_PURPOSE ? 2 : 1; // Don't currently handle non-character set ECIs so just ignore } @@ -119,12 +118,9 @@ static int ProcessECI(const std::vector& codewords, int codeIndex, const in * * @param textCompactionData The text compaction data. * @param length The size of the text compaction data. -* @param resultEncoded The Unicode-encoded data. * @param result The data in the character set encoding. -* @param encoding Currently active character encoding. */ -static void DecodeTextCompaction(const std::vector& textCompactionData, int length, std::wstring& resultEncoded, - std::string& result, CharacterSet& encoding) +static void DecodeTextCompaction(const std::vector& textCompactionData, int length, Content& result) { // Beginning from an initial state of the Alpha sub-mode // The default compaction mode for PDF417 in effect at the start of each symbol shall always be Text @@ -138,13 +134,13 @@ static void DecodeTextCompaction(const std::vector& textCompactionData, int // Note only have ECI and MODE_SHIFT_TO_BYTE_COMPACTION_MODE function codewords in text compaction array if (IsECI(subModeCh)) { - i = ProcessECI(textCompactionData, i + 1, length, subModeCh, resultEncoded, result, encoding); + i = ProcessECI(textCompactionData, i + 1, length, subModeCh, result); continue; } if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) { i++; while (i < length && IsECI(textCompactionData[i])) - i = ProcessECI(textCompactionData, i + 1, length, textCompactionData[i], resultEncoded, result, encoding); + i = ProcessECI(textCompactionData, i + 1, length, textCompactionData[i], result); if (i < length) result.push_back((uint8_t)textCompactionData[i++]); @@ -257,13 +253,10 @@ static int ProcessTextECI(std::vector& textCompactionData, int& index, cons * * @param codewords The array of codewords (data + error) * @param codeIndex The current index into the codeword array. -* @param resultEncoded The Unicode-encoded data. * @param result The data in the character set encoding. -* @param encoding Currently active character encoding. * @return The next index into the codeword array. */ -static int TextCompaction(DecodeStatus& status, const std::vector& codewords, int codeIndex, - std::wstring& resultEncoded, std::string& result, CharacterSet& encoding) +static int TextCompaction(DecodeStatus& status, const std::vector& codewords, int codeIndex, Content& result) { // 2 characters per codeword std::vector textCompactionData((codewords[0] - codeIndex) * 2, 0); @@ -310,7 +303,7 @@ static int TextCompaction(DecodeStatus& status, const std::vector& codeword } } } - DecodeTextCompaction(textCompactionData, index, resultEncoded, result, encoding); + DecodeTextCompaction(textCompactionData, index, result); return codeIndex; } @@ -368,14 +361,13 @@ static int CountByteBatches(DecodeStatus& status, int mode, const std::vector& codewords, int codeIndex, - std::wstring& resultEncoded, std::string& result, CharacterSet& encoding) +static int ProcessByteECIs(const std::vector& codewords, int codeIndex, Content& result) { while (codeIndex < codewords[0] && codewords[codeIndex] >= TEXT_COMPACTION_MODE_LATCH && !TerminatesCompaction(codewords[codeIndex])) { int code = codewords[codeIndex++]; if (IsECI(code)) - codeIndex = ProcessECI(codewords, codeIndex, codewords[0], code, resultEncoded, result, encoding); + codeIndex = ProcessECI(codewords, codeIndex, codewords[0], code, result); } return codeIndex; @@ -390,13 +382,11 @@ static int ProcessByteECIs(const std::vector& codewords, int codeIndex, * @param mode The byte compaction mode i.e. 901 or 924 * @param codewords The array of codewords (data + error) * @param codeIndex The current index into the codeword array. -* @param resultEncoded The Unicode-encoded data. * @param result The data in the character set encoding. -* @param encoding Currently active character encoding. * @return The next index into the codeword array. */ static int ByteCompaction(DecodeStatus& status, int mode, const std::vector& codewords, int codeIndex, - std::wstring& resultEncoded, std::string& result, CharacterSet& encoding) + Content& result) { // Count number of 5-codeword batches and trailing bytes int trailingCount; @@ -406,7 +396,7 @@ static int ByteCompaction(DecodeStatus& status, int mode, const std::vector return codeIndex; // Deal with initial ECIs - codeIndex = ProcessByteECIs(codewords, codeIndex, resultEncoded, result, encoding); + codeIndex = ProcessByteECIs(codewords, codeIndex, result); for (int batch = 0; batch < batches; batch++) { int64_t value = 0; @@ -417,13 +407,13 @@ static int ByteCompaction(DecodeStatus& status, int mode, const std::vector result.push_back((uint8_t)(value >> (8 * (5 - j)))); // Deal with inter-batch ECIs - codeIndex = ProcessByteECIs(codewords, codeIndex, resultEncoded, result, encoding); + codeIndex = ProcessByteECIs(codewords, codeIndex, result); } for (int i = 0; i < trailingCount; i++) { result.push_back((uint8_t)codewords[codeIndex++]); // Deal with inter-byte ECIs - codeIndex = ProcessByteECIs(codewords, codeIndex, resultEncoded, result, encoding); + codeIndex = ProcessByteECIs(codewords, codeIndex, result); } return codeIndex; @@ -508,8 +498,7 @@ static DecodeStatus DecodeBase900toBase10(const std::vector& codewords, int * @param encoding Currently active character encoding. * @return The next index into the codeword array. */ -static int NumericCompaction(DecodeStatus& status, const std::vector& codewords, int codeIndex, - std::wstring& resultEncoded, std::string& result, CharacterSet& encoding) +static int NumericCompaction(DecodeStatus& status, const std::vector& codewords, int codeIndex, Content& result) { int count = 0; bool end = false; @@ -531,7 +520,7 @@ static int NumericCompaction(DecodeStatus& status, const std::vector& codew result += tmp; count = 0; } - codeIndex = ProcessECI(codewords, codeIndex, codewords[0], code, resultEncoded, result, encoding); + codeIndex = ProcessECI(codewords, codeIndex, codewords[0], code, result); continue; } if (!TerminatesCompaction(code)) { @@ -570,20 +559,17 @@ static int NumericCompaction(DecodeStatus& status, const std::vector& codew /* * Helper to deal with optional text fields in Macros. */ -static int DecodeMacroOptionalTextField(DecodeStatus& status, const std::vector& codewords, int codeIndex, - std::string& field) +static int DecodeMacroOptionalTextField(DecodeStatus& status, const std::vector& codewords, int codeIndex, std::string& field) { - std::wstring resultEncoded; - std::string result; + Content result; // Each optional field begins with an implied reset to ECI 2 (Annex H.2.3). ECI 2 is ASCII for 0-127, and Cp437 // for non-ASCII (128-255). Text optional fields can contain ECIs. - CharacterSet encoding = CharacterSet::Cp437; + result.hintedCharset = "Cp437"; - codeIndex = TextCompaction(status, codewords, codeIndex, resultEncoded, result, encoding); - TextDecoder::Append(resultEncoded, reinterpret_cast(result.data()), result.size(), encoding); + codeIndex = TextCompaction(status, codewords, codeIndex, result); // Converting to UTF-8 (backward-incompatible change for non-ASCII chars) - TextUtfEncoding::ToUtf8(resultEncoded, field); + TextUtfEncoding::ToUtf8(result.text(), field); return codeIndex; } @@ -594,16 +580,14 @@ static int DecodeMacroOptionalTextField(DecodeStatus& status, const std::vector< static int DecodeMacroOptionalNumericField(DecodeStatus& status, const std::vector& codewords, int codeIndex, uint64_t& field) { - std::wstring resultEncoded; - std::string result; - // Each optional field begins with an implied reset to ECI 2 (Annex H.2.3). - // Numeric optional fields should not contain ECIs, but processed anyway. - CharacterSet encoding = CharacterSet::Cp437; + Content result; + // Each optional field begins with an implied reset to ECI 2 (Annex H.2.3). ECI 2 is ASCII for 0-127, and Cp437 + // for non-ASCII (128-255). Text optional fields can contain ECIs. + result.hintedCharset = "Cp437"; - codeIndex = NumericCompaction(status, codewords, codeIndex, resultEncoded, result, encoding); - TextDecoder::Append(resultEncoded, reinterpret_cast(result.data()), result.size(), encoding); + codeIndex = NumericCompaction(status, codewords, codeIndex, result); - field = std::stoll(resultEncoded); + field = std::stoll(result.text()); return codeIndex; } @@ -725,9 +709,10 @@ DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, DecoderResult DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel, const std::string& characterSet) { - std::wstring resultEncoded; - std::string result; - auto encoding = CharacterSetECI::InitEncoding(characterSet); + Content result; + result.symbology = { 'L', '2', -1 }; + result.hintedCharset = characterSet; + bool readerInit = false; auto resultMetadata = std::make_shared(); int codeIndex = 1; @@ -737,24 +722,24 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel, c int code = codewords[codeIndex++]; switch (code) { case TEXT_COMPACTION_MODE_LATCH: - codeIndex = TextCompaction(status, codewords, codeIndex, resultEncoded, result, encoding); + codeIndex = TextCompaction(status, codewords, codeIndex, result); break; case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: // This should only be encountered once in this loop, when default Text Compaction mode applies // (see default case below) - codeIndex = TextCompaction(status, codewords, codeIndex - 1, resultEncoded, result, encoding); + codeIndex = TextCompaction(status, codewords, codeIndex - 1, result); break; case BYTE_COMPACTION_MODE_LATCH: case BYTE_COMPACTION_MODE_LATCH_6: - codeIndex = ByteCompaction(status, code, codewords, codeIndex, resultEncoded, result, encoding); + codeIndex = ByteCompaction(status, code, codewords, codeIndex, result); break; case NUMERIC_COMPACTION_MODE_LATCH: - codeIndex = NumericCompaction(status, codewords, codeIndex, resultEncoded, result, encoding); + codeIndex = NumericCompaction(status, codewords, codeIndex, result); break; case ECI_CHARSET: case ECI_GENERAL_PURPOSE: case ECI_USER_DEFINED: - codeIndex = ProcessECI(codewords, codeIndex, codewords[0], code, resultEncoded, result, encoding); + codeIndex = ProcessECI(codewords, codeIndex, codewords[0], code, result); break; case BEGIN_MACRO_PDF417_CONTROL_BLOCK: status = DecodeMacroBlock(codewords, codeIndex, *resultMetadata, codeIndex); @@ -785,19 +770,18 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel, c status = DecodeStatus::FormatError; // TODO: add NotSupported error } else { // Default mode is Text Compaction mode Alpha sub-mode (ISO/IEC 15438:2015 5.4.2.1) - codeIndex = TextCompaction(status, codewords, codeIndex - 1, resultEncoded, result, encoding); + codeIndex = TextCompaction(status, codewords, codeIndex - 1, result); } break; } } - TextDecoder::Append(resultEncoded, reinterpret_cast(result.data()), result.size(), encoding); - - if (resultEncoded.empty() && resultMetadata->segmentIndex() == -1) - return DecodeStatus::FormatError; if (StatusIsError(status)) return status; + if (result.empty() && resultMetadata->segmentIndex() == -1) + return DecodeStatus::FormatError; + StructuredAppendInfo sai; if (resultMetadata->segmentIndex() > -1) { sai.count = resultMetadata->segmentCount() != -1 @@ -807,14 +791,8 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel, c sai.id = resultMetadata->fileId(); } - Content content; - content.append(result); - - return DecoderResult(ByteArray(), std::move(resultEncoded), std::move(content)) + return DecoderResult(ByteArray(), {}, std::move(result)) .setEcLevel(std::to_string(ecLevel)) - // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using modifier - // that indicates ECI protocol (ISO/IEC 15438:2015 Annex L Table L.1) - .setSymbologyIdentifier("]L2") .setStructuredAppend(sai) .setReaderInit(readerInit) .setExtra(resultMetadata); diff --git a/test/unit/pdf417/PDF417DecoderTest.cpp b/test/unit/pdf417/PDF417DecoderTest.cpp index cf9e4359e2..5b3668843b 100644 --- a/test/unit/pdf417/PDF417DecoderTest.cpp +++ b/test/unit/pdf417/PDF417DecoderTest.cpp @@ -508,7 +508,8 @@ TEST(PDF417DecoderTest, ECIMultipleNumeric) TEST(PDF417DecoderTest, ECIInvalid) { - EXPECT_EQ(decode({ 4, 927, 901, 0 }), L"AA"); // Invalid Character Set ECI (> 899) silently ignored + EXPECT_EQ(decode({ 4, 927, 901, 0 }), L""); // non-charset ECI > 899 -> empty text result + EXPECT_EQ(parse({4, 927, 901, 0}).content().binary, ByteArray("AA")); // non-charset ECI > 899 -> ignored in binary result EXPECT_EQ(decode({ 3, 0, 927 }), L"AA"); // Malformed ECI at end silently ignored } From cb52d7f3821e3e24eb8a6ee030cd500ba97afffe Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 31 May 2022 15:29:57 +0200 Subject: [PATCH 062/180] ECI: return empty ECI strings for empty Results --- core/src/Content.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index a0ec9951fa..1f00029e5f 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -82,7 +82,7 @@ std::wstring Content::text() const std::string Content::utf8Protocol() const { - if (!canProcess()) + if (empty() || !canProcess()) return {}; std::wstring res = TextDecoder::FromLatin1(symbology.toString(true)); @@ -119,6 +119,9 @@ std::string Content::utf8Protocol() const ByteArray Content::binaryECI() const { + if (empty()) + return {}; + std::string res = symbology.toString(true); ForEachECIBlock([&](ECI eci, int begin, int end) { From e34e45655cc0ab221fe781863322991d16f1c0c6 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 31 May 2022 23:00:45 +0200 Subject: [PATCH 063/180] ByteArray: introduce asString() member casting to std::string_view --- core/src/ByteArray.h | 11 +++++++++++ core/src/aztec/AZDecoder.cpp | 4 ++-- core/src/qrcode/QRDecoder.cpp | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/core/src/ByteArray.h b/core/src/ByteArray.h index 85712f20e5..ad87c2961c 100644 --- a/core/src/ByteArray.h +++ b/core/src/ByteArray.h @@ -10,6 +10,10 @@ #include #include +#ifdef __cpp_lib_string_view +#include +#endif + namespace ZXing { /** @@ -24,6 +28,13 @@ class ByteArray : public std::vector explicit ByteArray(const std::string& str) : std::vector(str.begin(), str.end()) {} void append(const ByteArray& other) { insert(end(), other.begin(), other.end()); } + +#ifdef __cpp_lib_string_view + std::string_view asString(size_t pos = 0, size_t len = std::string_view::npos) const + { + return std::string_view(reinterpret_cast(data()), size()).substr(pos, len); + } +#endif }; inline std::string ToHex(const ByteArray& bytes) diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index 6c295a6593..7d0ad24dbe 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -333,13 +333,13 @@ DecoderResult Decode(const BitArray& bits, const std::string& characterSet) // FNC1 following single uppercase letter (the AIM Application Indicator) res.symbology.modifier = '2'; // AIM // TODO: remove the AI from the content? - res.applicationIndicator = std::string(reinterpret_cast(res.binary.data()), 1); + res.applicationIndicator = res.binary.asString(0, 1); res.erase(1, 1); // Remove FNC1, // The AIM Application Indicator character "A"-"Z" is left in the stream (ISO/IEC 24778:2008 16.2) } else if (res.binary.size() > 3 && std::isdigit(res.binary[0]) && std::isdigit(res.binary[1]) && res.binary[2] == 29) { // FNC1 following 2 digits (the AIM Application Indicator) res.symbology.modifier = '2'; // AIM - res.applicationIndicator = std::string(reinterpret_cast(res.binary.data()), 2); + res.applicationIndicator = res.binary.asString(0, 2); res.erase(2, 1); // Remove FNC1 // The AIM Application Indicator characters "00"-"99" are left in the stream (ISO/IEC 24778:2008 16.2) } diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index a5b2c579dd..507666987d 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -293,7 +293,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo result += static_cast(appInd - 100); else throw std::runtime_error("Invalid AIM Application Indicator"); - result.applicationIndicator = std::string(result.binary.begin(), result.binary.end()); // see also above + result.applicationIndicator = result.binary.asString(); // see also above break; case CodecMode::STRUCTURED_APPEND: // sequence number and parity is added later to the result metadata From f920604125ab65d9b984ffd3108a8a546bcffa23 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 31 May 2022 23:03:01 +0200 Subject: [PATCH 064/180] ECI: switch MCDecoder to use new Content class --- core/src/Content.cpp | 12 +++- core/src/Content.h | 1 + core/src/maxicode/MCDecoder.cpp | 57 +++++++------------ test/samples/maxicode-1/mode4-mixed-ecis.bin | Bin 0 -> 28 bytes test/samples/maxicode-1/mode4-mixed-ecis.txt | 1 - test/unit/maxicode/MCDecoderTest.cpp | 2 +- 6 files changed, 34 insertions(+), 39 deletions(-) create mode 100644 test/samples/maxicode-1/mode4-mixed-ecis.bin delete mode 100644 test/samples/maxicode-1/mode4-mixed-ecis.txt diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 1f00029e5f..1754e31fa0 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -57,6 +57,14 @@ void Content::erase(int pos, int n) pos -= n; } +void Content::insert(int pos, const std::string& str) +{ + binary.insert(binary.begin() + pos, str.begin(), str.end()); + for (auto& e : encodings) + if (e.pos > pos) + pos += Size(str); +} + bool Content::canProcess() const { return std::all_of(encodings.begin(), encodings.end(), [](Encoding e) { return CanProcess(e.eci); }); @@ -87,7 +95,9 @@ std::string Content::utf8Protocol() const std::wstring res = TextDecoder::FromLatin1(symbology.toString(true)); ECI lastECI = ECI::Unknown; - auto fallbackCS = guessEncoding(); + auto fallbackCS = CharacterSetECI::CharsetFromName(hintedCharset.c_str()); + if (!hasECI && fallbackCS == CharacterSet::Unknown) + fallbackCS = guessEncoding(); ForEachECIBlock([&](ECI eci, int begin, int end) { // first determine how to decode the content (choose character set) diff --git a/core/src/Content.h b/core/src/Content.h index 33e7991ba2..b1c747ef96 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -62,6 +62,7 @@ class Content void operator+=(const std::string& str) { append(str); } void erase(int pos, int n); + void insert(int pos, const std::string& str); bool empty() const { return binary.empty(); } bool canProcess() const; diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 9a499f9399..76886ace7a 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -179,22 +179,22 @@ static int GetServiceClass(const ByteArray& bytes) /** * See ISO/IEC 16023:2000 Section 4.6 Table 3 */ -static int ParseECIValue(const ByteArray& bytes, int& i) +static ZXing::ECI ParseECIValue(const ByteArray& bytes, int& i) { int firstByte = bytes[++i]; if ((firstByte & 0x20) == 0) - return firstByte; + return ZXing::ECI(firstByte); int secondByte = bytes[++i]; if ((firstByte & 0x10) == 0) - return ((firstByte & 0x0F) << 6) | secondByte; + return ZXing::ECI(((firstByte & 0x0F) << 6) | secondByte); int thirdByte = bytes[++i]; if ((firstByte & 0x08) == 0) - return ((firstByte & 0x07) << 12) | (secondByte << 6) | thirdByte; + return ZXing::ECI(((firstByte & 0x07) << 12) | (secondByte << 6) | thirdByte); int fourthByte = bytes[++i]; - return ((firstByte & 0x03) << 18) | (secondByte << 12) | (thirdByte << 6) | fourthByte; + return ZXing::ECI(((firstByte & 0x03) << 18) | (secondByte << 12) | (thirdByte << 6) | fourthByte); } /** @@ -210,14 +210,11 @@ static void ParseStructuredAppend(const ByteArray& bytes, int& i, StructuredAppe // No id } -static std::wstring GetMessage(const ByteArray& bytes, int start, int len, const std::string& characterSet, StructuredAppendInfo& sai) +static void GetMessage(const ByteArray& bytes, int start, int len, Content& result, StructuredAppendInfo& sai) { - std::string sb; - std::wstring sbEncoded; int shift = -1; int set = 0; int lastset = 0; - CharacterSet encoding = CharacterSetECI::InitEncoding(characterSet); for (int i = start; i < start + len; i++) { int c = CHARSETS[set].at(bytes[i]); @@ -250,65 +247,53 @@ static std::wstring GetMessage(const ByteArray& bytes, int start, int len, const shift = 3; break; case NS: - sb.append( + result.append( ToString((bytes[i + 1] << 24) + (bytes[i + 2] << 18) + (bytes[i + 3] << 12) + (bytes[i + 4] << 6) + bytes[i + 5], 9)); i += 5; break; case LOCK: shift = -1; break; - case ECI: encoding = CharacterSetECI::OnChangeAppendReset(ParseECIValue(bytes, i), sbEncoded, sb, encoding); break; + case ECI: result.switchEncoding(ParseECIValue(bytes, i)); break; case PAD: if (i == start) ParseStructuredAppend(bytes, i, sai); shift = -1; break; - default: sb.push_back((unsigned char)c); + default: result.push_back(c); } if (shift-- == 0) set = lastset; } - - TextDecoder::Append(sbEncoded, reinterpret_cast(sb.data()), sb.size(), encoding); - - return sbEncoded; } ZXING_EXPORT_TEST_ONLY -DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& characterSet) +DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& /*characterSet*/) { - std::wstring result; - result.reserve(144); + Content result; + result.symbology = {'U', (mode == 2 || mode == 3) ? '1' : '0', 2}; // TODO: No identifier defined for mode 6? + result.hintedCharset = "ISO8859_1"; StructuredAppendInfo sai; + switch (mode) { case 2: case 3: { auto postcode = mode == 2 ? ToString(GetPostCode2(bytes), GetPostCode2Length(bytes)) : GetPostCode3(bytes); auto country = ToString(GetCountry(bytes), 3); auto service = ToString(GetServiceClass(bytes), 3); - result.append(GetMessage(bytes, 10, 84, characterSet, sai)); - if (result.size() >= 9 && result.compare(0, 7, L"[)>\u001E01\u001D") == 0) // "[)>" + RS + "01" + GS - result.insert(9, TextDecoder::FromLatin1(postcode + GS + country + GS + service + GS)); + GetMessage(bytes, 10, 84, result, sai); + if (result.binary.asString().compare(0, 7, "[)>\u001E01\u001D") == 0) // "[)>" + RS + "01" + GS + result.insert(9, postcode + GS + country + GS + service + GS); else - result.insert(0, TextDecoder::FromLatin1(postcode + GS + country + GS + service + GS)); + result.insert(0, postcode + GS + country + GS + service + GS); break; } case 4: - case 6: result.append(GetMessage(bytes, 1, 93, characterSet, sai)); break; - case 5: result.append(GetMessage(bytes, 1, 77, characterSet, sai)); break; + case 6: GetMessage(bytes, 1, 93, result, sai); break; + case 5: GetMessage(bytes, 1, 77, result, sai); break; } - // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using modifiers - // that indicate ECI protocol (ISO/IEC 16023:2000 Annexe E Table E1) - std::string symbologyIdentifier; - if (mode == 4 || mode == 5) - symbologyIdentifier = "]U0"; - else if (mode == 2 || mode == 3) - symbologyIdentifier = "]U1"; - // No identifier defined for mode 6 - - return DecoderResult(std::move(bytes), std::move(result)) + return DecoderResult(std::move(bytes), {}, std::move(result)) .setEcLevel(std::to_string(mode)) - .setSymbologyIdentifier(std::move(symbologyIdentifier)) .setStructuredAppend(sai) .setReaderInit(mode == 6); } diff --git a/test/samples/maxicode-1/mode4-mixed-ecis.bin b/test/samples/maxicode-1/mode4-mixed-ecis.bin new file mode 100644 index 0000000000000000000000000000000000000000..c8ab77e10b34e08f4c7ac0ff5acf417ba4a4c280 GIT binary patch literal 28 jcmaF35Coc2C&x1^{4k+s+s~^%7rxxEaKobS{}uuO01Xn( literal 0 HcmV?d00001 diff --git a/test/samples/maxicode-1/mode4-mixed-ecis.txt b/test/samples/maxicode-1/mode4-mixed-ecis.txt deleted file mode 100644 index 9fcc6b661d..0000000000 --- a/test/samples/maxicode-1/mode4-mixed-ecis.txt +++ /dev/null @@ -1 +0,0 @@ -á¡ĦĐâ€Ä„ă†ç‚ąÂˇđڶéľéľ¤Ă©ę°€ę°é˝„ŘŚ \ No newline at end of file diff --git a/test/unit/maxicode/MCDecoderTest.cpp b/test/unit/maxicode/MCDecoderTest.cpp index 21f9447d9e..17a8caf3f2 100644 --- a/test/unit/maxicode/MCDecoderTest.cpp +++ b/test/unit/maxicode/MCDecoderTest.cpp @@ -76,7 +76,7 @@ TEST(MCDecodeTest, StructuredAppendSymbologyIdentifier) EXPECT_EQ(info({49}, 6).index, -1); // Mode 6 EXPECT_EQ(info({49}, 6).count, -1); EXPECT_TRUE(info({49}, 6).id.empty()); - EXPECT_TRUE(parse({49}, 6).symbologyIdentifier().empty()); // Not defined for reader initialisation/programming +// EXPECT_TRUE(parse({49}, 6).symbologyIdentifier().empty()); // Not defined for reader initialisation/programming // ISO/IEC 16023:2000 4.9.1 example EXPECT_EQ(info({33, 22, 49}, 2).index, 2); // Mode 2 - 3rd position 1-based == index 2 From 299c4537ba48cdc1e7a93fd7c83a50e2967429e0 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 1 Jun 2022 00:09:12 +0200 Subject: [PATCH 065/180] test: add unit tests for Content class --- core/src/Content.h | 4 +- test/unit/CMakeLists.txt | 1 + test/unit/ContentTest.cpp | 93 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 test/unit/ContentTest.cpp diff --git a/core/src/Content.h b/core/src/Content.h index b1c747ef96..b786b88386 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -16,12 +16,12 @@ std::string ToString(ContentType type); struct SymbologyIdentifier { - char code, modifier; + char code = 0, modifier = 0; int eciModifierOffset = 0; std::string toString(bool hasECI = false) const { - return ']' + std::string(1, code) + static_cast(modifier + eciModifierOffset * hasECI); + return code ? ']' + std::string(1, code) + static_cast(modifier + eciModifierOffset * hasECI) : std::string(); } }; diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 57f1dfafd0..9bd281796d 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable (UnitTest PseudoRandom.h BitHacksTest.cpp CharacterSetECITest.cpp + ContentTest.cpp GTINTest.cpp ReedSolomonTest.cpp TextDecoderTest.cpp diff --git a/test/unit/ContentTest.cpp b/test/unit/ContentTest.cpp new file mode 100644 index 0000000000..5b3129a4d2 --- /dev/null +++ b/test/unit/ContentTest.cpp @@ -0,0 +1,93 @@ +/* +* Copyright 2022 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "Content.h" + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +using namespace ZXing; +using namespace testing; + +TEST(ContentTest, Base) +{ + { // Null + Content c; + EXPECT_EQ(c.guessEncoding(), CharacterSet::Unknown); + EXPECT_EQ(c.symbology.toString(), ""); + EXPECT_TRUE(c.empty()); + } + + { // set latin1 + Content c; + c.switchEncoding(CharacterSet::ISO8859_1); + c.append(ByteArray{'A', 0xE9, 'Z'}); + EXPECT_EQ(c.text(), L"A\u00E9Z"); + } + + { // set CharacterSet::ISO8859_5 + Content c; + c.switchEncoding(CharacterSet::ISO8859_5); + c.append(ByteArray{'A', 0xE9, 'Z'}); + EXPECT_EQ(c.text(), L"A\u0449Z"); + } + + { // switch to CharacterSet::ISO8859_5 + Content c; + c.append(ByteArray{'A', 0xE9, 'Z'}); + EXPECT_FALSE(c.hasECI); + c.switchEncoding(CharacterSet::ISO8859_5); + EXPECT_FALSE(c.hasECI); + c.append(ByteArray{'A', 0xE9, 'Z'}); + EXPECT_EQ(c.text(), L"A\u00E9ZA\u0449Z"); + } +} + +TEST(ContentTest, GuessEncoding) +{ + { // guess latin1 + Content c; + c.append(ByteArray{'A', 0xE9, 'Z'}); + EXPECT_EQ(c.guessEncoding(), CharacterSet::ISO8859_1); + EXPECT_EQ(c.text(), L"A\u00E9Z"); + EXPECT_EQ(c.binaryECI(), c.binary); + } + + { // guess Shift_JIS + Content c; + c.append(ByteArray{'A', 0x83, 0x65, 'Z'}); + EXPECT_EQ(c.guessEncoding(), CharacterSet::Shift_JIS); + EXPECT_EQ(c.text(), L"A\u30C6Z"); + } +} + +TEST(ContentTest, ECI) +{ + { // switch to ECI::ISO8859_5 + Content c; + c.append(ByteArray{'A', 0xE9, 'Z'}); + c.switchEncoding(ECI::ISO8859_5); + c.append(ByteArray{'A', 0xE9, 'Z'}); + EXPECT_TRUE(c.hasECI); + EXPECT_EQ(c.text(), L"A\u00E9ZA\u0449Z"); + EXPECT_EQ(c.binaryECI().asString(), std::string_view("\\000003A\xE9Z\\000007A\xE9Z")); + } + + { // switch ECI -> latin1 for unknown (instead of Shift_JIS) + Content c; + c.append(ByteArray{'A', 0x83, 0x65, 'Z'}); + c.switchEncoding(ECI::ISO8859_5); + c.append(ByteArray{'A', 0xE9, 'Z'}); + EXPECT_EQ(c.text(), L"A\u0083\u0065ZA\u0449Z"); + EXPECT_EQ(c.binaryECI().asString(), std::string_view("\\000003A\x83\x65Z\\000007A\xE9Z")); + } + + { // double '\' + Content c; + c.append("C:\\Test"); + EXPECT_EQ(c.text(), L"C:\\Test"); + EXPECT_EQ(c.binaryECI().asString(), std::string_view("C:\\\\Test")); + } +} From 91cfc5475d5a25862c706d4a35833e238ff506ea Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 1 Jun 2022 00:13:31 +0200 Subject: [PATCH 066/180] ECI: remove deprecated/unused OnChangeAppendReset and InitEncoding --- core/src/CharacterSetECI.cpp | 30 ------------ core/src/CharacterSetECI.h | 17 ------- test/unit/CharacterSetECITest.cpp | 78 ------------------------------- 3 files changed, 125 deletions(-) diff --git a/core/src/CharacterSetECI.cpp b/core/src/CharacterSetECI.cpp index d5640e3e32..95bff2f722 100644 --- a/core/src/CharacterSetECI.cpp +++ b/core/src/CharacterSetECI.cpp @@ -102,34 +102,4 @@ CharacterSet CharsetFromName(const char* name) return CharacterSet::Unknown; } -CharacterSet InitEncoding(const std::string& name, CharacterSet encodingDefault) -{ - if (!name.empty()) { - auto encodingInit = CharacterSetECI::CharsetFromName(name.c_str()); - if (encodingInit != CharacterSet::Unknown) { - encodingDefault = encodingInit; - } - } - - return encodingDefault; -} - -#ifdef ZXING_BUILD_READERS -CharacterSet OnChangeAppendReset(const int eci, std::wstring& encoded, std::string& data, CharacterSet encoding) -{ - // Character set ECIs only - if (eci >= 0 && eci <= 899) { - auto encodingNew = ToCharacterSet(ECI(eci)); - if (encodingNew != CharacterSet::Unknown && encodingNew != encoding) { - // Encode data so far in current encoding and reset - TextDecoder::Append(encoded, reinterpret_cast(data.data()), data.size(), encoding); - data.clear(); - encoding = encodingNew; - } - } - - return encoding; -} -#endif - } // namespace ZXing::CharacterSetECI diff --git a/core/src/CharacterSetECI.h b/core/src/CharacterSetECI.h index e665f920dd..249c729a74 100644 --- a/core/src/CharacterSetECI.h +++ b/core/src/CharacterSetECI.h @@ -26,22 +26,5 @@ namespace CharacterSetECI { */ CharacterSet CharsetFromName(const char* name); -/** - * @param name character set ECI encoding name - * @param encodingDefault default {@code CharacterSet} encoding to return if name is empty or unsupported - * @return {@code CharacterSet} representing ECI for character encoding - */ -CharacterSet InitEncoding(const std::string& name, CharacterSet encodingDefault = CharacterSet::ISO8859_1); - -/** - * @param eci ECI requested, which if different when resolved to the existing {@code CharacterSet} encoding will - * cause conversion to occur - * @param encoded Unicode buffer to append the data to if conversion occurs - * @param data buffer to be converted and appended to Unicode buffer and then cleared if conversion occurs - * @param encoding existing {@code CharacterSet} encoding to use for conversion if conversion occurs - * @return {@code CharacterSet} representing ECI for new character encoding if changed, or existing encoding if not - */ -CharacterSet OnChangeAppendReset(const int eci, std::wstring& encoded, std::string& data, CharacterSet encoding); - } // namespace CharacterSetECI } // namespace ZXing diff --git a/test/unit/CharacterSetECITest.cpp b/test/unit/CharacterSetECITest.cpp index 3495bc3b7e..aeef242add 100644 --- a/test/unit/CharacterSetECITest.cpp +++ b/test/unit/CharacterSetECITest.cpp @@ -22,81 +22,3 @@ TEST(CharacterSetECITest, Charset2ECI) EXPECT_EQ(ToInt(ToECI(CharacterSet::BINARY)), 899); EXPECT_EQ(ToInt(ToECI(CharacterSet::Unknown)), -1); } - -TEST(CharacterSetECITest, InitEncoding) -{ - EXPECT_EQ(InitEncoding(""), CharacterSet::ISO8859_1); - EXPECT_EQ(InitEncoding("", CharacterSet::ISO8859_2), CharacterSet::ISO8859_2); - EXPECT_EQ(InitEncoding("asdfasdf"), CharacterSet::ISO8859_1); - EXPECT_EQ(InitEncoding("asdfasdf", CharacterSet::ISO8859_2), CharacterSet::ISO8859_2); - EXPECT_EQ(InitEncoding("ISO-8859-1"), CharacterSet::ISO8859_1); - EXPECT_EQ(InitEncoding("ISO-8859-2"), CharacterSet::ISO8859_2); - EXPECT_EQ(InitEncoding("UTF-16BE"), CharacterSet::UnicodeBig); - EXPECT_EQ(InitEncoding("", CharacterSet::Unknown), CharacterSet::Unknown); -} - -TEST(CharacterSetECITest, OnChangeAppendReset) -{ - { - std::string data; - std::wstring encoded; - - // Null - auto result = OnChangeAppendReset(3, encoded, data, CharacterSet::Unknown); - EXPECT_EQ(result, CharacterSet::ISO8859_1); - EXPECT_TRUE(data.empty()); - EXPECT_TRUE(encoded.empty()); - } - - { - static const uint8_t bytes[] = { 'A', 0xE9, 'Z' }; - std::string data(reinterpret_cast(&bytes), sizeof(bytes)); - std::wstring encoded; - - // Encoding change - auto result = OnChangeAppendReset(3, encoded, data, CharacterSet::Unknown); - EXPECT_EQ(result, CharacterSet::ISO8859_1); - EXPECT_TRUE(data.empty()); - EXPECT_EQ(encoded, L"A\u00E9Z"); - } - - { - static const uint8_t bytes[] = { 'A', 0xE9, 'Z' }; - std::string data(reinterpret_cast(&bytes), sizeof(bytes)); - std::wstring encoded; - - // Encoding same - auto result = OnChangeAppendReset(3, encoded, data, CharacterSet::ISO8859_1); - EXPECT_EQ(result, CharacterSet::ISO8859_1); - EXPECT_EQ(data, "A\xE9Z"); - EXPECT_TRUE(encoded.empty()); - } - - { - CharacterSet result; - static const uint8_t bytes[] = { 'A', 0xE9, 'Z' }; - std::string data(reinterpret_cast(&bytes), sizeof(bytes)); - std::wstring encoded(L"A\u00E9Z"); - - // Encoding change - result = OnChangeAppendReset(20, encoded, data, CharacterSet::ISO8859_5); - EXPECT_EQ(result, CharacterSet::Shift_JIS); - EXPECT_TRUE(data.empty()); - EXPECT_EQ(encoded, L"A\u00E9ZA\u0449Z"); - - static const uint8_t bytes2[] = { 'A', 0x83, 0x65, 'Z' }; - std::string data2(reinterpret_cast(&bytes2), sizeof(bytes2)); - - // Encoding same - result = OnChangeAppendReset(20, encoded, data2, result); - EXPECT_EQ(result, CharacterSet::Shift_JIS); - EXPECT_THAT(data2, ElementsAreArray(bytes2, sizeof(bytes2))); - EXPECT_EQ(encoded, L"A\u00E9ZA\u0449Z"); - - // Encoding change - result = OnChangeAppendReset(4, encoded, data2, result); - EXPECT_EQ(result, CharacterSet::ISO8859_2); - EXPECT_TRUE(data2.empty()); - EXPECT_EQ(encoded, L"A\u00E9ZA\u0449ZA\u30C6Z"); - } -} From bee813aebf01d42476b1b5c463f9e07ed0067443 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 2 Jun 2022 01:57:16 +0200 Subject: [PATCH 067/180] ci: exclude test/samples from "Source code" release assets disable blackbox tests by default and tune ZXingReaderTest to not fail when samples folder is missing. This fixes #312. --- .gitattributes | 1 + .github/workflows/ci.yml | 2 +- CMakeLists.txt | 2 +- example/CMakeLists.txt | 15 +++++++-------- 4 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..482add0e9b --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +test/samples export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79becdffc6..6858fb4f2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_UNIT_TESTS=ON -DBUILD_PYTHON_MODULE=ON + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_BLACKBOX_TESTS=ON -DBUILD_UNIT_TESTS=ON -DBUILD_PYTHON_MODULE=ON - name: Build working-directory: ${{runner.workspace}}/build diff --git a/CMakeLists.txt b/CMakeLists.txt index b9052cac91..2e215cf44e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project (ZXing VERSION "1.3.0" LANGUAGES CXX) option (BUILD_WRITERS "Build with writer support (encoders)" ON) option (BUILD_READERS "Build with reader support (decoders)" ON) option (BUILD_EXAMPLES "Build the example barcode reader/writer applications" ON) -option (BUILD_BLACKBOX_TESTS "Build the black box reader/writer tests" ON) +option (BUILD_BLACKBOX_TESTS "Build the black box reader/writer tests" OFF) option (BUILD_UNIT_TESTS "Build the unit tests (don't enable for production builds)" OFF) option (BUILD_PYTHON_MODULE "Build the python module" OFF) set(BUILD_DEPENDENCIES "AUTO" CACHE STRING "Fetch from github or use locally installed (AUTO/GITHUB/LOCAL)") diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index ec593db9ae..471050dda5 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -3,14 +3,6 @@ set (CMAKE_CXX_STANDARD 11) zxing_add_package_stb() -if (BUILD_READERS) - add_executable (ZXingReader ZXingReader.cpp) - - target_link_libraries (ZXingReader ZXing::ZXing stb::stb) - - add_test(NAME ZXingReaderTest COMMAND ZXingReader -fast -format qrcode "${CMAKE_SOURCE_DIR}/test/samples/qrcode-1/1.png") -endif() - if (BUILD_WRITERS) add_executable (ZXingWriter ZXingWriter.cpp) @@ -19,6 +11,13 @@ if (BUILD_WRITERS) add_test(NAME ZXingWriterTest COMMAND ZXingWriter qrcode "I have the best words." test.png) endif() +if (BUILD_READERS) + add_executable (ZXingReader ZXingReader.cpp) + + target_link_libraries (ZXingReader ZXing::ZXing stb::stb) + + add_test(NAME ZXingReaderTest COMMAND ZXingReader -fast -format qrcode test.png) # see above +endif() if (BUILD_READERS) find_package(Qt5 COMPONENTS Gui Multimedia Quick) From 14a2e78a92722fecb6770fe57dc400a113e220f6 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 3 Jun 2022 10:16:32 +0200 Subject: [PATCH 068/180] example: install the ZXingReader and ZXingWriter Fixes #339. --- example/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 471050dda5..8cb2e1b4d8 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -3,12 +3,16 @@ set (CMAKE_CXX_STANDARD 11) zxing_add_package_stb() +include (GNUInstallDirs) + if (BUILD_WRITERS) add_executable (ZXingWriter ZXingWriter.cpp) target_link_libraries (ZXingWriter ZXing::ZXing stb::stb) add_test(NAME ZXingWriterTest COMMAND ZXingWriter qrcode "I have the best words." test.png) + + install(TARGETS ZXingWriter DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() if (BUILD_READERS) @@ -17,6 +21,8 @@ if (BUILD_READERS) target_link_libraries (ZXingReader ZXing::ZXing stb::stb) add_test(NAME ZXingReaderTest COMMAND ZXingReader -fast -format qrcode test.png) # see above + + install(TARGETS ZXingReader DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() if (BUILD_READERS) From e79228141731b2e30d2e855bfc577b357db4748d Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 3 Jun 2022 17:11:01 +0200 Subject: [PATCH 069/180] CharacterSet: new CharacterSetFromString and deprecated CharacterSetECI Deprecate the namespace `CharacterSetECI` and header and use name consistent with `BarcodeFormatFromString()`. --- core/CMakeLists.txt | 2 +- .../{CharacterSetECI.cpp => CharacterSet.cpp} | 19 +++++++------------ core/src/CharacterSet.h | 4 ++++ core/src/CharacterSetECI.h | 4 +--- core/src/Content.cpp | 6 +++--- core/src/datamatrix/DMDecoder.cpp | 2 +- core/src/maxicode/MCDecoder.cpp | 2 +- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 2 +- core/src/qrcode/QRDecoder.cpp | 1 - example/ZXingWriter.cpp | 4 ++-- test/unit/CharacterSetECITest.cpp | 2 -- 11 files changed, 21 insertions(+), 27 deletions(-) rename core/src/{CharacterSetECI.cpp => CharacterSet.cpp} (90%) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index d52b08570d..000dde3433 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -49,8 +49,8 @@ set (COMMON_FILES src/ByteArray.h src/ByteMatrix.h src/CharacterSet.h + src/CharacterSet.cpp src/CharacterSetECI.h - src/CharacterSetECI.cpp src/ConcentricFinder.h src/ConcentricFinder.cpp src/CustomData.h diff --git a/core/src/CharacterSetECI.cpp b/core/src/CharacterSet.cpp similarity index 90% rename from core/src/CharacterSetECI.cpp rename to core/src/CharacterSet.cpp index 95bff2f722..0badb9c7a6 100644 --- a/core/src/CharacterSetECI.cpp +++ b/core/src/CharacterSet.cpp @@ -4,18 +4,13 @@ */ // SPDX-License-Identifier: Apache-2.0 -#include "CharacterSetECI.h" - -#include "ECI.h" -#include "TextDecoder.h" +#include "CharacterSet.h" #include -#include #include -#include #include -namespace ZXing::CharacterSetECI { +namespace ZXing { struct CompareNoCase { bool operator ()(const char* a, const char* b) const { @@ -93,13 +88,13 @@ static const std::map ECI_NAME_TO_CHA {"BINARY", CharacterSet::BINARY}, }; -CharacterSet CharsetFromName(const char* name) +CharacterSet CharacterSetFromString(const std::string& name) { - auto it = ECI_NAME_TO_CHARSET.find(name); - if (it != ECI_NAME_TO_CHARSET.end()) { + auto it = ECI_NAME_TO_CHARSET.find(name.c_str()); + if (it != ECI_NAME_TO_CHARSET.end()) return it->second; - } + return CharacterSet::Unknown; } -} // namespace ZXing::CharacterSetECI +} // namespace ZXing diff --git a/core/src/CharacterSet.h b/core/src/CharacterSet.h index e078f1fa89..b26f4b23f2 100644 --- a/core/src/CharacterSet.h +++ b/core/src/CharacterSet.h @@ -5,6 +5,8 @@ #pragma once +#include + namespace ZXing { enum class CharacterSet @@ -46,4 +48,6 @@ enum class CharacterSet CharsetCount }; +CharacterSet CharacterSetFromString(const std::string& name); + } // ZXing diff --git a/core/src/CharacterSetECI.h b/core/src/CharacterSetECI.h index 249c729a74..e70f9dc3bc 100644 --- a/core/src/CharacterSetECI.h +++ b/core/src/CharacterSetECI.h @@ -8,8 +8,6 @@ #include "CharacterSet.h" -#include - namespace ZXing { /** @@ -24,7 +22,7 @@ namespace CharacterSetECI { * @return {@code CharacterSet} representing ECI of given value, or {@code CharacterSet::Unknown} if it is * unsupported */ -CharacterSet CharsetFromName(const char* name); +[[deprecated]] inline CharacterSet CharsetFromName(const char* name) { return CharacterSetFromString(name); } } // namespace CharacterSetECI } // namespace ZXing diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 1754e31fa0..c730e9ca03 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -5,7 +5,7 @@ #include "Content.h" -#include "CharacterSetECI.h" +#include "CharacterSet.h" #include "TextDecoder.h" #include "TextUtfEncoding.h" #include "ZXContainerAlgorithms.h" @@ -75,7 +75,7 @@ std::wstring Content::text() const if (!canProcess()) return {}; - auto fallbackCS = CharacterSetECI::CharsetFromName(hintedCharset.c_str()); + auto fallbackCS = CharacterSetFromString(hintedCharset); if (!hasECI && fallbackCS == CharacterSet::Unknown) fallbackCS = guessEncoding(); @@ -95,7 +95,7 @@ std::string Content::utf8Protocol() const std::wstring res = TextDecoder::FromLatin1(symbology.toString(true)); ECI lastECI = ECI::Unknown; - auto fallbackCS = CharacterSetECI::CharsetFromName(hintedCharset.c_str()); + auto fallbackCS = CharacterSetFromString(hintedCharset); if (!hasECI && fallbackCS == CharacterSet::Unknown) fallbackCS = guessEncoding(); diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 87286c8300..a7303018a9 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -8,7 +8,7 @@ #include "BitMatrix.h" #include "BitSource.h" -#include "CharacterSetECI.h" +#include "CharacterSet.h" #include "DMBitLayout.h" #include "DMDataBlock.h" #include "DMVersion.h" diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 76886ace7a..8d4be16bea 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -7,7 +7,7 @@ #include "MCDecoder.h" #include "ByteArray.h" -#include "CharacterSetECI.h" +#include "CharacterSet.h" #include "DecoderResult.h" #include "DecodeStatus.h" #include "GenericGF.h" diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index e3ebc2a8ff..515c066b1d 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -7,7 +7,7 @@ #include "PDFDecodedBitStreamParser.h" #include "ByteArray.h" -#include "CharacterSetECI.h" +#include "CharacterSet.h" #include "DecoderResult.h" #include "DecodeStatus.h" #include "PDFDecoderResultExtra.h" diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 507666987d..703d28d941 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -9,7 +9,6 @@ #include "BitMatrix.h" #include "BitSource.h" #include "CharacterSet.h" -#include "CharacterSetECI.h" #include "DecodeStatus.h" #include "DecoderResult.h" #include "GenericGF.h" diff --git a/example/ZXingWriter.cpp b/example/ZXingWriter.cpp index 14934d62bf..80a62d5c58 100644 --- a/example/ZXingWriter.cpp +++ b/example/ZXingWriter.cpp @@ -6,9 +6,9 @@ #include "BarcodeFormat.h" #include "BitMatrix.h" #include "BitMatrixIO.h" +#include "CharacterSet.h" #include "MultiFormatWriter.h" #include "TextUtfEncoding.h" -#include "CharacterSetECI.h" #include #include @@ -73,7 +73,7 @@ static bool ParseOptions(int argc, char* argv[], int* width, int* height, int* m } else if (strcmp(argv[i], "-encoding") == 0) { if (++i == argc) return false; - *encoding = CharacterSetECI::CharsetFromName(argv[i]); + *encoding = CharacterSetFromString(argv[i]); } else if (nonOptArgCount == 0) { *format = BarcodeFormatFromString(argv[i]); if (*format == BarcodeFormat::None) { diff --git a/test/unit/CharacterSetECITest.cpp b/test/unit/CharacterSetECITest.cpp index aeef242add..2ff4dbf14d 100644 --- a/test/unit/CharacterSetECITest.cpp +++ b/test/unit/CharacterSetECITest.cpp @@ -3,14 +3,12 @@ */ // SPDX-License-Identifier: Apache-2.0 -#include "CharacterSetECI.h" #include "ECI.h" #include "gtest/gtest.h" #include "gmock/gmock.h" using namespace ZXing; -using namespace ZXing::CharacterSetECI; using namespace testing; TEST(CharacterSetECITest, Charset2ECI) From 0559973638a2cc9e871b570fd334884b7d3dae39 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 3 Jun 2022 17:49:25 +0200 Subject: [PATCH 070/180] CharacterSet: introduce name normalization like in BarcodeFormatFromString --- core/src/CharacterSet.cpp | 58 +++++++++---------------------- test/unit/CharacterSetECITest.cpp | 10 ++++++ 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/core/src/CharacterSet.cpp b/core/src/CharacterSet.cpp index 0badb9c7a6..57ee86eb01 100644 --- a/core/src/CharacterSet.cpp +++ b/core/src/CharacterSet.cpp @@ -6,59 +6,34 @@ #include "CharacterSet.h" -#include -#include -#include +#include "ZXContainerAlgorithms.h" + +#include namespace ZXing { -struct CompareNoCase { - bool operator ()(const char* a, const char* b) const { - while (*a != '\0' && *b != '\0') { - auto ca = std::tolower(*a++); - auto cb = std::tolower(*b++); - if (ca < cb) { - return true; - } - else if (ca > cb) { - return false; - } - } - return *a == '\0' && *b != '\0'; - } +struct CharacterSetName +{ + const char* name; + CharacterSet cs; }; -static const std::map ECI_NAME_TO_CHARSET = { +static CharacterSetName NAME_TO_CHARSET[] = { {"Cp437", CharacterSet::Cp437}, - {"ISO8859_1", CharacterSet::ISO8859_1}, {"ISO-8859-1", CharacterSet::ISO8859_1}, - {"ISO8859_2", CharacterSet::ISO8859_2}, {"ISO-8859-2", CharacterSet::ISO8859_2}, - {"ISO8859_3", CharacterSet::ISO8859_3}, {"ISO-8859-3", CharacterSet::ISO8859_3}, - {"ISO8859_4", CharacterSet::ISO8859_4}, {"ISO-8859-4", CharacterSet::ISO8859_4}, - {"ISO8859_5", CharacterSet::ISO8859_5}, {"ISO-8859-5", CharacterSet::ISO8859_5}, - {"ISO8859_6", CharacterSet::ISO8859_6}, {"ISO-8859-6", CharacterSet::ISO8859_6}, - {"ISO8859_7", CharacterSet::ISO8859_7}, {"ISO-8859-7", CharacterSet::ISO8859_7}, - {"ISO8859_8", CharacterSet::ISO8859_8}, {"ISO-8859-8", CharacterSet::ISO8859_8}, - {"ISO8859_9", CharacterSet::ISO8859_9}, {"ISO-8859-9", CharacterSet::ISO8859_9}, - {"ISO8859_10", CharacterSet::ISO8859_10}, {"ISO-8859-10", CharacterSet::ISO8859_10}, - {"ISO8859_11", CharacterSet::ISO8859_11}, {"ISO-8859-11", CharacterSet::ISO8859_11}, - {"ISO8859_13", CharacterSet::ISO8859_13}, {"ISO-8859-13", CharacterSet::ISO8859_13}, - {"ISO8859_14", CharacterSet::ISO8859_14}, {"ISO-8859-14", CharacterSet::ISO8859_14}, - {"ISO8859_15", CharacterSet::ISO8859_15}, {"ISO-8859-15", CharacterSet::ISO8859_15}, - {"ISO8859_16", CharacterSet::ISO8859_16}, {"ISO-8859-16", CharacterSet::ISO8859_16}, {"SJIS", CharacterSet::Shift_JIS}, {"Shift_JIS", CharacterSet::Shift_JIS}, @@ -73,28 +48,29 @@ static const std::map ECI_NAME_TO_CHA {"UnicodeBigUnmarked", CharacterSet::UnicodeBig}, {"UTF-16BE", CharacterSet::UnicodeBig}, {"UnicodeBig", CharacterSet::UnicodeBig}, - {"UTF8", CharacterSet::UTF8}, {"UTF-8", CharacterSet::UTF8}, {"ASCII", CharacterSet::ASCII}, {"US-ASCII", CharacterSet::ASCII}, {"Big5", CharacterSet::Big5}, {"GB2312", CharacterSet::GB2312}, {"GB18030", CharacterSet::GB18030}, - {"EUC_CN", CharacterSet::GB18030}, {"EUC-CN", CharacterSet::GB18030}, {"GBK", CharacterSet::GB18030}, - {"EUC_KR", CharacterSet::EUC_KR}, {"EUC-KR", CharacterSet::EUC_KR}, {"BINARY", CharacterSet::BINARY}, }; -CharacterSet CharacterSetFromString(const std::string& name) +static std::string NormalizeName(std::string str) { - auto it = ECI_NAME_TO_CHARSET.find(name.c_str()); - if (it != ECI_NAME_TO_CHARSET.end()) - return it->second; + std::transform(str.begin(), str.end(), str.begin(), [](char c) { return (char)std::tolower(c); }); + str.erase(std::remove_if(str.begin(), str.end(), [](char c) { return Contains("_-[] ", c); }), str.end()); + return str; +} - return CharacterSet::Unknown; +CharacterSet CharacterSetFromString(const std::string& name) +{ + auto i = FindIf(NAME_TO_CHARSET, [str = NormalizeName(name)](auto& v) { return NormalizeName(v.name) == str; }); + return i == std::end(NAME_TO_CHARSET) ? CharacterSet::Unknown : i->cs; } } // namespace ZXing diff --git a/test/unit/CharacterSetECITest.cpp b/test/unit/CharacterSetECITest.cpp index 2ff4dbf14d..e34dd9640f 100644 --- a/test/unit/CharacterSetECITest.cpp +++ b/test/unit/CharacterSetECITest.cpp @@ -3,6 +3,7 @@ */ // SPDX-License-Identifier: Apache-2.0 +#include "CharacterSet.h" #include "ECI.h" #include "gtest/gtest.h" @@ -20,3 +21,12 @@ TEST(CharacterSetECITest, Charset2ECI) EXPECT_EQ(ToInt(ToECI(CharacterSet::BINARY)), 899); EXPECT_EQ(ToInt(ToECI(CharacterSet::Unknown)), -1); } + +TEST(CharacterSetECITest, CharacterSetFromString) +{ + EXPECT_EQ(CharacterSet::ISO8859_1, CharacterSetFromString("ISO-8859-1")); + EXPECT_EQ(CharacterSet::ISO8859_1, CharacterSetFromString("ISO8859_1")); + EXPECT_EQ(CharacterSet::ISO8859_1, CharacterSetFromString("ISO 8859-1")); + EXPECT_EQ(CharacterSet::ISO8859_1, CharacterSetFromString("iso88591")); + EXPECT_EQ(CharacterSet::Unknown, CharacterSetFromString("invalid-name")); +} From 6e21219ac5a6f9108db239ed072576d0596cd57a Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 3 Jun 2022 18:45:53 +0200 Subject: [PATCH 071/180] ECI: remove the need to publish ECI.h (via Content.h -> Result.h) --- core/src/Content.cpp | 5 +++++ core/src/Content.h | 10 ++++++---- test/unit/ContentTest.cpp | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index c730e9ca03..e89c86a534 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -6,6 +6,7 @@ #include "Content.h" #include "CharacterSet.h" +#include "ECI.h" #include "TextDecoder.h" #include "TextUtfEncoding.h" #include "ZXContainerAlgorithms.h" @@ -44,6 +45,10 @@ void Content::switchEncoding(ECI eci, bool isECI) hasECI |= isECI; } +Content::Content() : encodings({{ECI::Unknown, 0}}) {} + +Content::Content(ByteArray&& binary) : binary(std::move(binary)), encodings{{ECI::ISO8859_1, 0}} {} + void Content::switchEncoding(CharacterSet cs) { switchEncoding(ToECI(cs), false); diff --git a/core/src/Content.h b/core/src/Content.h index b786b88386..6b3e874545 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -6,10 +6,12 @@ #pragma once #include "ByteArray.h" -#include "ECI.h" namespace ZXing { +enum class ECI : int; +enum class CharacterSet; + enum class ContentType { Text, Binary, Mixed }; std::string ToString(ContentType type); @@ -40,14 +42,14 @@ class Content }; ByteArray binary; - std::vector encodings = {{ECI::Unknown, 0}}; + std::vector encodings; std::string hintedCharset; std::string applicationIndicator; SymbologyIdentifier symbology; bool hasECI = false; - Content() = default; - Content(ByteArray&& binary) : binary(std::move(binary)), encodings{{ECI::ISO8859_1, 0}} {} + Content(); + Content(ByteArray&& binary); void switchEncoding(ECI eci) { switchEncoding(eci, true); } void switchEncoding(CharacterSet cs); diff --git a/test/unit/ContentTest.cpp b/test/unit/ContentTest.cpp index 5b3129a4d2..b200f51530 100644 --- a/test/unit/ContentTest.cpp +++ b/test/unit/ContentTest.cpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "Content.h" +#include "ECI.h" #include "gtest/gtest.h" #include "gmock/gmock.h" From f86c4b69ca40dd9cbbc556bdf092b8d7832bd036 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 3 Jun 2022 18:51:45 +0200 Subject: [PATCH 072/180] cmake: only install minimal set of header files Fixes #168. This has been tested to work with the example applications. This is a pretty sharp "deprecation" of a whole lot of internal API and reduces the published interface to merely `ReadBarcode` and `MultiFormatWriter`. It is obviously somewhat risky but I see no other practical way of moving forward with limiting the API. --- CMakeLists.txt | 16 --------------- core/CMakeLists.txt | 49 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e215cf44e..50e2702c03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,22 +87,6 @@ install ( DESTINATION ${CMAKECONFIG_INSTALL_DIR} NAMESPACE ZXing:: ) -install ( - DIRECTORY core/src/ - DESTINATION include/ZXing - FILES_MATCHING PATTERN "*.h" -) - -configure_file ( - core/ZXVersion.h.in - ZXVersion.h -) - -install ( - FILES "${CMAKE_CURRENT_BINARY_DIR}/ZXVersion.h" - DESTINATION include/ZXing -) - # PC file generation. if (NOT DEFINED INSTALLDIR) set (INSTALLDIR ${CMAKE_INSTALL_PREFIX}) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 000dde3433..7b34509510 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -133,6 +133,41 @@ if (BUILD_WRITERS) ) endif() +# define subset of public headers that get distributed with the binaries +set (PUBLIC_HEADERS + src/BarcodeFormat.h + src/BitHacks.h + src/ByteArray.h + src/CharacterSet.h + src/CharacterSetECI.h # deprecated + src/Flags.h + src/GTIN.h + src/TextUtfEncoding.h + src/ZXConfig.h + src/ZXContainerAlgorithms.h +) +if (BUILD_READERS) + set (PUBLIC_HEADERS ${PUBLIC_HEADERS} + src/Content.h + src/DecodeHints.h + src/DecodeStatus.h + src/ImageView.h + src/Point.h + src/Quadrilateral.h + src/ReadBarcode.h + src/Result.h + src/StructuredAppend.h + ) +endif() +if (BUILD_WRITERS) + set (PUBLIC_HEADERS ${PUBLIC_HEADERS} + src/BitMatrix.h + src/BitMatrixIO.h + src/Matrix.h + src/MultiFormatWriter.h + ) +endif() +# end of public header set set (AZTEC_FILES ) @@ -426,7 +461,7 @@ add_library (ZXing target_include_directories (ZXing PUBLIC "$" - INTERFACE "$" + INTERFACE "$" ) target_compile_options (ZXing @@ -462,6 +497,8 @@ if (PROJECT_VERSION) set_target_properties(ZXing PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR}) endif() +set_target_properties(ZXing PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}") + include (GNUInstallDirs) install ( @@ -469,7 +506,15 @@ install ( LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION include +# INCLUDES DESTINATION include + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ZXing" +) + +configure_file (ZXVersion.h.in ZXVersion.h) + +install ( + FILES "${CMAKE_CURRENT_BINARY_DIR}/ZXVersion.h" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ZXing" ) if(MSVC) From f502cb6fc6e9ec5e857dbe1c098980ab56489d6d Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 3 Jun 2022 22:45:59 +0200 Subject: [PATCH 073/180] CharacterSet: replace deprecated CharsetFromName in WASM wrapper --- wrappers/wasm/BarcodeWriter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrappers/wasm/BarcodeWriter.cpp b/wrappers/wasm/BarcodeWriter.cpp index 209fec1a89..ed2a2b1663 100644 --- a/wrappers/wasm/BarcodeWriter.cpp +++ b/wrappers/wasm/BarcodeWriter.cpp @@ -6,7 +6,7 @@ #include "BarcodeFormat.h" #include "MultiFormatWriter.h" #include "BitMatrix.h" -#include "CharacterSetECI.h" +#include "CharacterSet.h" #include #include @@ -59,7 +59,7 @@ WriteResult generateBarcode(std::wstring text, std::string format, std::string e if (margin >= 0) writer.setMargin(margin); - CharacterSet charset = CharacterSetECI::CharsetFromName(encoding.c_str()); + CharacterSet charset = CharacterSetFromString(encoding); if (charset != CharacterSet::Unknown) writer.setEncoding(charset); From 24c50536ad68aecfb48ccbe31b150c9b05033026 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 6 Jun 2022 13:18:16 +0200 Subject: [PATCH 074/180] DMDecoder: set missing Result::applicationIndicator --- core/src/datamatrix/DMDecoder.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index a7303018a9..efd6f1bea7 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -320,11 +320,11 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b readerInit = true; break; case 235: upperShift.set = true; break; // Upper Shift (shift to Extended ASCII) - case 236: // 05 Macro + case 236: // ISO 15434 format "05" Macro result.append("[)>\x1E" "05\x1D"); resultTrailer.insert(0, "\x1E\x04"); break; - case 237: // 06 Macro + case 237: // ISO 15434 format "06" Macro result.append("[)>\x1E" "06\x1D"); resultTrailer.insert(0, "\x1E\x04"); break; @@ -357,6 +357,7 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b } result.append(resultTrailer); + result.applicationIndicator = result.symbology.modifier == '2' ? "GS1" : ""; result.symbology.modifier += isDMRE * 6; return DecoderResult(std::move(bytes), {}, std::move(result)) From 79b0f438fc5065481a055c10df208285a9e33c58 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 6 Jun 2022 13:27:07 +0200 Subject: [PATCH 075/180] Result: add GS1, ISO15434 and UnknownECI ContentTypes Also tuned binary detection to possibly return true even if a 'text' ECI like latin1 was used (see https://github.com/nu-book/zxing-cpp/issues/334#issuecomment-1146083723) --- core/src/Content.cpp | 32 +++++++++++++++++++++++--------- core/src/Content.h | 2 +- core/src/TextDecoder.cpp | 4 ---- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index e89c86a534..c9482336e0 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -15,7 +15,7 @@ namespace ZXing { std::string ToString(ContentType type) { - const char* t2s[] = {"Text", "Binary", "Mixed"}; + const char* t2s[] = {"Text", "Binary", "Mixed", "GS1", "ISO15434", "UnknownECI"}; return t2s[static_cast(type)]; } @@ -166,21 +166,35 @@ CharacterSet Content::guessEncoding() const if (input.empty()) return CharacterSet::Unknown; - return TextDecoder::GuessEncoding(input.data(), input.size(), CharacterSet::BINARY); + return TextDecoder::GuessEncoding(input.data(), input.size(), CharacterSet::ISO8859_1); } ContentType Content::type() const { - auto isBinary = [](Encoding e) { return !IsText(e.eci); }; - auto es = encodings; + if (!canProcess()) + return ContentType::UnknownECI; + + if (applicationIndicator == "GS1") + return ContentType::GS1; + + // check for the absolut minimum of a ISO 15434 conforming message ("[)>" + RS + digit + digit) + if (binary.size() > 6 && binary.asString(0, 4) == "[)>\x1E" && std::isdigit(binary[4]) && std::isdigit(binary[5])) + return ContentType::ISO15434; - for (auto& e : es) - if (e.eci == ECI::Unknown) - e.eci = ToECI(guessEncoding()); + ECI fallback = ToECI(guessEncoding()); + std::vector binaryECIs; + ForEachECIBlock([&](ECI eci, int begin, int end) { + if (eci == ECI::Unknown) + eci = fallback; + binaryECIs.push_back((!IsText(eci) + || (ToInt(eci) > 0 && ToInt(eci) < 28 && ToInt(eci) != 25 + && std::any_of(binary.begin() + begin, binary.begin() + end, + [](auto c) { return c < 0x20 && c != 0xa && c != 0xd; })))); + }); - if (std::none_of(es.begin(), es.end(), isBinary)) + if (!Contains(binaryECIs, true)) return ContentType::Text; - if (std::all_of(es.begin(), es.end(), isBinary)) + if (!Contains(binaryECIs, false)) return ContentType::Binary; return ContentType::Mixed; diff --git a/core/src/Content.h b/core/src/Content.h index 6b3e874545..34aa6b92d7 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -12,7 +12,7 @@ namespace ZXing { enum class ECI : int; enum class CharacterSet; -enum class ContentType { Text, Binary, Mixed }; +enum class ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI }; std::string ToString(ContentType type); diff --git a/core/src/TextDecoder.cpp b/core/src/TextDecoder.cpp index 98677d63a4..4df8a65f14 100644 --- a/core/src/TextDecoder.cpp +++ b/core/src/TextDecoder.cpp @@ -402,10 +402,6 @@ TextDecoder::GuessEncoding(const uint8_t* bytes, size_t length, CharacterSet fal if (value > 0x7F && value < 0xA0) { canBeISO88591 = false; } - // treat all ANSI control characters as binary, except EOT, LF, CR, GS and RS (see ISO/IEC 15434) - else if (value < 0x20 && value != 0x04 && value != 0x0a && value != 0x0d && value != 0x1d && value != 0x1e) { - canBeISO88591 = false; - } else if (value > 0x9F) { if (value < 0xC0 || value == 0xD7 || value == 0xF7) { isoHighOther++; From 3dad01926d2371603ae5b162d685be0d81d07a27 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 6 Jun 2022 17:41:39 +0200 Subject: [PATCH 076/180] Result: deprecate rawBytes and numBits --- core/src/Result.h | 12 +++++------- example/ZXingQtReader.h | 9 ++++----- test/blackbox/BlackboxTestRunner.cpp | 2 -- test/unit/aztec/AZDecoderTest.cpp | 5 ----- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/core/src/Result.h b/core/src/Result.h index f13afcc233..6f196fb198 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -24,10 +24,8 @@ class DecoderResult; using Position = QuadrilateralI; /** -*

Encapsulates the result of decoding a barcode within an image.

-* -* @author Sean Owen -*/ + * @brief The Result class encapsulates the result of decoding a barcode within an image. + */ class Result { public: @@ -66,9 +64,9 @@ class Result */ bool isMirrored() const { return _isMirrored; } - const ByteArray& rawBytes() const { return _rawBytes; } - - int numBits() const { return _numBits; } + /// see binary() above for a proper replacement of rawByes + [[deprecated]] const ByteArray& rawBytes() const { return _rawBytes; } + [[deprecated]] int numBits() const { return _numBits; } const std::wstring& ecLevel() const { return _ecLevel; } diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index 2a05bbf5d8..9db4a7c528 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -97,13 +97,13 @@ class Result : private ZXing::Result Q_PROPERTY(BarcodeFormat format READ format) Q_PROPERTY(QString formatName READ formatName) Q_PROPERTY(QString text READ text) - Q_PROPERTY(QByteArray rawBytes READ rawBytes) + Q_PROPERTY(QByteArray binary READ binary) Q_PROPERTY(bool isValid READ isValid) Q_PROPERTY(DecodeStatus status READ status) Q_PROPERTY(Position position READ position) QString _text; - QByteArray _rawBytes; + QByteArray _binary; Position _position; public: @@ -111,8 +111,7 @@ class Result : private ZXing::Result explicit Result(ZXing::Result&& r) : ZXing::Result(std::move(r)) { _text = QString::fromWCharArray(ZXing::Result::text().c_str()); - _rawBytes = QByteArray(reinterpret_cast(ZXing::Result::rawBytes().data()), - Size(ZXing::Result::rawBytes())); + _binary = QByteArray(reinterpret_cast(ZXing::Result::binary().data()), Size(ZXing::Result::binary())); auto& pos = ZXing::Result::position(); auto qp = [&pos](int i) { return QPoint(pos[i].x, pos[i].y); }; _position = {qp(0), qp(1), qp(2), qp(3)}; @@ -124,7 +123,7 @@ class Result : private ZXing::Result DecodeStatus status() const { return static_cast(ZXing::Result::status()); } QString formatName() const { return QString::fromStdString(ZXing::ToString(ZXing::Result::format())); } const QString& text() const { return _text; } - const QByteArray& rawBytes() const { return _rawBytes; } + const QByteArray& binary() const { return _binary; } const Position& position() const { return _position; } // For debugging/development diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 9f16fa2aa3..8864d224a8 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -68,8 +68,6 @@ static std::string getResultValue(const Result& result, const std::string& key) return TextUtfEncoding::ToUtf8(result.ecLevel()); if (key == "orientation") return std::to_string(result.orientation()); - if (key == "numBits") - return std::to_string(result.numBits()); if (key == "symbologyIdentifier") return result.symbologyIdentifier(); if (key == "sequenceSize") diff --git a/test/unit/aztec/AZDecoderTest.cpp b/test/unit/aztec/AZDecoderTest.cpp index 523bb8b920..62d70977ff 100644 --- a/test/unit/aztec/AZDecoderTest.cpp +++ b/test/unit/aztec/AZDecoderTest.cpp @@ -60,11 +60,6 @@ TEST(AZDecoderTest, AztecResult) DecoderResult result = parse(std::move(bits), false, 30, 2); EXPECT_EQ(result.isValid(), true); EXPECT_EQ(result.text(), L"88888TTTTTTTTTTTTTTTTTTTTTTTTTTTTTT"); - EXPECT_EQ(result.rawBytes(), ByteArray({ - 0xf5, 0x55, 0x55, 0x75, 0x6b, 0x5a, 0xd6, 0xb5, 0xad, 0x6b, - 0x5a, 0xd6, 0xb5, 0xad, 0x6b, 0x5a, 0xd6, 0xb5, 0xad, 0x6b, - 0x5a, 0xd6, 0xb0 })); - EXPECT_EQ(result.numBits(), 180); EXPECT_EQ(result.symbologyIdentifier(), "]z0"); } From 26c117c4a196e6a5ec7caf5d1cc6db22bd0641d8 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 7 Jun 2022 12:24:40 +0200 Subject: [PATCH 077/180] Result: rename `binary` to `bytes` Originally the `binary` property was envisioned to contain only the binary parts (if present) of a barcode content. During the discussion, this has changed. Hence the name became misleading and should be changed before releasing it. Reasons for the choice `bytes`: * does not somehow imply that the content is 'binary' in nature * is closer to `rawBytes` which was there before with a similar intention * is used in Python as the name of the type this data would be stored in --- core/src/Content.cpp | 26 +++++++++++----------- core/src/Content.h | 16 ++++++------- core/src/Result.h | 6 ++--- core/src/aztec/AZDecoder.cpp | 20 ++++++++--------- core/src/maxicode/MCDecoder.cpp | 2 +- core/src/qrcode/QRDecoder.cpp | 2 +- example/ZXingQtReader.h | 8 +++---- example/ZXingReader.cpp | 6 ++--- test/blackbox/BlackboxTestRunner.cpp | 6 ++--- test/unit/ContentTest.cpp | 8 +++---- test/unit/aztec/AZEncodeDecodeTest.cpp | 4 ++-- test/unit/aztec/AZHighLevelEncoderTest.cpp | 2 +- test/unit/pdf417/PDF417DecoderTest.cpp | 2 +- 13 files changed, 54 insertions(+), 54 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index c9482336e0..90dd68d9f8 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -24,7 +24,7 @@ void Content::ForEachECIBlock(FUNC func) const { for (int i = 0; i < Size(encodings); ++i) { auto [eci, start] = encodings[i]; - int end = i + 1 == Size(encodings) ? Size(binary) : encodings[i + 1].pos; + int end = i + 1 == Size(encodings) ? Size(bytes) : encodings[i + 1].pos; if (start != end) func(eci, start, end); @@ -37,17 +37,17 @@ void Content::switchEncoding(ECI eci, bool isECI) if (isECI && !hasECI) encodings = {{ECI::ISO8859_1, 0}}; if (isECI || !hasECI) { - if (encodings.back().pos == Size(binary)) + if (encodings.back().pos == Size(bytes)) encodings.back().eci = eci; // no point in recording 0 length segments else - encodings.push_back({eci, Size(binary)}); + encodings.push_back({eci, Size(bytes)}); } hasECI |= isECI; } Content::Content() : encodings({{ECI::Unknown, 0}}) {} -Content::Content(ByteArray&& binary) : binary(std::move(binary)), encodings{{ECI::ISO8859_1, 0}} {} +Content::Content(ByteArray&& bytes) : bytes(std::move(bytes)), encodings{{ECI::ISO8859_1, 0}} {} void Content::switchEncoding(CharacterSet cs) { @@ -56,7 +56,7 @@ void Content::switchEncoding(CharacterSet cs) void Content::erase(int pos, int n) { - binary.erase(binary.begin() + pos, binary.begin() + pos + n); + bytes.erase(bytes.begin() + pos, bytes.begin() + pos + n); for (auto& e : encodings) if (e.pos > pos) pos -= n; @@ -64,7 +64,7 @@ void Content::erase(int pos, int n) void Content::insert(int pos, const std::string& str) { - binary.insert(binary.begin() + pos, str.begin(), str.end()); + bytes.insert(bytes.begin() + pos, str.begin(), str.end()); for (auto& e : encodings) if (e.pos > pos) pos += Size(str); @@ -88,7 +88,7 @@ std::wstring Content::text() const ForEachECIBlock([&](ECI eci, int begin, int end) { CharacterSet cs = eci == ECI::Unknown ? fallbackCS : ToCharacterSet(eci); - TextDecoder::Append(wstr, binary.data() + begin, end - begin, cs); + TextDecoder::Append(wstr, bytes.data() + begin, end - begin, cs); }); return wstr; } @@ -121,7 +121,7 @@ std::string Content::utf8Protocol() const lastECI = eci; std::wstring tmp; - TextDecoder::Append(tmp, binary.data() + begin, end - begin, cs); + TextDecoder::Append(tmp, bytes.data() + begin, end - begin, cs); for (auto c : tmp) { res += c; if (c == L'\\') // in the ECI protocol a '\' has to be doubled @@ -132,7 +132,7 @@ std::string Content::utf8Protocol() const return TextUtfEncoding::ToUtf8(res); } -ByteArray Content::binaryECI() const +ByteArray Content::bytesECI() const { if (empty()) return {}; @@ -144,7 +144,7 @@ ByteArray Content::binaryECI() const res += ToString(eci); for (int i = begin; i != end; ++i) { - char c = static_cast(binary[i]); + char c = static_cast(bytes[i]); res += c; if (c == '\\') // in the ECI protocol a '\' has to be doubled res += c; @@ -160,7 +160,7 @@ CharacterSet Content::guessEncoding() const ByteArray input; ForEachECIBlock([&](ECI eci, int begin, int end) { if (eci == ECI::Unknown) - input.insert(input.end(), binary.begin() + begin, binary.begin() + end); + input.insert(input.end(), bytes.begin() + begin, bytes.begin() + end); }); if (input.empty()) @@ -178,7 +178,7 @@ ContentType Content::type() const return ContentType::GS1; // check for the absolut minimum of a ISO 15434 conforming message ("[)>" + RS + digit + digit) - if (binary.size() > 6 && binary.asString(0, 4) == "[)>\x1E" && std::isdigit(binary[4]) && std::isdigit(binary[5])) + if (bytes.size() > 6 && bytes.asString(0, 4) == "[)>\x1E" && std::isdigit(bytes[4]) && std::isdigit(bytes[5])) return ContentType::ISO15434; ECI fallback = ToECI(guessEncoding()); @@ -188,7 +188,7 @@ ContentType Content::type() const eci = fallback; binaryECIs.push_back((!IsText(eci) || (ToInt(eci) > 0 && ToInt(eci) < 28 && ToInt(eci) != 25 - && std::any_of(binary.begin() + begin, binary.begin() + end, + && std::any_of(bytes.begin() + begin, bytes.begin() + end, [](auto c) { return c < 0x20 && c != 0xa && c != 0xd; })))); }); diff --git a/core/src/Content.h b/core/src/Content.h index 34aa6b92d7..a96113300e 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -41,7 +41,7 @@ class Content int pos; }; - ByteArray binary; + ByteArray bytes; std::vector encodings; std::string hintedCharset; std::string applicationIndicator; @@ -49,16 +49,16 @@ class Content bool hasECI = false; Content(); - Content(ByteArray&& binary); + Content(ByteArray&& bytes); void switchEncoding(ECI eci) { switchEncoding(eci, true); } void switchEncoding(CharacterSet cs); - void reserve(int count) { binary.reserve(binary.size() + count); } + void reserve(int count) { bytes.reserve(bytes.size() + count); } - void push_back(uint8_t val) { binary.push_back(val); } - void append(const std::string& str) { binary.insert(binary.end(), str.begin(), str.end()); } - void append(const ByteArray& bytes) { binary.insert(binary.end(), bytes.begin(), bytes.end()); } + void push_back(uint8_t val) { bytes.push_back(val); } + void append(const std::string& str) { bytes.insert(bytes.end(), str.begin(), str.end()); } + void append(const ByteArray& ba) { bytes.insert(bytes.end(), ba.begin(), ba.end()); } void operator+=(char val) { push_back(val); } void operator+=(const std::string& str) { append(str); } @@ -66,12 +66,12 @@ class Content void erase(int pos, int n); void insert(int pos, const std::string& str); - bool empty() const { return binary.empty(); } + bool empty() const { return bytes.empty(); } bool canProcess() const; std::wstring text() const; std::string utf8Protocol() const; - ByteArray binaryECI() const; + ByteArray bytesECI() const; CharacterSet guessEncoding() const; ContentType type() const; }; diff --git a/core/src/Result.h b/core/src/Result.h index 6f196fb198..17d969d5b3 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -46,8 +46,8 @@ class Result const std::wstring& text() const { return _text; } // WARNING: this is an experimental API and may change/disappear - const ByteArray& binary() const { return _content.binary; } - const ByteArray binaryECI() const { return _content.binaryECI(); } + const ByteArray& bytes() const { return _content.bytes; } + const ByteArray bytesECI() const { return _content.bytesECI(); } const std::string utf8Protocol() const { return _content.utf8Protocol(); } const std::string& applicationIndicator() const { return _content.applicationIndicator; } ContentType contentType() const { return _content.type(); } @@ -64,7 +64,7 @@ class Result */ bool isMirrored() const { return _isMirrored; } - /// see binary() above for a proper replacement of rawByes + /// see bytes() above for a proper replacement of rawByes [[deprecated]] const ByteArray& rawBytes() const { return _rawBytes; } [[deprecated]] int numBits() const { return _numBits; } diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index 7d0ad24dbe..b099e7434a 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -223,9 +223,9 @@ static ECI ParseECIValue(BitArray::Range& bits, const int flg) /** * See ISO/IEC 24778:2008 Section 8 */ -static StructuredAppendInfo ParseStructuredAppend(ByteArray& binary) +static StructuredAppendInfo ParseStructuredAppend(ByteArray& bytes) { - std::string text(binary.begin(), binary.end()); + std::string text(bytes.begin(), bytes.end()); StructuredAppendInfo sai; std::string::size_type i = 0; @@ -247,7 +247,7 @@ static StructuredAppendInfo ParseStructuredAppend(ByteArray& binary) sai.count = 0; // Choose to mark count as unknown text.erase(0, i + 2); // Remove - binary = ByteArray(text); + bytes = ByteArray(text); return sai; } @@ -314,32 +314,32 @@ DecoderResult Decode(const BitArray& bits, const std::string& characterSet) return DecodeStatus::FormatError; } - if (res.binary.empty()) + if (res.bytes.empty()) return DecodeStatus::FormatError; // Check for Structured Append - need 4 5-bit words, beginning with ML UL, ending with index and count bool haveStructuredAppend = Size(bits) > 20 && ToInt(bits, 0, 5) == 29 // latch to MIXED (from UPPER) && ToInt(bits, 5, 5) == 29; // latch back to UPPER (from MIXED) - StructuredAppendInfo sai = haveStructuredAppend ? ParseStructuredAppend(res.binary) : StructuredAppendInfo(); + StructuredAppendInfo sai = haveStructuredAppend ? ParseStructuredAppend(res.bytes) : StructuredAppendInfo(); // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using // modifiers that indicate ECI protocol (ISO/IEC 24778:2008 Annex F Table F.1) - if (res.binary[0] == 29) { + if (res.bytes[0] == 29) { res.symbology.modifier = '1'; // GS1 res.applicationIndicator = "GS1"; res.erase(0, 1); // Remove FNC1 - } else if (res.binary.size() > 2 && std::isupper(res.binary[0]) && res.binary[1] == 29) { + } else if (res.bytes.size() > 2 && std::isupper(res.bytes[0]) && res.bytes[1] == 29) { // FNC1 following single uppercase letter (the AIM Application Indicator) res.symbology.modifier = '2'; // AIM // TODO: remove the AI from the content? - res.applicationIndicator = res.binary.asString(0, 1); + res.applicationIndicator = res.bytes.asString(0, 1); res.erase(1, 1); // Remove FNC1, // The AIM Application Indicator character "A"-"Z" is left in the stream (ISO/IEC 24778:2008 16.2) - } else if (res.binary.size() > 3 && std::isdigit(res.binary[0]) && std::isdigit(res.binary[1]) && res.binary[2] == 29) { + } else if (res.bytes.size() > 3 && std::isdigit(res.bytes[0]) && std::isdigit(res.bytes[1]) && res.bytes[2] == 29) { // FNC1 following 2 digits (the AIM Application Indicator) res.symbology.modifier = '2'; // AIM - res.applicationIndicator = res.binary.asString(0, 2); + res.applicationIndicator = res.bytes.asString(0, 2); res.erase(2, 1); // Remove FNC1 // The AIM Application Indicator characters "00"-"99" are left in the stream (ISO/IEC 24778:2008 16.2) } diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 8d4be16bea..16292730ae 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -281,7 +281,7 @@ DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& /*cha auto country = ToString(GetCountry(bytes), 3); auto service = ToString(GetServiceClass(bytes), 3); GetMessage(bytes, 10, 84, result, sai); - if (result.binary.asString().compare(0, 7, "[)>\u001E01\u001D") == 0) // "[)>" + RS + "01" + GS + if (result.bytes.asString().compare(0, 7, "[)>\u001E01\u001D") == 0) // "[)>" + RS + "01" + GS result.insert(9, postcode + GS + country + GS + service + GS); else result.insert(0, postcode + GS + country + GS + service + GS); diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 703d28d941..e4ebed8192 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -292,7 +292,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo result += static_cast(appInd - 100); else throw std::runtime_error("Invalid AIM Application Indicator"); - result.applicationIndicator = result.binary.asString(); // see also above + result.applicationIndicator = result.bytes.asString(); // see also above break; case CodecMode::STRUCTURED_APPEND: // sequence number and parity is added later to the result metadata diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index 9db4a7c528..7435e56c85 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -97,13 +97,13 @@ class Result : private ZXing::Result Q_PROPERTY(BarcodeFormat format READ format) Q_PROPERTY(QString formatName READ formatName) Q_PROPERTY(QString text READ text) - Q_PROPERTY(QByteArray binary READ binary) + Q_PROPERTY(QByteArray bytes READ bytes) Q_PROPERTY(bool isValid READ isValid) Q_PROPERTY(DecodeStatus status READ status) Q_PROPERTY(Position position READ position) QString _text; - QByteArray _binary; + QByteArray _bytes; Position _position; public: @@ -111,7 +111,7 @@ class Result : private ZXing::Result explicit Result(ZXing::Result&& r) : ZXing::Result(std::move(r)) { _text = QString::fromWCharArray(ZXing::Result::text().c_str()); - _binary = QByteArray(reinterpret_cast(ZXing::Result::binary().data()), Size(ZXing::Result::binary())); + _bytes = QByteArray(reinterpret_cast(ZXing::Result::bytes().data()), Size(ZXing::Result::bytes())); auto& pos = ZXing::Result::position(); auto qp = [&pos](int i) { return QPoint(pos[i].x, pos[i].y); }; _position = {qp(0), qp(1), qp(2), qp(3)}; @@ -123,7 +123,7 @@ class Result : private ZXing::Result DecodeStatus status() const { return static_cast(ZXing::Result::status()); } QString formatName() const { return QString::fromStdString(ZXing::ToString(ZXing::Result::format())); } const QString& text() const { return _text; } - const QByteArray& binary() const { return _binary; } + const QByteArray& bytes() const { return _bytes; } const Position& position() const { return _position; } // For debugging/development diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 9baf5961f8..937da9077a 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -160,7 +160,7 @@ int main(int argc, char* argv[]) ret |= static_cast(result.status()); if (binaryOutput) { - std::cout.write(reinterpret_cast(result.binary().data()), result.binary().size()); + std::cout.write(reinterpret_cast(result.bytes().data()), result.bytes().size()); continue; } @@ -183,9 +183,9 @@ int main(int argc, char* argv[]) firstFile = false; } std::cout << "Text: \"" << ToUtf8(result.text(), angleEscape) << "\"\n" - << "Binary: \"" << ToHex(result.binary()) << "\"\n" + << "Bytes: \"" << ToHex(result.bytes()) << "\"\n" << "TextECI: \"" << result.utf8Protocol() << "\"\n" - << "BinaryECI: \"" << ToHex(result.binaryECI()) << "\"\n" + << "BytesECI: \"" << ToHex(result.bytesECI()) << "\"\n" << "Format: " << ToString(result.format()) << "\n" << "Identifier: " << result.symbologyIdentifier() << "\n" << "Content: " << ToString(result.contentType()) << "\n" diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 8864d224a8..108e91f9b8 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -141,8 +141,8 @@ static std::string checkResult(const fs::path& imgPath, std::string_view expecte if (auto expected = readFile(".bin")) { ByteArray binaryExpected(*expected); - return result.binary() != binaryExpected - ? fmt::format("Content mismatch: expected '{}' but got '{}'", ToHex(binaryExpected), ToHex(result.binary())) + return result.bytes() != binaryExpected + ? fmt::format("Content mismatch: expected '{}' but got '{}'", ToHex(binaryExpected), ToHex(result.bytes())) : ""; } @@ -266,7 +266,7 @@ static Result readMultiple(const std::vector& imgPaths, std::string_vi Content content; for (const auto& r : allResults) { text.append(r.text()); - content.append(r.binary()); + content.append(r.bytes()); } const auto& first = allResults.front(); diff --git a/test/unit/ContentTest.cpp b/test/unit/ContentTest.cpp index b200f51530..19c3a5f8b1 100644 --- a/test/unit/ContentTest.cpp +++ b/test/unit/ContentTest.cpp @@ -53,7 +53,7 @@ TEST(ContentTest, GuessEncoding) c.append(ByteArray{'A', 0xE9, 'Z'}); EXPECT_EQ(c.guessEncoding(), CharacterSet::ISO8859_1); EXPECT_EQ(c.text(), L"A\u00E9Z"); - EXPECT_EQ(c.binaryECI(), c.binary); + EXPECT_EQ(c.bytesECI(), c.bytes); } { // guess Shift_JIS @@ -73,7 +73,7 @@ TEST(ContentTest, ECI) c.append(ByteArray{'A', 0xE9, 'Z'}); EXPECT_TRUE(c.hasECI); EXPECT_EQ(c.text(), L"A\u00E9ZA\u0449Z"); - EXPECT_EQ(c.binaryECI().asString(), std::string_view("\\000003A\xE9Z\\000007A\xE9Z")); + EXPECT_EQ(c.bytesECI().asString(), std::string_view("\\000003A\xE9Z\\000007A\xE9Z")); } { // switch ECI -> latin1 for unknown (instead of Shift_JIS) @@ -82,13 +82,13 @@ TEST(ContentTest, ECI) c.switchEncoding(ECI::ISO8859_5); c.append(ByteArray{'A', 0xE9, 'Z'}); EXPECT_EQ(c.text(), L"A\u0083\u0065ZA\u0449Z"); - EXPECT_EQ(c.binaryECI().asString(), std::string_view("\\000003A\x83\x65Z\\000007A\xE9Z")); + EXPECT_EQ(c.bytesECI().asString(), std::string_view("\\000003A\x83\x65Z\\000007A\xE9Z")); } { // double '\' Content c; c.append("C:\\Test"); EXPECT_EQ(c.text(), L"C:\\Test"); - EXPECT_EQ(c.binaryECI().asString(), std::string_view("C:\\\\Test")); + EXPECT_EQ(c.bytesECI().asString(), std::string_view("C:\\\\Test")); } } diff --git a/test/unit/aztec/AZEncodeDecodeTest.cpp b/test/unit/aztec/AZEncodeDecodeTest.cpp index 54c7d2fe7f..b28b9135e3 100644 --- a/test/unit/aztec/AZEncodeDecodeTest.cpp +++ b/test/unit/aztec/AZEncodeDecodeTest.cpp @@ -88,7 +88,7 @@ namespace { DecoderResult res = parse(matrix.copy(), aztec.compact, aztec.codeWords, aztec.layers); EXPECT_EQ(res.isValid(), true); - EXPECT_EQ(res.content().binary, ByteArray(textBytes)); + EXPECT_EQ(res.content().bytes, ByteArray(textBytes)); // Check error correction by introducing up to eccPercent/2 errors int ecWords = aztec.codeWords * eccPercent / 100 / 2; @@ -105,7 +105,7 @@ namespace { } res = parse(std::move(matrix), aztec.compact, aztec.codeWords, aztec.layers); EXPECT_EQ(res.isValid(), true); - EXPECT_EQ(res.content().binary, ByteArray(textBytes)); + EXPECT_EQ(res.content().bytes, ByteArray(textBytes)); } } diff --git a/test/unit/aztec/AZHighLevelEncoderTest.cpp b/test/unit/aztec/AZHighLevelEncoderTest.cpp index 7e80feb770..5f82f99d4a 100644 --- a/test/unit/aztec/AZHighLevelEncoderTest.cpp +++ b/test/unit/aztec/AZHighLevelEncoderTest.cpp @@ -38,7 +38,7 @@ namespace { BitArray bits = Aztec::HighLevelEncoder::Encode(s); int receivedBitCount = Size(Utility::ToString(bits)); EXPECT_EQ(receivedBitCount, expectedReceivedBits) << "highLevelEncode() failed for input string: " + s; - EXPECT_EQ(ByteArray(s), Aztec::Decode(bits).content().binary); + EXPECT_EQ(ByteArray(s), Aztec::Decode(bits).content().bytes); } } diff --git a/test/unit/pdf417/PDF417DecoderTest.cpp b/test/unit/pdf417/PDF417DecoderTest.cpp index 5b3668843b..2df84c85db 100644 --- a/test/unit/pdf417/PDF417DecoderTest.cpp +++ b/test/unit/pdf417/PDF417DecoderTest.cpp @@ -509,7 +509,7 @@ TEST(PDF417DecoderTest, ECIMultipleNumeric) TEST(PDF417DecoderTest, ECIInvalid) { EXPECT_EQ(decode({ 4, 927, 901, 0 }), L""); // non-charset ECI > 899 -> empty text result - EXPECT_EQ(parse({4, 927, 901, 0}).content().binary, ByteArray("AA")); // non-charset ECI > 899 -> ignored in binary result + EXPECT_EQ(parse({4, 927, 901, 0}).content().bytes, ByteArray("AA")); // non-charset ECI > 899 -> ignored in binary result EXPECT_EQ(decode({ 3, 0, 927 }), L"AA"); // Malformed ECI at end silently ignored } From a8629e04bc0a5369227f035613dc960ae9298984 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 7 Jun 2022 12:29:29 +0200 Subject: [PATCH 078/180] python: add the `bytes` property to the Result struct --- wrappers/python/test.py | 1 + wrappers/python/zxing.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/wrappers/python/test.py b/wrappers/python/test.py index 350d08cd0e..8f37dbce59 100644 --- a/wrappers/python/test.py +++ b/wrappers/python/test.py @@ -23,6 +23,7 @@ def check_res(self, res, format, text): self.assertTrue(res.valid) self.assertEqual(res.format, format) self.assertEqual(res.text, text) + self.assertEqual(res.bytes, bytes(text, 'utf-8')) self.assertEqual(res.orientation, 0) def test_write_read_cycle(self): diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index 60db5387a2..72336d5c0c 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -205,6 +205,9 @@ PYBIND11_MODULE(zxingcpp, m) .def_property_readonly("text", &Result::text, ":return: text of the decoded symbol\n" ":rtype: str") + .def_property_readonly("bytes", [](const Result& res) { return py::bytes(res.bytes().asString()); }, + ":return: uninterpreted bytes of the decoded symbol\n" + ":rtype: bytes") .def_property_readonly("format", &Result::format, ":return: decoded symbol format\n" ":rtype: zxing.BarcodeFormat") From 5d37ef8d90d3001efddba3cf691abd3f1d1f0bdb Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 8 Jun 2022 10:27:29 +0200 Subject: [PATCH 079/180] Result: add MergeStructuredAppendResults() --- core/src/Content.cpp | 7 +++++++ core/src/Content.h | 1 + core/src/Result.cpp | 23 +++++++++++++++++++++++ core/src/Result.h | 7 +++++++ test/blackbox/BlackboxTestRunner.cpp | 27 ++------------------------- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 90dd68d9f8..15ad8787d5 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -54,6 +54,13 @@ void Content::switchEncoding(CharacterSet cs) switchEncoding(ToECI(cs), false); } +void Content::append(const Content& other) +{ + for (auto& e : other.encodings) + encodings.push_back({e.eci, Size(bytes) + e.pos}); + append(other.bytes); +} + void Content::erase(int pos, int n) { bytes.erase(bytes.begin() + pos, bytes.begin() + pos + n); diff --git a/core/src/Content.h b/core/src/Content.h index a96113300e..f429286d9f 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -59,6 +59,7 @@ class Content void push_back(uint8_t val) { bytes.push_back(val); } void append(const std::string& str) { bytes.insert(bytes.end(), str.begin(), str.end()); } void append(const ByteArray& ba) { bytes.insert(bytes.end(), ba.begin(), ba.end()); } + void append(const Content& other); void operator+=(char val) { push_back(val); } void operator+=(const std::string& str) { append(str); } diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 9d3c417868..6850dd167e 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -10,6 +10,7 @@ #include "TextDecoder.h" #include +#include #include namespace ZXing { @@ -69,4 +70,26 @@ bool Result::operator==(const Result& o) const return std::min(dTop, dBot) < length / 2; } +Result MergeStructuredAppendResults(const Results& results) +{ + if (results.empty()) + return Result(DecodeStatus::NotFound); + + std::list allResults(results.begin(), results.end()); + allResults.sort([](const Result& r1, const Result& r2) { return r1.sequenceIndex() < r2.sequenceIndex(); }); + + if (allResults.back().sequenceSize() != Size(allResults) || + !std::all_of(allResults.begin(), allResults.end(), + [&](Result& it) { return it.sequenceId() == allResults.front().sequenceId(); })) + return Result(DecodeStatus::FormatError); + + Result res = allResults.front(); + for (auto i = std::next(allResults.begin()); i != allResults.end(); ++i) + res._content.append(i->_content); + + res._text = res._content.text(); + + return res; +} + } // ZXing diff --git a/core/src/Result.h b/core/src/Result.h index 17d969d5b3..13ce7f2bb8 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -114,6 +114,8 @@ class Result bool operator==(const Result& o) const; + friend Result MergeStructuredAppendResults(const std::vector& results); + private: DecodeStatus _status = DecodeStatus::NoError; BarcodeFormat _format = BarcodeFormat::None; @@ -132,4 +134,9 @@ class Result using Results = std::vector; +/** + * @brief Merge a list of Results from one Structured Append set to a single result + */ +Result MergeStructuredAppendResults(const Results& results); + } // ZXing diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 108e91f9b8..4fff96eb5d 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -245,37 +245,14 @@ static void doRunTests(const fs::path& directory, std::string_view format, int t static Result readMultiple(const std::vector& imgPaths, std::string_view format) { - std::list allResults; + Results allResults; for (const auto& imgPath : imgPaths) { auto results = ReadBarcodes(ImageLoader::load(imgPath), DecodeHints().setFormats(BarcodeFormatFromString(format.data())).setTryDownscale(false)); allResults.insert(allResults.end(), results.begin(), results.end()); } - if (allResults.empty()) - return Result(DecodeStatus::NotFound); - - allResults.sort([](const Result& r1, const Result& r2) { return r1.sequenceIndex() < r2.sequenceIndex(); }); - - if (allResults.back().sequenceSize() != Size(allResults) || - !std::all_of(allResults.begin(), allResults.end(), - [&](Result& it) { return it.sequenceId() == allResults.front().sequenceId(); })) - return Result(DecodeStatus::FormatError); - - std::wstring text; - Content content; - for (const auto& r : allResults) { - text.append(r.text()); - content.append(r.bytes()); - } - - const auto& first = allResults.front(); - return {DecoderResult({}, std::move(text), std::move(content)) - .setStructuredAppend({first.sequenceIndex(), first.sequenceSize(), first.sequenceId()}) - .setSymbologyIdentifier(first.symbologyIdentifier()) - .setReaderInit(first.readerInit()), - {}, - first.format()}; + return MergeStructuredAppendResults(allResults); } static void doRunStructuredAppendTest(const fs::path& directory, std::string_view format, int totalTests, From 4d7ea2a603d2ad770d259f436b0078b500a13f21 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 8 Jun 2022 10:59:36 +0200 Subject: [PATCH 080/180] DecoderResult: remove `text` and `symbologyIdentifier` member variables This further consolidates the internal structure with respect to the new `Content` class and its application. --- core/src/Content.cpp | 4 ++- core/src/Content.h | 2 +- core/src/DecoderResult.h | 26 ++++--------------- core/src/Result.cpp | 6 ++--- core/src/aztec/AZDecoder.cpp | 2 +- core/src/datamatrix/DMDecoder.cpp | 2 +- core/src/maxicode/MCDecoder.cpp | 2 +- core/src/oned/ODDataBarExpandedReader.cpp | 5 ++-- core/src/oned/ODDataBarReader.cpp | 3 +-- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 2 +- core/src/qrcode/QRDecoder.cpp | 2 +- 11 files changed, 20 insertions(+), 36 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 15ad8787d5..14fddaad2d 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -47,7 +47,9 @@ void Content::switchEncoding(ECI eci, bool isECI) Content::Content() : encodings({{ECI::Unknown, 0}}) {} -Content::Content(ByteArray&& bytes) : bytes(std::move(bytes)), encodings{{ECI::ISO8859_1, 0}} {} +Content::Content(ByteArray&& bytes, SymbologyIdentifier si) + : bytes(std::move(bytes)), encodings{{ECI::ISO8859_1, 0}}, symbology(si) +{} void Content::switchEncoding(CharacterSet cs) { diff --git a/core/src/Content.h b/core/src/Content.h index f429286d9f..5d1f982069 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -49,7 +49,7 @@ class Content bool hasECI = false; Content(); - Content(ByteArray&& bytes); + Content(ByteArray&& bytes, SymbologyIdentifier si); void switchEncoding(ECI eci) { switchEncoding(eci, true); } void switchEncoding(CharacterSet cs); diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 8714396f29..a6ce5e6ab3 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -20,25 +20,16 @@ namespace ZXing { class CustomData; -/** -*

Encapsulates the result of decoding a matrix of bits. This typically -* applies to 2D barcode formats. For now it contains the raw bytes obtained, -* as well as a string interpretation of those bytes, if applicable.

-* -* @author Sean Owen -*/ class DecoderResult { DecodeStatus _status = DecodeStatus::NoError; ByteArray _rawBytes; Content _content; int _numBits = 0; - std::wstring _text; std::string _ecLevel; int _errorsCorrected = -1; int _erasures = -1; int _lineCount = 0; - std::string _symbologyIdentifier; StructuredAppendInfo _structuredAppend; bool _isMirrored = false; bool _readerInit = false; @@ -49,17 +40,9 @@ class DecoderResult public: DecoderResult(DecodeStatus status) : _status(status) {} - DecoderResult(ByteArray&& rawBytes, std::wstring&& text, Content&& binary = {}) - : _rawBytes(std::move(rawBytes)), _content(std::move(binary)), _text(std::move(text)) + DecoderResult(ByteArray&& rawBytes, Content&& bytes = {}) : _rawBytes(std::move(rawBytes)), _content(std::move(bytes)) { _numBits = 8 * Size(_rawBytes); - if (_text.empty()) - _text = _content.text(); - // provide some best guess fallback for barcodes not, yet supporting the content info - if (_content.empty() && std::all_of(_text.begin(), _text.end(), [](auto c) { return c < 256; })) - std::for_each(_text.begin(), _text.end(), [this](wchar_t c) { _content += static_cast(c); }); - if (_content.symbology.code != 0) - _symbologyIdentifier = _content.symbology.toString(); } DecoderResult() = default; @@ -71,11 +54,13 @@ class DecoderResult const ByteArray& rawBytes() const & { return _rawBytes; } ByteArray&& rawBytes() && { return std::move(_rawBytes); } - const std::wstring& text() const & { return _text; } - std::wstring&& text() && { return std::move(_text); } 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.text(); } + 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 @@ -96,7 +81,6 @@ class DecoderResult ZX_PROPERTY(int, errorsCorrected, setErrorsCorrected) ZX_PROPERTY(int, erasures, setErasures) ZX_PROPERTY(int, lineCount, setLineCount) - ZX_PROPERTY(std::string, symbologyIdentifier, setSymbologyIdentifier) ZX_PROPERTY(StructuredAppendInfo, structuredAppend, setStructuredAppend) ZX_PROPERTY(bool, isMirrored, setIsMirrored) ZX_PROPERTY(bool, readerInit, setReaderInit) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 6850dd167e..b4948f3721 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -19,7 +19,7 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor SymbologyIdentifier si, ByteArray&& rawBytes, const bool readerInit) : _format(format), - _content({ByteArray(text)}), + _content({ByteArray(text)}, si), _text(TextDecoder::FromLatin1(text)), _position(Line(y, xStart, xStop)), _rawBytes(std::move(rawBytes)), @@ -33,12 +33,12 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat : _status(decodeResult.errorCode()), _format(format), _content(std::move(decodeResult).content()), - _text(std::move(decodeResult).text()), + _text(_content.text()), _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), _ecLevel(TextDecoder::FromLatin1(decodeResult.ecLevel())), - _symbologyIdentifier(decodeResult.symbologyIdentifier()), + _symbologyIdentifier(_content.symbology.toString(false)), _sai(decodeResult.structuredAppend()), _isMirrored(decodeResult.isMirrored()), _readerInit(decodeResult.readerInit()), diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index b099e7434a..86b802c532 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -347,7 +347,7 @@ DecoderResult Decode(const BitArray& bits, const std::string& characterSet) if (sai.index != -1) res.symbology.modifier += 6; // TODO: this is wrong as long as we remove the sai info from the content in ParseStructuredAppend - return DecoderResult(bits.toBytes(), {}, std::move(res)).setNumBits(Size(bits)).setStructuredAppend(sai); + return DecoderResult(bits.toBytes(), std::move(res)).setNumBits(Size(bits)).setStructuredAppend(sai); } DecoderResult Decode(const DetectorResult& detectorResult, const std::string& characterSet) diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index efd6f1bea7..1b1f5a785d 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -360,7 +360,7 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b result.applicationIndicator = result.symbology.modifier == '2' ? "GS1" : ""; result.symbology.modifier += isDMRE * 6; - return DecoderResult(std::move(bytes), {}, std::move(result)) + return DecoderResult(std::move(bytes), std::move(result)) .setStructuredAppend(sai) .setReaderInit(readerInit); } diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 16292730ae..e3a781f2bb 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -292,7 +292,7 @@ DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& /*cha case 5: GetMessage(bytes, 1, 77, result, sai); break; } - return DecoderResult(std::move(bytes), {}, std::move(result)) + return DecoderResult(std::move(bytes), std::move(result)) .setEcLevel(std::to_string(mode)) .setStructuredAppend(sai) .setReaderInit(mode == 6); diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index 42fed5a6fa..2260931761 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -375,9 +375,8 @@ Result DataBarExpandedReader::decodePattern(int rowNumber, PatternView& view, // TODO: EstimatePosition misses part of the symbol in the stacked case where the last row contains less pairs than // the first - return {DecoderResult({}, TextDecoder::FromLatin1(txt)) - .setSymbologyIdentifier("]e0") // ISO/IEC 24724:2011 Section 9 and GS1 General Specifications 5.1.3 Figure 5.1.3-2 - .setLineCount(EstimateLineCount(pairs.front(), pairs.back())), + // Symbology identifier: ISO/IEC 24724:2011 Section 9 and GS1 General Specifications 5.1.3 Figure 5.1.3-2 + return {DecoderResult({}, Content(ByteArray(txt), {'e', '0'})).setLineCount(EstimateLineCount(pairs.front(), pairs.back())), EstimatePosition(pairs.front(), pairs.back()), BarcodeFormat::DataBarExpanded}; } diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index b9f4fa9601..a39ec9f741 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -198,8 +198,7 @@ Result DataBarReader::decodePattern(int rowNumber, PatternView& next, for (const auto& rightPair : prevState->rightPairs) if (ChecksumIsValid(leftPair, rightPair)) { // Symbology identifier ISO/IEC 24724:2011 Section 9 and GS1 General Specifications 5.1.3 Figure 5.1.3-2 - Result res{DecoderResult({}, TextDecoder::FromLatin1(ConstructText(leftPair, rightPair))) - .setSymbologyIdentifier("]e0") + Result res{DecoderResult({}, Content(ByteArray(ConstructText(leftPair, rightPair)), {'e', '0'})) .setLineCount(EstimateLineCount(leftPair, rightPair)), EstimatePosition(leftPair, rightPair), BarcodeFormat::DataBar}; diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index 515c066b1d..6f7a835309 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -791,7 +791,7 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel, c sai.id = resultMetadata->fileId(); } - return DecoderResult(ByteArray(), {}, std::move(result)) + return DecoderResult({}, std::move(result)) .setEcLevel(std::to_string(ecLevel)) .setStructuredAppend(sai) .setReaderInit(readerInit) diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index e4ebed8192..29d35f0ac3 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -338,7 +338,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo return DecodeStatus::FormatError; } - return DecoderResult(std::move(bytes), {}, std::move(result)) + return DecoderResult(std::move(bytes), std::move(result)) .setEcLevel(ToString(ecLevel)) .setStructuredAppend(structuredAppend); } From fbbe4c4f0d1beb36d5eee9ab7a8491b95a4e730d Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 8 Jun 2022 11:50:33 +0200 Subject: [PATCH 081/180] TextDecoder: non-printable ASCII control chars -> probably not Shift_JIS This fixes a regression with some Aztec binary symbols reported by @vkrause. --- core/src/TextDecoder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/TextDecoder.cpp b/core/src/TextDecoder.cpp index 4df8a65f14..b9835c77ad 100644 --- a/core/src/TextDecoder.cpp +++ b/core/src/TextDecoder.cpp @@ -426,6 +426,9 @@ TextDecoder::GuessEncoding(const uint8_t* bytes, size_t length, CharacterSet fal else if (value == 0x80 || value == 0xA0 || value > 0xEF) { canBeShiftJIS = false; } + else if (value < 0x20 && value != 0xa && value != 0xd) { + canBeShiftJIS = false; // use non-printable ASCII as indication for binary content + } else if (value > 0xA0 && value < 0xE0) { sjisKatakanaChars++; sjisCurDoubleBytesWordLength = 0; From 364007b662004261b01435600e61aa072aed94e1 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 9 Jun 2022 12:53:47 +0200 Subject: [PATCH 082/180] ECI: change handling of pseudo-ECIs to fix MergeStructuredAppendResults This fixes the cross symbol ECI sample discussed in https://github.com/nu-book/zxing-cpp/issues/334#issuecomment-1150297982 --- core/src/Content.cpp | 34 ++++++++++++++++----------- test/blackbox/BlackboxTestRunner.cpp | 4 ++-- test/samples/pdf417-4/03-01.png | Bin 0 -> 583 bytes test/samples/pdf417-4/03.txt | 1 + 4 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 test/samples/pdf417-4/03-01.png create mode 100644 test/samples/pdf417-4/03.txt diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 14fddaad2d..21fde3c976 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -22,6 +22,12 @@ std::string ToString(ContentType type) template void Content::ForEachECIBlock(FUNC func) const { + ECI defaultECI = hasECI ? ECI::ISO8859_1 : ECI::Unknown; + if (encodings.empty()) + func(defaultECI, 0, Size(bytes)); + else if (encodings.front().pos != 0) + func(defaultECI, 0, encodings.front().pos); + for (int i = 0; i < Size(encodings); ++i) { auto [eci, start] = encodings[i]; int end = i + 1 == Size(encodings) ? Size(bytes) : encodings[i + 1].pos; @@ -33,23 +39,18 @@ void Content::ForEachECIBlock(FUNC func) const void Content::switchEncoding(ECI eci, bool isECI) { - // replace all non-ECI entries on first ECI entry with default ECI + // remove all non-ECI entries on first ECI entry if (isECI && !hasECI) - encodings = {{ECI::ISO8859_1, 0}}; - if (isECI || !hasECI) { - if (encodings.back().pos == Size(bytes)) - encodings.back().eci = eci; // no point in recording 0 length segments - else - encodings.push_back({eci, Size(bytes)}); - } + encodings.clear(); + if (isECI || !hasECI) + encodings.push_back({eci, Size(bytes)}); + hasECI |= isECI; } -Content::Content() : encodings({{ECI::Unknown, 0}}) {} +Content::Content() {} -Content::Content(ByteArray&& bytes, SymbologyIdentifier si) - : bytes(std::move(bytes)), encodings{{ECI::ISO8859_1, 0}}, symbology(si) -{} +Content::Content(ByteArray&& bytes, SymbologyIdentifier si) : bytes(std::move(bytes)), symbology(si) {} void Content::switchEncoding(CharacterSet cs) { @@ -58,9 +59,14 @@ void Content::switchEncoding(CharacterSet cs) void Content::append(const Content& other) { - for (auto& e : other.encodings) - encodings.push_back({e.eci, Size(bytes) + e.pos}); + if (!hasECI && other.hasECI) + encodings.clear(); + if (other.hasECI || !hasECI) + for (auto& e : other.encodings) + encodings.push_back({e.eci, Size(bytes) + e.pos}); append(other.bytes); + + hasECI |= other.hasECI; } void Content::erase(int pos, int n) diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 4fff96eb5d..2d7fe005dd 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -615,8 +615,8 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set { 7, 0, pure }, }); - runStructuredAppendTest("pdf417-4", "PDF417", 2, { - { 2, 2, 0 }, + runStructuredAppendTest("pdf417-4", "PDF417", 3, { + { 3, 3, 0 }, }); runTests("falsepositives-1", "None", 26, { diff --git a/test/samples/pdf417-4/03-01.png b/test/samples/pdf417-4/03-01.png new file mode 100644 index 0000000000000000000000000000000000000000..3e54a6e9e2b882192aa8601545b77df60e4d388d GIT binary patch literal 583 zcmV-N0=WH&P)Px#22e~?MF0Q*|NsA`*`M7200G}gL_t(o!|m6xswOcA z1z^Ee@Kpp`!B-d327DDEt&q)s*=n1u7S_w1YA|0DGEB~a`?sfI7->Lt)VNE8iYNEuj}jBVf%#~nhIgCc5eK>;hcb9J0l^)@r=1O5n8IK*p|~DiOMzAw zoJG-hGy#RRe8X`Lq74okdoRpCh5~pGHp}^sU>g;Sh*NPe{ zbrp^#T6e9?R<7&=#c#k<$@fb^Kja^i0M%0CRt)#kopQNQ!GJn#DuLaEo-VL`UC~EY zPbNphJ2Tui-U!9|{N6?rK`^`a@>}>SXW1B|Nh!9W)cqV|B(B<=J({49z3`SCY|*dI zDd=c&jDN>9-yHh7s>YuuJ&P3Drz9i;ZGQQ!WrNHnZ`B~irtPyBWq3^(@Rov~Ci- zB(HWylbvwO0^jQLPxjbZJen-wmz Date: Thu, 9 Jun 2022 12:55:00 +0200 Subject: [PATCH 083/180] Result: clear position and structuredAppendIndex in merged result --- core/src/Result.cpp | 2 ++ core/src/Result.h | 2 +- test/samples/qrcode-7/01.result.txt | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index b4948f3721..a516f9087b 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -88,6 +88,8 @@ Result MergeStructuredAppendResults(const Results& results) res._content.append(i->_content); res._text = res._content.text(); + res._position = {}; + res._sai.index = -1; return res; } diff --git a/core/src/Result.h b/core/src/Result.h index 13ce7f2bb8..f52d41e0b6 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -99,7 +99,7 @@ class Result const std::string& sequenceId() const { return _sai.id; } bool isLastInSequence() const { return sequenceSize() == sequenceIndex() + 1; } - bool isPartOfSequence() const { return sequenceSize() > -1; } + bool isPartOfSequence() const { return sequenceSize() > -1 && sequenceIndex() > -1; } /** * @brief readerInit Set if Reader Initialisation/Programming symbol. diff --git a/test/samples/qrcode-7/01.result.txt b/test/samples/qrcode-7/01.result.txt index 7867b23fec..0631ce5a33 100644 --- a/test/samples/qrcode-7/01.result.txt +++ b/test/samples/qrcode-7/01.result.txt @@ -1,6 +1,6 @@ symbologyIdentifier=]Q1 # ecLevel not set when "runStructuredAppendTest" sequenceSize=4 -# sequenceIndex set to first when "runStructuredAppendTest" -sequenceIndex=0 +# sequenceIndex set to -1 in MergeStructuredAppendResults +sequenceIndex=-1 sequenceId=95 From 856903310d157fc1fee482efef53899ff6f46ecf Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 9 Jun 2022 13:43:38 +0200 Subject: [PATCH 084/180] Result: add MergeStructuredAppendSequences for automatically merging Add some automatic structured append sequence merging to ZXingReader. All merged sequences are reported under the last file name (for now). --- core/src/Result.cpp | 21 ++++++++++++++++++++- core/src/Result.h | 11 ++++++++--- example/ZXingReader.cpp | 11 +++++++++++ test/blackbox/BlackboxTestRunner.cpp | 2 +- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index a516f9087b..b077830286 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -11,6 +11,7 @@ #include #include +#include #include namespace ZXing { @@ -70,7 +71,7 @@ bool Result::operator==(const Result& o) const return std::min(dTop, dBot) < length / 2; } -Result MergeStructuredAppendResults(const Results& results) +Result MergeStructuredAppendSequence(const Results& results) { if (results.empty()) return Result(DecodeStatus::NotFound); @@ -94,4 +95,22 @@ Result MergeStructuredAppendResults(const Results& results) return res; } +Results MergeStructuredAppendSequences(const Results& results) +{ + std::map sas; + for (auto& res : results) { + if (res.isPartOfSequence()) + sas[res.sequenceId()].push_back(res); + } + + Results saiResults; + for (auto& [id, seq] : sas) { + auto res = MergeStructuredAppendSequence(seq); + if (res.isValid()) + saiResults.push_back(std::move(res)); + } + + return saiResults; +} + } // ZXing diff --git a/core/src/Result.h b/core/src/Result.h index f52d41e0b6..8a07381e1c 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -114,7 +114,7 @@ class Result bool operator==(const Result& o) const; - friend Result MergeStructuredAppendResults(const std::vector& results); + friend Result MergeStructuredAppendSequence(const std::vector& results); private: DecodeStatus _status = DecodeStatus::NoError; @@ -135,8 +135,13 @@ class Result using Results = std::vector; /** - * @brief Merge a list of Results from one Structured Append set to a single result + * @brief Merge a list of Results from one Structured Append sequence to a single result */ -Result MergeStructuredAppendResults(const Results& results); +Result MergeStructuredAppendSequence(const Results& results); + +/** + * @brief Automatically merge all structured append sequences found in the given results + */ +Results MergeStructuredAppendSequences(const Results& results); } // ZXing diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 937da9077a..578e7714c6 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -115,6 +115,7 @@ int main(int argc, char* argv[]) { DecodeHints hints; std::vector filePaths; + Results allResults; std::string outPath; bool oneLine = false; bool angleEscape = false; @@ -152,6 +153,13 @@ int main(int argc, char* argv[]) if (results.empty()) results.emplace_back(DecodeStatus::NotFound); + allResults.insert(allResults.end(), results.begin(), results.end()); + if (filePath == filePaths.back()) { + auto merged = MergeStructuredAppendSequences(allResults); + // report all merged sequences as part of the last file to make the logic not overly complicated here + results.insert(results.end(), merged.begin(), merged.end()); + } + for (auto&& result : results) { if (!outPath.empty()) @@ -219,6 +227,9 @@ int main(int argc, char* argv[]) if (result.isPartOfSequence()) std::cout << "Structured Append: symbol " << result.sequenceIndex() + 1 << " of " << result.sequenceSize() << " (parity/id: '" << result.sequenceId() << "')\n"; + else if (result.sequenceSize() > 0) + std::cout << "Structured Append: merged result from " << result.sequenceSize() << " symbols (parity/id: '" + << result.sequenceId() << "')\n"; if (result.readerInit()) std::cout << "Reader Initialisation/Programming\n"; diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 2d7fe005dd..085bb41d95 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -252,7 +252,7 @@ static Result readMultiple(const std::vector& imgPaths, std::string_vi allResults.insert(allResults.end(), results.begin(), results.end()); } - return MergeStructuredAppendResults(allResults); + return MergeStructuredAppendSequence(allResults); } static void doRunStructuredAppendTest(const fs::path& directory, std::string_view format, int totalTests, From 7da0d436842ce1026591202eccad1429f661e2bb Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 13 Jun 2022 18:37:49 +0200 Subject: [PATCH 085/180] example: port ZXingQtReader to Qt6 Note: to compile it with qt6, increase c++ standard to 17 and replace `find_package(Qt5 ...` with `ind_package(Qt6 ...`. The overlay drawing feature is missing due to a removed `videoOutput.mapPointToItem()` function. The default video resolution is different and the `Q_ENUM_NS` macros don't work correct (int value instead of string in GUI). --- example/CMakeLists.txt | 8 +- ...gQtCamReader.qml => ZXingQt5CamReader.qml} | 22 +-- example/ZXingQt6CamReader.qml | 148 ++++++++++++++++++ example/ZXingQtCamReader.cpp | 8 +- example/ZXingQtCamReader.qrc | 3 +- example/ZXingQtReader.h | 138 +++++++++++----- 6 files changed, 265 insertions(+), 62 deletions(-) rename example/{ZXingQtCamReader.qml => ZXingQt5CamReader.qml} (87%) create mode 100644 example/ZXingQt6CamReader.qml diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 8cb2e1b4d8..36d663a67f 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -30,14 +30,14 @@ if (BUILD_READERS) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) - if (TARGET Qt5::Gui) + if (TARGET Qt::Gui) add_executable (ZXingQtReader ZXingQtReader.cpp ZXingQtReader.h) - target_link_libraries(ZXingQtReader ZXing::ZXing Qt5::Gui) + target_link_libraries(ZXingQtReader ZXing::ZXing Qt::Gui) endif() - if (TARGET Qt5::Multimedia) + if (TARGET Qt::Multimedia AND TARGET Qt::Quick) add_executable(ZXingQtCamReader ZXingQtCamReader.cpp ZXingQtCamReader.qrc ZXingQtReader.h) - target_link_libraries(ZXingQtCamReader ZXing::ZXing Qt5::Gui Qt5::Multimedia Qt5::Quick) + target_link_libraries(ZXingQtCamReader ZXing::ZXing Qt::Gui Qt::Multimedia Qt::Quick) endif() find_package(OpenCV) diff --git a/example/ZXingQtCamReader.qml b/example/ZXingQt5CamReader.qml similarity index 87% rename from example/ZXingQtCamReader.qml rename to example/ZXingQt5CamReader.qml index 9a90cf20d6..bf3f8d3ed2 100644 --- a/example/ZXingQtCamReader.qml +++ b/example/ZXingQt5CamReader.qml @@ -1,18 +1,7 @@ /* * Copyright 2020 Axel Waggershauser - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 import QtQuick 2.12 import QtQuick.Window 2.12 @@ -36,12 +25,13 @@ Window { interval: 2000 } - VideoFilter { - id: zxingFilter + BarcodeReader { + id: barcodeReader formats: (oneDSwitch.checked ? (ZXing.OneDCodes) : ZXing.None) | (twoDSwitch.checked ? (ZXing.TwoDCodes) : ZXing.None) tryRotate: tryRotateSwitch.checked tryHarder: tryHarderSwitch.checked + tryDownscale: tryDownscaleSwitch.checked // callback with parameter 'result', called for every successfully processed frame // onFoundBarcode: {} @@ -100,7 +90,7 @@ Window { id: videoOutput Layout.fillHeight: true Layout.fillWidth: true - filters: [zxingFilter] + filters: [barcodeReader] source: camera autoOrientation: true @@ -144,9 +134,11 @@ Window { ColumnLayout { anchors.right: parent.right + anchors.bottom: parent.bottom Switch {id: tryRotateSwitch; text: qsTr("Try Rotate"); checked: true } Switch {id: tryHarderSwitch; text: qsTr("Try Harder"); checked: true } + Switch {id: tryDownscaleSwitch; text: qsTr("Try Downscale"); checked: true } Switch {id: oneDSwitch; text: qsTr("1D Codes"); checked: true } Switch {id: twoDSwitch; text: qsTr("2D Codes"); checked: true } } diff --git a/example/ZXingQt6CamReader.qml b/example/ZXingQt6CamReader.qml new file mode 100644 index 0000000000..7c80e77855 --- /dev/null +++ b/example/ZXingQt6CamReader.qml @@ -0,0 +1,148 @@ +/* + * Copyright 2022 Axel Waggershauser + */ +// SPDX-License-Identifier: Apache-2.0 + +import QtQuick +import QtQuick.Window +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Shapes +import QtMultimedia +import ZXing + +Window { + visible: true + width: 640 + height: 480 + title: Qt.application.name + + property var nullPoints: [Qt.point(0,0), Qt.point(0,0), Qt.point(0,0), Qt.point(0,0)] + property var points: nullPoints + + Timer { + id: resetInfo + interval: 2000 + } + + BarcodeReader { + id: barcodeReader + videoSink: videoOutput.videoSink + + formats: (oneDSwitch.checked ? (ZXing.OneDCodes) : ZXing.None) | (twoDSwitch.checked ? (ZXing.TwoDCodes) : ZXing.None) + tryRotate: tryRotateSwitch.checked + tryHarder: tryHarderSwitch.checked + tryDownscale: tryDownscaleSwitch.checked + + // callback with parameter 'result', called for every successfully processed frame + // onFoundBarcode: {} + + // callback with parameter 'result', called for every processed frame + onNewResult: { + points = result.isValid + ? [result.position.topLeft, result.position.topRight, result.position.bottomRight, result.position.bottomLeft] + : nullPoints + + if (result.isValid) + resetInfo.restart() + + if (result.isValid || !resetInfo.running) + info.text = qsTr("Format: \t %1 \nText: \t %2 \nError: \t %3 \nTime: \t %4 ms").arg(result.formatName).arg(result.text).arg(result.status).arg(result.runTime) + +// console.log(result) + } + } + + MediaDevices { + id: devices + } + + Camera { + id: camera + cameraDevice: devices.videoInputs[camerasComboBox.currentIndex] ? devices.videoInputs[camerasComboBox.currentIndex] : devices.defaultVideoInput + focusMode: Camera.FocusModeAutoNear + onErrorOccurred: console.log("camera error:" + errorString) + } + + CaptureSession { + id: captureSession + camera: camera + videoOutput: videoOutput + } + + ColumnLayout { + anchors.fill: parent + + RowLayout { + Layout.fillWidth: true + Layout.fillHeight: false + visible: devices.videoInputs.length > 1 + Label { + text: qsTr("Camera: ") + Layout.fillWidth: false + } + ComboBox { + id: camerasComboBox + Layout.fillWidth: true + model: devices.videoInputs + textRole: "displayName" + currentIndex: 0 + } + } + + VideoOutput { + id: videoOutput + Layout.fillHeight: true + Layout.fillWidth: true + +// Shape { +// id: polygon +// anchors.fill: parent +// visible: points.length == 4 +// ShapePath { +// strokeWidth: 3 +// strokeColor: "red" +// strokeStyle: ShapePath.SolidLine +// fillColor: "transparent" +// //TODO: really? I don't know qml... +// startX: videoOutput.mapPointToItem(points[3]).x +// startY: videoOutput.mapPointToItem(points[3]).y +// PathLine { +// x: videoOutput.mapPointToItem(points[0]).x +// y: videoOutput.mapPointToItem(points[0]).y +// } +// PathLine { +// x: videoOutput.mapPointToItem(points[1]).x +// y: videoOutput.mapPointToItem(points[1]).y +// } +// PathLine { +// x: videoOutput.mapPointToItem(points[2]).x +// y: videoOutput.mapPointToItem(points[2]).y +// } +// PathLine { +// x: videoOutput.mapPointToItem(points[3]).x +// y: videoOutput.mapPointToItem(points[3]).y +// } +// } +// } + + Label { + id: info + color: "white" + padding: 10 + background: Rectangle { color: "#80808080" } + } + + ColumnLayout { + anchors.right: parent.right + anchors.bottom: parent.bottom + + Switch {id: tryRotateSwitch; text: qsTr("Try Rotate"); checked: true } + Switch {id: tryHarderSwitch; text: qsTr("Try Harder"); checked: true } + Switch {id: tryDownscaleSwitch; text: qsTr("Try Downscale"); checked: true } + Switch {id: oneDSwitch; text: qsTr("1D Codes"); checked: true } + Switch {id: twoDSwitch; text: qsTr("2D Codes"); checked: true } + } + } + } +} diff --git a/example/ZXingQtCamReader.cpp b/example/ZXingQtCamReader.cpp index 102312e0e6..3815bfe43b 100644 --- a/example/ZXingQtCamReader.cpp +++ b/example/ZXingQtCamReader.cpp @@ -10,14 +10,20 @@ int main(int argc, char *argv[]) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif ZXingQt::registerQmlAndMetaTypes(); QGuiApplication app(argc, argv); app.setApplicationName("ZXingQtCamReader"); QQmlApplicationEngine engine; - engine.load(QUrl(QStringLiteral("qrc:/ZXingQtCamReader.qml"))); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + engine.load(QUrl(QStringLiteral("qrc:/ZXingQt5CamReader.qml"))); +#else + engine.load(QUrl(QStringLiteral("qrc:/ZXingQt6CamReader.qml"))); +#endif if (engine.rootObjects().isEmpty()) return -1; diff --git a/example/ZXingQtCamReader.qrc b/example/ZXingQtCamReader.qrc index 967e6ec7d1..b6b772c75c 100644 --- a/example/ZXingQtCamReader.qrc +++ b/example/ZXingQtCamReader.qrc @@ -1,5 +1,6 @@ - ZXingQtCamReader.qml + ZXingQt5CamReader.qml + ZXingQt6CamReader.qml diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index 7435e56c85..07effc10d0 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -12,7 +12,12 @@ #include #ifdef QT_MULTIMEDIA_LIB +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include +#else +#include +#include +#endif #include #endif @@ -154,7 +159,7 @@ inline Result ReadBarcode(const QImage& img, const DecodeHints& hints = {}) auto exec = [&](const QImage& img) { return Result(ZXing::ReadBarcode( - {img.bits(), img.width(), img.height(), ImgFmtFromQImg(img), img.bytesPerLine()}, hints)); + {img.bits(), img.width(), img.height(), ImgFmtFromQImg(img), static_cast(img.bytesPerLine())}, hints)); }; return ImgFmtFromQImg(img) == ImageFormat::None ? exec(img.convertToFormat(QImage::Format_Grayscale8)) : exec(img); @@ -166,7 +171,11 @@ inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = { using namespace ZXing; auto img = frame; // shallow copy just get access to non-const map() function +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (!frame.isValid() || !img.map(QAbstractVideoBuffer::ReadOnly)){ +#else + if (!frame.isValid() || !img.map(QVideoFrame::ReadOnly)){ +#endif qWarning() << "invalid QVideoFrame: could not map memory"; return {}; } @@ -176,10 +185,18 @@ inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = { int pixStride = 0; int pixOffset = 0; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#define FORMAT(F5, F6) QVideoFrame::Format_##F5 +#define FIRST_PLANE +#else +#define FORMAT(F5, F6) QVideoFrameFormat::Format_##F6 +#define FIRST_PLANE 0 +#endif + switch (img.pixelFormat()) { - case QVideoFrame::Format_ARGB32: - case QVideoFrame::Format_ARGB32_Premultiplied: - case QVideoFrame::Format_RGB32: + case FORMAT(ARGB32, ARGB8888): + case FORMAT(ARGB32_Premultiplied, ARGB8888_Premultiplied): + case FORMAT(RGB32, RGBX8888): #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN fmt = ImageFormat::BGRX; #else @@ -187,11 +204,9 @@ inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = { #endif break; - case QVideoFrame::Format_RGB24: fmt = ImageFormat::RGB; break; - - case QVideoFrame::Format_BGRA32: - case QVideoFrame::Format_BGRA32_Premultiplied: - case QVideoFrame::Format_BGR32: + case FORMAT(BGRA32, BGRA8888): + case FORMAT(BGRA32_Premultiplied, BGRA8888_Premultiplied): + case FORMAT(BGR32, BGRX8888): #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN fmt = ImageFormat::RGBX; #else @@ -199,10 +214,17 @@ inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = { #endif break; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + case QVideoFrame::Format_RGB24: fmt = ImageFormat::RGB; break; case QVideoFrame::Format_BGR24: fmt = ImageFormat::BGR; break; + case QVideoFrame::Format_YUV444: fmt = ImageFormat::Lum, pixStride = 3; break; +#else + case QVideoFrameFormat::Format_P010: + case QVideoFrameFormat::Format_P016: fmt = ImageFormat::Lum, pixStride = 1; break; +#endif - case QVideoFrame::Format_AYUV444: - case QVideoFrame::Format_AYUV444_Premultiplied: + case FORMAT(AYUV444, AYUV): + case FORMAT(AYUV444_Premultiplied, AYUV_Premultiplied): #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN fmt = ImageFormat::Lum, pixStride = 4, pixOffset = 3; #else @@ -210,23 +232,22 @@ inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = { #endif break; - case QVideoFrame::Format_YUV444: fmt = ImageFormat::Lum, pixStride = 3; break; - case QVideoFrame::Format_YUV420P: - case QVideoFrame::Format_NV12: - case QVideoFrame::Format_NV21: - case QVideoFrame::Format_IMC1: - case QVideoFrame::Format_IMC2: - case QVideoFrame::Format_IMC3: - case QVideoFrame::Format_IMC4: - case QVideoFrame::Format_YV12: fmt = ImageFormat::Lum; break; - case QVideoFrame::Format_UYVY: fmt = ImageFormat::Lum, pixStride = 2, pixOffset = 1; break; - case QVideoFrame::Format_YUYV: fmt = ImageFormat::Lum, pixStride = 2; break; - - case QVideoFrame::Format_Y8: fmt = ImageFormat::Lum; break; - case QVideoFrame::Format_Y16: fmt = ImageFormat::Lum, pixStride = 2, pixOffset = 1; break; + case FORMAT(YUV420P, YUV420P): + case FORMAT(NV12, NV12): + case FORMAT(NV21, NV21): + case FORMAT(IMC1, IMC1): + case FORMAT(IMC2, IMC2): + case FORMAT(IMC3, IMC3): + case FORMAT(IMC4, IMC4): + case FORMAT(YV12, YV12): fmt = ImageFormat::Lum; break; + case FORMAT(UYVY, UYVY): fmt = ImageFormat::Lum, pixStride = 2, pixOffset = 1; break; + case FORMAT(YUYV, YUYV): fmt = ImageFormat::Lum, pixStride = 2; break; + + case FORMAT(Y8, Y8): fmt = ImageFormat::Lum; break; + case FORMAT(Y16, Y16): fmt = ImageFormat::Lum, pixStride = 2, pixOffset = 1; break; #if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) - case QVideoFrame::Format_ABGR32: + case FORMAT(ABGR32, ABGR8888): #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN fmt = ImageFormat::RGBX; #else @@ -235,19 +256,22 @@ inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = { break; #endif #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - case QVideoFrame::Format_YUV422P: fmt = ImageFormat::Lum; break; + case FORMAT(YUV422P, YUV422P): fmt = ImageFormat::Lum; break; #endif default: break; } Result res; if (fmt != ImageFormat::None) { - res = Result( - ZXing::ReadBarcode({img.bits() + pixOffset, img.width(), img.height(), fmt, img.bytesPerLine(), pixStride}, - hints)); + res = Result(ZXing::ReadBarcode( + {img.bits(FIRST_PLANE) + pixOffset, img.width(), img.height(), fmt, img.bytesPerLine(FIRST_PLANE), pixStride}, hints)); } else { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (QVideoFrame::imageFormatFromPixelFormat(img.pixelFormat()) != QImage::Format_Invalid) res = ReadBarcode(img.image(), hints); +#else + res = ReadBarcode(img.toImage(), hints); +#endif } img.unmap(); @@ -268,14 +292,21 @@ public: \ } \ Q_SIGNAL void name##Changed(); -class VideoFilter : public QAbstractVideoFilter, private DecodeHints + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +class BarcodeReader : public QAbstractVideoFilter, private DecodeHints +#else +class BarcodeReader : public QObject, private DecodeHints +#endif { Q_OBJECT public: - VideoFilter(QObject* parent = nullptr) : QAbstractVideoFilter(parent) {} - - QVideoFilterRunnable* createFilterRunnable() override; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + BarcodeReader(QObject* parent = nullptr) : QAbstractVideoFilter(parent) {} +#else + BarcodeReader(QObject* parent = nullptr) : QObject(parent) {} +#endif // TODO: find out how to properly expose QFlags to QML // simply using ZQ_PROPERTY(BarcodeFormats, formats, setFormats) @@ -298,9 +329,10 @@ class VideoFilter : public QAbstractVideoFilter, private DecodeHints ZQ_PROPERTY(bool, tryRotate, setTryRotate) ZQ_PROPERTY(bool, tryHarder, setTryHarder) + ZQ_PROPERTY(bool, tryDownscale, setTryDownscale) public slots: - Result process(const QVideoFrame& image) + ZXingQt::Result process(const QVideoFrame& image) { QElapsedTimer t; t.start(); @@ -316,18 +348,41 @@ public slots: } signals: - void newResult(Result result); - void foundBarcode(Result result); + void newResult(ZXingQt::Result result); + void foundBarcode(ZXingQt::Result result); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +public: + QVideoFilterRunnable *createFilterRunnable() override; +#else +private: + QVideoSink *_sink = nullptr; + +public: + void setVideoSink(QVideoSink* sink) { + if (_sink == sink) + return; + + if (_sink) + disconnect(_sink, nullptr, this, nullptr); + + _sink = sink; + connect(_sink, &QVideoSink::videoFrameChanged, this, &BarcodeReader::process); + } + Q_PROPERTY(QVideoSink* videoSink WRITE setVideoSink) +#endif + }; #undef ZX_PROPERTY +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) class VideoFilterRunnable : public QVideoFilterRunnable { - VideoFilter* _filter = nullptr; + BarcodeReader* _filter = nullptr; public: - explicit VideoFilterRunnable(VideoFilter* filter) : _filter(filter) {} + explicit VideoFilterRunnable(BarcodeReader* filter) : _filter(filter) {} QVideoFrame run(QVideoFrame* input, const QVideoSurfaceFormat& /*surfaceFormat*/, RunFlags /*flags*/) override { @@ -336,10 +391,11 @@ class VideoFilterRunnable : public QVideoFilterRunnable } }; -inline QVideoFilterRunnable* VideoFilter::createFilterRunnable() +inline QVideoFilterRunnable* BarcodeReader::createFilterRunnable() { return new VideoFilterRunnable(this); } +#endif #endif // QT_MULTIMEDIA_LIB @@ -367,7 +423,7 @@ inline void registerQmlAndMetaTypes() qmlRegisterUncreatableMetaObject( ZXingQt::staticMetaObject, "ZXing", 1, 0, "ZXing", "Access to enums & flags only"); - qmlRegisterType("ZXing", 1, 0, "VideoFilter"); + qmlRegisterType("ZXing", 1, 0, "BarcodeReader"); } } // namespace ZXingQt From 86d28521300d58cb1f8848946729d0cc6ca60ea7 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 13 Jun 2022 19:03:41 +0200 Subject: [PATCH 086/180] cmake: revert breaking change of `INTERFACE "$"` The change was deliberate but ill advised. Need a different solution to make the examples compile with the installed includes. --- core/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 7b34509510..e6c4d1a07f 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -461,7 +461,7 @@ add_library (ZXing target_include_directories (ZXing PUBLIC "$" - INTERFACE "$" + INTERFACE "$" ) target_compile_options (ZXing From a555eb01e13eb2768ffbe340526409edf14a6074 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 14 Jun 2022 13:29:25 +0200 Subject: [PATCH 087/180] API: improve a few comments for public symbols --- core/src/DecodeHints.h | 8 ++------ core/src/ReadBarcode.h | 8 +++++++- core/src/Result.h | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 68ffe031b9..26813d387e 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -109,7 +109,7 @@ class DecodeHints /// The maximum number of symbols (barcodes) to detect / look for in the image with ReadBarcodes ZX_PROPERTY(uint8_t, maxNumberOfSymbols, setMaxNumberOfSymbols) - /// Specifies what character encoding to use when decoding, where applicable. + /// Specifies fallback character set to use instead of auto-detecting it (when applicable) ZX_PROPERTY(std::string, characterSet, setCharacterSet) /// Allowed lengths of encoded data -- reject anything else.. @@ -124,11 +124,7 @@ class DecodeHints /// Assume ITF codes employ a GS1 check digit and validate it. ZX_PROPERTY(bool, validateITFCheckSum, setValidateITFCheckSum) - /** - * If true, return the start and end digits in a Codabar barcode instead of stripping them. They - * are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them - * to not be. - */ + /// If true, return the start and end chars in a Codabar barcode instead of stripping them. ZX_PROPERTY(bool, returnCodabarStartEnd, setReturnCodabarStartEnd) /// Specify whether to ignore, read or require EAN-2/5 add-on symbols while scanning EAN/UPC codes diff --git a/core/src/ReadBarcode.h b/core/src/ReadBarcode.h index a76d60c31b..5b73d16913 100644 --- a/core/src/ReadBarcode.h +++ b/core/src/ReadBarcode.h @@ -20,7 +20,13 @@ namespace ZXing { */ Result ReadBarcode(const ImageView& buffer, const DecodeHints& hints = {}); -// WARNING: this API is experimental and may change/disappear +/** + * Read barcodes from an ImageView + * + * @param buffer view of the image data including layout and format + * @param hints optional DecodeHints to parameterize / speed up decoding + * @return #Results list of results found, may be empty + */ Results ReadBarcodes(const ImageView& buffer, const DecodeHints& hints = {}); } // ZXing diff --git a/core/src/Result.h b/core/src/Result.h index 8a07381e1c..c0a27513d7 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -140,7 +140,7 @@ using Results = std::vector; Result MergeStructuredAppendSequence(const Results& results); /** - * @brief Automatically merge all structured append sequences found in the given results + * @brief Automatically merge all Structured Append sequences found in the given results */ Results MergeStructuredAppendSequences(const Results& results); From 5b461cb5cd1cf3ba0ff0049b43ed03ade429874c Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 14 Jun 2022 13:31:34 +0200 Subject: [PATCH 088/180] Result: remove applicationIndicator property (for now) --- core/src/Result.h | 1 - example/ZXingReader.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/core/src/Result.h b/core/src/Result.h index c0a27513d7..7f6db93e59 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -49,7 +49,6 @@ class Result const ByteArray& bytes() const { return _content.bytes; } const ByteArray bytesECI() const { return _content.bytesECI(); } const std::string utf8Protocol() const { return _content.utf8Protocol(); } - const std::string& applicationIndicator() const { return _content.applicationIndicator; } ContentType contentType() const { return _content.type(); } bool hasECI() const { return _content.hasECI; } // END WARNING diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 578e7714c6..56f6759491 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -209,7 +209,6 @@ int main(int argc, char* argv[]) }; printOptional("EC Level: ", ToUtf8(result.ecLevel())); - printOptional("App-Ind.: ", result.applicationIndicator()); if (result.lineCount()) std::cout << "Lines: " << result.lineCount() << "\n"; From e6be0662da80be5a4c923e0880457b26221380fc Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 14 Jun 2022 13:46:44 +0200 Subject: [PATCH 089/180] Result: increase API resilience against future ABI breakages (de-inlining) --- core/src/Result.cpp | 55 ++++++++++++++++++++++++++++++++++++++++----- core/src/Result.h | 24 ++++++++++---------- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index b077830286..72c995e0f5 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -21,11 +21,9 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor : _format(format), _content({ByteArray(text)}, si), - _text(TextDecoder::FromLatin1(text)), _position(Line(y, xStart, xStop)), _rawBytes(std::move(rawBytes)), _numBits(Size(_rawBytes) * 8), - _symbologyIdentifier(si.toString()), _readerInit(readerInit), _lineCount(0) {} @@ -34,12 +32,10 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat : _status(decodeResult.errorCode()), _format(format), _content(std::move(decodeResult).content()), - _text(_content.text()), _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), _ecLevel(TextDecoder::FromLatin1(decodeResult.ecLevel())), - _symbologyIdentifier(_content.symbology.toString(false)), _sai(decodeResult.structuredAppend()), _isMirrored(decodeResult.isMirrored()), _readerInit(decodeResult.readerInit()), @@ -48,12 +44,62 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat // TODO: add type opaque and code specific 'extra data'? (see DecoderResult::extra()) } +std::wstring Result::text() const +{ + return _content.text(); +} + +const ByteArray& Result::bytes() const +{ + return _content.bytes; +} + +ByteArray Result::bytesECI() const +{ + return _content.bytesECI(); +} + +std::string Result::utf8Protocol() const +{ + return _content.utf8Protocol(); +} + +ContentType Result::contentType() const +{ + return _content.type(); +} + +bool Result::hasECI() const +{ + return _content.hasECI; +} + int Result::orientation() const { constexpr auto std_numbers_pi_v = 3.14159265358979323846; // TODO: c++20 return std::lround(_position.orientation() * 180 / std_numbers_pi_v); } +std::string Result::symbologyIdentifier() const +{ + return _content.symbology.toString(); +} + +int Result::sequenceSize() const +{ + return _sai.count; +} + +int Result::sequenceIndex() const +{ + return _sai.index; +} + +std::string Result::sequenceId() const +{ + return _sai.id; +} + bool Result::operator==(const Result& o) const { if (format() != o.format() || text() != o.text()) @@ -88,7 +134,6 @@ Result MergeStructuredAppendSequence(const Results& results) for (auto i = std::next(allResults.begin()); i != allResults.end(); ++i) res._content.append(i->_content); - res._text = res._content.text(); res._position = {}; res._sai.index = -1; diff --git a/core/src/Result.h b/core/src/Result.h index 7f6db93e59..6a8ae00000 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -43,14 +43,14 @@ class Result BarcodeFormat format() const { return _format; } - const std::wstring& text() const { return _text; } + std::wstring text() const; // WARNING: this is an experimental API and may change/disappear - const ByteArray& bytes() const { return _content.bytes; } - const ByteArray bytesECI() const { return _content.bytesECI(); } - const std::string utf8Protocol() const { return _content.utf8Protocol(); } - ContentType contentType() const { return _content.type(); } - bool hasECI() const { return _content.hasECI; } + const ByteArray& bytes() const; + ByteArray bytesECI() const; + std::string utf8Protocol() const; + ContentType contentType() const; + bool hasECI() const; // END WARNING const Position& position() const { return _position; } @@ -72,7 +72,7 @@ class Result /** * @brief symbologyIdentifier Symbology identifier "]cm" where "c" is symbology code character, "m" the modifier. */ - const std::string& symbologyIdentifier() const { return _symbologyIdentifier; } + std::string symbologyIdentifier() const; /** * @brief sequenceSize number of symbols in a structured append sequence. @@ -81,12 +81,12 @@ class Result * If it is a structured append symbol but the total number of symbols is unknown, the * returned value is 0 (see PDF417 if optional "Segment Count" not given). */ - int sequenceSize() const { return _sai.count; } + int sequenceSize() const; /** * @brief sequenceIndex the 0-based index of this symbol in a structured append sequence. */ - int sequenceIndex() const { return _sai.index; } + int sequenceIndex() const; /** * @brief sequenceId id to check if a set of symbols belongs to the same structured append sequence. @@ -95,7 +95,7 @@ class Result * For QR Code, this is the parity integer converted to a string. * For PDF417 and DataMatrix, this is the "fileId". */ - const std::string& sequenceId() const { return _sai.id; } + std::string sequenceId() const; bool isLastInSequence() const { return sequenceSize() == sequenceIndex() + 1; } bool isPartOfSequence() const { return sequenceSize() > -1 && sequenceIndex() > -1; } @@ -109,6 +109,8 @@ class Result * @brief How many lines have been detected with this code (applies only to 1D symbologies) */ int lineCount() const { return _lineCount; } + + // only for internal use void incrementLineCount() { ++_lineCount; } bool operator==(const Result& o) const; @@ -119,12 +121,10 @@ class Result DecodeStatus _status = DecodeStatus::NoError; BarcodeFormat _format = BarcodeFormat::None; Content _content; - std::wstring _text; Position _position; ByteArray _rawBytes; int _numBits = 0; std::wstring _ecLevel; - std::string _symbologyIdentifier; StructuredAppendInfo _sai; bool _isMirrored = false; bool _readerInit = false; From 2d712ea4874f00bbe8f63e7afd9d964f315b5e92 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 18 Jun 2022 15:42:50 +0200 Subject: [PATCH 090/180] GridSampler: support multi-symbol debugging --- core/src/GridSampler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/GridSampler.cpp b/core/src/GridSampler.cpp index b612f3a5ef..9bc3490212 100644 --- a/core/src/GridSampler.cpp +++ b/core/src/GridSampler.cpp @@ -22,7 +22,8 @@ DetectorResult SampleGrid(const BitMatrix& image, int width, int height, const P { #ifdef PRINT_DEBUG LogMatrix log; - LogMatrixWriter lmw(log, image, 5, "grid.pnm"); + static int i = 0; + LogMatrixWriter lmw(log, image, 5, "grid" + std::to_string(i++) + ".pnm"); #endif if (width <= 0 || height <= 0 || !mod2Pix.isValid()) return {}; From 5a839fea00cb0acb1d3aad6f62f89ee6c896bf62 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 18 Jun 2022 15:47:18 +0200 Subject: [PATCH 091/180] QRCode: detect mirrored symbols directly from FormatInfo bits Also prepares MicroQRCode improvement based on hamming distance info in FormatInfo --- core/src/qrcode/QRBitMatrixParser.cpp | 41 +++++++------- core/src/qrcode/QRBitMatrixParser.h | 4 +- core/src/qrcode/QRDecoder.cpp | 21 +++----- core/src/qrcode/QRDetector.cpp | 2 +- core/src/qrcode/QRFormatInformation.cpp | 56 +++++++++----------- core/src/qrcode/QRFormatInformation.h | 34 ++++-------- test/unit/qrcode/QRBitMatrixParserTest.cpp | 8 +-- test/unit/qrcode/QRFormatInformationTest.cpp | 8 +-- 8 files changed, 73 insertions(+), 101 deletions(-) diff --git a/core/src/qrcode/QRBitMatrixParser.cpp b/core/src/qrcode/QRBitMatrixParser.cpp index ed614fe9a3..7cdb3e7529 100644 --- a/core/src/qrcode/QRBitMatrixParser.cpp +++ b/core/src/qrcode/QRBitMatrixParser.cpp @@ -17,7 +17,7 @@ namespace ZXing::QRCode { -static bool getBit(const BitMatrix& bitMatrix, int x, int y, bool mirrored) +static bool getBit(const BitMatrix& bitMatrix, int x, int y, bool mirrored = false) { return mirrored ? bitMatrix.get(y, x) : bitMatrix.get(x, y); } @@ -60,7 +60,7 @@ const Version* ReadVersion(const BitMatrix& bitMatrix) return nullptr; } -FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrored, bool isMicro) +FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro) { if (!hasValidDimension(bitMatrix, isMicro)) return {}; @@ -69,9 +69,9 @@ FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrore // Read top-left format info bits int formatInfoBits = 0; for (int x = 1; x < 9; x++) - AppendBit(formatInfoBits, getBit(bitMatrix, x, 8, mirrored)); + AppendBit(formatInfoBits, getBit(bitMatrix, x, 8)); for (int y = 7; y >= 1; y--) - AppendBit(formatInfoBits, getBit(bitMatrix, 8, y, mirrored)); + AppendBit(formatInfoBits, getBit(bitMatrix, 8, y)); return FormatInformation::DecodeMQR(formatInfoBits); } @@ -79,27 +79,27 @@ FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrore // Read top-left format info bits int formatInfoBits1 = 0; for (int x = 0; x < 6; x++) - AppendBit(formatInfoBits1, getBit(bitMatrix, x, 8, mirrored)); + AppendBit(formatInfoBits1, getBit(bitMatrix, x, 8)); // .. and skip a bit in the timing pattern ... - AppendBit(formatInfoBits1, getBit(bitMatrix, 7, 8, mirrored)); - AppendBit(formatInfoBits1, getBit(bitMatrix, 8, 8, mirrored)); - AppendBit(formatInfoBits1, getBit(bitMatrix, 8, 7, mirrored)); + 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, mirrored)); + AppendBit(formatInfoBits1, getBit(bitMatrix, 8, y)); // Read the top-right/bottom-left pattern too int dimension = bitMatrix.height(); int formatInfoBits2 = 0; for (int y = dimension - 1; y >= dimension - 7; y--) - AppendBit(formatInfoBits2, getBit(bitMatrix, 8, y, mirrored)); + AppendBit(formatInfoBits2, getBit(bitMatrix, 8, y)); for (int x = dimension - 8; x < dimension; x++) - AppendBit(formatInfoBits2, getBit(bitMatrix, x, 8, mirrored)); + AppendBit(formatInfoBits2, getBit(bitMatrix, x, 8)); return FormatInformation::DecodeQR(formatInfoBits1, formatInfoBits2); } -static ByteArray ReadQRCodewords(const BitMatrix& bitMatrix, const Version& version, int maskIndex, bool mirrored) +static ByteArray ReadQRCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo) { BitMatrix functionPattern = version.buildFunctionPattern(); @@ -122,7 +122,8 @@ static ByteArray ReadQRCodewords(const BitMatrix& bitMatrix, const Version& vers // Ignore bits covered by the function pattern if (!functionPattern.get(xx, y)) { // Read a bit - AppendBit(currentByte, GetDataMaskBit(maskIndex, xx, y) != getBit(bitMatrix, xx, y, mirrored)); + 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)); @@ -137,8 +138,7 @@ static ByteArray ReadQRCodewords(const BitMatrix& bitMatrix, const Version& vers return result; } -static ByteArray ReadMQRCodewords(const BitMatrix& bitMatrix, const QRCode::Version& version, - const FormatInformation& formatInformation, bool mirrored) +static ByteArray ReadMQRCodewords(const BitMatrix& bitMatrix, const QRCode::Version& version, const FormatInformation& formatInfo) { BitMatrix functionPattern = version.buildFunctionPattern(); @@ -147,7 +147,7 @@ static ByteArray ReadMQRCodewords(const BitMatrix& bitMatrix, const QRCode::Vers // See ISO 18004:2006 6.7.3. bool hasD4mBlock = version.versionNumber() % 2 == 1; int d4mBlockIndex = - version.versionNumber() == 1 ? 3 : (formatInformation.errorCorrectionLevel() == QRCode::ErrorCorrectionLevel::Low ? 11 : 9); + version.versionNumber() == 1 ? 3 : (formatInfo.ecLevel == QRCode::ErrorCorrectionLevel::Low ? 11 : 9); ByteArray result; result.reserve(version.totalCodewords()); @@ -166,7 +166,7 @@ static ByteArray ReadMQRCodewords(const BitMatrix& bitMatrix, const QRCode::Vers if (!functionPattern.get(xx, y)) { // Read a bit AppendBit(currentByte, - GetDataMaskBit(formatInformation.dataMask(), xx, y, true) != getBit(bitMatrix, xx, y, mirrored)); + 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)) { @@ -184,14 +184,13 @@ static ByteArray ReadMQRCodewords(const BitMatrix& bitMatrix, const QRCode::Vers return result; } -ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInformation, - bool mirrored) +ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo) { if (!hasValidDimension(bitMatrix, version.isMicroQRCode())) return {}; - return version.isMicroQRCode() ? ReadMQRCodewords(bitMatrix, version, formatInformation, mirrored) - : ReadQRCodewords(bitMatrix, version, formatInformation.dataMask(), mirrored); + return version.isMicroQRCode() ? ReadMQRCodewords(bitMatrix, version, formatInfo) + : ReadQRCodewords(bitMatrix, version, formatInfo); } } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRBitMatrixParser.h b/core/src/qrcode/QRBitMatrixParser.h index cabb260210..0f070c8403 100644 --- a/core/src/qrcode/QRBitMatrixParser.h +++ b/core/src/qrcode/QRBitMatrixParser.h @@ -27,13 +27,13 @@ const Version* ReadVersion(const BitMatrix& bitMatrix); * @return {@link FormatInformation} encapsulating the QR Code's format info, result is invalid if both format * information locations cannot be parsed as the valid encoding of format information */ -FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool mirrored, bool isMicro); +FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro); /** * @brief Reads the codewords from the BitMatrix. * @return bytes encoded within the QR Code or empty array if the exact number of bytes expected is not read */ -ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInformation, bool mirrored); +ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo); } // QRCode } // ZXing diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 29d35f0ac3..f454b012fc 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -343,19 +343,19 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo .setStructuredAppend(structuredAppend); } -static DecoderResult DoDecode(const BitMatrix& bits, const Version& version, const std::string& hintedCharset, bool mirrored) +static DecoderResult DoDecode(const BitMatrix& bits, const Version& version, const std::string& hintedCharset) { - auto formatInfo = ReadFormatInformation(bits, mirrored, version.isMicroQRCode()); + auto formatInfo = ReadFormatInformation(bits, version.isMicroQRCode()); if (!formatInfo.isValid()) return DecodeStatus::FormatError; // Read codewords - ByteArray codewords = ReadCodewords(bits, version, formatInfo, mirrored); + ByteArray codewords = ReadCodewords(bits, version, formatInfo); if (codewords.empty()) return DecodeStatus::FormatError; // Separate into data blocks - std::vector dataBlocks = DataBlock::GetDataBlocks(codewords, version, formatInfo.errorCorrectionLevel()); + std::vector dataBlocks = DataBlock::GetDataBlocks(codewords, version, formatInfo.ecLevel); if (dataBlocks.empty()) return DecodeStatus::FormatError; @@ -378,7 +378,7 @@ static DecoderResult DoDecode(const BitMatrix& bits, const Version& version, con } // Decode the contents of that stream of bytes - return DecodeBitStream(std::move(resultBytes), version, formatInfo.errorCorrectionLevel(), hintedCharset); + return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel, hintedCharset).setIsMirrored(formatInfo.isMirrored); } DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset) @@ -387,16 +387,7 @@ DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset) if (!version) return DecodeStatus::FormatError; - auto res = DoDecode(bits, *version, hintedCharset, false); - if (res.isValid()) - return res; - - if (auto resMirrored = DoDecode(bits, *version, hintedCharset, true); resMirrored.isValid()) { - resMirrored.setIsMirrored(true); - return resMirrored; - } - - return res; + return DoDecode(bits, *version, hintedCharset); } } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 3b0ea99766..1fdaa431ce 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -432,7 +432,7 @@ DetectorResult SampleMQR(const BitMatrix& image, const ConcentricPattern& fp) if (!fi.isValid()) continue; - const int dim = Version::DimensionOfVersion(fi.microVersion(), true); + const int dim = Version::DimensionOfVersion(fi.microVersion, true); // check that we are in fact not looking at a corner of a non-micro QRCode symbol // we accept at most 1/3rd black pixels in the quite zone (in a QRCode symbol we expect about 1/2). diff --git a/core/src/qrcode/QRFormatInformation.cpp b/core/src/qrcode/QRFormatInformation.cpp index 44db397c06..7f4cba4aa8 100644 --- a/core/src/qrcode/QRFormatInformation.cpp +++ b/core/src/qrcode/QRFormatInformation.cpp @@ -87,64 +87,60 @@ static const std::array, 32> FORMAT_INFO_DECODE_LOOKUP_MICRO {0x3BBA, 0x1F}, }}; -static int FindBestFormatInfo(int mask, const std::array, 32> lookup, const std::vector& bits) +static FormatInformation FindBestFormatInfo(int mask, const std::array, 32> lookup, + const std::vector& bits) { - // Find the int in lookup with fewest bits differing - int bestDifference = 32; - int bestFormatInfo = -1; + FormatInformation fi; // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. - // TODO: test for mirrored format for (auto mask : {0, mask}) for (uint32_t bits : bits) - for (const auto& [pattern, decodedInfo] : lookup) - if (int bitsDifference = BitHacks::CountBitsSet((bits ^ mask) ^ pattern); bitsDifference < bestDifference) { - bestFormatInfo = decodedInfo; - bestDifference = bitsDifference; + for (bool mirror : {false, true}) + for (const auto& [pattern, index] : lookup) { + if (mirror) + bits = BitHacks::Reverse(bits) >> 17; + // Find the int in lookup with fewest bits differing + if (int hammingDist = BitHacks::CountBitsSet((bits ^ mask) ^ pattern); hammingDist < fi.hammingDistance) { + fi.index = index; + fi.hammingDistance = hammingDist; + fi.isMirrored = mirror; + } } - // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits - // differing means we found a match - if (bestDifference <= 3) - return bestFormatInfo; - - return -1; + return fi; } /** * @param formatInfoBits1 format info indicator, with mask still applied -* @param formatInfoBits2 second copy of same info; both are checked at the same time -* to establish best match -* @return information about the format it specifies, or {@code null} -* if doesn't seem to match any known pattern +* @param formatInfoBits2 second copy of same info; both are checked at the same time to establish best match */ FormatInformation FormatInformation::DecodeQR(uint32_t formatInfoBits1, uint32_t formatInfoBits2) { - int bestFormatInfo = FindBestFormatInfo(FORMAT_INFO_MASK_QR, FORMAT_INFO_DECODE_LOOKUP, {formatInfoBits1, formatInfoBits2}); - if (bestFormatInfo < 0) - return {}; + auto fi = FindBestFormatInfo(FORMAT_INFO_MASK_QR, FORMAT_INFO_DECODE_LOOKUP, {formatInfoBits1, formatInfoBits2}); // Use bits 3/4 for error correction, and 0-2 for mask. - return {ECLevelFromBits((bestFormatInfo >> 3) & 0x03), static_cast(bestFormatInfo & 0x07)}; + fi.ecLevel = ECLevelFromBits((fi.index >> 3) & 0x03); + fi.dataMask = static_cast(fi.index & 0x07); + + return fi; } /** * @param formatInfoBits format info indicator, with mask still applied - * @return information about the format it specifies, or {@code null} - * if doesn't seem to match any known pattern */ FormatInformation FormatInformation::DecodeMQR(uint32_t formatInfoBits) { // We don't use the additional masking (with 0x4445) to work around potentially non complying MircoQRCode encoders - int bestFormatInfo = FindBestFormatInfo(0, FORMAT_INFO_DECODE_LOOKUP_MICRO, {formatInfoBits}); - if (bestFormatInfo < 0) - return {}; + auto fi = FindBestFormatInfo(0, FORMAT_INFO_DECODE_LOOKUP_MICRO, {formatInfoBits}); constexpr uint8_t BITS_TO_VERSION[] = {1, 2, 2, 3, 3, 4, 4, 4}; // Bits 2/3/4 contain both error correction level and version, 0/1 contain mask. - return {ECLevelFromBits((bestFormatInfo >> 2) & 0x07, true), static_cast(bestFormatInfo & 0x03), - BITS_TO_VERSION[(bestFormatInfo >> 2) & 0x07]}; + fi.ecLevel = ECLevelFromBits((fi.index >> 2) & 0x07, true); + fi.dataMask = static_cast(fi.index & 0x03); + fi.microVersion = BITS_TO_VERSION[(fi.index >> 2) & 0x07]; + + return fi; } } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRFormatInformation.h b/core/src/qrcode/QRFormatInformation.h index e83775b0fc..d7d59a2d9d 100644 --- a/core/src/qrcode/QRFormatInformation.h +++ b/core/src/qrcode/QRFormatInformation.h @@ -13,42 +13,28 @@ namespace ZXing { namespace QRCode { -/** -*

Encapsulates a QR Code's format information, including the data mask used and -* error correction level.

-* -* @author Sean Owen -* @see DataMask -* @see ErrorCorrectionLevel -*/ class FormatInformation { public: + uint8_t index = 255; + uint8_t hammingDistance = 255; + bool isMirrored = false; + uint8_t dataMask = 0; + uint8_t microVersion = 0; + ErrorCorrectionLevel ecLevel = ErrorCorrectionLevel::Invalid; + FormatInformation() = default; static FormatInformation DecodeQR(uint32_t formatInfoBits1, uint32_t formatInfoBits2); static FormatInformation DecodeMQR(uint32_t formatInfoBits); - ErrorCorrectionLevel errorCorrectionLevel() const { return _errorCorrectionLevel; } - - uint8_t dataMask() const { return _dataMask; } - uint8_t microVersion() const { return _microVersion; } - - bool isValid() const { return _errorCorrectionLevel != ErrorCorrectionLevel::Invalid; } + // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match + bool isValid() const { return hammingDistance <= 3; } bool operator==(const FormatInformation& other) const { - return _dataMask == other._dataMask && _errorCorrectionLevel == other._errorCorrectionLevel; + return dataMask == other.dataMask && ecLevel == other.ecLevel; } - -private: - ErrorCorrectionLevel _errorCorrectionLevel = ErrorCorrectionLevel::Invalid; - uint8_t _dataMask = 0; - uint8_t _microVersion = 0; - - FormatInformation(const ErrorCorrectionLevel& errorCorrectionLevel, uint8_t dataMask, uint8_t microVersion = 0) - : _errorCorrectionLevel(errorCorrectionLevel), _dataMask(dataMask), _microVersion(microVersion) - {} }; } // QRCode diff --git a/test/unit/qrcode/QRBitMatrixParserTest.cpp b/test/unit/qrcode/QRBitMatrixParserTest.cpp index 2cd7a9c5cf..f8c5a93a0d 100644 --- a/test/unit/qrcode/QRBitMatrixParserTest.cpp +++ b/test/unit/qrcode/QRBitMatrixParserTest.cpp @@ -37,8 +37,8 @@ TEST(QRBitMatrixParserTest, MQRCodeM3L) const auto version = ReadVersion(bitMatrix); EXPECT_EQ(3, version->versionNumber()); - const auto format = ReadFormatInformation(bitMatrix, false, true); - const auto codewords = ReadCodewords(bitMatrix, *version, format, false); + const auto format = ReadFormatInformation(bitMatrix, true); + const auto codewords = ReadCodewords(bitMatrix, *version, format); EXPECT_EQ(17, codewords.size()); EXPECT_EQ(0x0, codewords[10]); EXPECT_EQ(0xd1, codewords[11]); @@ -65,8 +65,8 @@ TEST(QRBitMatrixParserTest, MQRCodeM3M) const auto version = ReadVersion(bitMatrix); EXPECT_EQ(3, version->versionNumber()); - const auto format = ReadFormatInformation(bitMatrix, false, true); - const auto codewords = ReadCodewords(bitMatrix, *version, format, false); + const auto format = ReadFormatInformation(bitMatrix, true); + const auto codewords = ReadCodewords(bitMatrix, *version, format); EXPECT_EQ(17, codewords.size()); EXPECT_EQ(0x0, codewords[8]); EXPECT_EQ(0x89, codewords[9]); diff --git a/test/unit/qrcode/QRFormatInformationTest.cpp b/test/unit/qrcode/QRFormatInformationTest.cpp index 959f5ae42e..0dffa5ea27 100644 --- a/test/unit/qrcode/QRFormatInformationTest.cpp +++ b/test/unit/qrcode/QRFormatInformationTest.cpp @@ -20,8 +20,8 @@ static void DoFormatInformationTest(const int formatInfo, const uint8_t expected { FormatInformation parsedFormat = FormatInformation::DecodeMQR(formatInfo); EXPECT_TRUE(parsedFormat.isValid()); - EXPECT_EQ(expectedMask, parsedFormat.dataMask()); - EXPECT_EQ(expectedECL, parsedFormat.errorCorrectionLevel()); + EXPECT_EQ(expectedMask, parsedFormat.dataMask); + EXPECT_EQ(expectedECL, parsedFormat.ecLevel); } TEST(QRFormatInformationTest, Decode) @@ -29,8 +29,8 @@ TEST(QRFormatInformationTest, Decode) // Normal case FormatInformation expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO); EXPECT_TRUE(expected.isValid()); - EXPECT_EQ(0x07, expected.dataMask()); - EXPECT_EQ(ErrorCorrectionLevel::Quality, expected.errorCorrectionLevel()); + EXPECT_EQ(0x07, expected.dataMask); + EXPECT_EQ(ErrorCorrectionLevel::Quality, expected.ecLevel); // where the code forgot the mask! EXPECT_EQ(expected, FormatInformation::DecodeQR(UNMASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO)); } From dd14a3b5ee725492de9f779829952dc5757f0e9f Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 18 Jun 2022 16:01:52 +0200 Subject: [PATCH 092/180] MicoQRCode: choose orientation with the lowest hamming distance FormatInfo This fixes #344. --- core/src/PerspectiveTransform.h | 1 + core/src/qrcode/QRDetector.cpp | 36 +++++++++++++++++++-------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/core/src/PerspectiveTransform.h b/core/src/PerspectiveTransform.h index 0e99c3c101..c2f5043e18 100644 --- a/core/src/PerspectiveTransform.h +++ b/core/src/PerspectiveTransform.h @@ -34,6 +34,7 @@ class PerspectiveTransform static PerspectiveTransform UnitSquareTo(const QuadrilateralF& q); public: + PerspectiveTransform() = default; PerspectiveTransform(const QuadrilateralF& src, const QuadrilateralF& dst); /// Project from the destination space (grid of modules) into the image space (bit matrix) diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 1fdaa431ce..5fdad20b89 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -412,6 +412,9 @@ DetectorResult SampleMQR(const BitMatrix& image, const ConcentricPattern& fp) constexpr PointI FORMAT_INFO_COORDS[] = {{0, 8}, {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {6, 8}, {7, 8}, {8, 8}, {8, 7}, {8, 6}, {8, 5}, {8, 4}, {8, 3}, {8, 2}, {8, 1}, {8, 0}}; + FormatInformation bestFI; + PerspectiveTransform bestPT; + for (int i = 0; i < 4; ++i) { auto mod2Pix = PerspectiveTransform(srcQuad, RotatedCorners(*fpQuad, i)); @@ -429,26 +432,29 @@ DetectorResult SampleMQR(const BitMatrix& image, const ConcentricPattern& fp) AppendBit(formatInfoBits, image.get(mod2Pix(centered(FORMAT_INFO_COORDS[i])))); auto fi = FormatInformation::DecodeMQR(formatInfoBits); - if (!fi.isValid()) - continue; + if (fi.hammingDistance < bestFI.hammingDistance) { + bestFI = fi; + bestPT = mod2Pix; + } + } - const int dim = Version::DimensionOfVersion(fi.microVersion, true); + if (!bestFI.isValid()) + return {}; - // check that we are in fact not looking at a corner of a non-micro QRCode symbol - // we accept at most 1/3rd black pixels in the quite zone (in a QRCode symbol we expect about 1/2). - int blackPixels = 0; - for (int i = 0; i < dim; ++i) { - auto px = mod2Pix(centered(PointI{i, dim})); - auto py = mod2Pix(centered(PointI{dim, i})); - blackPixels += (image.isIn(px) && image.get(px)) + (image.isIn(py) && image.get(py)); - } - if (blackPixels > 2 * dim / 3) - continue; + const int dim = Version::DimensionOfVersion(bestFI.microVersion, true); - return SampleGrid(image, dim, dim, mod2Pix); + // check that we are in fact not looking at a corner of a non-micro QRCode symbol + // we accept at most 1/3rd black pixels in the quite zone (in a QRCode symbol we expect about 1/2). + int blackPixels = 0; + for (int i = 0; i < dim; ++i) { + auto px = bestPT(centered(PointI{i, dim})); + auto py = bestPT(centered(PointI{dim, i})); + blackPixels += (image.isIn(px) && image.get(px)) + (image.isIn(py) && image.get(py)); } + if (blackPixels > 2 * dim / 3) + return {}; - return {}; + return SampleGrid(image, dim, dim, bestPT); } } // namespace ZXing::QRCode From 3093ee5bff1f4523a38ced56049f6952b1978de8 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 19 Jun 2022 01:08:54 +0200 Subject: [PATCH 093/180] Result: compare bytes instead of text in opoerator== (performance) --- core/src/Result.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 72c995e0f5..3e0f62e38a 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -102,7 +102,7 @@ std::string Result::sequenceId() const bool Result::operator==(const Result& o) const { - if (format() != o.format() || text() != o.text()) + if (format() != o.format() || bytes() != o.bytes()) return false; if (BarcodeFormats(BarcodeFormat::TwoDCodes).testFlag(format())) From 18cd69a628bd58962091fa0e6d5d842dbb663955 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 19 Jun 2022 01:14:32 +0200 Subject: [PATCH 094/180] Result: rename utf8Protocol -> utf8ECI --- core/src/Content.cpp | 2 +- core/src/Content.h | 2 +- core/src/Result.cpp | 4 ++-- core/src/Result.h | 2 +- example/ZXingReader.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 21fde3c976..f5b4e1a852 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -108,7 +108,7 @@ std::wstring Content::text() const return wstr; } -std::string Content::utf8Protocol() const +std::string Content::utf8ECI() const { if (empty() || !canProcess()) return {}; diff --git a/core/src/Content.h b/core/src/Content.h index 5d1f982069..756d8b72fe 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -71,7 +71,7 @@ class Content bool canProcess() const; std::wstring text() const; - std::string utf8Protocol() const; + std::string utf8ECI() const; ByteArray bytesECI() const; CharacterSet guessEncoding() const; ContentType type() const; diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 3e0f62e38a..15399dea0a 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -59,9 +59,9 @@ ByteArray Result::bytesECI() const return _content.bytesECI(); } -std::string Result::utf8Protocol() const +std::string Result::utf8ECI() const { - return _content.utf8Protocol(); + return _content.utf8ECI(); } ContentType Result::contentType() const diff --git a/core/src/Result.h b/core/src/Result.h index 6a8ae00000..0bad7c7f03 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -48,7 +48,7 @@ class Result // WARNING: this is an experimental API and may change/disappear const ByteArray& bytes() const; ByteArray bytesECI() const; - std::string utf8Protocol() const; + std::string utf8ECI() const; ContentType contentType() const; bool hasECI() const; // END WARNING diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 56f6759491..d8bf8fbd99 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -192,7 +192,7 @@ int main(int argc, char* argv[]) } std::cout << "Text: \"" << ToUtf8(result.text(), angleEscape) << "\"\n" << "Bytes: \"" << ToHex(result.bytes()) << "\"\n" - << "TextECI: \"" << result.utf8Protocol() << "\"\n" + << "TextECI: \"" << result.utf8ECI() << "\"\n" << "BytesECI: \"" << ToHex(result.bytesECI()) << "\"\n" << "Format: " << ToString(result.format()) << "\n" << "Identifier: " << result.symbologyIdentifier() << "\n" From 1f7d12b6fd5c7ad61ee1252c12d6ab96c1580f22 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 19 Jun 2022 23:14:21 +0200 Subject: [PATCH 095/180] GTIN: parse the `bytes()` instead of `text()` in `EanAddOn()` --- core/src/GTIN.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/GTIN.cpp b/core/src/GTIN.cpp index 4231597aa8..252393dcbb 100644 --- a/core/src/GTIN.cpp +++ b/core/src/GTIN.cpp @@ -205,9 +205,9 @@ std::string EanAddOn(const Result& result) if (!(BarcodeFormat::EAN13 | BarcodeFormat::UPCA | BarcodeFormat::UPCE | BarcodeFormat::EAN8) .testFlag(result.format())) return {}; - auto txt = result.text(); - auto pos = txt.find(L' '); - return pos != std::wstring::npos ? TextUtfEncoding::ToUtf8(txt.substr(pos + 1)) : std::string(); + auto txt = result.bytes().asString(); + auto pos = txt.find(' '); + return pos != std::string::npos ? std::string(txt.substr(pos + 1)) : std::string(); } std::string IssueNr(const std::string& ean2AddOn) From 4b0a575e4d4ae71b5771c9214785b9f43f3afb41 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 19 Jun 2022 23:27:52 +0200 Subject: [PATCH 096/180] example: don't double quote the `bytes` result in ZXingReader output --- example/ZXingReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index d8bf8fbd99..206c400bcb 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -191,9 +191,9 @@ int main(int argc, char* argv[]) firstFile = false; } std::cout << "Text: \"" << ToUtf8(result.text(), angleEscape) << "\"\n" - << "Bytes: \"" << ToHex(result.bytes()) << "\"\n" + << "Bytes: " << ToHex(result.bytes()) << "\n" << "TextECI: \"" << result.utf8ECI() << "\"\n" - << "BytesECI: \"" << ToHex(result.bytesECI()) << "\"\n" + << "BytesECI: " << ToHex(result.bytesECI()) << "\n" << "Format: " << ToString(result.format()) << "\n" << "Identifier: " << result.symbologyIdentifier() << "\n" << "Content: " << ToString(result.contentType()) << "\n" From e37b857aa37b667931a0aa0297eafdddb5f84e29 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 20 Jun 2022 09:04:11 +0200 Subject: [PATCH 097/180] example: use new Result::utf8() --- example/ZXingReader.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 206c400bcb..64f91e1701 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -111,6 +111,11 @@ void drawRect(const ImageView& image, const Position& pos) drawLine(image, pos[i], pos[(i + 1) % 4]); } +std::string escapeNonGraphical(const std::string& str) +{ + return ToUtf8(FromUtf8(str), true); +} + int main(int argc, char* argv[]) { DecodeHints hints; @@ -130,9 +135,6 @@ int main(int argc, char* argv[]) hints.setEanAddOnSymbol(EanAddOnSymbol::Read); - if (oneLine) - angleEscape = true; - if (angleEscape) std::setlocale(LC_CTYPE, "en_US.UTF-8"); // Needed so `std::iswgraph()` in `ToUtf8(angleEscape)` does not 'swallow' all printable non-ascii utf8 chars @@ -175,7 +177,7 @@ int main(int argc, char* argv[]) if (oneLine) { std::cout << filePath << " " << ToString(result.format()); if (result.isValid()) - std::cout << " \"" << ToUtf8(result.text(), angleEscape) << "\""; + std::cout << " \"" << escapeNonGraphical(result.utf8()) << "\""; else if (result.format() != BarcodeFormat::None) std::cout << " " << ToString(result.status()); std::cout << "\n"; @@ -190,9 +192,9 @@ int main(int argc, char* argv[]) std::cout << "File: " << filePath << "\n"; firstFile = false; } - std::cout << "Text: \"" << ToUtf8(result.text(), angleEscape) << "\"\n" - << "Bytes: " << ToHex(result.bytes()) << "\n" + std::cout << "Text: \"" << (angleEscape ? escapeNonGraphical(result.utf8()) : result.utf8()) << "\"\n" << "TextECI: \"" << result.utf8ECI() << "\"\n" + << "Bytes: " << ToHex(result.bytes()) << "\n" << "BytesECI: " << ToHex(result.bytesECI()) << "\n" << "Format: " << ToString(result.format()) << "\n" << "Identifier: " << result.symbologyIdentifier() << "\n" From 95d6a540ed95ca07c0a19ef2906d210785d46210 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 20 Jun 2022 09:06:37 +0200 Subject: [PATCH 098/180] Result: add new `utf8` property (forgot in last commit) --- core/src/Result.cpp | 6 ++++++ core/src/Result.h | 1 + 2 files changed, 7 insertions(+) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 15399dea0a..fc1a86656a 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -8,6 +8,7 @@ #include "DecoderResult.h" #include "TextDecoder.h" +#include "TextUtfEncoding.h" #include #include @@ -59,6 +60,11 @@ ByteArray Result::bytesECI() const return _content.bytesECI(); } +std::string Result::utf8() const +{ + return TextUtfEncoding::ToUtf8(_content.text()); +} + std::string Result::utf8ECI() const { return _content.utf8ECI(); diff --git a/core/src/Result.h b/core/src/Result.h index 0bad7c7f03..24238bcee8 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -48,6 +48,7 @@ class Result // WARNING: this is an experimental API and may change/disappear const ByteArray& bytes() const; ByteArray bytesECI() const; + std::string utf8() const; std::string utf8ECI() const; ContentType contentType() const; bool hasECI() const; From 1c5af714b3c36cc1f05184a5b03bed80e8260b68 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 20 Jun 2022 10:00:04 +0200 Subject: [PATCH 099/180] Writers: add utf8 overload for `encode()` functions To prevent a blatant inconsistency between writing and reading with regards to utf8 vs utf16 input/output... --- core/src/MultiFormatWriter.cpp | 6 ++++++ core/src/MultiFormatWriter.h | 1 + core/src/aztec/AZWriter.cpp | 6 ++++++ core/src/aztec/AZWriter.h | 1 + core/src/datamatrix/DMWriter.cpp | 6 ++++++ core/src/datamatrix/DMWriter.h | 1 + core/src/oned/ODCodabarWriter.cpp | 6 ++++++ core/src/oned/ODCodabarWriter.h | 1 + core/src/oned/ODCode128Writer.cpp | 6 ++++++ core/src/oned/ODCode128Writer.h | 1 + core/src/oned/ODCode39Writer.cpp | 6 ++++++ core/src/oned/ODCode39Writer.h | 1 + core/src/oned/ODCode93Writer.cpp | 6 ++++++ core/src/oned/ODCode93Writer.h | 1 + core/src/oned/ODEAN13Writer.cpp | 6 ++++++ core/src/oned/ODEAN13Writer.h | 1 + core/src/oned/ODEAN8Writer.cpp | 6 ++++++ core/src/oned/ODEAN8Writer.h | 1 + core/src/oned/ODITFWriter.cpp | 6 ++++++ core/src/oned/ODITFWriter.h | 1 + core/src/oned/ODUPCAWriter.cpp | 6 ++++++ core/src/oned/ODUPCAWriter.h | 1 + core/src/oned/ODUPCEWriter.cpp | 6 ++++++ core/src/oned/ODUPCEWriter.h | 1 + core/src/pdf417/PDFWriter.cpp | 6 ++++++ core/src/pdf417/PDFWriter.h | 1 + core/src/qrcode/QRWriter.cpp | 6 ++++++ core/src/qrcode/QRWriter.h | 1 + test/blackbox/TestWriterMain.cpp | 4 ++-- test/unit/oned/ODCodaBarWriterTest.cpp | 17 +++++++++-------- test/unit/oned/ODCode128WriterTest.cpp | 16 ++++++++-------- test/unit/oned/ODCode39ExtendedModeTest.cpp | 12 ++++++------ test/unit/oned/ODEAN13WriterTest.cpp | 8 ++++---- test/unit/oned/ODEAN8WriterTest.cpp | 8 ++++---- test/unit/oned/ODUPCAWriterTest.cpp | 6 +++--- test/unit/oned/ODUPCEWriterTest.cpp | 10 +++++----- 36 files changed, 139 insertions(+), 40 deletions(-) diff --git a/core/src/MultiFormatWriter.cpp b/core/src/MultiFormatWriter.cpp index 464dbc8054..5a03a6a4b3 100644 --- a/core/src/MultiFormatWriter.cpp +++ b/core/src/MultiFormatWriter.cpp @@ -20,6 +20,7 @@ #include "pdf417/PDFWriter.h" #include "qrcode/QRErrorCorrectionLevel.h" #include "qrcode/QRWriter.h" +#include "TextUtfEncoding.h" #include @@ -66,4 +67,9 @@ MultiFormatWriter::encode(const std::wstring& contents, int width, int height) c } } +BitMatrix MultiFormatWriter::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // ZXing diff --git a/core/src/MultiFormatWriter.h b/core/src/MultiFormatWriter.h index 88b1c61591..f63ca03edb 100644 --- a/core/src/MultiFormatWriter.h +++ b/core/src/MultiFormatWriter.h @@ -50,6 +50,7 @@ class MultiFormatWriter } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: BarcodeFormat _format; diff --git a/core/src/aztec/AZWriter.cpp b/core/src/aztec/AZWriter.cpp index b41a15a62a..6e6b0ad0d7 100644 --- a/core/src/aztec/AZWriter.cpp +++ b/core/src/aztec/AZWriter.cpp @@ -9,6 +9,7 @@ #include "AZEncoder.h" #include "CharacterSet.h" #include "TextEncoder.h" +#include "TextUtfEncoding.h" #include @@ -29,4 +30,9 @@ Writer::encode(const std::wstring& contents, int width, int height) const return Inflate(std::move(aztec.matrix), width, height, _margin); } +BitMatrix Writer::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::Aztec diff --git a/core/src/aztec/AZWriter.h b/core/src/aztec/AZWriter.h index 1a41f6267e..aa80d46a30 100644 --- a/core/src/aztec/AZWriter.h +++ b/core/src/aztec/AZWriter.h @@ -41,6 +41,7 @@ class Writer } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: CharacterSet _encoding; diff --git a/core/src/datamatrix/DMWriter.cpp b/core/src/datamatrix/DMWriter.cpp index a95f5ad8a2..8c8f7bd3d3 100644 --- a/core/src/datamatrix/DMWriter.cpp +++ b/core/src/datamatrix/DMWriter.cpp @@ -13,6 +13,7 @@ #include "DMHighLevelEncoder.h" #include "DMSymbolInfo.h" #include "DMSymbolShape.h" +#include "TextUtfEncoding.h" #include #include @@ -110,4 +111,9 @@ Writer::encode(const std::wstring& contents, int width, int height) const return Inflate(std::move(result), width, height, _quietZone); } +BitMatrix Writer::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::DataMatrix diff --git a/core/src/datamatrix/DMWriter.h b/core/src/datamatrix/DMWriter.h index 61b0caba2a..f126e6eb35 100644 --- a/core/src/datamatrix/DMWriter.h +++ b/core/src/datamatrix/DMWriter.h @@ -44,6 +44,7 @@ class Writer } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: SymbolShape _shapeHint; diff --git a/core/src/oned/ODCodabarWriter.cpp b/core/src/oned/ODCodabarWriter.cpp index 1d74711299..25c42edbfc 100644 --- a/core/src/oned/ODCodabarWriter.cpp +++ b/core/src/oned/ODCodabarWriter.cpp @@ -7,6 +7,7 @@ #include "ODCodabarWriter.h" #include "ODWriterHelper.h" +#include "TextUtfEncoding.h" #include "ZXContainerAlgorithms.h" #include @@ -126,4 +127,9 @@ CodabarWriter::encode(const std::wstring& contents_, int width, int height) cons return WriterHelper::RenderResult(result, width, height, _sidesMargin >= 0 ? _sidesMargin : 10); } +BitMatrix CodabarWriter::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::OneD diff --git a/core/src/oned/ODCodabarWriter.h b/core/src/oned/ODCodabarWriter.h index 7304d49efc..10914313a7 100644 --- a/core/src/oned/ODCodabarWriter.h +++ b/core/src/oned/ODCodabarWriter.h @@ -24,6 +24,7 @@ class CodabarWriter public: CodabarWriter& setMargin(int sidesMargin) { _sidesMargin = sidesMargin; return *this; } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _sidesMargin = -1; diff --git a/core/src/oned/ODCode128Writer.cpp b/core/src/oned/ODCode128Writer.cpp index 2325376d5e..b737af2fca 100644 --- a/core/src/oned/ODCode128Writer.cpp +++ b/core/src/oned/ODCode128Writer.cpp @@ -8,6 +8,7 @@ #include "ODCode128Patterns.h" #include "ODWriterHelper.h" +#include "TextUtfEncoding.h" #include #include @@ -252,4 +253,9 @@ Code128Writer::encode(const std::wstring& contents, int width, int height) const return WriterHelper::RenderResult(result, width, height, _sidesMargin >= 0 ? _sidesMargin : 10); } +BitMatrix Code128Writer::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::OneD diff --git a/core/src/oned/ODCode128Writer.h b/core/src/oned/ODCode128Writer.h index 5e517a66a6..386bf5cefc 100644 --- a/core/src/oned/ODCode128Writer.h +++ b/core/src/oned/ODCode128Writer.h @@ -24,6 +24,7 @@ class Code128Writer public: Code128Writer& setMargin(int sidesMargin) { _sidesMargin = sidesMargin; return *this; } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _sidesMargin = -1; diff --git a/core/src/oned/ODCode39Writer.cpp b/core/src/oned/ODCode39Writer.cpp index e05faa661f..d6732cd801 100644 --- a/core/src/oned/ODCode39Writer.cpp +++ b/core/src/oned/ODCode39Writer.cpp @@ -9,6 +9,7 @@ #include "CharacterSet.h" #include "ODWriterHelper.h" #include "TextEncoder.h" +#include "TextUtfEncoding.h" #include "ZXContainerAlgorithms.h" #include @@ -167,4 +168,9 @@ Code39Writer::encode(const std::wstring& contents, int width, int height) const return WriterHelper::RenderResult(result, width, height, _sidesMargin >= 0 ? _sidesMargin : 10); } +BitMatrix Code39Writer::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::OneD diff --git a/core/src/oned/ODCode39Writer.h b/core/src/oned/ODCode39Writer.h index 3342490301..20194b572f 100644 --- a/core/src/oned/ODCode39Writer.h +++ b/core/src/oned/ODCode39Writer.h @@ -24,6 +24,7 @@ class Code39Writer public: Code39Writer& setMargin(int sidesMargin) { _sidesMargin = sidesMargin; return *this; } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _sidesMargin = -1; diff --git a/core/src/oned/ODCode93Writer.cpp b/core/src/oned/ODCode93Writer.cpp index f84693ffce..fb2c26157c 100644 --- a/core/src/oned/ODCode93Writer.cpp +++ b/core/src/oned/ODCode93Writer.cpp @@ -7,6 +7,7 @@ #include "ODCode93Writer.h" #include "ODWriterHelper.h" +#include "TextUtfEncoding.h" #include "ZXContainerAlgorithms.h" #include "ZXTestSupport.h" @@ -183,4 +184,9 @@ Code93Writer::encode(const std::wstring& contents_, int width, int height) const return WriterHelper::RenderResult(result, width, height, _sidesMargin >= 0 ? _sidesMargin : 10); } +BitMatrix Code93Writer::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::OneD diff --git a/core/src/oned/ODCode93Writer.h b/core/src/oned/ODCode93Writer.h index e33f4f512b..40654c8ccf 100644 --- a/core/src/oned/ODCode93Writer.h +++ b/core/src/oned/ODCode93Writer.h @@ -22,6 +22,7 @@ class Code93Writer public: Code93Writer& setMargin(int sidesMargin) { _sidesMargin = sidesMargin; return *this; } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _sidesMargin = -1; diff --git a/core/src/oned/ODEAN13Writer.cpp b/core/src/oned/ODEAN13Writer.cpp index 156ac51999..cf99be12cc 100644 --- a/core/src/oned/ODEAN13Writer.cpp +++ b/core/src/oned/ODEAN13Writer.cpp @@ -8,6 +8,7 @@ #include "ODUPCEANCommon.h" #include "ODWriterHelper.h" +#include "TextUtfEncoding.h" #include #include @@ -53,4 +54,9 @@ EAN13Writer::encode(const std::wstring& contents, int width, int height) const return WriterHelper::RenderResult(result, width, height, _sidesMargin >= 0 ? _sidesMargin : 9); } +BitMatrix EAN13Writer::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::OneD diff --git a/core/src/oned/ODEAN13Writer.h b/core/src/oned/ODEAN13Writer.h index bfed737bc1..c9086941d6 100644 --- a/core/src/oned/ODEAN13Writer.h +++ b/core/src/oned/ODEAN13Writer.h @@ -24,6 +24,7 @@ class EAN13Writer public: EAN13Writer& setMargin(int sidesMargin) { _sidesMargin = sidesMargin; return *this; } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _sidesMargin = -1; diff --git a/core/src/oned/ODEAN8Writer.cpp b/core/src/oned/ODEAN8Writer.cpp index f659a1cb18..652c26f719 100644 --- a/core/src/oned/ODEAN8Writer.cpp +++ b/core/src/oned/ODEAN8Writer.cpp @@ -8,6 +8,7 @@ #include "ODUPCEANCommon.h" #include "ODWriterHelper.h" +#include "TextUtfEncoding.h" #include @@ -42,4 +43,9 @@ EAN8Writer::encode(const std::wstring& contents, int width, int height) const return WriterHelper::RenderResult(result, width, height, _sidesMargin >= 0 ? _sidesMargin : 9); } +BitMatrix EAN8Writer::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::OneD diff --git a/core/src/oned/ODEAN8Writer.h b/core/src/oned/ODEAN8Writer.h index 2bbabd265a..8c42ea969c 100644 --- a/core/src/oned/ODEAN8Writer.h +++ b/core/src/oned/ODEAN8Writer.h @@ -24,6 +24,7 @@ class EAN8Writer public: EAN8Writer& setMargin(int sidesMargin) { _sidesMargin = sidesMargin; return *this; } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _sidesMargin = -1; diff --git a/core/src/oned/ODITFWriter.cpp b/core/src/oned/ODITFWriter.cpp index 27b8f58e58..1f3e6f611d 100644 --- a/core/src/oned/ODITFWriter.cpp +++ b/core/src/oned/ODITFWriter.cpp @@ -7,6 +7,7 @@ #include "ODITFWriter.h" #include "ODWriterHelper.h" +#include "TextUtfEncoding.h" #include #include @@ -69,4 +70,9 @@ ITFWriter::encode(const std::wstring& contents, int width, int height) const return WriterHelper::RenderResult(result, width, height, _sidesMargin >= 0 ? _sidesMargin : 10); } +BitMatrix ITFWriter::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::OneD diff --git a/core/src/oned/ODITFWriter.h b/core/src/oned/ODITFWriter.h index de837dd3a5..f4cab9bb39 100644 --- a/core/src/oned/ODITFWriter.h +++ b/core/src/oned/ODITFWriter.h @@ -24,6 +24,7 @@ class ITFWriter public: ITFWriter& setMargin(int sidesMargin) { _sidesMargin = sidesMargin; return *this; } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _sidesMargin = -1; diff --git a/core/src/oned/ODUPCAWriter.cpp b/core/src/oned/ODUPCAWriter.cpp index 0a789b6e95..16574022fb 100644 --- a/core/src/oned/ODUPCAWriter.cpp +++ b/core/src/oned/ODUPCAWriter.cpp @@ -8,6 +8,7 @@ #include "BitMatrix.h" #include "ODEAN13Writer.h" +#include "TextUtfEncoding.h" #include @@ -24,4 +25,9 @@ UPCAWriter::encode(const std::wstring& contents, int width, int height) const return EAN13Writer().setMargin(_sidesMargin).encode(L'0' + contents, width, height); } +BitMatrix UPCAWriter::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::OneD diff --git a/core/src/oned/ODUPCAWriter.h b/core/src/oned/ODUPCAWriter.h index 467fbf0682..22dacdac20 100644 --- a/core/src/oned/ODUPCAWriter.h +++ b/core/src/oned/ODUPCAWriter.h @@ -24,6 +24,7 @@ class UPCAWriter public: UPCAWriter& setMargin(int sidesMargin) { _sidesMargin = sidesMargin; return *this; } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _sidesMargin = -1; diff --git a/core/src/oned/ODUPCEWriter.cpp b/core/src/oned/ODUPCEWriter.cpp index 5adf99cc4f..ea23e32290 100644 --- a/core/src/oned/ODUPCEWriter.cpp +++ b/core/src/oned/ODUPCEWriter.cpp @@ -8,6 +8,7 @@ #include "ODUPCEANCommon.h" #include "ODWriterHelper.h" +#include "TextUtfEncoding.h" #include #include @@ -47,4 +48,9 @@ UPCEWriter::encode(const std::wstring& contents, int width, int height) const return WriterHelper::RenderResult(result, width, height, _sidesMargin >= 0 ? _sidesMargin : 9); } +BitMatrix UPCEWriter::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::OneD diff --git a/core/src/oned/ODUPCEWriter.h b/core/src/oned/ODUPCEWriter.h index 6e48975970..90fd7b0b8c 100644 --- a/core/src/oned/ODUPCEWriter.h +++ b/core/src/oned/ODUPCEWriter.h @@ -24,6 +24,7 @@ class UPCEWriter public: UPCEWriter& setMargin(int sidesMargin) { _sidesMargin = sidesMargin; return *this; } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _sidesMargin = -1; diff --git a/core/src/pdf417/PDFWriter.cpp b/core/src/pdf417/PDFWriter.cpp index 19543119d6..400208eb50 100644 --- a/core/src/pdf417/PDFWriter.cpp +++ b/core/src/pdf417/PDFWriter.cpp @@ -7,6 +7,7 @@ #include "PDFWriter.h" #include "PDFEncoder.h" #include "BitMatrix.h" +#include "TextUtfEncoding.h" #include @@ -111,6 +112,11 @@ Writer::encode(const std::wstring& contents, int width, int height) const } } +BitMatrix Writer::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + Writer::Writer() { _encoder.reset(new Encoder); diff --git a/core/src/pdf417/PDFWriter.h b/core/src/pdf417/PDFWriter.h index a9e01c943b..b9bf3c3606 100644 --- a/core/src/pdf417/PDFWriter.h +++ b/core/src/pdf417/PDFWriter.h @@ -60,6 +60,7 @@ class Writer BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _margin = -1; diff --git a/core/src/qrcode/QRWriter.cpp b/core/src/qrcode/QRWriter.cpp index 63a161150e..8f0597cab3 100644 --- a/core/src/qrcode/QRWriter.cpp +++ b/core/src/qrcode/QRWriter.cpp @@ -11,6 +11,7 @@ #include "QREncodeResult.h" #include "QREncoder.h" #include "QRErrorCorrectionLevel.h" +#include "TextUtfEncoding.h" #include #include @@ -42,4 +43,9 @@ BitMatrix Writer::encode(const std::wstring& contents, int width, int height) co return Inflate(std::move(code.matrix), width, height, _margin); } +BitMatrix Writer::encode(const std::string& contents, int width, int height) const +{ + return encode(TextUtfEncoding::FromUtf8(contents), width, height); +} + } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRWriter.h b/core/src/qrcode/QRWriter.h index 5aef3b70b8..d088c8bc20 100644 --- a/core/src/qrcode/QRWriter.h +++ b/core/src/qrcode/QRWriter.h @@ -58,6 +58,7 @@ class Writer } BitMatrix encode(const std::wstring& contents, int width, int height) const; + BitMatrix encode(const std::string& contents, int width, int height) const; private: int _margin; diff --git a/test/blackbox/TestWriterMain.cpp b/test/blackbox/TestWriterMain.cpp index cb8e728198..420f0bf6ca 100644 --- a/test/blackbox/TestWriterMain.cpp +++ b/test/blackbox/TestWriterMain.cpp @@ -23,7 +23,7 @@ void savePng(const BitMatrix& matrix, BarcodeFormat format) int main() { - std::wstring text = L"http://www.google.com/"; + std::string text = "http://www.google.com/"; for (auto format : { BarcodeFormat::Aztec, BarcodeFormat::DataMatrix, @@ -33,7 +33,7 @@ int main() savePng(MultiFormatWriter(format).encode(text, 200, 200), format); } - text = L"012345678901234567890123456789"; + text = "012345678901234567890123456789"; using FormatSpecs = std::vector>; for (const auto& [format, length] : FormatSpecs({ {BarcodeFormat::Codabar, 0}, diff --git a/test/unit/oned/ODCodaBarWriterTest.cpp b/test/unit/oned/ODCodaBarWriterTest.cpp index f4071c33b7..f8662491fa 100644 --- a/test/unit/oned/ODCodaBarWriterTest.cpp +++ b/test/unit/oned/ODCodaBarWriterTest.cpp @@ -10,6 +10,7 @@ #include "DecodeHints.h" #include "Result.h" #include "oned/ODCodabarReader.h" +#include "TextUtfEncoding.h" #include "gtest/gtest.h" #include @@ -18,7 +19,7 @@ using namespace ZXing; using namespace ZXing::OneD; namespace { - std::string Encode(const std::wstring& input) + std::string Encode(const std::string& input) { auto result = ToString(CodabarWriter().encode(input, 0, 0), '1', '0', false); return result.substr(0, result.size() - 1); // remove the \n at the end @@ -27,7 +28,7 @@ namespace { TEST(ODCodaBarWriterTest, Encode) { - EXPECT_EQ(Encode(L"B515-3/B"), + EXPECT_EQ(Encode("B515-3/B"), "00000" "1001001011" "0110101001" "0101011001" "0110101001" "0101001101" "0110010101" "01101101011" "01001001011" @@ -36,7 +37,7 @@ TEST(ODCodaBarWriterTest, Encode) TEST(ODCodaBarWriterTest, Encode2) { - EXPECT_EQ(Encode(L"T123T"), + EXPECT_EQ(Encode("T123T"), "00000" "1011001001" "0101011001" "0101001011" "0110010101" "01011001001" "00000"); @@ -44,20 +45,20 @@ TEST(ODCodaBarWriterTest, Encode2) TEST(ODCodaBarWriterTest, AltStartEnd) { - EXPECT_EQ(Encode(L"T123456789-$T"), Encode(L"A123456789-$A")); + EXPECT_EQ(Encode("T123456789-$T"), Encode("A123456789-$A")); } TEST(ODCodaBarWriterTest, FullCircle) { - std::wstring text = L"A0123456789-$:/.+A"; + std::string text = "A0123456789-$:/.+A"; BitArray row; CodabarWriter().encode(text, 0, 0).getRow(0, row); Result res = CodabarReader(DecodeHints().setReturnCodabarStartEnd(true)).decodeSingleRow(0, row); - EXPECT_EQ(text, res.text()); + EXPECT_EQ(text, res.utf8()); } TEST(ODCodaBarWriterTest, InvalidChars) { - EXPECT_THROW({Encode(L"AxA");}, std::invalid_argument ); - EXPECT_THROW({Encode(L"a0a");}, std::invalid_argument ); + EXPECT_THROW({Encode("AxA");}, std::invalid_argument ); + EXPECT_THROW({Encode("a0a");}, std::invalid_argument ); } diff --git a/test/unit/oned/ODCode128WriterTest.cpp b/test/unit/oned/ODCode128WriterTest.cpp index 010854357f..0d25f605d4 100644 --- a/test/unit/oned/ODCode128WriterTest.cpp +++ b/test/unit/oned/ODCode128WriterTest.cpp @@ -92,11 +92,11 @@ TEST(ODCode128Writer, EncodeWithFncsAndNumberInCodesetA) TEST(ODCode128Writer, RoundtripGS1) { auto toEncode = L"\xf1" "10958" "\xf1" "17160526"; - auto expected = L"10958\u001D17160526"; + auto expected = "10958\u001D17160526"; auto encResult = Code128Writer().encode(toEncode, 0, 0); auto decResult = Decode(encResult); - auto actual = decResult.text(); + auto actual = decResult.utf8(); EXPECT_EQ(actual, expected); EXPECT_EQ(decResult.symbologyIdentifier(), "]C1"); } @@ -104,11 +104,11 @@ TEST(ODCode128Writer, RoundtripGS1) TEST(ODCode128Writer, RoundtripFNC1) { auto toEncode = L"1\xf1" "0958" "\xf1" "17160526"; - auto expected = L"1\u001D0958\u001D17160526"; + auto expected = "1\u001D0958\u001D17160526"; auto encResult = Code128Writer().encode(toEncode, 0, 0); auto decResult = Decode(encResult); - auto actual = decResult.text(); + auto actual = decResult.utf8(); EXPECT_EQ(actual, expected); EXPECT_EQ(decResult.symbologyIdentifier(), "]C0"); } @@ -116,7 +116,7 @@ TEST(ODCode128Writer, RoundtripFNC1) TEST(ODCode128Writer, EncodeSwitchCodesetFromAToB) { // start with A switch to B and back to A - auto toEncode = std::wstring(L"\0ABab\u0010", 6); + auto toEncode = std::string("\0ABab\u0010", 6); // "\0" "A" "B" Switch to B "a" "b" Switch to A "\u0010" check digit auto expected = QUIET_SPACE + START_CODE_A + "10100001100" + "10100011000" + "10001011000" + SWITCH_CODE_B + "10010110000" + "10010000110" + SWITCH_CODE_A + "10100111100" + "11001110100" + STOP + QUIET_SPACE; @@ -124,14 +124,14 @@ TEST(ODCode128Writer, EncodeSwitchCodesetFromAToB) auto actual = LineMatrixToString(encoded); EXPECT_EQ(actual, expected); - auto actualRoundTrip = Decode(encoded).text(); + auto actualRoundTrip = Decode(encoded).utf8(); EXPECT_EQ(actualRoundTrip, toEncode); } TEST(ODCode128Writer, EncodeSwitchCodesetFromBToA) { // start with B switch to A and back to B - auto toEncode = std::wstring(L"ab\0ab", 5); + auto toEncode = std::string("ab\0ab", 5); // "a" "b" Switch to A "\0 "Switch to B" "a" "b" check digit auto expected = QUIET_SPACE + START_CODE_B + "10010110000" + "10010000110" + SWITCH_CODE_A + "10100001100" + SWITCH_CODE_B + "10010110000" + "10010000110" + "11010001110" + STOP + QUIET_SPACE; @@ -139,6 +139,6 @@ TEST(ODCode128Writer, EncodeSwitchCodesetFromBToA) auto actual = LineMatrixToString(encoded); EXPECT_EQ(actual, expected); - auto actualRoundTrip = Decode(encoded).text(); + auto actualRoundTrip = Decode(encoded).utf8(); EXPECT_EQ(actualRoundTrip, toEncode); } diff --git a/test/unit/oned/ODCode39ExtendedModeTest.cpp b/test/unit/oned/ODCode39ExtendedModeTest.cpp index 0c256971a4..ca172b4ec1 100644 --- a/test/unit/oned/ODCode39ExtendedModeTest.cpp +++ b/test/unit/oned/ODCode39ExtendedModeTest.cpp @@ -15,12 +15,12 @@ using namespace ZXing; using namespace ZXing::OneD; -static std::wstring Decode(std::string_view encoded) +static std::string Decode(std::string_view encoded) { Code39Reader sut(DecodeHints().setTryCode39ExtendedMode(true)); BitArray row = Utility::ParseBitArray(encoded, '1'); Result result = sut.decodeSingleRow(0, row); - return result.text(); + return result.utf8(); } TEST(ODCode39ExtendedModeTest, Decode) @@ -37,7 +37,7 @@ TEST(ODCode39ExtendedModeTest, Decode) "10100100100101010010110101101001001001010110010110101010010010010101001101101010" "10100100100101101010010110101001001001010110100101101010010010010110110100101010" "1001001001010101100101101010010010010110101100101010010110110100000"), - std::wstring(L"\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9\xa\xb\xc\xd\xe\xf\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 0x20)); + std::string("\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9\xa\xb\xc\xd\xe\xf\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 0x20)); EXPECT_EQ(Decode( "00000100101101101010011010110101001001010010110101001011010010010100101011010010" @@ -49,7 +49,7 @@ TEST(ODCode39ExtendedModeTest, Decode) "10101010100101101101101001011010101100101101010010010100101001101101010101001001" "00101011011001010101001001001010101001101101010010010010110101001101010100100100" "1010110100110101010010010010101011001101010010110110100000"), - L" !\"#$%&'()*+,-./0123456789:;<=>?"); + " !\"#$%&'()*+,-./0123456789:;<=>?"); EXPECT_EQ(Decode( "00001001011011010101001001001010011010101101101010010110101101001011011011010010" @@ -59,7 +59,7 @@ TEST(ODCode39ExtendedModeTest, Decode) "10101011011001101010101001011010110110010110101010011011010101010010010010110101" "01001101010010010010101101010011010100100100101101101010010101001001001010101101" "001101010010010010110101101001010010110110100000"), - L"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"); + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"); EXPECT_EQ(Decode( "00000100101101101010100100100101100110101010100101001001011010100101101001010010" @@ -73,5 +73,5 @@ TEST(ODCode39ExtendedModeTest, Decode) "10100101001001010010110101101001010010010110010110101010010100100101001101101010" "10100100100101011011010010101001001001010101011001101010010010010110101011001010" "1001001001010110101100101010010010010101011011001010010110110100000"), - L"`abcdefghijklmnopqrstuvwxyz{|}~"); + "`abcdefghijklmnopqrstuvwxyz{|}~"); } diff --git a/test/unit/oned/ODEAN13WriterTest.cpp b/test/unit/oned/ODEAN13WriterTest.cpp index 22cfd92da9..c1bd1deee4 100644 --- a/test/unit/oned/ODEAN13WriterTest.cpp +++ b/test/unit/oned/ODEAN13WriterTest.cpp @@ -14,7 +14,7 @@ using namespace ZXing; using namespace ZXing::OneD; namespace { - std::string Encode(const std::wstring& input) + std::string Encode(const std::string& input) { auto result = ToString(EAN13Writer().encode(input, 0, 0), '1', '0', false); return result.substr(0, result.size() - 1); // remove the \n at the end @@ -23,19 +23,19 @@ namespace { TEST(ODEAN13WriterTest, Encode1) { - std::wstring toEncode = L"5901234123457"; + std::string toEncode = "5901234123457"; std::string expected = "00001010001011010011101100110010011011110100111010101011001101101100100001010111001001110100010010100000"; EXPECT_EQ(Encode(toEncode), expected); } TEST(ODEAN13WriterTest, AddChecksumAndEncode) { - std::wstring toEncode = L"590123412345"; + std::string toEncode = "590123412345"; std::string expected = "00001010001011010011101100110010011011110100111010101011001101101100100001010111001001110100010010100000"; EXPECT_EQ(Encode(toEncode), expected); } TEST(ODEAN13WriterTest, EncodeIllegalCharacters) { - EXPECT_THROW({ Encode(L"5901234123abc"); }, std::invalid_argument); + EXPECT_THROW({ Encode("5901234123abc"); }, std::invalid_argument); } diff --git a/test/unit/oned/ODEAN8WriterTest.cpp b/test/unit/oned/ODEAN8WriterTest.cpp index c2bebf5d54..6df37af6c9 100644 --- a/test/unit/oned/ODEAN8WriterTest.cpp +++ b/test/unit/oned/ODEAN8WriterTest.cpp @@ -13,7 +13,7 @@ using namespace ZXing; using namespace ZXing::OneD; namespace { - std::string Encode(const std::wstring& input) + std::string Encode(const std::string& input) { auto result = ToString(EAN8Writer().encode(input, 0, 0), '1', '0', false); return result.substr(0, result.size() - 1); // remove the \n at the end @@ -22,19 +22,19 @@ namespace { TEST(ODEAN8WriterTest, Encode1) { - std::wstring toEncode = L"96385074"; + std::string toEncode = "96385074"; std::string expected = "0000101000101101011110111101011011101010100111011100101000100101110010100000"; EXPECT_EQ(Encode(toEncode), expected); } TEST(ODEAN8WriterTest, AddChecksumAndEncode) { - std::wstring toEncode = L"9638507"; + std::string toEncode = "9638507"; std::string expected = "0000101000101101011110111101011011101010100111011100101000100101110010100000"; EXPECT_EQ(Encode(toEncode), expected); } TEST(ODEAN8WriterTest, EncodeIllegalCharacters) { - EXPECT_THROW({ Encode(L"96385abc"); }, std::invalid_argument); + EXPECT_THROW({ Encode("96385abc"); }, std::invalid_argument); } diff --git a/test/unit/oned/ODUPCAWriterTest.cpp b/test/unit/oned/ODUPCAWriterTest.cpp index f015c6cfbc..66e02f3f78 100644 --- a/test/unit/oned/ODUPCAWriterTest.cpp +++ b/test/unit/oned/ODUPCAWriterTest.cpp @@ -13,7 +13,7 @@ using namespace ZXing; using namespace ZXing::OneD; namespace { - std::string Encode(const std::wstring& input) + std::string Encode(const std::string& input) { auto result = ToString(UPCAWriter().encode(input, 0, 0), '1', '0', false); return result.substr(0, result.size() - 1); // remove the \n at the end @@ -22,14 +22,14 @@ namespace { TEST(ODUPCAWriterTest, Encode1) { - std::wstring toEncode = L"485963095124"; + std::string toEncode = "485963095124"; std::string expected = "00001010100011011011101100010001011010111101111010101011100101110100100111011001101101100101110010100000"; EXPECT_EQ(Encode(toEncode), expected); } TEST(ODUPCAWriterTest, AddChecksumAndEncode) { - std::wstring toEncode = L"12345678901"; + std::string toEncode = "12345678901"; std::string expected = "00001010011001001001101111010100011011000101011110101010001001001000111010011100101100110110110010100000"; EXPECT_EQ(Encode(toEncode), expected); } diff --git a/test/unit/oned/ODUPCEWriterTest.cpp b/test/unit/oned/ODUPCEWriterTest.cpp index 9db92ee6cd..271a817ced 100644 --- a/test/unit/oned/ODUPCEWriterTest.cpp +++ b/test/unit/oned/ODUPCEWriterTest.cpp @@ -14,7 +14,7 @@ using namespace ZXing; using namespace ZXing::OneD; namespace { - std::string Encode(const std::wstring& input) + std::string Encode(const std::string& input) { auto result = ToString(UPCEWriter().encode(input, 0, 0), '1', '0', false); return result.substr(0, result.size() - 1); // remove the \n at the end @@ -23,26 +23,26 @@ namespace { TEST(ODUPCEWriterTest, Encode1) { - std::wstring toEncode = L"05096893"; + std::string toEncode = "05096893"; std::string expected = "000010101110010100111000101101011110110111001011101010100000"; EXPECT_EQ(Encode(toEncode), expected); } TEST(ODUPCEWriterTest, EncodeSystem1) { - std::wstring toEncode = L"12345670"; + std::string toEncode = "12345670"; std::string expected = "000010100100110111101010001101110010000101001000101010100000"; EXPECT_EQ(Encode(toEncode), expected); } TEST(ODUPCEWriterTest, AddChecksumAndEncode) { - std::wstring toEncode = L"0509689"; + std::string toEncode = "0509689"; std::string expected = "000010101110010100111000101101011110110111001011101010100000"; EXPECT_EQ(Encode(toEncode), expected); } TEST(ODUPCEWriterTest, EncodeIllegalCharacters) { - EXPECT_THROW(Encode(L"05096abc"), std::invalid_argument); + EXPECT_THROW(Encode("05096abc"), std::invalid_argument); } From cce9f9dfdb7e60aa91339df6035a9c2863eec320 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 00:18:27 +0200 Subject: [PATCH 100/180] Content: cleanup text/utf8/utf16 situation and remove code duplication --- core/src/Content.cpp | 71 ++++++++++--------- core/src/Content.h | 4 +- core/src/DecoderResult.h | 2 +- core/src/Result.cpp | 2 +- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 4 +- test/unit/ContentTest.cpp | 16 ++--- 6 files changed, 52 insertions(+), 47 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index f5b4e1a852..c7fe98a619 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -90,30 +90,14 @@ bool Content::canProcess() const return std::all_of(encodings.begin(), encodings.end(), [](Encoding e) { return CanProcess(e.eci); }); } -std::wstring Content::text() const -{ - if (!canProcess()) - return {}; - - auto fallbackCS = CharacterSetFromString(hintedCharset); - if (!hasECI && fallbackCS == CharacterSet::Unknown) - fallbackCS = guessEncoding(); - - std::wstring wstr; - ForEachECIBlock([&](ECI eci, int begin, int end) { - CharacterSet cs = eci == ECI::Unknown ? fallbackCS : ToCharacterSet(eci); - - TextDecoder::Append(wstr, bytes.data() + begin, end - begin, cs); - }); - return wstr; -} - -std::string Content::utf8ECI() const +std::wstring Content::render(bool withECI) const { if (empty() || !canProcess()) return {}; - std::wstring res = TextDecoder::FromLatin1(symbology.toString(true)); + std::wstring res; + if (withECI) + res = TextDecoder::FromLatin1(symbology.toString(true)); ECI lastECI = ECI::Unknown; auto fallbackCS = CharacterSetFromString(hintedCharset); if (!hasECI && fallbackCS == CharacterSet::Unknown) @@ -125,26 +109,45 @@ std::string Content::utf8ECI() const // * if !IsText(eci) the ToCharcterSet(eci) will return Unknown and we decode as binary CharacterSet cs = eci == ECI::Unknown ? fallbackCS : ToCharacterSet(eci); - // then find the eci to report back in the ECI designator - if (IsText(ToECI(cs))) // everything decoded as text is reported as utf8 - eci = ECI::UTF8; - else if (eci == ECI::Unknown) // implies !hasECI and fallbackCS is Unknown or Binary - eci = ECI::Binary; + if (withECI) { + // then find the eci to report back in the ECI designator + if (IsText(ToECI(cs))) // everything decoded as text is reported as utf8 + eci = ECI::UTF8; + else if (eci == ECI::Unknown) // implies !hasECI and fallbackCS is Unknown or Binary + eci = ECI::Binary; - if (lastECI != eci) - TextDecoder::AppendLatin1(res, ToString(eci)); - lastECI = eci; + if (lastECI != eci) + TextDecoder::AppendLatin1(res, ToString(eci)); + lastECI = eci; - std::wstring tmp; - TextDecoder::Append(tmp, bytes.data() + begin, end - begin, cs); - for (auto c : tmp) { - res += c; - if (c == L'\\') // in the ECI protocol a '\' has to be doubled + std::wstring tmp; + TextDecoder::Append(tmp, bytes.data() + begin, end - begin, cs); + for (auto c : tmp) { res += c; + if (c == L'\\') // in the ECI protocol a '\' has to be doubled + res += c; + } + } else { + TextDecoder::Append(res, bytes.data() + begin, end - begin, cs); } }); - return TextUtfEncoding::ToUtf8(res); + return res; +} + +std::wstring Content::utf16() const +{ + return render(false); +} + +std::string Content::utf8() const +{ + return TextUtfEncoding::ToUtf8(render(false)); +} + +std::string Content::utf8ECI() const +{ + return TextUtfEncoding::ToUtf8(render(true)); } ByteArray Content::bytesECI() const diff --git a/core/src/Content.h b/core/src/Content.h index 756d8b72fe..022cdc9569 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -33,6 +33,7 @@ class Content void ForEachECIBlock(FUNC f) const; void switchEncoding(ECI eci, bool isECI); + std::wstring render(bool withECI) const; public: struct Encoding @@ -70,7 +71,8 @@ class Content bool empty() const { return bytes.empty(); } bool canProcess() const; - std::wstring text() const; + std::wstring utf16() const; + std::string utf8() const; std::string utf8ECI() const; ByteArray bytesECI() const; CharacterSet guessEncoding() const; diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index a6ce5e6ab3..9908d1121c 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -58,7 +58,7 @@ class DecoderResult Content&& content() && { return std::move(_content); } // to keep the unit tests happy for now: - std::wstring text() const { return _content.text(); } + std::wstring text() const { return _content.utf16(); } std::string symbologyIdentifier() const { return _content.symbology.toString(false); } // Simple macro to set up getter/setter methods that save lots of boilerplate. diff --git a/core/src/Result.cpp b/core/src/Result.cpp index fc1a86656a..1e6a7e8bcb 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -62,7 +62,7 @@ ByteArray Result::bytesECI() const std::string Result::utf8() const { - return TextUtfEncoding::ToUtf8(_content.text()); + return _content.utf8(); } std::string Result::utf8ECI() const diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index 6f7a835309..3a5cc5eef8 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -569,7 +569,7 @@ static int DecodeMacroOptionalTextField(DecodeStatus& status, const std::vector< codeIndex = TextCompaction(status, codewords, codeIndex, result); // Converting to UTF-8 (backward-incompatible change for non-ASCII chars) - TextUtfEncoding::ToUtf8(result.text(), field); + field = result.utf8(); return codeIndex; } @@ -587,7 +587,7 @@ static int DecodeMacroOptionalNumericField(DecodeStatus& status, const std::vect codeIndex = NumericCompaction(status, codewords, codeIndex, result); - field = std::stoll(result.text()); + field = std::stoll(result.utf8()); return codeIndex; } diff --git a/test/unit/ContentTest.cpp b/test/unit/ContentTest.cpp index 19c3a5f8b1..39fa72ba25 100644 --- a/test/unit/ContentTest.cpp +++ b/test/unit/ContentTest.cpp @@ -25,14 +25,14 @@ TEST(ContentTest, Base) Content c; c.switchEncoding(CharacterSet::ISO8859_1); c.append(ByteArray{'A', 0xE9, 'Z'}); - EXPECT_EQ(c.text(), L"A\u00E9Z"); + EXPECT_EQ(c.utf16(), L"A\u00E9Z"); } { // set CharacterSet::ISO8859_5 Content c; c.switchEncoding(CharacterSet::ISO8859_5); c.append(ByteArray{'A', 0xE9, 'Z'}); - EXPECT_EQ(c.text(), L"A\u0449Z"); + EXPECT_EQ(c.utf16(), L"A\u0449Z"); } { // switch to CharacterSet::ISO8859_5 @@ -42,7 +42,7 @@ TEST(ContentTest, Base) c.switchEncoding(CharacterSet::ISO8859_5); EXPECT_FALSE(c.hasECI); c.append(ByteArray{'A', 0xE9, 'Z'}); - EXPECT_EQ(c.text(), L"A\u00E9ZA\u0449Z"); + EXPECT_EQ(c.utf16(), L"A\u00E9ZA\u0449Z"); } } @@ -52,7 +52,7 @@ TEST(ContentTest, GuessEncoding) Content c; c.append(ByteArray{'A', 0xE9, 'Z'}); EXPECT_EQ(c.guessEncoding(), CharacterSet::ISO8859_1); - EXPECT_EQ(c.text(), L"A\u00E9Z"); + EXPECT_EQ(c.utf16(), L"A\u00E9Z"); EXPECT_EQ(c.bytesECI(), c.bytes); } @@ -60,7 +60,7 @@ TEST(ContentTest, GuessEncoding) Content c; c.append(ByteArray{'A', 0x83, 0x65, 'Z'}); EXPECT_EQ(c.guessEncoding(), CharacterSet::Shift_JIS); - EXPECT_EQ(c.text(), L"A\u30C6Z"); + EXPECT_EQ(c.utf16(), L"A\u30C6Z"); } } @@ -72,7 +72,7 @@ TEST(ContentTest, ECI) c.switchEncoding(ECI::ISO8859_5); c.append(ByteArray{'A', 0xE9, 'Z'}); EXPECT_TRUE(c.hasECI); - EXPECT_EQ(c.text(), L"A\u00E9ZA\u0449Z"); + EXPECT_EQ(c.utf16(), L"A\u00E9ZA\u0449Z"); EXPECT_EQ(c.bytesECI().asString(), std::string_view("\\000003A\xE9Z\\000007A\xE9Z")); } @@ -81,14 +81,14 @@ TEST(ContentTest, ECI) c.append(ByteArray{'A', 0x83, 0x65, 'Z'}); c.switchEncoding(ECI::ISO8859_5); c.append(ByteArray{'A', 0xE9, 'Z'}); - EXPECT_EQ(c.text(), L"A\u0083\u0065ZA\u0449Z"); + EXPECT_EQ(c.utf16(), L"A\u0083\u0065ZA\u0449Z"); EXPECT_EQ(c.bytesECI().asString(), std::string_view("\\000003A\x83\x65Z\\000007A\xE9Z")); } { // double '\' Content c; c.append("C:\\Test"); - EXPECT_EQ(c.text(), L"C:\\Test"); + EXPECT_EQ(c.utf16(), L"C:\\Test"); EXPECT_EQ(c.bytesECI().asString(), std::string_view("C:\\\\Test")); } } From 34b54060590485a0b0bef6cdb3b5baa70123918a Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 01:18:29 +0200 Subject: [PATCH 101/180] Result: #define ZX_USE_UTF8 to transition from std::wstring to std::string See https://github.com/nu-book/zxing-cpp/issues/338 for more information * Change the return type of `text()` and `ecLevel()` based on ZX_USE_UTF8. * Introduce `utf16()` as a (temporary?) backend for `text()`. * Add documentation. * Update examples, wrappers and testing code to use ZX_USE_UTF8. --- core/CMakeLists.txt | 1 + core/src/Result.cpp | 12 ++--- core/src/Result.h | 50 ++++++++++++++++--- example/ZXingOpenCV.h | 6 +-- example/ZXingQtReader.h | 4 +- example/ZXingReader.cpp | 17 ++++--- test/blackbox/BlackboxTestRunner.cpp | 5 +- test/blackbox/CMakeLists.txt | 1 + test/blackbox/TestReaderMain.cpp | 5 +- test/unit/CMakeLists.txt | 1 + test/unit/ThresholdBinarizerTest.cpp | 2 +- test/unit/oned/ODCode128ReaderTest.cpp | 18 +++---- test/unit/oned/ODCode39ReaderTest.cpp | 14 +++--- test/unit/oned/ODCode93ReaderTest.cpp | 6 +-- .../zxingcpp/src/main/cpp/CMakeLists.txt | 1 + .../zxingcpp/src/main/cpp/JNIUtils.cpp | 4 +- wrappers/python/zxing.cpp | 2 + wrappers/wasm/BarcodeReader.cpp | 16 +++--- wrappers/winrt/BarcodeReader.cpp | 4 +- 19 files changed, 107 insertions(+), 62 deletions(-) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index e6c4d1a07f..655d569305 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -19,6 +19,7 @@ set (ZXING_CORE_LOCAL_DEFINES $<$:-DZXING_BUILD_READERS> $<$:-DZXING_BUILD_WRITERS> $<$:-DZXING_BUILD_FOR_TEST> + -DZX_USE_UTF8 # silence deprecation warning in Result.h ) if (MSVC) set (ZXING_CORE_LOCAL_DEFINES ${ZXING_CORE_LOCAL_DEFINES} diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 1e6a7e8bcb..9539e273fc 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -36,7 +36,7 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), - _ecLevel(TextDecoder::FromLatin1(decodeResult.ecLevel())), + _ecLevel(decodeResult.ecLevel()), _sai(decodeResult.structuredAppend()), _isMirrored(decodeResult.isMirrored()), _readerInit(decodeResult.readerInit()), @@ -45,11 +45,6 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat // TODO: add type opaque and code specific 'extra data'? (see DecoderResult::extra()) } -std::wstring Result::text() const -{ - return _content.text(); -} - const ByteArray& Result::bytes() const { return _content.bytes; @@ -65,6 +60,11 @@ std::string Result::utf8() const return _content.utf8(); } +std::wstring Result::utf16() const +{ + return _content.utf16(); +} + std::string Result::utf8ECI() const { return _content.utf8ECI(); diff --git a/core/src/Result.h b/core/src/Result.h index 24238bcee8..74dcb5f411 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -43,21 +43,57 @@ class Result BarcodeFormat format() const { return _format; } - std::wstring text() const; - - // WARNING: this is an experimental API and may change/disappear + /** + * @brief bytes is the raw / standard content without any modifications like character set conversions + */ const ByteArray& bytes() const; + + /** + * @brief bytesECI is the raw / standard content following the ECI protocol + */ ByteArray bytesECI() const; + + /** + * @brief utf8/utf16 is the bytes() content converted to utf8/16 based on ECI or guessed character set information + * + * Note: these two properties might only be available while transitioning text() from std::wstring to std::string. time will tell. + * see https://github.com/nu-book/zxing-cpp/issues/338 for a background discussion on the issue. + */ std::string utf8() const; + std::wstring utf16() const; + +#ifdef ZX_USE_UTF8 + std::string text() const { return utf8(); } + std::string ecLevel() const { return _ecLevel; } +#else +#pragma message( \ + "Warning: the return type of text() and ecLevel() will change to std::string. Please #define ZX_USE_UTF8 to transition before the next release.") + std::wstring text() const { return utf16(); } + std::wstring ecLevel() const { return {_ecLevel.begin(), _ecLevel.end()}; } +#endif + + /** + * @brief utf8ECI is the standard content following the ECI protocol with every character set ECI segment transcoded to utf8 + */ std::string utf8ECI() const; + + /** + * @brief contentType gives a hint to the type of content found (Text/Binary/GS1/etc.) + */ ContentType contentType() const; + + /** + * @brief hasECI specifies wheter or not an ECI tag was found + */ bool hasECI() const; - // END WARNING const Position& position() const { return _position; } void setPosition(Position pos) { _position = pos; } - int orientation() const; //< orientation of barcode in degree, see also Position::orientation() + /** + * @brief orientation of barcode in degree, see also Position::orientation() + */ + int orientation() const; /** * @brief isMirrored is the symbol mirrored (currently only supported by QRCode and DataMatrix) @@ -68,8 +104,6 @@ class Result [[deprecated]] const ByteArray& rawBytes() const { return _rawBytes; } [[deprecated]] int numBits() const { return _numBits; } - const std::wstring& ecLevel() const { return _ecLevel; } - /** * @brief symbologyIdentifier Symbology identifier "]cm" where "c" is symbology code character, "m" the modifier. */ @@ -125,7 +159,7 @@ class Result Position _position; ByteArray _rawBytes; int _numBits = 0; - std::wstring _ecLevel; + std::string _ecLevel; StructuredAppendInfo _sai; bool _isMirrored = false; bool _readerInit = false; diff --git a/example/ZXingOpenCV.h b/example/ZXingOpenCV.h index bf9cca06e7..8dbfb2545d 100644 --- a/example/ZXingOpenCV.h +++ b/example/ZXingOpenCV.h @@ -5,8 +5,9 @@ #pragma once +#define ZX_USE_UTF8 1 // see Result.h + #include "ReadBarcode.h" -#include "TextUtfEncoding.h" #include @@ -41,6 +42,5 @@ inline void DrawResult(cv::Mat& img, ZXing::Result res) int npts = contour.size(); cv::polylines(img, &pts, &npts, 1, true, CV_RGB(0, 255, 0)); - cv::putText(img, ZXing::TextUtfEncoding::ToUtf8(res.text()), cv::Point(10, 20), cv::FONT_HERSHEY_DUPLEX, 0.5, - CV_RGB(0, 255, 0)); + cv::putText(img, res.text(), cv::Point(10, 20), cv::FONT_HERSHEY_DUPLEX, 0.5, CV_RGB(0, 255, 0)); } diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index 07effc10d0..675b2d0205 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -5,6 +5,8 @@ #pragma once +#define ZX_USE_UTF8 1 // see Result.h + #include "ReadBarcode.h" #include @@ -115,7 +117,7 @@ class Result : private ZXing::Result Result() : ZXing::Result(ZXing::DecodeStatus::NotFound) {} // required for qmetatype machinery explicit Result(ZXing::Result&& r) : ZXing::Result(std::move(r)) { - _text = QString::fromWCharArray(ZXing::Result::text().c_str()); + _text = QString::fromStdString(ZXing::Result::text()); _bytes = QByteArray(reinterpret_cast(ZXing::Result::bytes().data()), Size(ZXing::Result::bytes())); auto& pos = ZXing::Result::position(); auto qp = [&pos](int i) { return QPoint(pos[i].x, pos[i].y); }; diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 64f91e1701..db7ac9239f 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -4,6 +4,8 @@ */ // SPDX-License-Identifier: Apache-2.0 +#define ZX_USE_UTF8 1 // see Result.h + #include "ReadBarcode.h" #include "TextUtfEncoding.h" #include "GTIN.h" @@ -23,7 +25,6 @@ #include using namespace ZXing; -using namespace TextUtfEncoding; static void PrintUsage(const char* exePath) { @@ -113,7 +114,7 @@ void drawRect(const ImageView& image, const Position& pos) std::string escapeNonGraphical(const std::string& str) { - return ToUtf8(FromUtf8(str), true); + return TextUtfEncoding::ToUtf8(TextUtfEncoding::FromUtf8(str), true); } int main(int argc, char* argv[]) @@ -177,7 +178,7 @@ int main(int argc, char* argv[]) if (oneLine) { std::cout << filePath << " " << ToString(result.format()); if (result.isValid()) - std::cout << " \"" << escapeNonGraphical(result.utf8()) << "\""; + std::cout << " \"" << escapeNonGraphical(result.text()) << "\""; else if (result.format() != BarcodeFormat::None) std::cout << " " << ToString(result.status()); std::cout << "\n"; @@ -192,7 +193,7 @@ int main(int argc, char* argv[]) std::cout << "File: " << filePath << "\n"; firstFile = false; } - std::cout << "Text: \"" << (angleEscape ? escapeNonGraphical(result.utf8()) : result.utf8()) << "\"\n" + std::cout << "Text: \"" << (angleEscape ? escapeNonGraphical(result.text()) : result.text()) << "\"\n" << "TextECI: \"" << result.utf8ECI() << "\"\n" << "Bytes: " << ToHex(result.bytes()) << "\n" << "BytesECI: " << ToHex(result.bytesECI()) << "\n" @@ -210,19 +211,19 @@ int main(int argc, char* argv[]) std::cout << key << v << "\n"; }; - printOptional("EC Level: ", ToUtf8(result.ecLevel())); + printOptional("EC Level: ", result.ecLevel()); if (result.lineCount()) std::cout << "Lines: " << result.lineCount() << "\n"; if ((BarcodeFormat::EAN13 | BarcodeFormat::EAN8 | BarcodeFormat::UPCA | BarcodeFormat::UPCE) .testFlag(result.format())) { - printOptional("Country: ", GTIN::LookupCountryIdentifier(ToUtf8(result.text()), result.format())); + printOptional("Country: ", GTIN::LookupCountryIdentifier(result.text(), result.format())); printOptional("Add-On: ", GTIN::EanAddOn(result)); printOptional("Price: ", GTIN::Price(GTIN::EanAddOn(result))); printOptional("Issue #: ", GTIN::IssueNr(GTIN::EanAddOn(result))); - } else if (result.format() == BarcodeFormat::ITF && result.text().length() == 14) { - printOptional("Country: ", GTIN::LookupCountryIdentifier(ToUtf8(result.text()), result.format())); + } else if (result.format() == BarcodeFormat::ITF && Size(result.bytes()) == 14) { + printOptional("Country: ", GTIN::LookupCountryIdentifier(result.text(), result.format())); } if (result.isPartOfSequence()) diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 085bb41d95..e28f3a1e02 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -9,7 +9,6 @@ #include "DecoderResult.h" #include "ImageLoader.h" #include "ReadBarcode.h" -#include "TextUtfEncoding.h" #include "ThresholdBinarizer.h" #include "ZXContainerAlgorithms.h" #include "pdf417/PDFReader.h" @@ -65,7 +64,7 @@ namespace { static std::string getResultValue(const Result& result, const std::string& key) { if (key == "ecLevel") - return TextUtfEncoding::ToUtf8(result.ecLevel()); + return result.ecLevel(); if (key == "orientation") return std::to_string(result.orientation()); if (key == "symbologyIdentifier") @@ -135,7 +134,7 @@ static std::string checkResult(const fs::path& imgPath, std::string_view expecte } if (auto expected = readFile(".txt")) { - auto utf8Result = TextUtfEncoding::ToUtf8(result.text()); + auto utf8Result = result.utf8(); return utf8Result != *expected ? fmt::format("Content mismatch: expected '{}' but got '{}'", *expected, utf8Result) : ""; } diff --git a/test/blackbox/CMakeLists.txt b/test/blackbox/CMakeLists.txt index f20f4127c7..2d7d9e3036 100644 --- a/test/blackbox/CMakeLists.txt +++ b/test/blackbox/CMakeLists.txt @@ -15,6 +15,7 @@ if (BUILD_READERS) ZXing::ZXing fmt::fmt stb::stb $<$,$,9.0>>:stdc++fs> ) + target_compile_definitions(ReaderTest PRIVATE ZX_USE_UTF8) add_test(NAME ReaderTest COMMAND ReaderTest ${CMAKE_CURRENT_SOURCE_DIR}/../samples) endif() diff --git a/test/blackbox/TestReaderMain.cpp b/test/blackbox/TestReaderMain.cpp index 4aad4905cc..ebc4ed972e 100644 --- a/test/blackbox/TestReaderMain.cpp +++ b/test/blackbox/TestReaderMain.cpp @@ -6,7 +6,6 @@ #include "BlackboxTestRunner.h" #include "ImageLoader.h" -#include "TextUtfEncoding.h" #include "ZXContainerAlgorithms.h" #include "ZXFilesystem.h" @@ -44,12 +43,12 @@ int main(int argc, char** argv) Result result = ReadBarcode(ImageLoader::load(argv[i]).rotated(rotation), hints); std::cout << argv[i] << ": "; if (result.isValid()) - std::cout << ToString(result.format()) << ": " << TextUtfEncoding::ToUtf8(result.text()) << "\n"; + std::cout << ToString(result.format()) << ": " << result.text() << "\n"; else std::cout << "FAILED\n"; if (result.isValid() && getenv("WRITE_TEXT")) { std::ofstream f(fs::path(argv[i]).replace_extension(".txt")); - f << TextUtfEncoding::ToUtf8(result.text()); + f << result.text(); } } return 0; diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 9bd281796d..96ddb9b60e 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -65,5 +65,6 @@ add_executable (UnitTest target_include_directories (UnitTest PRIVATE .) target_link_libraries (UnitTest ZXing::ZXing GTest::gtest_main GTest::gmock) +target_compile_definitions(UnitTest PRIVATE ZX_USE_UTF8) add_test(NAME UnitTest COMMAND UnitTest) diff --git a/test/unit/ThresholdBinarizerTest.cpp b/test/unit/ThresholdBinarizerTest.cpp index ae6449f8dd..aee132bdcc 100644 --- a/test/unit/ThresholdBinarizerTest.cpp +++ b/test/unit/ThresholdBinarizerTest.cpp @@ -100,5 +100,5 @@ TEST(ThresholdBinarizerTest, PatternRowClear) result = reader.decode(ThresholdBinarizer(getImageView(buf, bits), 0x7F)); EXPECT_TRUE(result.isValid()); - EXPECT_EQ(result.text(), L"(91)12345678901234567890123456789012345678901234567890123456789012345678"); + EXPECT_EQ(result.text(), "(91)12345678901234567890123456789012345678901234567890123456789012345678"); } diff --git a/test/unit/oned/ODCode128ReaderTest.cpp b/test/unit/oned/ODCode128ReaderTest.cpp index ed37bcf79c..c22f655333 100644 --- a/test/unit/oned/ODCode128ReaderTest.cpp +++ b/test/unit/oned/ODCode128ReaderTest.cpp @@ -37,7 +37,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 2, 2, 1, 2, 3, 1, 2, 2, 2, 1, 2, 2, 3, 1, 1, 2, 2, 2 }); auto result = parse('C', row); EXPECT_EQ(result.symbologyIdentifier(), "]C0"); - EXPECT_EQ(result.text(), L"2001"); + EXPECT_EQ(result.utf8(), "2001"); } { @@ -45,7 +45,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 4, 1, 1, 1, 3, 1, 2, 2, 1, 2, 3, 1, 2, 2, 2, 1, 2, 2, 1, 3, 2, 1, 3, 1 }); auto result = parse('C', row); EXPECT_EQ(result.symbologyIdentifier(), "]C1"); - EXPECT_EQ(result.text(), L"2001"); + EXPECT_EQ(result.utf8(), "2001"); } { @@ -53,7 +53,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 1, 1, 1, 3, 2, 3, 4, 1, 1, 1, 3, 1, 1, 3, 1, 1, 2, 3, 2, 1, 2, 3, 2, 1 }); auto result = parse('B', row); EXPECT_EQ(result.symbologyIdentifier(), "]C2"); - EXPECT_EQ(result.text(), L"AB"); + EXPECT_EQ(result.utf8(), "AB"); } { @@ -61,7 +61,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 2, 1, 4, 1, 2, 1, 4, 1, 1, 1, 3, 1, 1, 3, 1, 1, 2, 3, 4, 2, 1, 2, 1, 1 }); auto result = parse('B', row); EXPECT_EQ(result.symbologyIdentifier(), "]C2"); - EXPECT_EQ(result.text(), L"zB"); + EXPECT_EQ(result.utf8(), "zB"); } { @@ -69,7 +69,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 1, 1, 3, 1, 4, 1, 4, 1, 1, 1, 3, 1, 1, 1, 4, 1, 3, 1, 1, 1, 1, 3, 2, 3, 1, 2, 3, 1, 2, 2 }); auto result = parse('C', row); EXPECT_EQ(result.symbologyIdentifier(), "]C2"); - EXPECT_EQ(result.text(), L"99A"); + EXPECT_EQ(result.utf8(), "99A"); } { @@ -77,7 +77,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 2, 1, 2, 3, 2, 1, 4, 1, 1, 1, 3, 1, 1, 3, 1, 1, 2, 3, 3, 2, 2, 2, 1, 1 }); auto result = parse('B', row); EXPECT_EQ(result.symbologyIdentifier(), "]C0"); // Just ignoring, not giving FormatError - EXPECT_EQ(result.text(), L"?\u001DB"); + EXPECT_EQ(result.utf8(), "?\u001DB"); } } @@ -88,7 +88,7 @@ TEST(ODCode128ReaderTest, ReaderInit) PatternRow row({ 1, 1, 1, 1, 4, 3, 1, 3, 1, 1, 4, 1 }); auto result = parse('C', row); EXPECT_FALSE(result.readerInit()); - EXPECT_EQ(result.text(), L"92"); + EXPECT_EQ(result.utf8(), "92"); } { @@ -96,7 +96,7 @@ TEST(ODCode128ReaderTest, ReaderInit) PatternRow row({ 1, 1, 4, 3, 1, 1, 1, 1, 3, 1, 4, 1, 1, 1, 1, 1, 4, 3, 3, 3, 1, 1, 2, 1 }); auto result = parse('B', row); EXPECT_TRUE(result.readerInit()); - EXPECT_EQ(result.text(), L"92"); + EXPECT_EQ(result.utf8(), "92"); } { @@ -104,6 +104,6 @@ TEST(ODCode128ReaderTest, ReaderInit) PatternRow row({ 3, 2, 1, 1, 2, 2, 1, 1, 4, 3, 1, 1, 2, 2, 3, 2, 1, 1, 1, 2, 1, 4, 2, 1 }); auto result = parse('B', row); EXPECT_TRUE(result.readerInit()); - EXPECT_EQ(result.text(), L"92"); + EXPECT_EQ(result.utf8(), "92"); } } diff --git a/test/unit/oned/ODCode39ReaderTest.cpp b/test/unit/oned/ODCode39ReaderTest.cpp index 42da6c6ce0..893c2a008c 100644 --- a/test/unit/oned/ODCode39ReaderTest.cpp +++ b/test/unit/oned/ODCode39ReaderTest.cpp @@ -33,39 +33,39 @@ TEST(ODCode39ReaderTest, SymbologyIdentifier) PatternRow row({ 2, 1, 1, 1, 1, 2, 1, 1, 2 }); auto result = parse(row); EXPECT_EQ(result.symbologyIdentifier(), "]A0"); - EXPECT_EQ(result.text(), L"A"); + EXPECT_EQ(result.utf8(), "A"); } { // "A" with checksum PatternRow row({ 2, 1, 1, 1, 1, 2, 1, 1, 2, 0, 2, 1, 1, 1, 1, 2, 1, 1, 2 }); auto result = parse(row, DecodeHints().setValidateCode39CheckSum(true)); EXPECT_EQ(result.symbologyIdentifier(), "]A3"); - EXPECT_EQ(result.text(), L"A"); + EXPECT_EQ(result.utf8(), "A"); result = parse(row); EXPECT_EQ(result.symbologyIdentifier(), "]A0"); - EXPECT_EQ(result.text(), L"AA"); + EXPECT_EQ(result.utf8(), "AA"); } { // Extended "a" PatternRow row({ 1, 2, 1, 1, 1, 2, 1, 2, 1, 0, 2, 1, 1, 1, 1, 2, 1, 1, 2 }); auto result = parse(row, DecodeHints().setTryCode39ExtendedMode(true)); EXPECT_EQ(result.symbologyIdentifier(), "]A4"); - EXPECT_EQ(result.text(), L"a"); + EXPECT_EQ(result.utf8(), "a"); result = parse(row); EXPECT_EQ(result.symbologyIdentifier(), "]A0"); - EXPECT_EQ(result.text(), L"+A"); + EXPECT_EQ(result.utf8(), "+A"); } { // Extended "a" with checksum PatternRow row({ 1, 2, 1, 1, 1, 2, 1, 2, 1, 0, 2, 1, 1, 1, 1, 2, 1, 1, 2, 0, 2, 1, 1, 2, 1, 1, 2, 1, 1 }); auto result = parse(row, DecodeHints().setTryCode39ExtendedMode(true).setValidateCode39CheckSum(true)); EXPECT_EQ(result.symbologyIdentifier(), "]A7"); - EXPECT_EQ(result.text(), L"a"); + EXPECT_EQ(result.utf8(), "a"); result = parse(row); EXPECT_EQ(result.symbologyIdentifier(), "]A0"); - EXPECT_EQ(result.text(), L"+A8"); + EXPECT_EQ(result.utf8(), "+A8"); } } diff --git a/test/unit/oned/ODCode93ReaderTest.cpp b/test/unit/oned/ODCode93ReaderTest.cpp index 221d37d957..b037f1acbc 100644 --- a/test/unit/oned/ODCode93ReaderTest.cpp +++ b/test/unit/oned/ODCode93ReaderTest.cpp @@ -14,17 +14,17 @@ using namespace ZXing; using namespace ZXing::OneD; -static std::wstring Decode(std::string_view input) +static std::string Decode(std::string_view input) { Code93Reader sut; auto row = Utility::ParseBitArray(input, '1'); auto result = sut.decodeSingleRow(0, row); - return result.text(); + return result.utf8(); } TEST(ODCode93ReaderTest, Decode) { - auto expected = std::wstring(L"Code93!\n$%/+ :\x1b;[{\x7f\x00@`\x7f\x7f\x7f", 25); + auto expected = std::string("Code93!\n$%/+ :\x1b;[{\x7f\x00@`\x7f\x7f\x7f", 25); auto decoded = Decode( "00000010101111011010001010011001010010110010011001011001010010011001011001001010" "00010101010000101110101101101010001001001101001101001110010101101011101011011101" diff --git a/wrappers/android/zxingcpp/src/main/cpp/CMakeLists.txt b/wrappers/android/zxingcpp/src/main/cpp/CMakeLists.txt index e49d0b61e5..8d28b221a2 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/CMakeLists.txt +++ b/wrappers/android/zxingcpp/src/main/cpp/CMakeLists.txt @@ -12,5 +12,6 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../core ZXing EXCLUD add_library(zxing_android SHARED BarcodeReader.cpp JNIUtils.cpp) +target_compile_definitions(zxing_android PRIVATE -DZX_USE_UTF8) target_link_libraries(zxing_android PRIVATE ZXing::ZXing log jnigraphics) diff --git a/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp b/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp index 6ae1333f12..85d1717f92 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp +++ b/wrappers/android/zxingcpp/src/main/cpp/JNIUtils.cpp @@ -4,7 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "JNIUtils.h" -#include "TextDecoder.h" +#include "TextUtfEncoding.h" #include #include @@ -55,7 +55,7 @@ jstring C2JString(JNIEnv* env, const std::wstring& str) jstring C2JString(JNIEnv* env, const std::string& str) { - return C2JString(env, ZXing::TextDecoder::FromLatin1(str)); + return C2JString(env, ZXing::TextUtfEncoding::FromUtf8(str)); } std::string J2CString(JNIEnv* env, jstring str) diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index 72336d5c0c..17c79e80df 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -7,6 +7,8 @@ #include "BarcodeFormat.h" +#define ZX_USE_UTF8 1 // see Result.h + // Reader #include "ReadBarcode.h" diff --git a/wrappers/wasm/BarcodeReader.cpp b/wrappers/wasm/BarcodeReader.cpp index eb6e437b5a..7efee44486 100644 --- a/wrappers/wasm/BarcodeReader.cpp +++ b/wrappers/wasm/BarcodeReader.cpp @@ -3,6 +3,8 @@ */ // SPDX-License-Identifier: Apache-2.0 +#define ZX_USE_UTF8 1 // see Result.h + #include "ReadBarcode.h" #include @@ -16,7 +18,7 @@ struct ReadResult { std::string format; - std::wstring text; + std::string text; std::string error; ZXing::Position position; }; @@ -37,7 +39,7 @@ ReadResult readBarcodeFromImage(int bufferPtr, int bufferLength, bool tryHarder, &channels, 4), stbi_image_free); if (buffer == nullptr) { - return { "", L"", "Error loading image" }; + return { "", "", "Error loading image" }; } auto result = ReadBarcode({buffer.get(), width, height, ImageFormat::RGBX}, hints); @@ -46,10 +48,10 @@ ReadResult readBarcodeFromImage(int bufferPtr, int bufferLength, bool tryHarder, } } catch (const std::exception& e) { - return { "", L"", e.what() }; + return { "", "", e.what() }; } catch (...) { - return { "", L"", "Unknown error" }; + return { "", "", "Unknown error" }; } return {}; } @@ -72,10 +74,10 @@ ReadResult readBarcodeFromPixmap(int bufferPtr, int imgWidth, int imgHeight, boo } } catch (const std::exception& e) { - return { "", L"", e.what() }; + return { "", "", e.what() }; } catch (...) { - return { "", L"", "Unknown error" }; + return { "", "", "Unknown error" }; } return {}; } @@ -106,7 +108,7 @@ EMSCRIPTEN_BINDINGS(BarcodeReader) function("readBarcodeFromImage", &readBarcodeFromImage); function("readBarcodeFromPixmap", &readBarcodeFromPixmap); - // obsoletes + // obsoletes [[deprecated]] function("readBarcode", &readBarcodeFromImage); function("readBarcodeFromPng", &readBarcodeFromImage); } diff --git a/wrappers/winrt/BarcodeReader.cpp b/wrappers/winrt/BarcodeReader.cpp index 9695d26400..7080be6241 100644 --- a/wrappers/winrt/BarcodeReader.cpp +++ b/wrappers/winrt/BarcodeReader.cpp @@ -21,6 +21,8 @@ #include "BarcodeReader.h" +#define ZX_USE_UTF8 1 // silence deprecation warning in Result.h + #include "BarcodeFormat.h" #include "DecodeHints.h" #include "ReadBarcode.h" @@ -202,7 +204,7 @@ BarcodeReader::Read(SoftwareBitmap^ bitmap, int cropWidth, int cropHeight) auto result = ReadBarcode(img, *m_hints); if (result.isValid()) { - return ref new ReadResult(ToPlatformString(ZXing::ToString(result.format())), ToPlatformString(result.text()), ConvertNativeToRuntime(result.format())); + return ref new ReadResult(ToPlatformString(ZXing::ToString(result.format())), ToPlatformString(result.utf16()), ConvertNativeToRuntime(result.format())); } } else { throw std::runtime_error("Failed to read bitmap's data"); From 907b735a5ded24e8fb3376175f0465495247ecb0 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 01:21:55 +0200 Subject: [PATCH 102/180] License: add overlooked SPDX-License-Identifier in 2 files --- core/ZXVersion.h.in | 16 +++------------- wrappers/winrt/BarcodeReader.cpp | 13 +------------ 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/core/ZXVersion.h.in b/core/ZXVersion.h.in index 8e2151f629..78fde4106a 100644 --- a/core/ZXVersion.h.in +++ b/core/ZXVersion.h.in @@ -1,19 +1,9 @@ -#pragma once /* * Copyright 2019 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once // Version numbering #define ZXING_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ diff --git a/wrappers/winrt/BarcodeReader.cpp b/wrappers/winrt/BarcodeReader.cpp index 7080be6241..5d3360985e 100644 --- a/wrappers/winrt/BarcodeReader.cpp +++ b/wrappers/winrt/BarcodeReader.cpp @@ -1,18 +1,7 @@ /* * Copyright 2016 Nu-book Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. */ +// SPDX-License-Identifier: Apache-2.0 #if (_MSC_VER >= 1915) #define no_init_all deprecated From 728b5a99a74132251933685299f880bfa300761c Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 12:32:35 +0200 Subject: [PATCH 103/180] android: add `bytes` and `contentType` members to `Result` Use in demo app to show hex coded binary content. --- .../com/example/zxingcppdemo/MainActivity.kt | 4 ++- .../zxingcpp/src/main/cpp/BarcodeReader.cpp | 29 +++++++++++++++++++ .../main/java/com/zxingcpp/BarcodeReader.kt | 5 ++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt index d023a70676..82cf02fd9e 100644 --- a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt +++ b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt @@ -166,7 +166,9 @@ class MainActivity : AppCompatActivity() { p.toPointF() } } - (result?.let { "${it.format}: ${it.text}" } ?: "") + (result?.let { "${it.format} (${it.contentType}):" + + "${if (it.contentType != BarcodeReader.ContentType.BINARY) it.text else it.bytes!!.joinToString(separator = "") { v -> "%02x".format(v) }}" } + ?: "") } catch (e: Throwable) { e.message ?: "Error" } diff --git a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp index ce37a1ecde..571434cff1 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp +++ b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp @@ -39,6 +39,20 @@ static const char* JavaBarcodeFormatName(BarcodeFormat format) } } +static const char* JavaContentTypeName(ContentType contentType) +{ + // These have to be the names of the enum constants in the kotlin code. + switch (contentType) { + case ContentType::Text: return "TEXT"; + case ContentType::Binary: return "BINARY"; + case ContentType::Mixed: return "MIXED"; + case ContentType::GS1: return "GS1"; + case ContentType::ISO15434: return "ISO15434"; + case ContentType::UnknownECI: return "UNKNOWN_ECI"; + default: throw std::invalid_argument("Invalid contentType"); + } +} + static jstring ThrowJavaException(JNIEnv* env, const char* message) { // if (env->ExceptionCheck()) @@ -48,6 +62,13 @@ static jstring ThrowJavaException(JNIEnv* env, const char* message) return nullptr; } +static jobject CreateContentType(JNIEnv* env, ContentType contentType) +{ + jclass cls = env->FindClass("com/zxingcpp/BarcodeReader$ContentType"); + jfieldID fidCT = env->GetStaticFieldID(cls , JavaContentTypeName(contentType), "Lcom/zxingcpp/BarcodeReader$ContentType;"); + return env->GetStaticObjectField(cls, fidCT); +} + static jobject CreateAndroidPoint(JNIEnv* env, const PointT& point) { jclass cls = env->FindClass("android/graphics/Point"); @@ -99,9 +120,17 @@ jstring Read(JNIEnv *env, ImageView image, jstring formats, jboolean tryHarder, env->SetObjectField(result, fidTime, C2JString(env, time)); if (res.isValid()) { + jbyteArray jByteArray = env->NewByteArray(res.bytes().size()); + env->SetByteArrayRegion(jByteArray, 0, res.bytes().size(), (jbyte*)res.bytes().data()); + jfieldID fidBytes = env->GetFieldID(clResult, "bytes", "[B"); + env->SetObjectField(result, fidBytes, jByteArray); + jfieldID fidText = env->GetFieldID(clResult, "text", "Ljava/lang/String;"); env->SetObjectField(result, fidText, C2JString(env, res.text())); + jfieldID fidContentType = env->GetFieldID(clResult , "contentType", "Lcom/zxingcpp/BarcodeReader$ContentType;"); + env->SetObjectField(result, fidContentType, CreateContentType(env, res.contentType())); + jfieldID fidPosition = env->GetFieldID(clResult, "position", "Lcom/zxingcpp/BarcodeReader$Position;"); env->SetObjectField(result, fidPosition, CreatePosition(env, res.position())); diff --git a/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt b/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt index 6dbddecdac..8f5745976d 100644 --- a/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt +++ b/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt @@ -32,6 +32,9 @@ class BarcodeReader { NONE, AZTEC, CODABAR, CODE_39, CODE_93, CODE_128, DATA_BAR, DATA_BAR_EXPANDED, DATA_MATRIX, EAN_8, EAN_13, ITF, MAXICODE, PDF_417, QR_CODE, MICRO_QR_CODE, UPC_A, UPC_E } + enum class ContentType { + TEXT, BINARY, MIXED, GS1, ISO15434, UNKNOWN_ECI + } data class Options( val formats: Set = setOf(), @@ -50,8 +53,10 @@ class BarcodeReader { data class Result( val format: Format = Format.NONE, + val bytes: ByteArray? = null, val text: String? = null, val time: String? = null, // for development/debug purposes only + val contentType: ContentType = ContentType.TEXT, val position: Position? = null, val orientation: Int = 0, val ecLevel: String? = null, From 5a0cb4569843818710fc546a8421a4277a07c6e3 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 12:33:28 +0200 Subject: [PATCH 104/180] python: add `content_type` property in Result --- wrappers/python/zxing.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index 17c79e80df..59be2d6e24 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -175,6 +175,14 @@ PYBIND11_MODULE(zxingcpp, m) .value("Read", EanAddOnSymbol::Read) .value("Require", EanAddOnSymbol::Require) .export_values(); + py::enum_(m, "ContentType", "Enumeration of content types") + .value("Text", ContentType::Text) + .value("Binary", ContentType::Binary) + .value("Mixed", ContentType::Mixed) + .value("GS1", ContentType::GS1) + .value("ISO15434", ContentType::ISO15434) + .value("UnknownECI", ContentType::UnknownECI) + .export_values(); py::class_(m, "Point", "Represents the coordinates of a point in an image") .def_readonly("x", &PointI::x, ":return: horizontal coordinate of the point\n" @@ -216,6 +224,9 @@ PYBIND11_MODULE(zxingcpp, m) .def_property_readonly("symbology_identifier", &Result::symbologyIdentifier, ":return: decoded symbology idendifier\n" ":rtype: str") + .def_property_readonly("content_type", &Result::contentType, + ":return: content type of symbol\n" + ":rtype: zxing.ContentType") .def_property_readonly("position", &Result::position, ":return: position of the decoded symbol\n" ":rtype: zxing.Position") From ce08cb337be8322740714683d8ac4e0299c7a0a3 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 13:33:19 +0200 Subject: [PATCH 105/180] Code128: properly report contentType GS1 --- core/src/Content.cpp | 4 +++- core/src/Content.h | 2 +- core/src/Result.cpp | 4 ++-- core/src/Result.h | 4 ++-- core/src/oned/ODCode128Reader.cpp | 9 ++++++--- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index c7fe98a619..16f290044d 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -50,7 +50,9 @@ void Content::switchEncoding(ECI eci, bool isECI) Content::Content() {} -Content::Content(ByteArray&& bytes, SymbologyIdentifier si) : bytes(std::move(bytes)), symbology(si) {} +Content::Content(ByteArray&& bytes, SymbologyIdentifier si, std::string ai) + : bytes(std::move(bytes)), applicationIndicator(std::move(ai)), symbology(si) +{} void Content::switchEncoding(CharacterSet cs) { diff --git a/core/src/Content.h b/core/src/Content.h index 022cdc9569..13e5e7c365 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -50,7 +50,7 @@ class Content bool hasECI = false; Content(); - Content(ByteArray&& bytes, SymbologyIdentifier si); + Content(ByteArray&& bytes, SymbologyIdentifier si, std::string ai = {}); void switchEncoding(ECI eci) { switchEncoding(eci, true); } void switchEncoding(CharacterSet cs); diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 9539e273fc..8e3ed62867 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -18,10 +18,10 @@ namespace ZXing { Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, - SymbologyIdentifier si, ByteArray&& rawBytes, const bool readerInit) + SymbologyIdentifier si, ByteArray&& rawBytes, bool readerInit, const std::string& ai) : _format(format), - _content({ByteArray(text)}, si), + _content({ByteArray(text)}, si, ai), _position(Line(y, xStart, xStop)), _rawBytes(std::move(rawBytes)), _numBits(Size(_rawBytes) * 8), diff --git a/core/src/Result.h b/core/src/Result.h index 74dcb5f411..735f2f2d23 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -32,8 +32,8 @@ class Result explicit Result(DecodeStatus status) : _status(status) {} // 1D convenience constructor - Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, - SymbologyIdentifier si, ByteArray&& rawBytes = {}, const bool readerInit = false); + Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, + ByteArray&& rawBytes = {}, bool readerInit = false, const std::string& ai = {}); Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format); diff --git a/core/src/oned/ODCode128Reader.cpp b/core/src/oned/ODCode128Reader.cpp index a9e55b7f5d..be000e4f7b 100644 --- a/core/src/oned/ODCode128Reader.cpp +++ b/core/src/oned/ODCode128Reader.cpp @@ -43,6 +43,7 @@ class Raw2TxtDecoder int codeSet = 0; SymbologyIdentifier _symbologyIdentifier = {'C', '0'}; // ISO/IEC 15417:2007 Annex C Table C.1 bool _readerInit = false; + std::string _applicationIndicator; std::string txt; size_t lastTxtSize = 0; @@ -59,6 +60,7 @@ class Raw2TxtDecoder // GS1 General Specifications Section 5.4.6.4 // "Transmitted data ... is prefixed by the symbology identifier ]C1, if used." // Choosing not to use symbology identifier, i.e. to not prefix to data. + _applicationIndicator = "GS1"; } else if ((isCodeSetC && txt.size() == 2 && txt[0] >= '0' && txt[0] <= '9' && txt[1] >= '0' && txt[1] <= '9') || (!isCodeSetC && txt.size() == 1 && ((txt[0] >= 'A' && txt[0] <= 'Z') @@ -66,6 +68,7 @@ class Raw2TxtDecoder // ISO/IEC 15417:2007 Annex B.2 // FNC1 in second position following Code Set C "00-99" or Code Set A/B "A-Za-z" - AIM _symbologyIdentifier.modifier = '2'; + _applicationIndicator = txt; } else { // ISO/IEC 15417:2007 Annex B.3. Otherwise FNC1 is returned as ASCII 29 (GS) @@ -154,7 +157,7 @@ class Raw2TxtDecoder } SymbologyIdentifier symbologyIdentifier() const { return _symbologyIdentifier; } - + std::string applicationIndicator() const { return _applicationIndicator; } bool readerInit() const { return _readerInit; } }; @@ -268,13 +271,13 @@ Result Code128Reader::decodePattern(int rowNumber, PatternView& next, std::uniqu int checksum = rawCodes.front(); for (int i = 1; i < Size(rawCodes) - 1; ++i) checksum += i * rawCodes[i]; - // the second last code is the checksum (last one is the stop code): + // the last code is the checksum: if (checksum % 103 != rawCodes.back()) return Result(DecodeStatus::ChecksumError); int xStop = next.pixelsTillEnd(); return Result(raw2txt.text(), rowNumber, xStart, xStop, BarcodeFormat::Code128, raw2txt.symbologyIdentifier(), - std::move(rawCodes), raw2txt.readerInit()); + std::move(rawCodes), raw2txt.readerInit(), raw2txt.applicationIndicator()); } } // namespace ZXing::OneD From 3df848f1a788a429a2e9f21ef00ea965ca3e8726 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 13:34:16 +0200 Subject: [PATCH 106/180] Result: report (empty) NotFound results as ContentType::Text --- core/src/Content.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 16f290044d..280c3e41ee 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -191,6 +191,9 @@ CharacterSet Content::guessEncoding() const ContentType Content::type() const { + if (empty()) + return ContentType::Text; + if (!canProcess()) return ContentType::UnknownECI; From 02b6999ee28f6b48b4a025a1a947e26da41b52b7 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 13:35:22 +0200 Subject: [PATCH 107/180] test: add a few checks for contentType to black box tests --- test/blackbox/BlackboxTestRunner.cpp | 2 ++ test/samples/code128-1/1.result.txt | 1 + test/samples/datamatrix-1/eci-mixed.result.txt | 2 ++ test/samples/datamatrix-1/gs1-figure-4.15.1-2-32x32.result.txt | 1 + test/samples/datamatrix-3/dm-c.result.txt | 1 + 5 files changed, 7 insertions(+) create mode 100644 test/samples/datamatrix-1/eci-mixed.result.txt create mode 100644 test/samples/datamatrix-3/dm-c.result.txt diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index e28f3a1e02..7f9a1d7ab9 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -63,6 +63,8 @@ namespace { // Helper for `compareResult()` - map `key` to Result property, converting value to std::string static std::string getResultValue(const Result& result, const std::string& key) { + if (key == "contentType") + return ToString(result.contentType()); if (key == "ecLevel") return result.ecLevel(); if (key == "orientation") diff --git a/test/samples/code128-1/1.result.txt b/test/samples/code128-1/1.result.txt index 90af6c5d09..ec5103cdec 100644 --- a/test/samples/code128-1/1.result.txt +++ b/test/samples/code128-1/1.result.txt @@ -1 +1,2 @@ symbologyIdentifier=]C1 +contentType=GS1 diff --git a/test/samples/datamatrix-1/eci-mixed.result.txt b/test/samples/datamatrix-1/eci-mixed.result.txt new file mode 100644 index 0000000000..352fff3edf --- /dev/null +++ b/test/samples/datamatrix-1/eci-mixed.result.txt @@ -0,0 +1,2 @@ +symbologyIdentifier=]d1 +contentType=UnknownECI diff --git a/test/samples/datamatrix-1/gs1-figure-4.15.1-2-32x32.result.txt b/test/samples/datamatrix-1/gs1-figure-4.15.1-2-32x32.result.txt index c31c096dbb..1edb45e39f 100644 --- a/test/samples/datamatrix-1/gs1-figure-4.15.1-2-32x32.result.txt +++ b/test/samples/datamatrix-1/gs1-figure-4.15.1-2-32x32.result.txt @@ -1 +1,2 @@ symbologyIdentifier=]d2 +contentType=GS1 diff --git a/test/samples/datamatrix-3/dm-c.result.txt b/test/samples/datamatrix-3/dm-c.result.txt new file mode 100644 index 0000000000..d0aaba1474 --- /dev/null +++ b/test/samples/datamatrix-3/dm-c.result.txt @@ -0,0 +1 @@ +contentType=Binary From 6527a7cea4f1f8e702abc93021ff7011731dfff9 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 15:47:47 +0200 Subject: [PATCH 108/180] example: add contentType support to Qt wrapper --- example/ZXingQtReader.cpp | 1 + example/ZXingQtReader.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/example/ZXingQtReader.cpp b/example/ZXingQtReader.cpp index 2fa90d1a8e..7d35d9553a 100644 --- a/example/ZXingQtReader.cpp +++ b/example/ZXingQtReader.cpp @@ -34,6 +34,7 @@ int main(int argc, char* argv[]) qDebug() << "Text: " << result.text(); qDebug() << "Format: " << result.format(); + qDebug() << "Content:" << result.contentType(); qDebug() << "Error: " << result.status(); return result.isValid() ? 0 : 1; diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index 675b2d0205..0756fa661c 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -57,6 +57,8 @@ enum class BarcodeFormat TwoDCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, }; +enum class ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI }; + enum class DecodeStatus { NoError = 0, @@ -66,6 +68,7 @@ enum class DecodeStatus }; #else using ZXing::BarcodeFormat; +using ZXing::ContentType; using ZXing::DecodeStatus; #endif @@ -74,6 +77,7 @@ using ZXing::Binarizer; using ZXing::BarcodeFormats; Q_ENUM_NS(BarcodeFormat) +Q_ENUM_NS(ContentType) Q_ENUM_NS(DecodeStatus) template @@ -107,6 +111,7 @@ class Result : private ZXing::Result Q_PROPERTY(QByteArray bytes READ bytes) Q_PROPERTY(bool isValid READ isValid) Q_PROPERTY(DecodeStatus status READ status) + Q_PROPERTY(ContentType contentType READ contentType) Q_PROPERTY(Position position READ position) QString _text; @@ -128,6 +133,7 @@ class Result : private ZXing::Result BarcodeFormat format() const { return static_cast(ZXing::Result::format()); } DecodeStatus status() const { return static_cast(ZXing::Result::status()); } + ContentType contentType() const { return static_cast(ZXing::Result::contentType()); } QString formatName() const { return QString::fromStdString(ZXing::ToString(ZXing::Result::format())); } const QString& text() const { return _text; } const QByteArray& bytes() const { return _bytes; } From dee1d41f3ec899751fc46c7fbea5c36419ed6462 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 16:57:33 +0200 Subject: [PATCH 109/180] example: add basic multi-symbol support to Qt wrapper --- example/ZXingQtReader.cpp | 19 +++++++++++-------- example/ZXingQtReader.h | 40 ++++++++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/example/ZXingQtReader.cpp b/example/ZXingQtReader.cpp index 7d35d9553a..c07544b08f 100644 --- a/example/ZXingQtReader.cpp +++ b/example/ZXingQtReader.cpp @@ -26,16 +26,19 @@ int main(int argc, char* argv[]) } auto hints = DecodeHints() - .setFormats(BarcodeFormat::QRCode) + .setFormats(BarcodeFormat::Any) .setTryRotate(false) - .setBinarizer(Binarizer::FixedThreshold); + .setMaxNumberOfSymbols(10); - auto result = ReadBarcode(fileImage, hints); + auto results = ReadBarcodes(fileImage, hints); - qDebug() << "Text: " << result.text(); - qDebug() << "Format: " << result.format(); - qDebug() << "Content:" << result.contentType(); - qDebug() << "Error: " << result.status(); + for (auto& result : results) { + qDebug() << "Text: " << result.text(); + qDebug() << "Format: " << result.format(); + qDebug() << "Content:" << result.contentType(); + qDebug() << "Error: " << result.status(); + qDebug() << ""; + } - return result.isValid() ? 0 : 1; + return results.isEmpty() ? 1 : 0; } diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index 0756fa661c..d97d634d9a 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef QT_MULTIMEDIA_LIB #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) @@ -144,7 +145,15 @@ class Result : private ZXing::Result Q_PROPERTY(int runTime MEMBER runTime) }; -inline Result ReadBarcode(const QImage& img, const DecodeHints& hints = {}) +inline QList QListResults(ZXing::Results&& zxres) +{ + QList res; + for (auto&& r : zxres) + res.push_back(Result(std::move(r))); + return res; +} + +inline QList ReadBarcodes(const QImage& img, const DecodeHints& hints = {}) { using namespace ZXing; @@ -166,15 +175,21 @@ inline Result ReadBarcode(const QImage& img, const DecodeHints& hints = {}) }; auto exec = [&](const QImage& img) { - return Result(ZXing::ReadBarcode( + return QListResults(ZXing::ReadBarcodes( {img.bits(), img.width(), img.height(), ImgFmtFromQImg(img), static_cast(img.bytesPerLine())}, hints)); }; return ImgFmtFromQImg(img) == ImageFormat::None ? exec(img.convertToFormat(QImage::Format_Grayscale8)) : exec(img); } +inline Result ReadBarcode(const QImage& img, const DecodeHints& hints = {}) +{ + auto res = ReadBarcodes(img, DecodeHints(hints).setMaxNumberOfSymbols(1)); + return !res.isEmpty() ? res.takeFirst() : Result(); +} + #ifdef QT_MULTIMEDIA_LIB -inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = {}) +inline QList ReadBarcodes(const QVideoFrame& frame, const DecodeHints& hints = {}) { using namespace ZXing; @@ -187,7 +202,7 @@ inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = { qWarning() << "invalid QVideoFrame: could not map memory"; return {}; } - //TODO c++17: SCOPE_EXIT([&] { img.unmap(); }); + auto unmap = qScopeGuard([&] { img.unmap(); }); ImageFormat fmt = ImageFormat::None; int pixStride = 0; @@ -269,22 +284,25 @@ inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = { default: break; } - Result res; if (fmt != ImageFormat::None) { - res = Result(ZXing::ReadBarcode( + return QListResults(ZXing::ReadBarcodes( {img.bits(FIRST_PLANE) + pixOffset, img.width(), img.height(), fmt, img.bytesPerLine(FIRST_PLANE), pixStride}, hints)); } else { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (QVideoFrame::imageFormatFromPixelFormat(img.pixelFormat()) != QImage::Format_Invalid) - res = ReadBarcode(img.image(), hints); + return ReadBarcodes(img.image(), hints); + qWarning() << "unsupported QVideoFrame::pixelFormat"; + return {}; #else - res = ReadBarcode(img.toImage(), hints); + return ReadBarcodes(img.toImage(), hints); #endif } +} - img.unmap(); - - return res; +inline Result ReadBarcode(const QVideoFrame& frame, const DecodeHints& hints = {}) +{ + auto res = ReadBarcodes(frame, DecodeHints(hints).setMaxNumberOfSymbols(1)); + return !res.isEmpty() ? res.takeFirst() : Result(); } #define ZQ_PROPERTY(Type, name, setter) \ From f70f64ca2eee582c5688945ed852a49837b33c48 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 18:18:20 +0200 Subject: [PATCH 110/180] example: update "TextECI" -> "Utf8ECI" in console output --- example/ZXingReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index db7ac9239f..32cd7c7674 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -194,7 +194,7 @@ int main(int argc, char* argv[]) firstFile = false; } std::cout << "Text: \"" << (angleEscape ? escapeNonGraphical(result.text()) : result.text()) << "\"\n" - << "TextECI: \"" << result.utf8ECI() << "\"\n" + << "Utf8ECI: \"" << result.utf8ECI() << "\"\n" << "Bytes: " << ToHex(result.bytes()) << "\n" << "BytesECI: " << ToHex(result.bytesECI()) << "\n" << "Format: " << ToString(result.format()) << "\n" From 74bbc150ff81c73c086c5199bbc0c0eb14537795 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 21 Jun 2022 23:27:10 +0200 Subject: [PATCH 111/180] DecoderResult: remove unused error correction related properties --- core/src/DecoderResult.h | 4 ---- core/src/pdf417/PDFScanningDecoder.cpp | 7 +------ 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 9908d1121c..0071bfae27 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -27,8 +27,6 @@ class DecoderResult Content _content; int _numBits = 0; std::string _ecLevel; - int _errorsCorrected = -1; - int _erasures = -1; int _lineCount = 0; StructuredAppendInfo _structuredAppend; bool _isMirrored = false; @@ -78,8 +76,6 @@ class DecoderResult ZX_PROPERTY(int, numBits, setNumBits) ZX_PROPERTY(std::string, ecLevel, setEcLevel) - ZX_PROPERTY(int, errorsCorrected, setErrorsCorrected) - ZX_PROPERTY(int, erasures, setErasures) ZX_PROPERTY(int, lineCount, setLineCount) ZX_PROPERTY(StructuredAppendInfo, structuredAppend, setStructuredAppend) ZX_PROPERTY(bool, isMirrored, setIsMirrored) diff --git a/core/src/pdf417/PDFScanningDecoder.cpp b/core/src/pdf417/PDFScanningDecoder.cpp index 4d4217f31a..5191dffd5e 100644 --- a/core/src/pdf417/PDFScanningDecoder.cpp +++ b/core/src/pdf417/PDFScanningDecoder.cpp @@ -585,12 +585,7 @@ DecoderResult DecodeCodewords(std::vector& codewords, int ecLevel, const st return DecodeStatus::FormatError; // Decode the codewords - auto result = DecodedBitStreamParser::Decode(codewords, ecLevel, characterSet); - if (result.isValid()) { - result.setErrorsCorrected(correctedErrorsCount); - result.setErasures(Size(erasures)); - } - return result; + return DecodedBitStreamParser::Decode(codewords, ecLevel, characterSet); } From 1db3ff0a7604f9715a9260d8b18d93d950a5d7b7 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 22 Jun 2022 20:47:01 +0200 Subject: [PATCH 112/180] documentation: randomly remove/fix outdated/unhelpful/verbose comments --- core/src/BarcodeFormat.h | 2 +- core/src/BitArray.h | 1 - core/src/BitSource.h | 4 +-- core/src/GenericGFPoly.h | 3 -- core/src/Reader.h | 28 ------------------- core/src/WhiteRectDetector.cpp | 14 ---------- core/src/WhiteRectDetector.h | 2 +- core/src/aztec/AZDecoder.cpp | 5 +--- core/src/aztec/AZDecoder.h | 3 -- core/src/aztec/AZDetector.cpp | 4 +-- core/src/aztec/AZDetector.h | 2 -- core/src/aztec/AZReader.h | 5 ---- core/src/datamatrix/DMDecoder.cpp | 2 +- core/src/datamatrix/DMDecoder.h | 10 ------- core/src/datamatrix/DMDetector.h | 3 -- core/src/datamatrix/DMReader.cpp | 11 +------- core/src/datamatrix/DMReader.h | 5 ---- core/src/maxicode/MCDecoder.h | 6 ---- core/src/oned/ODCodabarReader.h | 6 ---- core/src/oned/ODCode128Reader.h | 5 ---- core/src/oned/ODCode39Reader.h | 11 -------- core/src/oned/ODCode93Reader.h | 6 ---- core/src/oned/ODITFReader.h | 4 +-- core/src/oned/ODMultiUPCEANReader.h | 6 +--- core/src/oned/ODReader.h | 4 --- .../oned/rss/ODRSSExpandedBinaryDecoder.cpp | 1 - core/src/pdf417/PDFDetector.cpp | 3 -- core/src/pdf417/PDFScanningDecoder.cpp | 5 ++-- core/src/qrcode/QRCodecMode.h | 2 +- core/src/qrcode/QRDecoder.cpp | 2 +- core/src/qrcode/QRDecoder.h | 3 -- core/src/qrcode/QRReader.h | 5 ---- 32 files changed, 14 insertions(+), 159 deletions(-) diff --git a/core/src/BarcodeFormat.h b/core/src/BarcodeFormat.h index 2d82784525..186e52eee6 100644 --- a/core/src/BarcodeFormat.h +++ b/core/src/BarcodeFormat.h @@ -80,7 +80,7 @@ BarcodeFormat BarcodeFormatFromString(const std::string& str); * Separators can be (any combination of) '|', ',' or ' '. * Underscores are optional and input can be lower case. * e.g. "EAN-8 qrcode, Itf" would be parsed into [EAN8, QRCode, ITF]. - * @throws std::invalid_parameter Throws if the string can not be fully parsed. + * @throws std::invalid_parameter if the string can not be fully parsed. */ BarcodeFormats BarcodeFormatsFromString(const std::string& str); diff --git a/core/src/BitArray.h b/core/src/BitArray.h index 91d60a8b5e..355c76eea3 100644 --- a/core/src/BitArray.h +++ b/core/src/BitArray.h @@ -257,7 +257,6 @@ class BitArray * @param end end of range, exclusive * @param value if true, checks that bits in range are set, otherwise checks that they are not set * @return true iff all bits are set or not set in range, according to value argument - * @throws IllegalArgumentException if end is less than or equal to start */ #ifdef ZX_FAST_BIT_STORAGE bool isRange(int start, int end, bool value) const diff --git a/core/src/BitSource.h b/core/src/BitSource.h index 1db7c9eeb8..9e9bbe977f 100644 --- a/core/src/BitSource.h +++ b/core/src/BitSource.h @@ -52,9 +52,7 @@ class BitSource /** * @param numBits number of bits to read - * @return int representing the bits read. The bits will appear as the least-significant - * bits of the int - * @throws IllegalArgumentException if numBits isn't in [1,32] or more than is available + * @return int representing the bits read. The bits will appear as the least-significant bits of the int */ int readBits(int numBits); diff --git a/core/src/GenericGFPoly.h b/core/src/GenericGFPoly.h index 7aa56b927d..11b03a70d6 100644 --- a/core/src/GenericGFPoly.h +++ b/core/src/GenericGFPoly.h @@ -59,9 +59,6 @@ class GenericGFPoly * to perform computations * @param coefficients coefficients as ints representing elements of GF(size), arranged * from most significant (highest-power term) coefficient to least significant - * @throws IllegalArgumentException if argument is null or empty, - * or if leading coefficient is 0 and this is not a - * constant polynomial (that is, it is not the monomial "0"). */ GenericGFPoly(const GenericGF& field, std::vector&& coefficients) : _field(&field) { diff --git a/core/src/Reader.h b/core/src/Reader.h index 99ea56d3ff..4e8a2721d8 100644 --- a/core/src/Reader.h +++ b/core/src/Reader.h @@ -12,39 +12,11 @@ namespace ZXing { class BinaryBitmap; -/** -* Implementations of this interface can decode an image of a barcode in some format into -* the string it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can -* decode a QR code. The decoder may optionally receive hints from the caller which may help -* it decode more quickly or accurately. -* -* See {@link MultiFormatReader}, which attempts to determine what barcode -* format is present within the image as well, and then decodes it accordingly. -* -* All readers are thread-safe with no temporary state left behind after decode(). -* -* @author Sean Owen -* @author dswitkin@google.com (Daniel Switkin) -*/ class Reader { public: virtual ~Reader() = default; - /** - * Locates and decodes a barcode in some format within an image. This method also accepts - * hints, each possibly associated to some data, which may help the implementation decode. - * - * @param image image of barcode to decode - * @param hints passed as a {@link java.util.Map} from {@link DecodeHintType} - * to arbitrary data. The - * meaning of the data depends upon the hint type. The implementation may or may not do - * anything with these hints. - * @return string which the barcode encodes - * @throws NotFoundException if no potential barcode is found - * @throws ChecksumException if a potential barcode is found but does not pass its checksum - * @throws FormatException if a potential barcode is found but format is invalid - */ virtual Result decode(const BinaryBitmap& image) const = 0; // WARNING: this API is experimental and may change/disappear diff --git a/core/src/WhiteRectDetector.cpp b/core/src/WhiteRectDetector.cpp index 5148e9cfdd..87cfca924e 100644 --- a/core/src/WhiteRectDetector.cpp +++ b/core/src/WhiteRectDetector.cpp @@ -118,20 +118,6 @@ static void CenterEdges(const ResultPoint& y, const ResultPoint& z, const Result } } -/** -*

-* Detects a candidate barcode-like rectangular region within an image. It -* starts around the center of the image, increases the size of the candidate -* region until it finds a white rectangular region. -*

-* -* @return {@link ResultPoint}[] describing the corners of the rectangular -* region. The first and last points are opposed on the diagonal, as -* are the second and third. The first point will be the topmost -* point and the last, the bottommost. The second point will be -* leftmost and the third, the rightmost -* @throws NotFoundException if no Data Matrix Code can be found -*/ bool DetectWhiteRect(const BitMatrix& image, int initSize, int x, int y, ResultPoint& p0, ResultPoint& p1, ResultPoint& p2, ResultPoint& p3) { diff --git a/core/src/WhiteRectDetector.h b/core/src/WhiteRectDetector.h index 834d5a6db6..db4d11f0c5 100644 --- a/core/src/WhiteRectDetector.h +++ b/core/src/WhiteRectDetector.h @@ -27,7 +27,7 @@ class ResultPoint; * are the second and third. The first point will be the topmost * point and the last, the bottommost. The second point will be * leftmost and the third, the rightmost - * @throws NotFoundException if no Data Matrix Code can be found + * @return true iff white rect was found */ bool DetectWhiteRect(const BitMatrix& image, int initSize, int x, int y, ResultPoint& p0, ResultPoint& p1, ResultPoint& p2, ResultPoint& p3); diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index 86b802c532..d322157139 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -117,10 +117,7 @@ static BitArray ExtractBits(const DetectorResult& ddata) } /** -*

Performs RS error correction on an array of bits.

-* -* @return the corrected array -* @throws FormatException if the input contains too many errors +* @brief Performs RS error correction on an array of bits. */ static BitArray CorrectBits(const DetectorResult& ddata, const BitArray& rawbits) { diff --git a/core/src/aztec/AZDecoder.h b/core/src/aztec/AZDecoder.h index 9db9f80a3e..81b8ca0d9b 100644 --- a/core/src/aztec/AZDecoder.h +++ b/core/src/aztec/AZDecoder.h @@ -16,9 +16,6 @@ namespace Aztec { class DetectorResult; -/** - * @brief Decode Aztec Code after locating and extracting from an image. - */ DecoderResult Decode(const DetectorResult& detectorResult, const std::string& characterSet = ""); } // Aztec diff --git a/core/src/aztec/AZDetector.cpp b/core/src/aztec/AZDetector.cpp index edfcff0295..453509b29c 100644 --- a/core/src/aztec/AZDetector.cpp +++ b/core/src/aztec/AZDetector.cpp @@ -143,7 +143,7 @@ static bool GetCorrectedParameterData(int64_t parameterData, bool compact, int& * Extracts the number of data layers and data blocks from the layer around the bull's eye. * * @param bullsEyeCorners the array of bull's eye corners -* @throws NotFoundException in case of too many errors or invalid parameters +* @return false in case of too many errors or invalid parameters */ static bool ExtractParameters(const BitMatrix& image, const std::array& bullsEyeCorners, bool compact, int nbCenterLayers, int& nbLayers, int& nbDataBlocks, bool& readerInit, int& shift) @@ -354,7 +354,7 @@ static void ExpandSquare(std::array& cornerPoints, float oldSide * * @param pCenter Center point * @return The corners of the bull-eye -* @throws NotFoundException If no valid bull-eye can be found +* @return false If no valid bull-eye can be found */ static bool GetBullsEyeCorners(const BitMatrix& image, const PointI& pCenter, std::array& result, bool& compact, int& nbCenterLayers) { diff --git a/core/src/aztec/AZDetector.h b/core/src/aztec/AZDetector.h index 08a1edda7e..25927e2f3f 100644 --- a/core/src/aztec/AZDetector.h +++ b/core/src/aztec/AZDetector.h @@ -18,8 +18,6 @@ class DetectorResult; * Detects an Aztec Code in an image. * * @param isMirror if true, image is a mirror-image of original - * @return {@link AztecDetectorResult} encapsulating results of detecting an Aztec Code - * @throws NotFoundException if no Aztec Code can be found */ DetectorResult Detect(const BitMatrix& image, bool isMirror, bool isPure); diff --git a/core/src/aztec/AZReader.h b/core/src/aztec/AZReader.h index 6997b57ad5..3d3b1cb392 100644 --- a/core/src/aztec/AZReader.h +++ b/core/src/aztec/AZReader.h @@ -16,11 +16,6 @@ class DecodeHints; namespace Aztec { -/** -* This implementation can detect and decode Aztec codes in an image. -* -* @author David Olivier -*/ class Reader : public ZXing::Reader { public: diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 1b1f5a785d..6f2748323d 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -373,7 +373,7 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b * * @param codewordBytes data and error correction codewords * @param numDataCodewords number of codewords that are data bytes -* @throws ChecksumException if error correction fails +* @return false if error correction fails */ static bool CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) diff --git a/core/src/datamatrix/DMDecoder.h b/core/src/datamatrix/DMDecoder.h index b91fa0b47e..165b97a642 100644 --- a/core/src/datamatrix/DMDecoder.h +++ b/core/src/datamatrix/DMDecoder.h @@ -15,16 +15,6 @@ class BitMatrix; namespace DataMatrix { -/** - * @brief Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken - * to mean a black module. - * - * @param bits booleans representing white/black Data Matrix Code modules - * @param characterSet initial character encoding to use as a {@link CharacterSetECI} name string - * @return text and bytes encoded within the Data Matrix Code - * @throws FormatException if the Data Matrix Code cannot be decoded - * @throws ChecksumException if error correction fails - */ DecoderResult Decode(const BitMatrix& bits, const std::string& characterSet = ""); } // DataMatrix diff --git a/core/src/datamatrix/DMDetector.h b/core/src/datamatrix/DMDetector.h index e632b08681..4ec5d7e135 100644 --- a/core/src/datamatrix/DMDetector.h +++ b/core/src/datamatrix/DMDetector.h @@ -13,9 +13,6 @@ class DetectorResult; namespace DataMatrix { -/** - * @brief Detects a Data Matrix symbol in an image. - */ DetectorResult Detect(const BitMatrix& image, bool tryHarder, bool tryRotate, bool isPure); } // DataMatrix diff --git a/core/src/datamatrix/DMReader.cpp b/core/src/datamatrix/DMReader.cpp index a2ac999a84..988cd71d6a 100644 --- a/core/src/datamatrix/DMReader.cpp +++ b/core/src/datamatrix/DMReader.cpp @@ -25,16 +25,7 @@ Reader::Reader(const DecodeHints& hints) _characterSet(hints.characterSet()) {} -/** -* Locates and decodes a Data Matrix code in an image. -* -* @return a string representing the content encoded by the Data Matrix code -* @throws NotFoundException if a Data Matrix code cannot be found -* @throws FormatException if a Data Matrix code cannot be decoded -* @throws ChecksumException if error correction fails -*/ -Result -Reader::decode(const BinaryBitmap& image) const +Result Reader::decode(const BinaryBitmap& image) const { auto binImg = image.getBitMatrix(); if (binImg == nullptr) diff --git a/core/src/datamatrix/DMReader.h b/core/src/datamatrix/DMReader.h index 571a9c0e46..54fdb74b48 100644 --- a/core/src/datamatrix/DMReader.h +++ b/core/src/datamatrix/DMReader.h @@ -15,11 +15,6 @@ class DecodeHints; namespace DataMatrix { -/** -* This implementation can detect and decode Data Matrix codes in an image. -* -* @author bbrown@google.com (Brian Brown) -*/ class Reader : public ZXing::Reader { bool _tryRotate, _tryHarder, _isPure; diff --git a/core/src/maxicode/MCDecoder.h b/core/src/maxicode/MCDecoder.h index 7a112c64ac..d7098cd9e3 100644 --- a/core/src/maxicode/MCDecoder.h +++ b/core/src/maxicode/MCDecoder.h @@ -15,12 +15,6 @@ class BitMatrix; namespace MaxiCode { -/** -*

The main class which implements MaxiCode decoding -- as opposed to locating and extracting -* the MaxiCode from an image.

-* -* @author Manuel Kasten -*/ class Decoder { public: diff --git a/core/src/oned/ODCodabarReader.h b/core/src/oned/ODCodabarReader.h index 52e9c7a8f8..82c539f1ff 100644 --- a/core/src/oned/ODCodabarReader.h +++ b/core/src/oned/ODCodabarReader.h @@ -14,12 +14,6 @@ class DecodeHints; namespace OneD { -/** -*

Decodes Codabar barcodes.

-* -* @author Bas Vijfwinkel -* @author David Walker -*/ class CodabarReader : public RowReader { public: diff --git a/core/src/oned/ODCode128Reader.h b/core/src/oned/ODCode128Reader.h index 1a2a8db4d1..e555afa93e 100644 --- a/core/src/oned/ODCode128Reader.h +++ b/core/src/oned/ODCode128Reader.h @@ -14,11 +14,6 @@ class DecodeHints; namespace OneD { -/** -*

Decodes Code 128 barcodes.

-* -* @author Sean Owen -*/ class Code128Reader : public RowReader { public: diff --git a/core/src/oned/ODCode39Reader.h b/core/src/oned/ODCode39Reader.h index 58b4de493a..4bda353dc0 100644 --- a/core/src/oned/ODCode39Reader.h +++ b/core/src/oned/ODCode39Reader.h @@ -14,12 +14,6 @@ class DecodeHints; namespace OneD { -/** -*

Decodes Code 39 barcodes. Supports "Full ASCII Code 39" if extendedMode is true.

-* -* @author Sean Owen -* @see Code93Reader -*/ class Code39Reader : public RowReader { public: @@ -27,11 +21,6 @@ class Code39Reader : public RowReader * Creates a reader that can be configured to check the last character as a check digit, * or optionally attempt to decode "extended Code 39" sequences that are used to encode * the full ASCII character set. - * - * @param usingCheckDigit if true, treat the last data character as a check digit, not - * data, and verify that the checksum passes. - * @param extendedMode if true, will attempt to decode extended Code 39 sequences in the - * text. */ explicit Code39Reader(const DecodeHints& hints); diff --git a/core/src/oned/ODCode93Reader.h b/core/src/oned/ODCode93Reader.h index 86ea8ac593..c0733533e3 100644 --- a/core/src/oned/ODCode93Reader.h +++ b/core/src/oned/ODCode93Reader.h @@ -11,12 +11,6 @@ namespace ZXing { namespace OneD { -/** -*

Decodes Code 93 barcodes.

-* -* @author Sean Owen -* @see Code39Reader -*/ class Code93Reader : public RowReader { public: diff --git a/core/src/oned/ODITFReader.h b/core/src/oned/ODITFReader.h index 7f494d76bf..0277c52a7d 100644 --- a/core/src/oned/ODITFReader.h +++ b/core/src/oned/ODITFReader.h @@ -24,12 +24,10 @@ namespace OneD { * lengths are scanned, especially shorter ones, to avoid false positives. This in turn is due to a lack of * required checksum function.

* -*

The checksum is optional and is only applied by this Reader if the assumeITFCheckDigit hint is given.

+*

The checksum is optional and is only applied by this Reader if the validateITFCheckSum hint is given.

* *
-* -* @author kevin.osullivan@sita.aero, SITA Lab. */ class ITFReader : public RowReader { diff --git a/core/src/oned/ODMultiUPCEANReader.h b/core/src/oned/ODMultiUPCEANReader.h index 999c542290..e4537ca287 100644 --- a/core/src/oned/ODMultiUPCEANReader.h +++ b/core/src/oned/ODMultiUPCEANReader.h @@ -14,11 +14,7 @@ namespace ZXing { namespace OneD { /** -*

A reader that can read all available UPC/EAN formats. If a caller wants to try to -* read all such formats, it is most efficient to use this implementation rather than invoke -* individual readers.

-* -* @author Sean Owen +* @brief A reader that can read all available UPC/EAN formats. */ class MultiUPCEANReader : public RowReader { diff --git a/core/src/oned/ODReader.h b/core/src/oned/ODReader.h index bccd417c2e..c2f5336a2e 100644 --- a/core/src/oned/ODReader.h +++ b/core/src/oned/ODReader.h @@ -19,10 +19,6 @@ namespace OneD { class RowReader; -/** -* @author dswitkin@google.com (Daniel Switkin) -* @author Sean Owen -*/ class Reader : public ZXing::Reader { public: diff --git a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp index 444ecf4982..1ebe6237f9 100644 --- a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp +++ b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp @@ -314,7 +314,6 @@ std::string DecodeExpandedBits(const BitArray& bits) } return {}; - //throw new IllegalStateException("unknown decoder: " + information); } } // namespace ZXing::OneD::DataBar diff --git a/core/src/pdf417/PDFDetector.cpp b/core/src/pdf417/PDFDetector.cpp index 3d29fc59fb..72bf872cff 100644 --- a/core/src/pdf417/PDFDetector.cpp +++ b/core/src/pdf417/PDFDetector.cpp @@ -329,11 +329,8 @@ bool HasStartPattern(const BitMatrix& m) *

Detects a PDF417 Code in an image. Only checks 0 and 180 degree rotations.

* * @param image barcode image to decode -* @param hints optional hints to detector * @param multiple if true, then the image is searched for multiple codes. If false, then at most one code will * be found and returned -* @return {@link PDF417DetectorResult} encapsulating results of detecting a PDF417 code -* @throws NotFoundException if no PDF417 Code can be found */ DecodeStatus Detector::Detect(const BinaryBitmap& image, bool multiple, Result& result) diff --git a/core/src/pdf417/PDFScanningDecoder.cpp b/core/src/pdf417/PDFScanningDecoder.cpp index 5191dffd5e..b36d55a1b5 100644 --- a/core/src/pdf417/PDFScanningDecoder.cpp +++ b/core/src/pdf417/PDFScanningDecoder.cpp @@ -458,8 +458,7 @@ static std::vector FindErrorMagnitudes(const ModulusPoly& errorEvaluator, c * @param received received codewords * @param numECCodewords number of those codewords used for EC * @param erasures location of erasures -* @return number of errors -* @throws ChecksumException if errors cannot be corrected, maybe because of too many errors +* @return false if errors cannot be corrected, maybe because of too many errors */ ZXING_EXPORT_TEST_ONLY bool DecodeErrorCorrection(std::vector& received, int numECCodewords, const std::vector& erasures, int& nbErrors) @@ -527,7 +526,7 @@ bool DecodeErrorCorrection(std::vector& received, int numECCodewords, const * @param codewords data and error correction codewords * @param erasures positions of any known erasures * @param numECCodewords number of error correction codewords that are available in codewords -* @throws ChecksumException if error correction fails +* @return false if error correction fails */ static bool CorrectErrors(std::vector& codewords, const std::vector& erasures, int numECCodewords, int& errorCount) { diff --git a/core/src/qrcode/QRCodecMode.h b/core/src/qrcode/QRCodecMode.h index f55bc677f6..830b2f897a 100644 --- a/core/src/qrcode/QRCodecMode.h +++ b/core/src/qrcode/QRCodecMode.h @@ -33,7 +33,7 @@ enum class CodecMode * @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 IllegalArgumentException if bits do not correspond to a known mode + * @throws std::invalid_argument if bits do not correspond to a known mode */ CodecMode CodecModeForBits(int bits, bool isMirco = false); diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index f454b012fc..994f044bb4 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -36,7 +36,7 @@ namespace ZXing::QRCode { * * @param codewordBytes data and error correction codewords * @param numDataCodewords number of codewords that are data bytes -* @throws ChecksumException if error correction fails +* @return false if error correction fails */ static bool CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) { diff --git a/core/src/qrcode/QRDecoder.h b/core/src/qrcode/QRDecoder.h index 32194d8899..e4ab05d5c7 100644 --- a/core/src/qrcode/QRDecoder.h +++ b/core/src/qrcode/QRDecoder.h @@ -15,9 +15,6 @@ class BitMatrix; namespace QRCode { -/** - * @brief Decodes a QR Code from the BitMatrix and the hinted charset. - */ DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset = {}); } // QRCode diff --git a/core/src/qrcode/QRReader.h b/core/src/qrcode/QRReader.h index d7e96f935f..f8fe736a2f 100644 --- a/core/src/qrcode/QRReader.h +++ b/core/src/qrcode/QRReader.h @@ -16,11 +16,6 @@ class DecodeHints; namespace QRCode { -/** -* This implementation can detect and decode QR Codes in an image. -* -* @author Sean Owen -*/ class Reader : public ZXing::Reader { public: From 77aa450a8edb253f3b32bb2b28e60b4bb125e2f9 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 22 Jun 2022 22:28:25 +0200 Subject: [PATCH 113/180] MCDecoder: make Decoder::Decode a simple function --- core/src/maxicode/MCDecoder.cpp | 2 +- core/src/maxicode/MCDecoder.h | 6 +----- core/src/maxicode/MCReader.cpp | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index e3a781f2bb..90f6bee8db 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -300,7 +300,7 @@ DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& /*cha } // DecodedBitStreamParser -DecoderResult Decoder::Decode(const BitMatrix& bits, const std::string& characterSet) +DecoderResult Decode(const BitMatrix& bits, const std::string& characterSet) { ByteArray codewords = BitMatrixParser::ReadCodewords(bits); diff --git a/core/src/maxicode/MCDecoder.h b/core/src/maxicode/MCDecoder.h index d7098cd9e3..1ed0c80eb8 100644 --- a/core/src/maxicode/MCDecoder.h +++ b/core/src/maxicode/MCDecoder.h @@ -15,11 +15,7 @@ class BitMatrix; namespace MaxiCode { -class Decoder -{ -public: - static DecoderResult Decode(const BitMatrix& bits, const std::string& characterSet); -}; +DecoderResult Decode(const BitMatrix& bits, const std::string& characterSet); } // MaxiCode } // ZXing diff --git a/core/src/maxicode/MCReader.cpp b/core/src/maxicode/MCReader.cpp index dd6f7cc0f3..9131b1200f 100644 --- a/core/src/maxicode/MCReader.cpp +++ b/core/src/maxicode/MCReader.cpp @@ -59,7 +59,7 @@ Reader::decode(const BinaryBitmap& image) const return Result(DecodeStatus::NotFound); } - return Result(Decoder::Decode(bits, _characterSet), {}, BarcodeFormat::MaxiCode); + return Result(Decode(bits, _characterSet), {}, BarcodeFormat::MaxiCode); } } // namespace ZXing::MaxiCode From aee37e7486ac5c2bc448bb5ff528376296e79718 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 23 Jun 2022 13:14:02 +0200 Subject: [PATCH 114/180] Errors: harmonize internal error reporting in QR and DM decoders --- core/src/datamatrix/DMDecoder.cpp | 8 ++++---- core/src/qrcode/QRDecoder.cpp | 20 ++++++++------------ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 6f2748323d..38a2f1aa0c 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -293,7 +293,7 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b while (!done && bits.available() >= 8) { int oneByte = bits.readBits(8); switch (oneByte) { - case 0: return DecodeStatus::FormatError; + case 0: throw std::runtime_error("invalid 0 code word"); case 129: done = true; break; // Pad -> we are done, ignore the rest of the bits case 230: DecodeC40OrTextSegment(bits, result, Mode::C40); break; case 231: DecodeBase256Segment(bits, result); break; @@ -310,13 +310,13 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b break; case 233: // Structured Append if (!firstCodeword) // Must be first ISO 16022:2006 5.6.1 - return DecodeStatus::FormatError; + throw std::runtime_error("structured append tag must be first code word"); ParseStructuredAppend(bits, sai); firstFNC1Position = 5; break; case 234: // Reader Programming if (!firstCodeword) // Must be first ISO 16022:2006 5.2.4.9 - return DecodeStatus::FormatError; + throw std::runtime_error("reader programming tag must be first code word"); readerInit = true; break; case 235: upperShift.set = true; break; // Upper Shift (shift to Extended ASCII) @@ -344,7 +344,7 @@ DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const b // work around encoders that use unlatch to ASCII as last code word (ask upstream) if (oneByte == 254 && bits.available() == 0) break; - return DecodeStatus::FormatError; + throw std::runtime_error("invalid code word"); } } firstCodeword = false; diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 994f044bb4..f56876e1f7 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -309,7 +309,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo // 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 - return DecodeStatus::FormatError; + throw std::runtime_error("Unsupported HANZI subset"); int count = bits.readBits(CharacterCountBits(mode, version)); DecodeHanziSegment(bits, count, result); break; @@ -323,7 +323,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo 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: return DecodeStatus::FormatError; + default: throw std::runtime_error("Invalid CodecMode"); } break; } @@ -343,8 +343,13 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo .setStructuredAppend(structuredAppend); } -static DecoderResult DoDecode(const BitMatrix& bits, const Version& version, const std::string& hintedCharset) +DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset) { + const Version* pversion = ReadVersion(bits); + if (!pversion) + return DecodeStatus::FormatError; + const Version& version = *pversion; + auto formatInfo = ReadFormatInformation(bits, version.isMicroQRCode()); if (!formatInfo.isValid()) return DecodeStatus::FormatError; @@ -381,13 +386,4 @@ static DecoderResult DoDecode(const BitMatrix& bits, const Version& version, con return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel, hintedCharset).setIsMirrored(formatInfo.isMirrored); } -DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset) -{ - const Version* version = ReadVersion(bits); - if (!version) - return DecodeStatus::FormatError; - - return DoDecode(bits, *version, hintedCharset); -} - } // namespace ZXing::QRCode From ccbf2bee9380f29b341dfb780c094405cc8d01fc Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 23 Jun 2022 13:52:19 +0200 Subject: [PATCH 115/180] ECI: remove the whole charset business from the decoder call tree Don't pass that string down to the inner most decoder functions, just to set the Content::defaultCharset member, which is only evaluated once the result is passed to the user code anyway. --- core/src/Content.cpp | 2 +- core/src/Content.h | 2 +- core/src/ReadBarcode.cpp | 5 +++- core/src/Result.cpp | 7 +++++ core/src/Result.h | 1 + core/src/aztec/AZDecoder.cpp | 7 +++-- core/src/aztec/AZDecoder.h | 2 +- core/src/aztec/AZReader.cpp | 6 ++--- core/src/aztec/AZReader.h | 1 - core/src/datamatrix/DMDecoder.cpp | 13 +++++----- core/src/datamatrix/DMDecoder.h | 2 +- core/src/datamatrix/DMReader.cpp | 5 ++-- core/src/datamatrix/DMReader.h | 2 +- core/src/maxicode/MCDecoder.cpp | 8 +++--- core/src/maxicode/MCDecoder.h | 2 +- core/src/maxicode/MCReader.cpp | 4 +-- core/src/maxicode/MCReader.h | 1 - core/src/pdf417/PDFDecodedBitStreamParser.cpp | 7 +++-- core/src/pdf417/PDFDecodedBitStreamParser.h | 2 +- core/src/pdf417/PDFReader.cpp | 22 +++++++--------- core/src/pdf417/PDFReader.h | 1 - core/src/pdf417/PDFScanningDecoder.cpp | 17 ++++++------ core/src/pdf417/PDFScanningDecoder.h | 2 +- core/src/qrcode/QRDecoder.cpp | 8 +++--- core/src/qrcode/QRDecoder.h | 2 +- core/src/qrcode/QRReader.cpp | 9 +++---- core/src/qrcode/QRReader.h | 1 - test/fuzz/fuzzDMDecoder.cpp | 4 +-- test/unit/aztec/AZDecoderTest.cpp | 2 +- test/unit/aztec/AZHighLevelEncoderTest.cpp | 2 +- .../DMDecodedBitStreamParserTest.cpp | 4 +-- test/unit/maxicode/MCDecoderTest.cpp | 4 +-- test/unit/pdf417/PDF417DecoderTest.cpp | 4 +-- .../qrcode/QRDecodedBitStreamParserTest.cpp | 26 +++++++++---------- 34 files changed, 92 insertions(+), 95 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 280c3e41ee..ae0ac9157b 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -101,7 +101,7 @@ std::wstring Content::render(bool withECI) const if (withECI) res = TextDecoder::FromLatin1(symbology.toString(true)); ECI lastECI = ECI::Unknown; - auto fallbackCS = CharacterSetFromString(hintedCharset); + auto fallbackCS = CharacterSetFromString(defaultCharset); if (!hasECI && fallbackCS == CharacterSet::Unknown) fallbackCS = guessEncoding(); diff --git a/core/src/Content.h b/core/src/Content.h index 13e5e7c365..cce0b541d2 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -44,7 +44,7 @@ class Content ByteArray bytes; std::vector encodings; - std::string hintedCharset; + std::string defaultCharset; std::string applicationIndicator; SymbologyIdentifier symbology; bool hasECI = false; diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 4ffc2e8aab..9f95eb4688 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -127,7 +127,7 @@ Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) LumImage lum; ImageView iv = SetupLumImageView(_iv, lum, hints); - return MultiFormatReader(hints).read(*CreateBitmap(hints.binarizer(), iv)); + return MultiFormatReader(hints).read(*CreateBitmap(hints.binarizer(), iv)).setCharacterSet(hints.characterSet()); } } @@ -159,6 +159,9 @@ Results ReadBarcodes(const ImageView& _iv, const DecodeHints& hints) break; } + for (auto& res : results) + res.setCharacterSet(hints.characterSet()); + return results; } diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 8e3ed62867..4bd0d320f0 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -106,6 +106,13 @@ std::string Result::sequenceId() const return _sai.id; } +Result& Result::setCharacterSet(const std::string& defaultCS) +{ + if (!defaultCS.empty()) + _content.defaultCharset = defaultCS; + return *this; +} + bool Result::operator==(const Result& o) const { if (format() != o.format() || bytes() != o.bytes()) diff --git a/core/src/Result.h b/core/src/Result.h index 735f2f2d23..c76f4abffd 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -147,6 +147,7 @@ class Result // only for internal use void incrementLineCount() { ++_lineCount; } + Result& setCharacterSet(const std::string& defaultCS); bool operator==(const Result& o) const; diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index d322157139..ebb7c6c083 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -299,11 +299,10 @@ static void DecodeContent(const BitArray& bits, Content& res) } ZXING_EXPORT_TEST_ONLY -DecoderResult Decode(const BitArray& bits, const std::string& characterSet) +DecoderResult Decode(const BitArray& bits) { Content res; res.symbology = {'z', '0', 3}; - res.hintedCharset = characterSet; try { DecodeContent(bits, res); @@ -347,14 +346,14 @@ DecoderResult Decode(const BitArray& bits, const std::string& characterSet) return DecoderResult(bits.toBytes(), std::move(res)).setNumBits(Size(bits)).setStructuredAppend(sai); } -DecoderResult Decode(const DetectorResult& detectorResult, const std::string& characterSet) +DecoderResult Decode(const DetectorResult& detectorResult) { BitArray bits = CorrectBits(detectorResult, ExtractBits(detectorResult)); if (!bits.size()) return DecodeStatus::FormatError; - return Decode(bits, characterSet).setReaderInit(detectorResult.readerInit()); + return Decode(bits).setReaderInit(detectorResult.readerInit()); } } // namespace ZXing::Aztec diff --git a/core/src/aztec/AZDecoder.h b/core/src/aztec/AZDecoder.h index 81b8ca0d9b..2054c2fd92 100644 --- a/core/src/aztec/AZDecoder.h +++ b/core/src/aztec/AZDecoder.h @@ -16,7 +16,7 @@ namespace Aztec { class DetectorResult; -DecoderResult Decode(const DetectorResult& detectorResult, const std::string& characterSet = ""); +DecoderResult Decode(const DetectorResult& detectorResult); } // Aztec } // ZXing diff --git a/core/src/aztec/AZReader.cpp b/core/src/aztec/AZReader.cpp index 0f6547ae4d..4ca316c416 100644 --- a/core/src/aztec/AZReader.cpp +++ b/core/src/aztec/AZReader.cpp @@ -20,7 +20,7 @@ namespace ZXing::Aztec { Reader::Reader(const DecodeHints& hints) - : _isPure(hints.isPure()), _characterSet(hints.characterSet()) + : _isPure(hints.isPure()) { } @@ -35,14 +35,14 @@ Reader::decode(const BinaryBitmap& image) const DetectorResult detectResult = Detect(*binImg, false, _isPure); DecoderResult decodeResult = DecodeStatus::NotFound; if (detectResult.isValid()) { - decodeResult = Decode(detectResult, _characterSet); + decodeResult = Decode(detectResult); } //TODO: don't start detection all over again, just to swap 2 corner points if (!decodeResult.isValid()) { detectResult = Detect(*binImg, true, _isPure); if (detectResult.isValid()) { - decodeResult = Decode(detectResult, _characterSet); + decodeResult = Decode(detectResult); } } diff --git a/core/src/aztec/AZReader.h b/core/src/aztec/AZReader.h index 3d3b1cb392..df5a837507 100644 --- a/core/src/aztec/AZReader.h +++ b/core/src/aztec/AZReader.h @@ -24,7 +24,6 @@ class Reader : public ZXing::Reader private: bool _isPure; - std::string _characterSet; }; } // Aztec diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 38a2f1aa0c..8284833b17 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -273,12 +273,11 @@ static void DecodeBase256Segment(BitSource& bits, Content& result) } ZXING_EXPORT_TEST_ONLY -DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const bool isDMRE) +DecoderResult Decode(ByteArray&& bytes, const bool isDMRE) { BitSource bits(bytes); Content result; result.symbology = {'d', '1', 3}; // ECC 200 (ISO 16022:2006 Annex N Table N.1) - result.hintedCharset = characterSet; std::string resultTrailer; struct StructuredAppendInfo sai; @@ -392,7 +391,7 @@ CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) return true; } -static DecoderResult DoDecode(const BitMatrix& bits, const std::string& characterSet) +static DecoderResult DoDecode(const BitMatrix& bits) { // Construct a parser and read version, error-correction level const Version* version = VersionForDimensionsOf(bits); @@ -428,7 +427,7 @@ static DecoderResult DoDecode(const BitMatrix& bits, const std::string& characte } // Decode the contents of that stream of bytes - return DecodedBitStreamParser::Decode(std::move(resultBytes), characterSet, version->isDMRE()); + return DecodedBitStreamParser::Decode(std::move(resultBytes), version->isDMRE()); } static BitMatrix FlippedL(const BitMatrix& bits) @@ -440,16 +439,16 @@ static BitMatrix FlippedL(const BitMatrix& bits) return res; } -DecoderResult Decode(const BitMatrix& bits, const std::string& characterSet) +DecoderResult Decode(const BitMatrix& bits) { - auto res = DoDecode(bits, characterSet); + auto res = DoDecode(bits); if (res.isValid()) return res; //TODO: // * unify bit mirroring helper code with QRReader? // * rectangular symbols with the a size of 8 x Y are not supported a.t.m. - if (auto mirroredRes = DoDecode(FlippedL(bits), characterSet); mirroredRes.isValid()) { + if (auto mirroredRes = DoDecode(FlippedL(bits)); mirroredRes.isValid()) { mirroredRes.setIsMirrored(true); return mirroredRes; } diff --git a/core/src/datamatrix/DMDecoder.h b/core/src/datamatrix/DMDecoder.h index 165b97a642..e8aac914f6 100644 --- a/core/src/datamatrix/DMDecoder.h +++ b/core/src/datamatrix/DMDecoder.h @@ -15,7 +15,7 @@ class BitMatrix; namespace DataMatrix { -DecoderResult Decode(const BitMatrix& bits, const std::string& characterSet = ""); +DecoderResult Decode(const BitMatrix& bits); } // DataMatrix } // ZXing diff --git a/core/src/datamatrix/DMReader.cpp b/core/src/datamatrix/DMReader.cpp index 988cd71d6a..fc4c1bdec6 100644 --- a/core/src/datamatrix/DMReader.cpp +++ b/core/src/datamatrix/DMReader.cpp @@ -21,8 +21,7 @@ namespace ZXing::DataMatrix { Reader::Reader(const DecodeHints& hints) : _tryRotate(hints.tryRotate()), _tryHarder(hints.tryHarder()), - _isPure(hints.isPure()), - _characterSet(hints.characterSet()) + _isPure(hints.isPure()) {} Result Reader::decode(const BinaryBitmap& image) const @@ -35,7 +34,7 @@ Result Reader::decode(const BinaryBitmap& image) const if (!detectorResult.isValid()) return Result(DecodeStatus::NotFound); - return Result(Decode(detectorResult.bits(), _characterSet), std::move(detectorResult).position(), BarcodeFormat::DataMatrix); + return Result(Decode(detectorResult.bits()), std::move(detectorResult).position(), BarcodeFormat::DataMatrix); } } // namespace ZXing::DataMatrix diff --git a/core/src/datamatrix/DMReader.h b/core/src/datamatrix/DMReader.h index 54fdb74b48..a0c728ae41 100644 --- a/core/src/datamatrix/DMReader.h +++ b/core/src/datamatrix/DMReader.h @@ -18,7 +18,7 @@ namespace DataMatrix { class Reader : public ZXing::Reader { bool _tryRotate, _tryHarder, _isPure; - std::string _characterSet; + public: explicit Reader(const DecodeHints& hints); Result decode(const BinaryBitmap& image) const override; diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 90f6bee8db..30a6b87913 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -267,11 +267,11 @@ static void GetMessage(const ByteArray& bytes, int start, int len, Content& resu } ZXING_EXPORT_TEST_ONLY -DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& /*characterSet*/) +DecoderResult Decode(ByteArray&& bytes, const int mode) { Content result; result.symbology = {'U', (mode == 2 || mode == 3) ? '1' : '0', 2}; // TODO: No identifier defined for mode 6? - result.hintedCharset = "ISO8859_1"; + result.defaultCharset = "ISO8859_1"; StructuredAppendInfo sai; switch (mode) { @@ -300,7 +300,7 @@ DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& /*cha } // DecodedBitStreamParser -DecoderResult Decode(const BitMatrix& bits, const std::string& characterSet) +DecoderResult Decode(const BitMatrix& bits) { ByteArray codewords = BitMatrixParser::ReadCodewords(bits); @@ -331,7 +331,7 @@ DecoderResult Decode(const BitMatrix& bits, const std::string& characterSet) std::copy_n(codewords.begin(), 10, datawords.begin()); std::copy_n(codewords.begin() + 20, datawords.size() - 10, datawords.begin() + 10); - return DecodedBitStreamParser::Decode(std::move(datawords), mode, characterSet); + return DecodedBitStreamParser::Decode(std::move(datawords), mode); } } // namespace ZXing::MaxiCode diff --git a/core/src/maxicode/MCDecoder.h b/core/src/maxicode/MCDecoder.h index 1ed0c80eb8..e53f533766 100644 --- a/core/src/maxicode/MCDecoder.h +++ b/core/src/maxicode/MCDecoder.h @@ -15,7 +15,7 @@ class BitMatrix; namespace MaxiCode { -DecoderResult Decode(const BitMatrix& bits, const std::string& characterSet); +DecoderResult Decode(const BitMatrix& bits); } // MaxiCode } // ZXing diff --git a/core/src/maxicode/MCReader.cpp b/core/src/maxicode/MCReader.cpp index 9131b1200f..ce3c9a9125 100644 --- a/core/src/maxicode/MCReader.cpp +++ b/core/src/maxicode/MCReader.cpp @@ -43,7 +43,7 @@ static BitMatrix ExtractPureBits(const BitMatrix& image) return result; } -Reader::Reader(const DecodeHints& hints) : _isPure(hints.isPure()), _characterSet(hints.characterSet()) {} +Reader::Reader(const DecodeHints& hints) : _isPure(hints.isPure()) {} Result Reader::decode(const BinaryBitmap& image) const @@ -59,7 +59,7 @@ Reader::decode(const BinaryBitmap& image) const return Result(DecodeStatus::NotFound); } - return Result(Decode(bits, _characterSet), {}, BarcodeFormat::MaxiCode); + return Result(Decode(bits), {}, BarcodeFormat::MaxiCode); } } // namespace ZXing::MaxiCode diff --git a/core/src/maxicode/MCReader.h b/core/src/maxicode/MCReader.h index 3dacd59f9c..9b907d0f37 100644 --- a/core/src/maxicode/MCReader.h +++ b/core/src/maxicode/MCReader.h @@ -19,7 +19,6 @@ namespace MaxiCode { class Reader : public ZXing::Reader { bool _isPure; - std::string _characterSet; public: explicit Reader(const DecodeHints& hints); diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index 3a5cc5eef8..b481549f29 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -564,7 +564,7 @@ static int DecodeMacroOptionalTextField(DecodeStatus& status, const std::vector< Content result; // Each optional field begins with an implied reset to ECI 2 (Annex H.2.3). ECI 2 is ASCII for 0-127, and Cp437 // for non-ASCII (128-255). Text optional fields can contain ECIs. - result.hintedCharset = "Cp437"; + result.defaultCharset = "Cp437"; codeIndex = TextCompaction(status, codewords, codeIndex, result); @@ -583,7 +583,7 @@ static int DecodeMacroOptionalNumericField(DecodeStatus& status, const std::vect Content result; // Each optional field begins with an implied reset to ECI 2 (Annex H.2.3). ECI 2 is ASCII for 0-127, and Cp437 // for non-ASCII (128-255). Text optional fields can contain ECIs. - result.hintedCharset = "Cp437"; + result.defaultCharset = "Cp437"; codeIndex = NumericCompaction(status, codewords, codeIndex, result); @@ -707,11 +707,10 @@ DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, } DecoderResult -DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel, const std::string& characterSet) +DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel) { Content result; result.symbology = { 'L', '2', -1 }; - result.hintedCharset = characterSet; bool readerInit = false; auto resultMetadata = std::make_shared(); diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.h b/core/src/pdf417/PDFDecodedBitStreamParser.h index 39b03fed41..103db7c5b0 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.h +++ b/core/src/pdf417/PDFDecodedBitStreamParser.h @@ -24,7 +24,7 @@ namespace Pdf417 { class DecodedBitStreamParser { public: - static DecoderResult Decode(const std::vector& codewords, int ecLevel, const std::string& characterSet); + static DecoderResult Decode(const std::vector& codewords, int ecLevel); }; } // Pdf417 diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index 7d179b37a7..130a06d3a7 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -66,8 +66,7 @@ static int GetMaxCodewordWidth(const std::array, 8>& p) std::max(GetMaxWidth(p[1], p[5]), GetMaxWidth(p[7], p[3]) * CodewordDecoder::MODULES_IN_CODEWORD / MODULES_IN_STOP_PATTERN)); } -DecodeStatus DoDecode(const BinaryBitmap& image, bool multiple, std::list& results, - const std::string& characterSet) +DecodeStatus DoDecode(const BinaryBitmap& image, bool multiple, std::list& results) { Detector::Result detectorResult; DecodeStatus status = Detector::Detect(image, multiple, detectorResult); @@ -78,7 +77,7 @@ DecodeStatus DoDecode(const BinaryBitmap& image, bool multiple, std::list ReadCodeWords(BitMatrixCursor topCur, SymbolInfo info) } // forward declaration from PDFScanningDecoder.cpp -DecoderResult DecodeCodewords(std::vector& codewords, int ecLevel, const std::vector& erasures, - const std::string& characterSet); +DecoderResult DecodeCodewords(std::vector& codewords, int ecLevel, const std::vector& erasures); -static Result DecodePure(const BinaryBitmap& image_, const std::string& characterSet) +static Result DecodePure(const BinaryBitmap& image_) { auto pimage = image_.getBitMatrix(); if (!pimage) @@ -301,18 +299,18 @@ static Result DecodePure(const BinaryBitmap& image_, const std::string& characte erasures.push_back(i); } - auto res = DecodeCodewords(codeWords, info.ecLevel, erasures, characterSet); + auto res = DecodeCodewords(codeWords, info.ecLevel, erasures); return Result(std::move(res), {{left, top}, {right, top}, {right, bottom}, {left, bottom}}, BarcodeFormat::PDF417); } -Reader::Reader(const DecodeHints& hints) : _isPure(hints.isPure()), _characterSet(hints.characterSet()) {} +Reader::Reader(const DecodeHints& hints) : _isPure(hints.isPure()) {} Result Reader::decode(const BinaryBitmap& image) const { if (_isPure) { - auto res = DecodePure(image, _characterSet); + auto res = DecodePure(image); if (res.status() != DecodeStatus::ChecksumError) return res; // This falls through and tries the non-pure code path if we have a checksum error. This approach is @@ -320,7 +318,7 @@ Reader::decode(const BinaryBitmap& image) const } std::list results; - DecodeStatus status = DoDecode(image, false, results, _characterSet); + DecodeStatus status = DoDecode(image, false, results); if (StatusIsOK(status)) { return results.front(); } @@ -330,7 +328,7 @@ Reader::decode(const BinaryBitmap& image) const Results Reader::decode(const BinaryBitmap& image, [[maybe_unused]] int maxSymbols) const { std::list results; - DoDecode(image, true, results, _characterSet); + DoDecode(image, true, results); return Results(results.begin(), results.end()); } @@ -338,7 +336,7 @@ std::list Reader::decodeMultiple(const BinaryBitmap& image) const { std::list results; - DoDecode(image, true, results, _characterSet); + DoDecode(image, true, results); return results; } diff --git a/core/src/pdf417/PDFReader.h b/core/src/pdf417/PDFReader.h index 435a50f252..b3cd81779a 100644 --- a/core/src/pdf417/PDFReader.h +++ b/core/src/pdf417/PDFReader.h @@ -25,7 +25,6 @@ namespace Pdf417 { class Reader : public ZXing::Reader { bool _isPure; - std::string _characterSet; public: explicit Reader(const DecodeHints& hints); diff --git a/core/src/pdf417/PDFScanningDecoder.cpp b/core/src/pdf417/PDFScanningDecoder.cpp index b36d55a1b5..d946fa0a16 100644 --- a/core/src/pdf417/PDFScanningDecoder.cpp +++ b/core/src/pdf417/PDFScanningDecoder.cpp @@ -568,8 +568,7 @@ static bool VerifyCodewordCount(std::vector& codewords, int numECCodewords) return true; } -DecoderResult DecodeCodewords(std::vector& codewords, int ecLevel, const std::vector& erasures, - const std::string& characterSet) +DecoderResult DecodeCodewords(std::vector& codewords, int ecLevel, const std::vector& erasures) { if (codewords.empty()) { return DecodeStatus::FormatError; @@ -584,7 +583,7 @@ DecoderResult DecodeCodewords(std::vector& codewords, int ecLevel, const st return DecodeStatus::FormatError; // Decode the codewords - return DecodedBitStreamParser::Decode(codewords, ecLevel, characterSet); + return DecodedBitStreamParser::Decode(codewords, ecLevel); } @@ -603,7 +602,7 @@ DecoderResult DecodeCodewords(std::vector& codewords, int ecLevel, const st */ static DecoderResult CreateDecoderResultFromAmbiguousValues(int ecLevel, std::vector& codewords, const std::vector& erasureArray, const std::vector& ambiguousIndexes, - const std::vector>& ambiguousIndexValues, const std::string& characterSet) + const std::vector>& ambiguousIndexValues) { std::vector ambiguousIndexCount(ambiguousIndexes.size(), 0); @@ -612,7 +611,7 @@ static DecoderResult CreateDecoderResultFromAmbiguousValues(int ecLevel, std::ve for (size_t i = 0; i < ambiguousIndexCount.size(); i++) { codewords[ambiguousIndexes[i]] = ambiguousIndexValues[i][ambiguousIndexCount[i]]; } - auto result = DecodeCodewords(codewords, ecLevel, erasureArray, characterSet); + auto result = DecodeCodewords(codewords, ecLevel, erasureArray); if (result.errorCode() != DecodeStatus::ChecksumError) { return result; } @@ -637,7 +636,7 @@ static DecoderResult CreateDecoderResultFromAmbiguousValues(int ecLevel, std::ve } -static DecoderResult CreateDecoderResult(DetectionResult& detectionResult, const std::string& characterSet) +static DecoderResult CreateDecoderResult(DetectionResult& detectionResult) { auto barcodeMatrix = CreateBarcodeMatrix(detectionResult); if (!AdjustCodewordCount(detectionResult, barcodeMatrix)) { @@ -664,7 +663,7 @@ static DecoderResult CreateDecoderResult(DetectionResult& detectionResult, const } } return CreateDecoderResultFromAmbiguousValues(detectionResult.barcodeECLevel(), codewords, erasures, - ambiguousIndexesList, ambiguousIndexValues, characterSet); + ambiguousIndexesList, ambiguousIndexValues); } @@ -675,7 +674,7 @@ static DecoderResult CreateDecoderResult(DetectionResult& detectionResult, const DecoderResult ScanningDecoder::Decode(const BitMatrix& image, const Nullable& imageTopLeft, const Nullable& imageBottomLeft, const Nullable& imageTopRight, const Nullable& imageBottomRight, - int minCodewordWidth, int maxCodewordWidth, const std::string& characterSet) + int minCodewordWidth, int maxCodewordWidth) { BoundingBox boundingBox; if (!BoundingBox::Create(image.width(), image.height(), imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight, boundingBox)) { @@ -736,7 +735,7 @@ ScanningDecoder::Decode(const BitMatrix& image, const Nullable& ima } } } - return CreateDecoderResult(detectionResult, characterSet); + return CreateDecoderResult(detectionResult); } } // Pdf417 diff --git a/core/src/pdf417/PDFScanningDecoder.h b/core/src/pdf417/PDFScanningDecoder.h index 08d795649c..2c34aea07c 100644 --- a/core/src/pdf417/PDFScanningDecoder.h +++ b/core/src/pdf417/PDFScanningDecoder.h @@ -26,7 +26,7 @@ class ScanningDecoder static DecoderResult Decode(const BitMatrix& image, const Nullable& imageTopLeft, const Nullable& imageBottomLeft, const Nullable& imageTopRight, const Nullable& imageBottomRight, - int minCodewordWidth, int maxCodewordWidth, const std::string& characterSet); + int minCodewordWidth, int maxCodewordWidth); }; } // Pdf417 diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index f56876e1f7..90d80b2723 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -253,13 +253,11 @@ bool IsEndOfStream(const BitSource& bits, const Version& version) *

See ISO 18004:2006, 6.4.3 - 6.4.7

*/ ZXING_EXPORT_TEST_ONLY -DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel, - const std::string& hintedCharset) +DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel) { BitSource bits(bytes); Content result; result.symbology = {'Q', '1', 1}; - result.hintedCharset = hintedCharset.empty() ? "Auto" : hintedCharset; StructuredAppendInfo structuredAppend; const int modeBitLength = CodecModeBitsLength(version); @@ -343,7 +341,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo .setStructuredAppend(structuredAppend); } -DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset) +DecoderResult Decode(const BitMatrix& bits) { const Version* pversion = ReadVersion(bits); if (!pversion) @@ -383,7 +381,7 @@ DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset) } // Decode the contents of that stream of bytes - return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel, hintedCharset).setIsMirrored(formatInfo.isMirrored); + return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel).setIsMirrored(formatInfo.isMirrored); } } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRDecoder.h b/core/src/qrcode/QRDecoder.h index e4ab05d5c7..ecfdc92be1 100644 --- a/core/src/qrcode/QRDecoder.h +++ b/core/src/qrcode/QRDecoder.h @@ -15,7 +15,7 @@ class BitMatrix; namespace QRCode { -DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset = {}); +DecoderResult Decode(const BitMatrix& bits); } // QRCode } // ZXing diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index 60007c87a4..869373415f 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -25,8 +25,7 @@ Reader::Reader(const DecodeHints& hints) : _tryHarder(hints.tryHarder()), _isPure(hints.isPure()), _testQR(hints.hasFormat(BarcodeFormat::QRCode)), - _testMQR(hints.hasFormat(BarcodeFormat::MicroQRCode)), - _charset(hints.characterSet()) + _testMQR(hints.hasFormat(BarcodeFormat::MicroQRCode)) {} Result Reader::decode(const BinaryBitmap& image) const @@ -52,7 +51,7 @@ Result Reader::decode(const BinaryBitmap& image) const if (!detectorResult.isValid()) return Result(DecodeStatus::NotFound); - auto decoderResult = Decode(detectorResult.bits(), _charset); + auto decoderResult = Decode(detectorResult.bits()); auto position = detectorResult.position(); return Result(std::move(decoderResult), std::move(position), @@ -82,7 +81,7 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const auto detectorResult = SampleQR(*binImg, fpSet); if (detectorResult.isValid()) { - auto decoderResult = Decode(detectorResult.bits(), _charset); + auto decoderResult = Decode(detectorResult.bits()); auto position = detectorResult.position(); if (decoderResult.isValid()) { usedFPs.push_back(fpSet.bl); @@ -103,7 +102,7 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const auto detectorResult = SampleMQR(*binImg, fp); if (detectorResult.isValid()) { - auto decoderResult = Decode(detectorResult.bits(), _charset); + auto decoderResult = Decode(detectorResult.bits()); auto position = detectorResult.position(); if (decoderResult.isValid()) { results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MicroQRCode); diff --git a/core/src/qrcode/QRReader.h b/core/src/qrcode/QRReader.h index f8fe736a2f..4f8454e18d 100644 --- a/core/src/qrcode/QRReader.h +++ b/core/src/qrcode/QRReader.h @@ -26,7 +26,6 @@ class Reader : public ZXing::Reader private: bool _tryHarder, _isPure, _testQR, _testMQR; - std::string _charset; }; } // QRCode diff --git a/test/fuzz/fuzzDMDecoder.cpp b/test/fuzz/fuzzDMDecoder.cpp index d0c07f070c..6b4500d8c7 100644 --- a/test/fuzz/fuzzDMDecoder.cpp +++ b/test/fuzz/fuzzDMDecoder.cpp @@ -12,7 +12,7 @@ using namespace ZXing; namespace ZXing::DataMatrix::DecodedBitStreamParser { -DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const bool isDMRE); +DecoderResult Decode(ByteArray&& bytes, const bool isDMRE); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) @@ -23,7 +23,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) ByteArray ba; ba.insert(ba.begin(), data, data + size); try { - DataMatrix::DecodedBitStreamParser::Decode(std::move(ba), "", false); + DataMatrix::DecodedBitStreamParser::Decode(std::move(ba), false); } catch (...) { } diff --git a/test/unit/aztec/AZDecoderTest.cpp b/test/unit/aztec/AZDecoderTest.cpp index 62d70977ff..175b041405 100644 --- a/test/unit/aztec/AZDecoderTest.cpp +++ b/test/unit/aztec/AZDecoderTest.cpp @@ -17,7 +17,7 @@ namespace ZXing::Aztec { -DecoderResult Decode(const BitArray& bits, const std::string& characterSet = ""); +DecoderResult Decode(const BitArray& bits); } diff --git a/test/unit/aztec/AZHighLevelEncoderTest.cpp b/test/unit/aztec/AZHighLevelEncoderTest.cpp index 5f82f99d4a..0869921bdf 100644 --- a/test/unit/aztec/AZHighLevelEncoderTest.cpp +++ b/test/unit/aztec/AZHighLevelEncoderTest.cpp @@ -16,7 +16,7 @@ namespace ZXing::Aztec { -DecoderResult Decode(const BitArray& bits, const std::string& characterSet = ""); +DecoderResult Decode(const BitArray& bits); } diff --git a/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp b/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp index 13d7f99cd0..c6b6e8de64 100644 --- a/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp +++ b/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp @@ -12,7 +12,7 @@ namespace ZXing::DataMatrix::DecodedBitStreamParser { -DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet, const bool isDMRE); +DecoderResult Decode(ByteArray&& bytes, const bool isDMRE); } @@ -21,7 +21,7 @@ using namespace ZXing; // Helper to call Decode() static DecoderResult parse(ByteArray bytes, const bool isDMRE = false) { - return DataMatrix::DecodedBitStreamParser::Decode(std::move(bytes), "", isDMRE); + return DataMatrix::DecodedBitStreamParser::Decode(std::move(bytes), isDMRE); } // Shorthand to return text diff --git a/test/unit/maxicode/MCDecoderTest.cpp b/test/unit/maxicode/MCDecoderTest.cpp index 17a8caf3f2..e811107c0f 100644 --- a/test/unit/maxicode/MCDecoderTest.cpp +++ b/test/unit/maxicode/MCDecoderTest.cpp @@ -11,7 +11,7 @@ namespace ZXing::MaxiCode::DecodedBitStreamParser { -DecoderResult Decode(ByteArray&& bytes, const int mode, const std::string& characterSet); +DecoderResult Decode(ByteArray&& bytes, const int mode); } @@ -41,7 +41,7 @@ static DecoderResult parse(ByteArray bytes, const int mode) } padded.insert(padded.end(), bytes.begin(), bytes.end()); pad(padded); - return MaxiCode::DecodedBitStreamParser::Decode(std::move(padded), mode, ""); + return MaxiCode::DecodedBitStreamParser::Decode(std::move(padded), mode); } // Helper to return Structured Append diff --git a/test/unit/pdf417/PDF417DecoderTest.cpp b/test/unit/pdf417/PDF417DecoderTest.cpp index 2df84c85db..55b902cd3f 100644 --- a/test/unit/pdf417/PDF417DecoderTest.cpp +++ b/test/unit/pdf417/PDF417DecoderTest.cpp @@ -19,9 +19,9 @@ using namespace ZXing; using namespace ZXing::Pdf417; // Shorthand for Decode() -static DecoderResult parse(const std::vector& codewords, int ecLevel = 0, const std::string& characterSet = "") +static DecoderResult parse(const std::vector& codewords, int ecLevel = 0) { - return DecodedBitStreamParser::Decode(codewords, ecLevel, characterSet); + return DecodedBitStreamParser::Decode(codewords, ecLevel); } /** diff --git a/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp b/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp index 81d5f97c9b..0d8c1b0def 100644 --- a/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp +++ b/test/unit/qrcode/QRDecodedBitStreamParserTest.cpp @@ -16,7 +16,7 @@ namespace ZXing { namespace QRCode { - DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel, const std::string& hintedCharset); + DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel); } } @@ -31,7 +31,7 @@ TEST(QRDecodedBitStreamParserTest, SimpleByteMode) ba.appendBits(0xF1, 8); ba.appendBits(0xF2, 8); ba.appendBits(0xF3, 8); - auto result = DecodeBitStream(ba.toBytes(), *Version::VersionForNumber(1), ErrorCorrectionLevel::Medium, "").text(); + auto result = DecodeBitStream(ba.toBytes(), *Version::VersionForNumber(1), ErrorCorrectionLevel::Medium).text(); EXPECT_EQ(L"\xF1\xF2\xF3", result); } @@ -44,7 +44,7 @@ TEST(QRDecodedBitStreamParserTest, SimpleSJIS) ba.appendBits(0xA2, 8); ba.appendBits(0xA3, 8); ba.appendBits(0xD0, 8); - auto result = DecodeBitStream(ba.toBytes(), *Version::VersionForNumber(1), ErrorCorrectionLevel::Medium, "").text(); + auto result = DecodeBitStream(ba.toBytes(), *Version::VersionForNumber(1), ErrorCorrectionLevel::Medium).text(); EXPECT_EQ(L"\uff61\uff62\uff63\uff90", result); } @@ -58,7 +58,7 @@ TEST(QRDecodedBitStreamParserTest, ECI) ba.appendBits(0xA1, 8); ba.appendBits(0xA2, 8); ba.appendBits(0xA3, 8); - auto result = DecodeBitStream(ba.toBytes(), *Version::VersionForNumber(1), ErrorCorrectionLevel::Medium, "").text(); + auto result = DecodeBitStream(ba.toBytes(), *Version::VersionForNumber(1), ErrorCorrectionLevel::Medium).text(); EXPECT_EQ(L"\xED\xF3\xFA", result); } @@ -69,7 +69,7 @@ TEST(QRDecodedBitStreamParserTest, Hanzi) ba.appendBits(0x01, 4); // Subset 1 = GB2312 encoding ba.appendBits(0x01, 8); // 1 characters ba.appendBits(0x03C1, 13); - auto result = DecodeBitStream(ba.toBytes(), *Version::VersionForNumber(1), ErrorCorrectionLevel::Medium, "").text(); + auto result = DecodeBitStream(ba.toBytes(), *Version::VersionForNumber(1), ErrorCorrectionLevel::Medium).text(); EXPECT_EQ(L"\u963f", result); } @@ -82,7 +82,7 @@ TEST(QRDecodedBitStreamParserTest, HanziLevel1) // A5A2 (U+30A2) => A5A2 - A1A1 = 401, 4*60 + 01 = 0181 ba.appendBits(0x0181, 13); - auto result = DecodeBitStream(ba.toBytes(), *Version::VersionForNumber(1), ErrorCorrectionLevel::Medium, "").text(); + auto result = DecodeBitStream(ba.toBytes(), *Version::VersionForNumber(1), ErrorCorrectionLevel::Medium).text(); EXPECT_EQ(L"\u30a2", result); } @@ -93,22 +93,22 @@ TEST(QRDecodedBitStreamParserTest, SymbologyIdentifier) DecoderResult result; // Plain "ANUM(1) A" - result = DecodeBitStream({0x20, 0x09, 0x40}, version, ecLevel, ""); + result = DecodeBitStream({0x20, 0x09, 0x40}, version, ecLevel); EXPECT_EQ(result.symbologyIdentifier(), "]Q1"); EXPECT_EQ(result.text(), L"A"); // GS1 "FNC1(1st) NUM(4) 2001" - result = DecodeBitStream({0x51, 0x01, 0x0C, 0x81, 0x00}, version, ecLevel, ""); + result = DecodeBitStream({0x51, 0x01, 0x0C, 0x81, 0x00}, version, ecLevel); EXPECT_EQ(result.symbologyIdentifier(), "]Q3"); EXPECT_EQ(result.text(), L"2001"); // "(20)01" // GS1 "NUM(4) 2001 FNC1(1st) 301" - FNC1(1st) can occur anywhere (this actually violates the specification) - result = DecodeBitStream({0x10, 0x10, 0xC8, 0x15, 0x10, 0x0D, 0x2D, 0x00}, version, ecLevel, ""); + result = DecodeBitStream({0x10, 0x10, 0xC8, 0x15, 0x10, 0x0D, 0x2D, 0x00}, version, ecLevel); EXPECT_EQ(result.symbologyIdentifier(), "]Q3"); EXPECT_EQ(result.text(), L"2001301"); // "(20)01(30)1" // AIM "FNC1(2nd) 99 (0x63) ANUM(1) A" - result = DecodeBitStream({0x96, 0x32, 0x00, 0x94, 0x00}, version, ecLevel, ""); + result = DecodeBitStream({0x96, 0x32, 0x00, 0x94, 0x00}, version, ecLevel); EXPECT_EQ(result.symbologyIdentifier(), "]Q5"); EXPECT_EQ(result.text(), L"99A"); @@ -119,16 +119,16 @@ TEST(QRDecodedBitStreamParserTest, SymbologyIdentifier) // EXPECT_EQ(result.text(), L"99AB"); // Application Indicator prefixed to data // AIM "FNC1(2nd) A (100 + 61 = 0xA5) ANUM(1) B" - result = DecodeBitStream({0x9A, 0x52, 0x00, 0x96, 0x00}, version, ecLevel, ""); + result = DecodeBitStream({0x9A, 0x52, 0x00, 0x96, 0x00}, version, ecLevel); EXPECT_EQ(result.symbologyIdentifier(), "]Q5"); EXPECT_EQ(result.text(), L"AB"); // AIM "FNC1(2nd) a (100 + 97 = 0xC5) ANUM(1) B" - result = DecodeBitStream({0x9C, 0x52, 0x00, 0x96, 0x00}, version, ecLevel, ""); + result = DecodeBitStream({0x9C, 0x52, 0x00, 0x96, 0x00}, version, ecLevel); EXPECT_EQ(result.symbologyIdentifier(), "]Q5"); EXPECT_EQ(result.text(), L"aB"); // Bad AIM Application Indicator "FNC1(2nd) @ (0xA4) ANUM(1) B" - result = DecodeBitStream({0x9A, 0x42, 0x00, 0x96, 0x00}, version, ecLevel, ""); + result = DecodeBitStream({0x9A, 0x42, 0x00, 0x96, 0x00}, version, ecLevel); EXPECT_FALSE(result.isValid()); } From d5c5286dedf65019be468b09ca963800bf72e9b2 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 23 Jun 2022 16:30:18 +0200 Subject: [PATCH 116/180] LogMatrix: remove non-scenical template specialization (g++ complained) --- core/src/LogMatrix.h | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/LogMatrix.h b/core/src/LogMatrix.h index 1d366b9ca1..4633a3ef7d 100644 --- a/core/src/LogMatrix.h +++ b/core/src/LogMatrix.h @@ -68,7 +68,6 @@ class LogMatrix _log.set(static_cast(p.x * _scale), static_cast(p.y * _scale), color); } - template <> void operator()(const PointT& p, int color) { operator()(centered(p), color); From d6dc409ef15ff7171c7f9130dff7266a7c65f053 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 24 Jun 2022 01:27:10 +0200 Subject: [PATCH 117/180] c++20: coroutine based multi-symbol detection support for DataMatrix To enable this, compile with `CMAKE_CXX_STANDARD 20` (see top-level CMakeLists.txt). This has been discussed in #344. --- core/src/Generator.h | 109 +++++++++++++++++++++++++++++ core/src/datamatrix/DMDetector.cpp | 26 ++++++- core/src/datamatrix/DMDetector.h | 13 +++- core/src/datamatrix/DMReader.cpp | 22 ++++++ core/src/datamatrix/DMReader.h | 3 + 5 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 core/src/Generator.h diff --git a/core/src/Generator.h b/core/src/Generator.h new file mode 100644 index 0000000000..922af66d85 --- /dev/null +++ b/core/src/Generator.h @@ -0,0 +1,109 @@ +/* + * Copyright 2022 Axel Waggershauser + */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef __cpp_impl_coroutine +#include +#include + +// this code is based on https://en.cppreference.com/w/cpp/coroutine/coroutine_handle#Example +// but modified trying to prevent accidental copying of generated objects + +template +class Generator +{ +public: + struct promise_type + { + Generator get_return_object() { return Generator{Handle::from_promise(*this)}; } + static std::suspend_always initial_suspend() noexcept { return {}; } + static std::suspend_always final_suspend() noexcept { return {}; } + std::suspend_always yield_value(T&& value) noexcept + { + current_value = std::move(value); + return {}; + } +// void return_value(T&& value) noexcept { current_value = std::move(value); } + // Disallow co_await in generator coroutines. + void await_transform() = delete; + [[noreturn]] static void unhandled_exception() { throw; } + + std::optional current_value; + }; + + using Handle = std::coroutine_handle; + + explicit Generator(const Handle coroutine) : _coroutine{coroutine} {} + + Generator() = default; + ~Generator() + { + if (_coroutine) + _coroutine.destroy(); + } + + Generator(const Generator&) = delete; + Generator& operator=(const Generator&) = delete; + + Generator(Generator&& other) noexcept : _coroutine{other._coroutine} { other._coroutine = {}; } +// Generator& operator=(Generator&& other) noexcept +// { +// if (this != &other) { +// if (_coroutine) +// _coroutine.destroy(); + +// _coroutine = other._coroutine; +// other._coroutine = {}; +// } +// return *this; +// } + + // Range-based for loop support. + class Iter + { + public: + void operator++() { _coroutine.resume(); } + T&& operator*() const { return std::move(*_coroutine.promise().current_value); } + bool operator==(std::default_sentinel_t) const { return !_coroutine || _coroutine.done(); } + + explicit Iter(const Handle coroutine) : _coroutine{coroutine} {} + + private: + Handle _coroutine; + }; + + Iter begin() + { + if (_coroutine) + _coroutine.resume(); + + return Iter{_coroutine}; + } + std::default_sentinel_t end() { return {}; } + +private: + Handle _coroutine; +}; + +#endif + +/* +usage example: + +template +Generator range(T first, const T last) +{ + while (first < last) + co_yield first++; +} + +int main() +{ + for (const char i : range(65, 91)) + std::cout << i << ' '; + std::cout << '\n'; +} +*/ diff --git a/core/src/datamatrix/DMDetector.cpp b/core/src/datamatrix/DMDetector.cpp index 4a9b6eb57d..e2b88b8853 100644 --- a/core/src/datamatrix/DMDetector.cpp +++ b/core/src/datamatrix/DMDetector.cpp @@ -775,7 +775,7 @@ static DetectorResult Scan(EdgeTracer startTracer, std::array +#include +#endif + namespace ZXing { class BitMatrix; @@ -13,7 +18,13 @@ class DetectorResult; namespace DataMatrix { -DetectorResult Detect(const BitMatrix& image, bool tryHarder, bool tryRotate, bool isPure); +#ifdef __cpp_impl_coroutine +using DetectorResults = Generator; +#else +using DetectorResults = DetectorResult; +#endif + +DetectorResults Detect(const BitMatrix& image, bool tryHarder, bool tryRotate, bool isPure); } // DataMatrix } // ZXing diff --git a/core/src/datamatrix/DMReader.cpp b/core/src/datamatrix/DMReader.cpp index fc4c1bdec6..a6736d85fa 100644 --- a/core/src/datamatrix/DMReader.cpp +++ b/core/src/datamatrix/DMReader.cpp @@ -26,6 +26,10 @@ Reader::Reader(const DecodeHints& hints) Result Reader::decode(const BinaryBitmap& image) const { +#ifdef __cpp_impl_coroutine + auto results = decode(image, 1); + return results.empty() ? Result(DecodeStatus::NotFound) : results.front(); +#else auto binImg = image.getBitMatrix(); if (binImg == nullptr) return Result(DecodeStatus::NotFound); @@ -35,6 +39,24 @@ Result Reader::decode(const BinaryBitmap& image) const return Result(DecodeStatus::NotFound); return Result(Decode(detectorResult.bits()), std::move(detectorResult).position(), BarcodeFormat::DataMatrix); +#endif } +#ifdef __cpp_impl_coroutine +Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const +{ + auto binImg = image.getBitMatrix(); + if (binImg == nullptr) + return {}; + + Results results; + for (auto&& res : Detect(*binImg, maxSymbols > 1, _tryRotate, _isPure)) { + results.push_back(Result(Decode(res.bits()), std::move(res).position(), BarcodeFormat::DataMatrix)); + if (maxSymbols > 0 && Size(results) >= maxSymbols) + break; + } + + return results; +} +#endif } // namespace ZXing::DataMatrix diff --git a/core/src/datamatrix/DMReader.h b/core/src/datamatrix/DMReader.h index a0c728ae41..dd472b6096 100644 --- a/core/src/datamatrix/DMReader.h +++ b/core/src/datamatrix/DMReader.h @@ -22,6 +22,9 @@ class Reader : public ZXing::Reader public: explicit Reader(const DecodeHints& hints); Result decode(const BinaryBitmap& image) const override; +#ifdef __cpp_impl_coroutine + Results decode(const BinaryBitmap& image, int maxSymbols) const override; +#endif }; } // DataMatrix From 3bf38b255c11955218f52f544ed44c54c39fffbb Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 24 Jun 2022 11:45:25 +0200 Subject: [PATCH 118/180] Result: deprecated `status()` and introduced `Error Result::error()` This is a preview/WIP as discussed in #345. The 'msg' functionality of the new Error class is not used inside the library, yet. --- core/CMakeLists.txt | 3 +++ core/src/Error.h | 50 +++++++++++++++++++++++++++++++++++ core/src/Result.cpp | 26 ++++++++++++++++-- core/src/Result.h | 11 +++++--- core/src/oned/ODReader.cpp | 2 +- core/src/pdf417/PDFReader.cpp | 2 +- example/ZXingQtReader.cpp | 1 - example/ZXingQtReader.h | 13 +-------- example/ZXingReader.cpp | 16 +++++++---- 9 files changed, 98 insertions(+), 26 deletions(-) create mode 100644 core/src/Error.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 655d569305..d59ed2d818 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -58,6 +58,7 @@ set (COMMON_FILES src/ECI.h src/ECI.cpp src/Flags.h + src/Generator.h src/GenericGF.h src/GenericGF.cpp src/GenericGFPoly.h @@ -95,6 +96,7 @@ if (BUILD_READERS) src/DecodeStatus.cpp src/DecoderResult.h src/DetectorResult.h + src/Error.h src/GlobalHistogramBinarizer.h src/GlobalHistogramBinarizer.cpp src/GridSampler.h @@ -152,6 +154,7 @@ if (BUILD_READERS) src/Content.h src/DecodeHints.h src/DecodeStatus.h + src/Error.h src/ImageView.h src/Point.h src/Quadrilateral.h diff --git a/core/src/Error.h b/core/src/Error.h new file mode 100644 index 0000000000..6cbee9f95e --- /dev/null +++ b/core/src/Error.h @@ -0,0 +1,50 @@ +/* + * Copyright 2022 Axel Waggershauser + */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +namespace ZXing { + +class Error +{ +public: + enum Type { None, Format, Checksum, Unsupported }; + Type type() const noexcept { return _type; } + const std::string& msg() const noexcept { return _msg; } + operator bool() const noexcept { return _type != None; } + + Error() = default; + +protected: + Type _type = None; + std::string _msg; + + Error(Type type, std::string msg) : _type(type), _msg(std::move(msg)) {} +}; + +class FormatError : public Error +{ +public: + FormatError(std::string msg = {}) : Error(Format, std::move(msg)) {} +}; + +class ChecksumError : public Error +{ +public: + ChecksumError(std::string msg = {}) : Error(Checksum, std::move(msg)) {} +}; + +inline std::string ToString(const Error& e) +{ + const char* name[] = {"", "FormatError", "ChecksumError", "Unsupported"}; + std::string ret = name[static_cast(e.type())]; + if (!e.msg().empty()) + ret += " (" + e.msg() + ")"; + return ret; +} + +} diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 4bd0d320f0..ef6f9a1214 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -17,6 +17,17 @@ namespace ZXing { +static Error Status2Error(DecodeStatus s) +{ + switch (s) { + case DecodeStatus::FormatError: return FormatError(); + case DecodeStatus::ChecksumError: return ChecksumError(); + default: return {}; + } +} + +Result::Result(DecodeStatus status) : _error(Status2Error(status)) {} + Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, ByteArray&& rawBytes, bool readerInit, const std::string& ai) : @@ -30,9 +41,9 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor {} Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format) - : _status(decodeResult.errorCode()), - _format(format), + : _format(decodeResult.errorCode() == DecodeStatus::NotFound ? BarcodeFormat::None : format), _content(std::move(decodeResult).content()), + _error(Status2Error(decodeResult.errorCode())), _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), @@ -45,6 +56,17 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat // TODO: add type opaque and code specific 'extra data'? (see DecoderResult::extra()) } +DecodeStatus Result::status() const +{ + switch(_error.type()) { + case Error::Format : return DecodeStatus::FormatError; + case Error::Checksum : return DecodeStatus::ChecksumError; + default: ; + } + + return format() == BarcodeFormat::None ? DecodeStatus::NotFound : DecodeStatus::NoError; +} + const ByteArray& Result::bytes() const { return _content.bytes; diff --git a/core/src/Result.h b/core/src/Result.h index c76f4abffd..41adcda416 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -11,6 +11,7 @@ #include "ByteArray.h" #include "Content.h" #include "DecodeStatus.h" +#include "Error.h" #include "Quadrilateral.h" #include "StructuredAppend.h" @@ -29,7 +30,7 @@ using Position = QuadrilateralI; class Result { public: - explicit Result(DecodeStatus status) : _status(status) {} + explicit Result(DecodeStatus status); // 1D convenience constructor Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, @@ -37,9 +38,11 @@ class Result Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format); - bool isValid() const { return StatusIsOK(_status); } + bool isValid() const { return format() != BarcodeFormat::None && !error(); } - DecodeStatus status() const { return _status; } + const Error& error() const { return _error; } + + [[deprecated]] DecodeStatus status() const; BarcodeFormat format() const { return _format; } @@ -154,9 +157,9 @@ class Result friend Result MergeStructuredAppendSequence(const std::vector& results); private: - DecodeStatus _status = DecodeStatus::NoError; BarcodeFormat _format = BarcodeFormat::None; Content _content; + Error _error; Position _position; ByteArray _rawBytes; int _numBits = 0; diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 59d61ad4e1..8b8abc1574 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -216,7 +216,7 @@ static Results DoDecode(const std::vector>& readers, *(a->lineCount() < b->lineCount() ? a : b) = Result(DecodeStatus::NotFound); //TODO: C++20 res.erase_if() - it = std::remove_if(res.begin(), res.end(), [](auto&& r) { return r.status() == DecodeStatus::NotFound; }); + it = std::remove_if(res.begin(), res.end(), [](auto&& r) { return r.format() == BarcodeFormat::None; }); res.erase(it, res.end()); return res; diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index 130a06d3a7..3673a73211 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -311,7 +311,7 @@ Reader::decode(const BinaryBitmap& image) const { if (_isPure) { auto res = DecodePure(image); - if (res.status() != DecodeStatus::ChecksumError) + if (res.error().type() != Error::Checksum) return res; // This falls through and tries the non-pure code path if we have a checksum error. This approach is // currently the best option to deal with 'aliased' input like e.g. 03-aliased.png diff --git a/example/ZXingQtReader.cpp b/example/ZXingQtReader.cpp index c07544b08f..757cabd87c 100644 --- a/example/ZXingQtReader.cpp +++ b/example/ZXingQtReader.cpp @@ -36,7 +36,6 @@ int main(int argc, char* argv[]) qDebug() << "Text: " << result.text(); qDebug() << "Format: " << result.format(); qDebug() << "Content:" << result.contentType(); - qDebug() << "Error: " << result.status(); qDebug() << ""; } diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index d97d634d9a..58998c20c8 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -60,17 +60,9 @@ enum class BarcodeFormat enum class ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI }; -enum class DecodeStatus -{ - NoError = 0, - NotFound, - FormatError, - ChecksumError, -}; #else using ZXing::BarcodeFormat; using ZXing::ContentType; -using ZXing::DecodeStatus; #endif using ZXing::DecodeHints; @@ -79,7 +71,6 @@ using ZXing::BarcodeFormats; Q_ENUM_NS(BarcodeFormat) Q_ENUM_NS(ContentType) -Q_ENUM_NS(DecodeStatus) template QDebug operator<<(QDebug dbg, const T& v) @@ -111,7 +102,6 @@ class Result : private ZXing::Result Q_PROPERTY(QString text READ text) Q_PROPERTY(QByteArray bytes READ bytes) Q_PROPERTY(bool isValid READ isValid) - Q_PROPERTY(DecodeStatus status READ status) Q_PROPERTY(ContentType contentType READ contentType) Q_PROPERTY(Position position READ position) @@ -133,7 +123,6 @@ class Result : private ZXing::Result using ZXing::Result::isValid; BarcodeFormat format() const { return static_cast(ZXing::Result::format()); } - DecodeStatus status() const { return static_cast(ZXing::Result::status()); } ContentType contentType() const { return static_cast(ZXing::Result::contentType()); } QString formatName() const { return QString::fromStdString(ZXing::ToString(ZXing::Result::format())); } const QString& text() const { return _text; } @@ -440,7 +429,7 @@ namespace ZXingQt { inline void registerQmlAndMetaTypes() { qRegisterMetaType("BarcodeFormat"); - qRegisterMetaType("DecodeStatus"); + qRegisterMetaType("ContentType"); // supposedly the Q_DECLARE_METATYPE should be used with the overload without a custom name // but then the qml side complains about "unregistered type" diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 32cd7c7674..21087efb24 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -168,7 +168,7 @@ int main(int argc, char* argv[]) if (!outPath.empty()) drawRect(image, result.position()); - ret |= static_cast(result.status()); + ret |= static_cast(result.error().type()); if (binaryOutput) { std::cout.write(reinterpret_cast(result.bytes().data()), result.bytes().size()); @@ -179,8 +179,8 @@ int main(int argc, char* argv[]) std::cout << filePath << " " << ToString(result.format()); if (result.isValid()) std::cout << " \"" << escapeNonGraphical(result.text()) << "\""; - else if (result.format() != BarcodeFormat::None) - std::cout << " " << ToString(result.status()); + else if (result.error()) + std::cout << " " << ToString(result.error()); std::cout << "\n"; continue; } @@ -193,6 +193,12 @@ int main(int argc, char* argv[]) std::cout << "File: " << filePath << "\n"; firstFile = false; } + + if (result.format() == BarcodeFormat::None) { + std::cout << "No barcode found\n"; + continue; + } + std::cout << "Text: \"" << (angleEscape ? escapeNonGraphical(result.text()) : result.text()) << "\"\n" << "Utf8ECI: \"" << result.utf8ECI() << "\"\n" << "Bytes: " << ToHex(result.bytes()) << "\n" @@ -203,8 +209,7 @@ int main(int argc, char* argv[]) << "HasECI: " << result.hasECI() << "\n" << "Position: " << result.position() << "\n" << "Rotation: " << result.orientation() << " deg\n" - << "IsMirrored: " << result.isMirrored() << "\n" - << "Error: " << ToString(result.status()) << "\n"; + << "IsMirrored: " << result.isMirrored() << "\n"; auto printOptional = [](const char* key, const std::string& v) { if (!v.empty()) @@ -212,6 +217,7 @@ int main(int argc, char* argv[]) }; printOptional("EC Level: ", result.ecLevel()); + printOptional("Error: ", ToString(result.error())); if (result.lineCount()) std::cout << "Lines: " << result.lineCount() << "\n"; From 20cd93ae70b96b959cf3e9c7803cc193a685b9b0 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 28 Jun 2022 23:58:56 +0200 Subject: [PATCH 119/180] python: add content_type output to demo_reader.py --- wrappers/python/demo_reader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrappers/python/demo_reader.py b/wrappers/python/demo_reader.py index d8e95cb9ef..c2a3ad9468 100644 --- a/wrappers/python/demo_reader.py +++ b/wrappers/python/demo_reader.py @@ -4,7 +4,7 @@ img = Image.open(sys.argv[1]) results = zxingcpp.read_barcodes(img) for result in results: - print("Found barcode:\n Text: '{}'\n Format: {}\n Position: {}" - .format(result.text, result.format, result.position)) + print("Found barcode:\n Text: '{}'\n Format: {}\n Content: {}\n Position: {}" + .format(result.text, result.format, result.content_type, result.position)) if len(results) == 0: print("Could not find any barcode.") From 5f13f87b429fd49f2b18d925b639247901a51a6d Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 28 Jun 2022 23:59:20 +0200 Subject: [PATCH 120/180] DecodeHints: remove dead code --- core/src/DecodeHints.h | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 26813d387e..1c1e9129aa 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -131,7 +131,6 @@ class DecodeHints ZX_PROPERTY(EanAddOnSymbol, eanAddOnSymbol, setEanAddOnSymbol) #undef ZX_PROPERTY -#undef ZX_PROPERTY_DEPRECATED /// NOTE: used to affect FNC1 handling for Code 128 (aka GS1-128) but behavior now based on position of FNC1. [[deprecated]] bool assumeGS1() const noexcept { return true; } From e322b4d6052d2c4cc7a0343b6870ae3ad0faccb1 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 29 Jun 2022 01:24:06 +0200 Subject: [PATCH 121/180] QRCode: minor performance improvement of MQR-only use case Save like 10% by not calculating the unused FinderPatternSets in the MQR case. Also fix wrong reference type in `GenerateFinderPatternSets`. --- core/src/qrcode/QRDetector.cpp | 2 +- core/src/qrcode/QRDetector.h | 2 +- core/src/qrcode/QRReader.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 5fdad20b89..9c93a13e9e 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -82,7 +82,7 @@ std::vector FindFinderPatterns(const BitMatrix& image, bool t * @param patterns list of ConcentricPattern objects, i.e. found finder pattern squares * @return list of plausible finder pattern sets, sorted by decreasing plausibility */ -FinderPatternSets GenerateFinderPatternSets(FinderPatterns&& patterns) +FinderPatternSets GenerateFinderPatternSets(FinderPatterns& patterns) { std::sort(patterns.begin(), patterns.end(), [](const auto& a, const auto& b) { return a.size < b.size; }); diff --git a/core/src/qrcode/QRDetector.h b/core/src/qrcode/QRDetector.h index 77b5914615..4173d59a7a 100644 --- a/core/src/qrcode/QRDetector.h +++ b/core/src/qrcode/QRDetector.h @@ -28,7 +28,7 @@ using FinderPatterns = std::vector; using FinderPatternSets = std::vector; FinderPatterns FindFinderPatterns(const BitMatrix& image, bool tryHarder); -FinderPatternSets GenerateFinderPatternSets(FinderPatterns&& patterns); +FinderPatternSets GenerateFinderPatternSets(FinderPatterns& patterns); DetectorResult SampleQR(const BitMatrix& image, const FinderPatternSet& fp); DetectorResult SampleMQR(const BitMatrix& image, const ConcentricPattern& fp); diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index 869373415f..d87161d381 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -69,12 +69,12 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const #endif auto allFPs = FindFinderPatterns(*binImg, _tryHarder); - auto allFPSets = GenerateFinderPatternSets(std::move(allFPs)); std::vector usedFPs; Results results; if (_testQR) { + auto allFPSets = GenerateFinderPatternSets(allFPs); for (auto& fpSet : allFPSets) { if (Contains(usedFPs, fpSet.bl) || Contains(usedFPs, fpSet.tl) || Contains(usedFPs, fpSet.tr)) continue; From c33f5f910f81a5f3d4e4f4ec8a3fbdeb543f6062 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 29 Jun 2022 10:54:53 +0200 Subject: [PATCH 122/180] DataMatrix: fix spurious detection with FormatError in new c++20 code See https://github.com/nu-book/zxing-cpp/issues/344#issuecomment-1169662905 --- core/src/datamatrix/DMDetector.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/datamatrix/DMDetector.cpp b/core/src/datamatrix/DMDetector.cpp index e2b88b8853..5760e52114 100644 --- a/core/src/datamatrix/DMDetector.cpp +++ b/core/src/datamatrix/DMDetector.cpp @@ -884,8 +884,11 @@ DetectorResults Detect(const BitMatrix& image, bool tryHarder, bool tryRotate, b found = true; co_yield std::move(r); } - if (!found) - co_yield DetectOld(image); + if (!found) { + auto r = DetectOld(image); + if (r.isValid()) + co_yield std::move(r); + } } #else if (isPure) From 022a0cf9fac1704176c9ed7dc029ef22830ec8a8 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 29 Jun 2022 13:20:18 +0200 Subject: [PATCH 123/180] example: clean up ZXingReader help text --- example/ZXingReader.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 21087efb24..da8a3a3e8d 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -28,16 +28,18 @@ using namespace ZXing; static void PrintUsage(const char* exePath) { - std::cout << "Usage: " << exePath << " [-fast] [-norotate] [-format ] [-pngout ] [-ispure] [-1] ...\n" + std::cout << "Usage: " << exePath << " [options] ...\n" << " -fast Skip some lines/pixels during detection (faster)\n" << " -norotate Don't try rotated image during detection (faster)\n" << " -noscale Don't try downscaled images during detection (faster)\n" - << " -format Only detect given format(s) (faster)\n" + << " -format \n" + << " Only detect given format(s) (faster)\n" << " -ispure Assume the image contains only a 'pure'/perfect code (faster)\n" - << " -1 Print only file name, text and status on one line per file/barcode\n" - << " -escape Escape non-graphical characters in angle brackets (implicit for -1 option, which always escapes)\n" + << " -1 Print only file name, content/error on one line per file/barcode (implies '-escape')\n" + << " -escape Escape non-graphical characters in angle brackets\n" << " -binary Write (only) the binary content of the symbol(s) to stdout\n" - << " -pngout Write a copy of the input image with barcodes outlined by a green line\n" + << " -pngout \n" + << " Write a copy of the input image with barcodes outlined by a green line\n" << "\n" << "Supported formats are:\n"; for (auto f : BarcodeFormats::all()) { From 7f306d287a03bf1b2a389a5f1e46c5f2f49bb349 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 29 Jun 2022 23:52:07 +0200 Subject: [PATCH 124/180] QRCode: simply variable renaming (preparing next change) --- core/src/qrcode/QRDetector.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 9c93a13e9e..f40419da53 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -110,20 +110,23 @@ FinderPatternSets GenerateFinderPatternSets(FinderPatterns& patterns) // Orders the three points in an order [A,B,C] such that AB is less than AC // and BC is less than AC, and the angle between BC and BA is less than 180 degrees. - auto distAB = squaredDistance(a, b); - auto distBC = squaredDistance(b, c); - auto distAC = squaredDistance(a, c); + auto distAB2 = squaredDistance(a, b); + auto distBC2 = squaredDistance(b, c); + auto distAC2 = squaredDistance(a, c); - if (distBC >= distAB && distBC >= distAC) { + if (distBC2 >= distAB2 && distBC2 >= distAC2) { std::swap(a, b); - std::swap(distBC, distAC); - } else if (distAB >= distAC && distAB >= distBC) { + std::swap(distBC2, distAC2); + } else if (distAB2 >= distAC2 && distAB2 >= distBC2) { std::swap(b, c); - std::swap(distAB, distAC); + std::swap(distAB2, distAC2); } + auto distAB = std::sqrt(distAB2); + auto distBC = std::sqrt(distBC2); + // Estimate the module count and ignore this set if it can not result in a valid decoding - if (auto moduleCount = (std::sqrt(distAB) + std::sqrt(distBC)) / (2 * (a->size + b->size + c->size) / (3 * 7.f)) + 7; + if (auto moduleCount = (distAB + distBC) / (2 * (a->size + b->size + c->size) / (3 * 7.f)) + 7; moduleCount < 21 * 0.9 || moduleCount > 177 * 1.05) continue; @@ -132,7 +135,7 @@ FinderPatternSets GenerateFinderPatternSets(FinderPatterns& patterns) // we need to check both two equal sides separately. // The value of |c^2 - 2 * b^2| + |c^2 - 2 * a^2| increases as dissimilarity // from isosceles right triangle. - double d = std::abs(distAC - 2 * distAB) + std::abs(distAC - 2 * distBC); + double d = (std::abs(distAC2 - 2 * distAB2) + std::abs(distAC2 - 2 * distBC2)) / distAC2; // Use cross product to figure out whether A and C are correct or flipped. // This asks whether BC x BA has a positive z component, which is the arrangement From c97736f147542ed5f48076f10e9b11f0c64a9565 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 30 Jun 2022 00:25:10 +0200 Subject: [PATCH 125/180] Reader: store a reference to DecodeHints vs. copies of members thereof --- core/src/Reader.h | 5 +++++ core/src/aztec/AZReader.cpp | 9 ++------- core/src/aztec/AZReader.h | 6 ++---- core/src/datamatrix/DMReader.cpp | 10 ++-------- core/src/datamatrix/DMReader.h | 5 ++--- core/src/maxicode/MCReader.cpp | 2 -- core/src/maxicode/MCReader.h | 5 ++--- core/src/oned/ODReader.cpp | 19 ++++++++----------- core/src/oned/ODReader.h | 4 ---- core/src/pdf417/PDFReader.cpp | 4 +--- core/src/pdf417/PDFReader.h | 4 +--- core/src/qrcode/QRReader.cpp | 23 +++++++++-------------- core/src/qrcode/QRReader.h | 7 ++----- 13 files changed, 36 insertions(+), 67 deletions(-) diff --git a/core/src/Reader.h b/core/src/Reader.h index 4e8a2721d8..bdc8cd0b1e 100644 --- a/core/src/Reader.h +++ b/core/src/Reader.h @@ -11,10 +11,15 @@ namespace ZXing { class BinaryBitmap; +class DecodeHints; class Reader { +protected: + const DecodeHints& _hints; + public: + explicit Reader(const DecodeHints& hints) : _hints(hints) {} virtual ~Reader() = default; virtual Result decode(const BinaryBitmap& image) const = 0; diff --git a/core/src/aztec/AZReader.cpp b/core/src/aztec/AZReader.cpp index 4ca316c416..de452c6a6e 100644 --- a/core/src/aztec/AZReader.cpp +++ b/core/src/aztec/AZReader.cpp @@ -19,11 +19,6 @@ namespace ZXing::Aztec { -Reader::Reader(const DecodeHints& hints) - : _isPure(hints.isPure()) -{ -} - Result Reader::decode(const BinaryBitmap& image) const { @@ -32,7 +27,7 @@ Reader::decode(const BinaryBitmap& image) const return Result(DecodeStatus::NotFound); } - DetectorResult detectResult = Detect(*binImg, false, _isPure); + DetectorResult detectResult = Detect(*binImg, false, _hints.isPure()); DecoderResult decodeResult = DecodeStatus::NotFound; if (detectResult.isValid()) { decodeResult = Decode(detectResult); @@ -40,7 +35,7 @@ Reader::decode(const BinaryBitmap& image) const //TODO: don't start detection all over again, just to swap 2 corner points if (!decodeResult.isValid()) { - detectResult = Detect(*binImg, true, _isPure); + detectResult = Detect(*binImg, true, _hints.isPure()); if (detectResult.isValid()) { decodeResult = Decode(detectResult); } diff --git a/core/src/aztec/AZReader.h b/core/src/aztec/AZReader.h index df5a837507..c0bab4fea4 100644 --- a/core/src/aztec/AZReader.h +++ b/core/src/aztec/AZReader.h @@ -19,11 +19,9 @@ namespace Aztec { class Reader : public ZXing::Reader { public: - explicit Reader(const DecodeHints& hints); - Result decode(const BinaryBitmap& image) const override; + using ZXing::Reader::Reader; -private: - bool _isPure; + Result decode(const BinaryBitmap& image) const override; }; } // Aztec diff --git a/core/src/datamatrix/DMReader.cpp b/core/src/datamatrix/DMReader.cpp index a6736d85fa..ed3b93979c 100644 --- a/core/src/datamatrix/DMReader.cpp +++ b/core/src/datamatrix/DMReader.cpp @@ -18,12 +18,6 @@ namespace ZXing::DataMatrix { -Reader::Reader(const DecodeHints& hints) - : _tryRotate(hints.tryRotate()), - _tryHarder(hints.tryHarder()), - _isPure(hints.isPure()) -{} - Result Reader::decode(const BinaryBitmap& image) const { #ifdef __cpp_impl_coroutine @@ -34,7 +28,7 @@ Result Reader::decode(const BinaryBitmap& image) const if (binImg == nullptr) return Result(DecodeStatus::NotFound); - auto detectorResult = Detect(*binImg, _tryHarder, _tryRotate, _isPure); + auto detectorResult = Detect(*binImg, _hints.tryHarder(), _hints.tryRotate(), _hints.isPure()); if (!detectorResult.isValid()) return Result(DecodeStatus::NotFound); @@ -50,7 +44,7 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const return {}; Results results; - for (auto&& res : Detect(*binImg, maxSymbols > 1, _tryRotate, _isPure)) { + for (auto&& res : Detect(*binImg, maxSymbols > 1, _hints.tryRotate(), _hints.isPure())) { results.push_back(Result(Decode(res.bits()), std::move(res).position(), BarcodeFormat::DataMatrix)); if (maxSymbols > 0 && Size(results) >= maxSymbols) break; diff --git a/core/src/datamatrix/DMReader.h b/core/src/datamatrix/DMReader.h index dd472b6096..1d6706072c 100644 --- a/core/src/datamatrix/DMReader.h +++ b/core/src/datamatrix/DMReader.h @@ -17,10 +17,9 @@ namespace DataMatrix { class Reader : public ZXing::Reader { - bool _tryRotate, _tryHarder, _isPure; - public: - explicit Reader(const DecodeHints& hints); + using ZXing::Reader::Reader; + Result decode(const BinaryBitmap& image) const override; #ifdef __cpp_impl_coroutine Results decode(const BinaryBitmap& image, int maxSymbols) const override; diff --git a/core/src/maxicode/MCReader.cpp b/core/src/maxicode/MCReader.cpp index ce3c9a9125..441bb529bd 100644 --- a/core/src/maxicode/MCReader.cpp +++ b/core/src/maxicode/MCReader.cpp @@ -43,8 +43,6 @@ static BitMatrix ExtractPureBits(const BitMatrix& image) return result; } -Reader::Reader(const DecodeHints& hints) : _isPure(hints.isPure()) {} - Result Reader::decode(const BinaryBitmap& image) const { diff --git a/core/src/maxicode/MCReader.h b/core/src/maxicode/MCReader.h index 9b907d0f37..8f209c0ff7 100644 --- a/core/src/maxicode/MCReader.h +++ b/core/src/maxicode/MCReader.h @@ -18,10 +18,9 @@ namespace MaxiCode { class Reader : public ZXing::Reader { - bool _isPure; - public: - explicit Reader(const DecodeHints& hints); + using ZXing::Reader::Reader; + Result decode(const BinaryBitmap& image) const override; }; diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 8b8abc1574..823432f260 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -24,11 +24,7 @@ namespace ZXing::OneD { -Reader::Reader(const DecodeHints& hints) : - _tryHarder(hints.tryHarder()), - _tryRotate(hints.tryRotate()), - _isPure(hints.isPure()), - _minLineCount(hints.minLineCount()) +Reader::Reader(const DecodeHints& hints) : ZXing::Reader(hints) { _readers.reserve(8); @@ -225,19 +221,20 @@ static Results DoDecode(const std::vector>& readers, Result Reader::decode(const BinaryBitmap& image) const { - auto result = DoDecode(_readers, image, _tryHarder, false, _isPure, 1, _minLineCount); + auto result = DoDecode(_readers, image, _hints.tryHarder(), false, _hints.isPure(), 1, _hints.minLineCount()); - if (result.empty() && _tryRotate) - result = DoDecode(_readers, image, _tryHarder, true, _isPure, 1, _minLineCount); + if (result.empty() && _hints.tryRotate()) + result = DoDecode(_readers, image, _hints.tryHarder(), true, _hints.isPure(), 1, _hints.minLineCount()); return result.empty() ? Result(DecodeStatus::NotFound) : result.front(); } Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const { - auto resH = DoDecode(_readers, image, _tryHarder, false, _isPure, maxSymbols, _minLineCount); - if ((!maxSymbols || Size(resH) < maxSymbols) && _tryRotate) { - auto resV = DoDecode(_readers, image, _tryHarder, true, _isPure, maxSymbols - Size(resH), _minLineCount); + auto resH = DoDecode(_readers, image, _hints.tryHarder(), false, _hints.isPure(), maxSymbols, _hints.minLineCount()); + if ((!maxSymbols || Size(resH) < maxSymbols) && _hints.tryRotate()) { + auto resV = DoDecode(_readers, image, _hints.tryHarder(), true, _hints.isPure(), maxSymbols - Size(resH), + _hints.minLineCount()); resH.insert(resH.end(), resV.begin(), resV.end()); } return resH; diff --git a/core/src/oned/ODReader.h b/core/src/oned/ODReader.h index c2f5336a2e..5ccf2b43a0 100644 --- a/core/src/oned/ODReader.h +++ b/core/src/oned/ODReader.h @@ -30,10 +30,6 @@ class Reader : public ZXing::Reader private: std::vector> _readers; - bool _tryHarder; - bool _tryRotate; - bool _isPure; - int _minLineCount; }; } // OneD diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index 3673a73211..7f015553ac 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -304,12 +304,10 @@ static Result DecodePure(const BinaryBitmap& image_) return Result(std::move(res), {{left, top}, {right, top}, {right, bottom}, {left, bottom}}, BarcodeFormat::PDF417); } -Reader::Reader(const DecodeHints& hints) : _isPure(hints.isPure()) {} - Result Reader::decode(const BinaryBitmap& image) const { - if (_isPure) { + if (_hints.isPure()) { auto res = DecodePure(image); if (res.error().type() != Error::Checksum) return res; diff --git a/core/src/pdf417/PDFReader.h b/core/src/pdf417/PDFReader.h index b3cd81779a..9d8be24824 100644 --- a/core/src/pdf417/PDFReader.h +++ b/core/src/pdf417/PDFReader.h @@ -24,10 +24,8 @@ namespace Pdf417 { */ class Reader : public ZXing::Reader { - bool _isPure; - public: - explicit Reader(const DecodeHints& hints); + using ZXing::Reader::Reader; Result decode(const BinaryBitmap& image) const override; Results decode(const BinaryBitmap& image, int maxSymbols) const override; diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index d87161d381..a7f2676509 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -21,17 +21,10 @@ namespace ZXing::QRCode { -Reader::Reader(const DecodeHints& hints) - : _tryHarder(hints.tryHarder()), - _isPure(hints.isPure()), - _testQR(hints.hasFormat(BarcodeFormat::QRCode)), - _testMQR(hints.hasFormat(BarcodeFormat::MicroQRCode)) -{} - Result Reader::decode(const BinaryBitmap& image) const { #if 1 - if (!_isPure) { + if (!_hints.isPure()) { auto res = decode(image, 1); return res.empty() ? Result(DecodeStatus::NotFound) : res.front(); } @@ -43,9 +36,9 @@ Result Reader::decode(const BinaryBitmap& image) const } DetectorResult detectorResult; - if (_testQR) + if (_hints.hasFormat(BarcodeFormat::QRCode)) detectorResult = DetectPureQR(*binImg); - if (_testMQR && !detectorResult.isValid()) + if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !detectorResult.isValid()) detectorResult = DetectPureMQR(*binImg); if (!detectorResult.isValid()) @@ -68,12 +61,12 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const LogMatrixWriter lmw(log, *binImg, 5, "qr-log.pnm"); #endif - auto allFPs = FindFinderPatterns(*binImg, _tryHarder); + auto allFPs = FindFinderPatterns(*binImg, _hints.tryHarder()); std::vector usedFPs; Results results; - if (_testQR) { + if (_hints.hasFormat(BarcodeFormat::QRCode)) { auto allFPSets = GenerateFinderPatternSets(allFPs); for (auto& fpSet : allFPSets) { if (Contains(usedFPs, fpSet.bl) || Contains(usedFPs, fpSet.tl) || Contains(usedFPs, fpSet.tr)) @@ -87,6 +80,8 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const usedFPs.push_back(fpSet.bl); usedFPs.push_back(fpSet.tl); usedFPs.push_back(fpSet.tr); + } + if (decoderResult.isValid(_hints.returnErrors())) { results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QRCode); if (maxSymbols && Size(results) == maxSymbols) break; @@ -95,7 +90,7 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const } } - if (_testMQR && !(maxSymbols && Size(results) == maxSymbols)) { + if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !(maxSymbols && Size(results) == maxSymbols)) { for (auto fp : allFPs) { if (Contains(usedFPs, fp)) continue; @@ -104,7 +99,7 @@ Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const if (detectorResult.isValid()) { auto decoderResult = Decode(detectorResult.bits()); auto position = detectorResult.position(); - if (decoderResult.isValid()) { + if (decoderResult.isValid(_hints.returnErrors())) { results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MicroQRCode); if (maxSymbols && Size(results) == maxSymbols) break; diff --git a/core/src/qrcode/QRReader.h b/core/src/qrcode/QRReader.h index 4f8454e18d..5b86edf751 100644 --- a/core/src/qrcode/QRReader.h +++ b/core/src/qrcode/QRReader.h @@ -19,13 +19,10 @@ namespace QRCode { class Reader : public ZXing::Reader { public: - explicit Reader(const DecodeHints& hints); - Result decode(const BinaryBitmap& image) const override; + using ZXing::Reader::Reader; + Result decode(const BinaryBitmap& image) const override; Results decode(const BinaryBitmap& image, int maxSymbols) const override; - -private: - bool _tryHarder, _isPure, _testQR, _testMQR; }; } // QRCode From 7f2240f9f9acf9763b461fb38931b524c906d7a9 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 30 Jun 2022 00:35:21 +0200 Subject: [PATCH 126/180] DecodeHints: add returnErrors property Add property to specify whether or not `ReadBarcodes` shall return symbols with errors (like ChecksumError) or only valid symbols. Add `-error` command line parameter to ZXingReader example. --- core/src/DecodeHints.h | 7 ++++++- core/src/DecoderResult.h | 5 ++++- core/src/MultiFormatReader.cpp | 23 +++++++++++------------ core/src/MultiFormatReader.h | 1 + core/src/Result.cpp | 3 ++- core/src/oned/ODReader.cpp | 16 +++++++++------- example/ZXingReader.cpp | 16 ++++++++++------ 7 files changed, 43 insertions(+), 28 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 1c1e9129aa..558dfc57be 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -47,6 +47,7 @@ class DecodeHints bool _validateCode39CheckSum : 1; bool _validateITFCheckSum : 1; bool _returnCodabarStartEnd : 1; + bool _returnErrors : 1; Binarizer _binarizer : 2; EanAddOnSymbol _eanAddOnSymbol : 2; @@ -59,7 +60,7 @@ class DecodeHints uint8_t _maxNumberOfSymbols = 0xff; public: - // bitfields don't get default initialized to 0. + // bitfields don't get default initialized to 0 before c++20 DecodeHints() : _tryHarder(1), _tryRotate(1), @@ -69,6 +70,7 @@ class DecodeHints _validateCode39CheckSum(0), _validateITFCheckSum(0), _returnCodabarStartEnd(0), + _returnErrors(0), _binarizer(Binarizer::LocalAverage), _eanAddOnSymbol(EanAddOnSymbol::Ignore) {} @@ -127,6 +129,9 @@ class DecodeHints /// If true, return the start and end chars in a Codabar barcode instead of stripping them. ZX_PROPERTY(bool, returnCodabarStartEnd, setReturnCodabarStartEnd) + /// If true, return the barcodes with errors as well (e.g. checksum errors, see @Result::error()) + ZX_PROPERTY(bool, returnErrors, setReturnErrors) + /// Specify whether to ignore, read or require EAN-2/5 add-on symbols while scanning EAN/UPC codes ZX_PROPERTY(EanAddOnSymbol, eanAddOnSymbol, setEanAddOnSymbol) diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 0071bfae27..0175db24d4 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -47,7 +47,10 @@ class DecoderResult DecoderResult(DecoderResult&&) noexcept = default; DecoderResult& operator=(DecoderResult&&) = default; - bool isValid() const { return StatusIsOK(_status); } + bool isValid(bool includeErrors = false) const + { + return StatusIsOK(_status) || (includeErrors && _status != DecodeStatus::NotFound); + } DecodeStatus errorCode() const { return _status; } const ByteArray& rawBytes() const & { return _rawBytes; } diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index 563c0397a1..38cdaf1c20 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -19,13 +19,12 @@ namespace ZXing { -MultiFormatReader::MultiFormatReader(const DecodeHints& hints) +MultiFormatReader::MultiFormatReader(const DecodeHints& hints) : _hints(hints) { - bool tryHarder = hints.tryHarder(); auto formats = hints.formats().empty() ? BarcodeFormat::Any : hints.formats(); // Put 1D readers upfront in "normal" mode - if (formats.testFlags(BarcodeFormat::OneDCodes) && !tryHarder) + if (formats.testFlags(BarcodeFormat::OneDCodes) && !hints.tryHarder()) _readers.emplace_back(new OneD::Reader(hints)); if (formats.testFlags(BarcodeFormat::QRCode | BarcodeFormat::MicroQRCode)) @@ -40,9 +39,8 @@ MultiFormatReader::MultiFormatReader(const DecodeHints& hints) _readers.emplace_back(new MaxiCode::Reader(hints)); // At end in "try harder" mode - if (formats.testFlags(BarcodeFormat::OneDCodes) && tryHarder) { + if (formats.testFlags(BarcodeFormat::OneDCodes) && hints.tryHarder()) _readers.emplace_back(new OneD::Reader(hints)); - } } MultiFormatReader::~MultiFormatReader() = default; @@ -50,17 +48,13 @@ MultiFormatReader::~MultiFormatReader() = default; Result MultiFormatReader::read(const BinaryBitmap& image) const { - // If we have only one reader in our list, just return whatever that decoded. - // This preserves information (e.g. ChecksumError) instead of just returning 'NotFound'. - if (_readers.size() == 1) - return _readers.front()->decode(image); - + Result r(DecodeStatus::NotFound); for (const auto& reader : _readers) { - Result r = reader->decode(image); + r = reader->decode(image); if (r.isValid()) return r; } - return Result(DecodeStatus::NotFound); + return _hints.returnErrors() ? r : Result(DecodeStatus::NotFound); } Results MultiFormatReader::readMultiple(const BinaryBitmap& image, int maxSymbols) const @@ -69,6 +63,11 @@ Results MultiFormatReader::readMultiple(const BinaryBitmap& image, int maxSymbol for (const auto& reader : _readers) { auto r = reader->decode(image, maxSymbols); + if (!_hints.returnErrors()) { + //TODO: C++20 res.erase_if() + auto it = std::remove_if(res.begin(), res.end(), [](auto&& r) { return !r.isValid(); }); + res.erase(it, res.end()); + } maxSymbols -= r.size(); res.insert(res.end(), std::move_iterator(r.begin()), std::move_iterator(r.end())); if (maxSymbols <= 0) diff --git a/core/src/MultiFormatReader.h b/core/src/MultiFormatReader.h index f418f229e7..a9022f246c 100644 --- a/core/src/MultiFormatReader.h +++ b/core/src/MultiFormatReader.h @@ -39,6 +39,7 @@ class MultiFormatReader private: std::vector> _readers; + const DecodeHints& _hints; }; } // ZXing diff --git a/core/src/Result.cpp b/core/src/Result.cpp index ef6f9a1214..5588cf9cb1 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -137,7 +137,8 @@ Result& Result::setCharacterSet(const std::string& defaultCS) bool Result::operator==(const Result& o) const { - if (format() != o.format() || bytes() != o.bytes()) + // two symbols may be considered the same if at least one of them has an error + if (!(format() == o.format() && (bytes() == o.bytes() || error() || o.error()))) return false; if (BarcodeFormats(BarcodeFormat::TwoDCodes).testFlag(format())) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 823432f260..a09858a192 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -61,7 +61,7 @@ Reader::~Reader() = default; * image if "trying harder". */ static Results DoDecode(const std::vector>& readers, const BinaryBitmap& image, - bool tryHarder, bool rotate, bool isPure, int maxSymbols, int minLineCount) + bool tryHarder, bool rotate, bool isPure, int maxSymbols, int minLineCount, bool returnErrors) { Results res; @@ -134,7 +134,7 @@ static Results DoDecode(const std::vector>& readers, PatternView next(bars); do { Result result = readers[r]->decodePattern(rowNumber, next, decodingState[r]); - if (result.isValid()) { + if (result.isValid() || (returnErrors && result.error())) { result.incrementLineCount(); if (upsideDown) { // update position (flip horizontally). @@ -175,7 +175,7 @@ static Results DoDecode(const std::vector>& readers, } } - if (result.isValid()) + if (result.format() != BarcodeFormat::None) res.push_back(std::move(result)); if (maxSymbols && Reduce(res, 0, [&](int s, const Result& r) { @@ -221,20 +221,22 @@ static Results DoDecode(const std::vector>& readers, Result Reader::decode(const BinaryBitmap& image) const { - auto result = DoDecode(_readers, image, _hints.tryHarder(), false, _hints.isPure(), 1, _hints.minLineCount()); + auto result = + DoDecode(_readers, image, _hints.tryHarder(), false, _hints.isPure(), 1, _hints.minLineCount(), _hints.returnErrors()); if (result.empty() && _hints.tryRotate()) - result = DoDecode(_readers, image, _hints.tryHarder(), true, _hints.isPure(), 1, _hints.minLineCount()); + result = DoDecode(_readers, image, _hints.tryHarder(), true, _hints.isPure(), 1, _hints.minLineCount(), _hints.returnErrors()); return result.empty() ? Result(DecodeStatus::NotFound) : result.front(); } Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const { - auto resH = DoDecode(_readers, image, _hints.tryHarder(), false, _hints.isPure(), maxSymbols, _hints.minLineCount()); + auto resH = DoDecode(_readers, image, _hints.tryHarder(), false, _hints.isPure(), maxSymbols, _hints.minLineCount(), + _hints.returnErrors()); if ((!maxSymbols || Size(resH) < maxSymbols) && _hints.tryRotate()) { auto resV = DoDecode(_readers, image, _hints.tryHarder(), true, _hints.isPure(), maxSymbols - Size(resH), - _hints.minLineCount()); + _hints.minLineCount(), _hints.returnErrors()); resH.insert(resH.end(), resV.begin(), resV.end()); } return resH; diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index da8a3a3e8d..42dda3837e 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -35,6 +35,7 @@ static void PrintUsage(const char* exePath) << " -format \n" << " Only detect given format(s) (faster)\n" << " -ispure Assume the image contains only a 'pure'/perfect code (faster)\n" + << " -errors Include results with errors (like checksum error)\n" << " -1 Print only file name, content/error on one line per file/barcode (implies '-escape')\n" << " -escape Escape non-graphical characters in angle brackets\n" << " -binary Write (only) the binary content of the symbol(s) to stdout\n" @@ -61,6 +62,8 @@ static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLi } else if (strcmp(argv[i], "-ispure") == 0) { hints.setIsPure(true); hints.setBinarizer(Binarizer::FixedThreshold); + } else if (strcmp(argv[i], "-errors") == 0) { + hints.setReturnErrors(true); } else if (strcmp(argv[i], "-format") == 0) { if (++i == argc) return false; @@ -95,7 +98,7 @@ std::ostream& operator<<(std::ostream& os, const Position& points) return os; } -void drawLine(const ImageView& iv, PointI a, PointI b) +void drawLine(const ImageView& iv, PointI a, PointI b, bool error) { int steps = maxAbsComponent(b - a); PointF dir = bresenhamDirection(PointF(b - a)); @@ -103,15 +106,16 @@ void drawLine(const ImageView& iv, PointI a, PointI b) for (int i = 0; i < steps; ++i) { auto p = PointI(centered(a + i * dir)); auto* dst = const_cast(iv.data(p.x, p.y)); - dst[R] = dst[B] = 0; - dst[G] = 0xff; + dst[R] = error ? 0xff : 0; + dst[G] = error ? 0 : 0xff; + dst[B] = 0; } } -void drawRect(const ImageView& image, const Position& pos) +void drawRect(const ImageView& image, const Position& pos, bool error) { for (int i = 0; i < 4; ++i) - drawLine(image, pos[i], pos[(i + 1) % 4]); + drawLine(image, pos[i], pos[(i + 1) % 4], error); } std::string escapeNonGraphical(const std::string& str) @@ -168,7 +172,7 @@ int main(int argc, char* argv[]) for (auto&& result : results) { if (!outPath.empty()) - drawRect(image, result.position()); + drawRect(image, result.position(), result.error()); ret |= static_cast(result.error().type()); From 53a5d670202750d717fae846f5e01dd464bbcba8 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 30 Jun 2022 00:38:57 +0200 Subject: [PATCH 127/180] Error: tune QR and DM detectors to detect less obviously bogous errors --- core/src/ConcentricFinder.cpp | 4 ++++ core/src/datamatrix/DMDetector.cpp | 4 ++++ core/src/qrcode/QRDetector.cpp | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/core/src/ConcentricFinder.cpp b/core/src/ConcentricFinder.cpp index 57b94f4704..494971de51 100644 --- a/core/src/ConcentricFinder.cpp +++ b/core/src/ConcentricFinder.cpp @@ -86,6 +86,10 @@ std::optional CenterOfRings(const BitMatrix& image, PointI center, int r std::optional FinetuneConcentricPatternCenter(const BitMatrix& image, PointF center, int range, int finderPatternSize) { + // make sure we have at least one path of white around the center + if (!CenterOfRing(image, PointI(center), range, 1)) + return {}; + auto res = CenterOfRings(image, PointI(center), range, finderPatternSize / 2); if (!res || !image.get(*res)) res = CenterOfDoubleCross(image, PointI(center), range, finderPatternSize / 2 + 1); diff --git a/core/src/datamatrix/DMDetector.cpp b/core/src/datamatrix/DMDetector.cpp index 5760e52114..7eb9dc20c6 100644 --- a/core/src/datamatrix/DMDetector.cpp +++ b/core/src/datamatrix/DMDetector.cpp @@ -270,6 +270,10 @@ static DetectorResult DetectOld(const BitMatrix& image) const auto& lSideOne = transitions[0]; const auto& lSideTwo = transitions[1]; + // We accept at most 4 transisions inside the L pattern (i.e. 2 corruptions) to reduce false positive FormatErrors + if (lSideTwo.transitions > 2) + return {}; + // Figure out which point is their intersection by tallying up the number of times we see the // endpoints in the four endpoints. One will show up twice. std::map pointCount; diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index f40419da53..2eb0e04c1a 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -130,6 +130,12 @@ FinderPatternSets GenerateFinderPatternSets(FinderPatterns& patterns) moduleCount < 21 * 0.9 || moduleCount > 177 * 1.05) continue; + // Make sure the angle between AB and BC does not deviate from 90° by more than 45° + auto alpha = std::acos((distAB2 + distBC2 - distAC2) / (2 * distAB * distBC)) / 3.1415 * 180; +// printf("alpha: %.1f\n", alpha); + if (std::isnan(alpha) || std::abs(90 - alpha) > 45) + continue; + // a^2 + b^2 = c^2 (Pythagorean theorem), and a = b (isosceles triangle). // Since any right triangle satisfies the formula c^2 - b^2 - a^2 = 0, // we need to check both two equal sides separately. From 9a3017b4f36a96ce4afc12ab55f3caffc02eed0d Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 30 Jun 2022 10:49:17 +0200 Subject: [PATCH 128/180] python: make `read_barcode` return `None` if no barcode is found See also https://github.com/nu-book/zxing-cpp/issues/345#issue-1279720947 --- wrappers/python/test.py | 4 +--- wrappers/python/zxing.cpp | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/wrappers/python/test.py b/wrappers/python/test.py index 8f37dbce59..ea979f9ee7 100644 --- a/wrappers/python/test.py +++ b/wrappers/python/test.py @@ -66,9 +66,7 @@ def test_failed_read(self): np.zeros((100, 100), np.uint8), formats=BF.EAN8 | BF.Aztec, binarizer=zxingcpp.Binarizer.BoolCast ) - self.assertFalse(res.valid) - self.assertEqual(res.format, BF.NONE) - self.assertEqual(res.text, '') + self.assertEqual(res, None) @unittest.skipIf(not has_pil, "need PIL for read/write tests") def test_write_read_cycle_pil(self): diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index 59be2d6e24..862d024947 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -43,10 +44,8 @@ std::ostream& operator<<(std::ostream& os, const Position& points) { return os; } -template -auto read_barcode_impl(FUNC func, py::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, - Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol, - uint8_t max_number_of_symbols = 0xff) +auto read_barcodes_impl(py::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, Binarizer binarizer, + bool is_pure, EanAddOnSymbol ean_add_on_symbol, uint8_t max_number_of_symbols = 0xff) { const auto hints = DecodeHints() .setFormats(formats) @@ -93,21 +92,20 @@ auto read_barcode_impl(FUNC func, py::object _image, const BarcodeFormats& forma } const auto bytes = image.data(); - return func({bytes, width, height, imgfmt, width * channels, channels}, hints); + return ReadBarcodes({bytes, width, height, imgfmt, width * channels, channels}, hints); } -Result read_barcode(py::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, - Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol) +std::optional read_barcode(py::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, + Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol) { - return read_barcode_impl(ReadBarcode, _image, formats, try_rotate, try_downscale, binarizer, is_pure, - ean_add_on_symbol, 1); + auto res = read_barcodes_impl(_image, formats, try_rotate, try_downscale, binarizer, is_pure, ean_add_on_symbol, 1); + return res.empty() ? std::nullopt : std::optional(res.front()); } Results read_barcodes(py::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol) { - return read_barcode_impl(ReadBarcodes, _image, formats, try_rotate, try_downscale, binarizer, is_pure, - ean_add_on_symbol); + return read_barcodes_impl(_image, formats, try_rotate, try_downscale, binarizer, is_pure, ean_add_on_symbol); } Image write_barcode(BarcodeFormat format, std::string text, int width, int height, int quiet_zone, int ec_level) @@ -278,7 +276,7 @@ PYBIND11_MODULE(zxingcpp, m) ":param ean_add_on_symbol: Specify whether to Ignore, Read or Require EAN-2/5 add-on symbols while scanning \n" " EAN/UPC codes. Default is ``Ignore``.\n" ":rtype: zxing.Result\n" - ":return: a zxing result containing decoded symbol if found." + ":return: a zxing result containing decoded symbol if found, None otherwise" ); m.def("read_barcodes", &read_barcodes, py::arg("image"), From a057c14f50ee205389bd227837095650057ed034 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 30 Jun 2022 19:18:47 +0200 Subject: [PATCH 129/180] PDF417: implement new DecodingHints::returnErrors feature --- core/src/pdf417/PDFDetector.cpp | 19 ++++++++-------- core/src/pdf417/PDFDetector.h | 2 +- core/src/pdf417/PDFReader.cpp | 40 ++++++++++++--------------------- 3 files changed, 24 insertions(+), 37 deletions(-) diff --git a/core/src/pdf417/PDFDetector.cpp b/core/src/pdf417/PDFDetector.cpp index 72bf872cff..003b4b4654 100644 --- a/core/src/pdf417/PDFDetector.cpp +++ b/core/src/pdf417/PDFDetector.cpp @@ -332,19 +332,17 @@ bool HasStartPattern(const BitMatrix& m) * @param multiple if true, then the image is searched for multiple codes. If false, then at most one code will * be found and returned */ -DecodeStatus -Detector::Detect(const BinaryBitmap& image, bool multiple, Result& result) +Detector::Result Detector::Detect(const BinaryBitmap& image, bool multiple) { // construct a 'dummy' shared pointer, just be able to pass it up the call chain in DecodeStatus // TODO: reimplement PDF Detector auto binImg = std::shared_ptr(image.getBitMatrix(), [](const BitMatrix*){}); - if (!binImg) { - return DecodeStatus::NotFound; - } + if (!binImg) + return {}; #if defined(ZX_FAST_BIT_STORAGE) if (!HasStartPattern(*binImg)) - return DecodeStatus::NotFound; + return {}; #endif auto barcodeCoordinates = DetectBarcode(*binImg, multiple); @@ -354,12 +352,13 @@ Detector::Detect(const BinaryBitmap& image, bool multiple, Result& result) binImg = newBits; barcodeCoordinates = DetectBarcode(*binImg, multiple); } - if (barcodeCoordinates.empty()) { - return DecodeStatus::NotFound; - } + if (barcodeCoordinates.empty()) + return {}; + + Result result; result.points = barcodeCoordinates; result.bits = binImg; - return DecodeStatus::NoError; + return result; } } // Pdf417 diff --git a/core/src/pdf417/PDFDetector.h b/core/src/pdf417/PDFDetector.h index e129d6d2c0..ab375ff1c1 100644 --- a/core/src/pdf417/PDFDetector.h +++ b/core/src/pdf417/PDFDetector.h @@ -38,7 +38,7 @@ class Detector std::list, 8>> points; }; - static DecodeStatus Detect(const BinaryBitmap& image, bool multiple, Result& result); + static Result Detect(const BinaryBitmap& image, bool multiple); }; } // Pdf417 diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index 7f015553ac..c773f34a2a 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -66,31 +66,26 @@ static int GetMaxCodewordWidth(const std::array, 8>& p) std::max(GetMaxWidth(p[1], p[5]), GetMaxWidth(p[7], p[3]) * CodewordDecoder::MODULES_IN_CODEWORD / MODULES_IN_STOP_PATTERN)); } -DecodeStatus DoDecode(const BinaryBitmap& image, bool multiple, std::list& results) +static Results DoDecode(const BinaryBitmap& image, bool multiple, bool returnErrors) { - Detector::Result detectorResult; - DecodeStatus status = Detector::Detect(image, multiple, detectorResult); - if (StatusIsError(status)) { - return status; - } + Detector::Result detectorResult = Detector::Detect(image, multiple); + if (detectorResult.points.empty()) + return {}; + Results results; for (const auto& points : detectorResult.points) { DecoderResult decoderResult = ScanningDecoder::Decode(*detectorResult.bits, points[4], points[5], points[6], points[7], GetMinCodewordWidth(points), GetMaxCodewordWidth(points)); - if (decoderResult.isValid()) { + if (decoderResult.isValid(returnErrors)) { auto point = [&](int i) { return points[i].value(); }; Result result(std::move(decoderResult), {point(0), point(2), point(3), point(1)}, BarcodeFormat::PDF417); results.push_back(result); - if (!multiple) { - return DecodeStatus::NoError; - } - } - else if (!multiple) { - return decoderResult.errorCode(); + if (!multiple) + return results; } } - return results.empty() ? DecodeStatus::NotFound : DecodeStatus::NoError; + return results; } // new implementation (only for isPure use case atm.) @@ -315,27 +310,20 @@ Reader::decode(const BinaryBitmap& image) const // currently the best option to deal with 'aliased' input like e.g. 03-aliased.png } - std::list results; - DecodeStatus status = DoDecode(image, false, results); - if (StatusIsOK(status)) { - return results.front(); - } - return Result(status); + Results results = DoDecode(image, false, _hints.returnErrors()); + return results.empty() ? Result(DecodeStatus::NotFound) : results.front(); } Results Reader::decode(const BinaryBitmap& image, [[maybe_unused]] int maxSymbols) const { - std::list results; - DoDecode(image, true, results); - return Results(results.begin(), results.end()); + return DoDecode(image, true, _hints.returnErrors()); } std::list Reader::decodeMultiple(const BinaryBitmap& image) const { - std::list results; - DoDecode(image, true, results); - return results; + Results results = DoDecode(image, true, _hints.returnErrors()); + return std::list(results.begin(), results.end()); } } // Pdf417 From 0da93e0983eea30dcfefdcfbf5832acab6de5d94 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 1 Jul 2022 00:43:14 +0200 Subject: [PATCH 130/180] Error: implement typesafe comparison between Error and Error::Type Prevent a comparison of `Error` and `Error::Type` (via `operator bool()`) by changing `enum Type` to `enum class Type`. --- core/src/Error.h | 15 ++++++++++++--- core/src/pdf417/PDFReader.cpp | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/core/src/Error.h b/core/src/Error.h index 6cbee9f95e..fc4d690467 100644 --- a/core/src/Error.h +++ b/core/src/Error.h @@ -12,20 +12,29 @@ namespace ZXing { class Error { public: - enum Type { None, Format, Checksum, Unsupported }; + enum class Type { None, Format, Checksum, Unsupported }; Type type() const noexcept { return _type; } const std::string& msg() const noexcept { return _msg; } - operator bool() const noexcept { return _type != None; } + operator bool() const noexcept { return _type != Type::None; } Error() = default; + static constexpr auto Format = Type::Format; + static constexpr auto Checksum = Type::Checksum; + static constexpr auto Unsupported = Type::Unsupported; + protected: - Type _type = None; + Type _type = Type::None; std::string _msg; Error(Type type, std::string msg) : _type(type), _msg(std::move(msg)) {} }; +inline bool operator==(const Error& e, Error::Type t) noexcept { return e.type() == t; } +inline bool operator!=(const Error& e, Error::Type t) noexcept { return !(e == t); } +inline bool operator==(Error::Type t, const Error& e) noexcept { return e.type() == t; } +inline bool operator!=(Error::Type t, const Error& e) noexcept { return !(t == e); } + class FormatError : public Error { public: diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index c773f34a2a..e731760916 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -304,7 +304,7 @@ Reader::decode(const BinaryBitmap& image) const { if (_hints.isPure()) { auto res = DecodePure(image); - if (res.error().type() != Error::Checksum) + if (res.error() != Error::Checksum) return res; // This falls through and tries the non-pure code path if we have a checksum error. This approach is // currently the best option to deal with 'aliased' input like e.g. 03-aliased.png From 0386fe9f0e74e621fe11503f33423aa59f79aa78 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 1 Jul 2022 00:45:22 +0200 Subject: [PATCH 131/180] Error: Prepare phasing out `DecodeStatus` usage in `DecoderResult` * introduce `DecoderResult::error()` * remove `DecoderResult::errorCode()` --- core/src/DecoderResult.h | 10 ++++++---- core/src/Error.h | 12 ++++++++++++ core/src/Result.cpp | 13 ++----------- core/src/pdf417/PDFScanningDecoder.cpp | 2 +- test/unit/aztec/AZDecoderTest.cpp | 4 ++-- .../datamatrix/DMDecodedBitStreamParserTest.cpp | 6 +++--- test/unit/qrcode/MQRDecoderTest.cpp | 10 +++++----- 7 files changed, 31 insertions(+), 26 deletions(-) diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 0175db24d4..e6682df153 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -9,6 +9,7 @@ #include "ByteArray.h" #include "Content.h" #include "DecodeStatus.h" +#include "Error.h" #include "StructuredAppend.h" #include "ZXContainerAlgorithms.h" @@ -22,7 +23,6 @@ class CustomData; class DecoderResult { - DecodeStatus _status = DecodeStatus::NoError; ByteArray _rawBytes; Content _content; int _numBits = 0; @@ -31,13 +31,15 @@ class DecoderResult StructuredAppendInfo _structuredAppend; bool _isMirrored = false; bool _readerInit = false; + Error _error; std::shared_ptr _extra; DecoderResult(const DecoderResult &) = delete; DecoderResult& operator=(const DecoderResult &) = delete; public: - DecoderResult(DecodeStatus status) : _status(status) {} + DecoderResult(DecodeStatus status) : _error(Status2Error(status)) {} + DecoderResult(Error error) : _error(error) {} DecoderResult(ByteArray&& rawBytes, Content&& bytes = {}) : _rawBytes(std::move(rawBytes)), _content(std::move(bytes)) { _numBits = 8 * Size(_rawBytes); @@ -49,9 +51,8 @@ class DecoderResult bool isValid(bool includeErrors = false) const { - return StatusIsOK(_status) || (includeErrors && _status != DecodeStatus::NotFound); + return _content.symbology.code != 0 && (!_error || includeErrors); } - DecodeStatus errorCode() const { return _status; } const ByteArray& rawBytes() const & { return _rawBytes; } ByteArray&& rawBytes() && { return std::move(_rawBytes); } @@ -81,6 +82,7 @@ class DecoderResult ZX_PROPERTY(std::string, ecLevel, setEcLevel) ZX_PROPERTY(int, lineCount, setLineCount) 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, extra, setExtra) diff --git a/core/src/Error.h b/core/src/Error.h index fc4d690467..4b63dd8952 100644 --- a/core/src/Error.h +++ b/core/src/Error.h @@ -5,6 +5,8 @@ #pragma once +#include "DecodeStatus.h" + #include namespace ZXing { @@ -56,4 +58,14 @@ inline std::string ToString(const Error& e) return ret; } +// transitional helper function +inline Error Status2Error(DecodeStatus s) +{ + switch (s) { + case DecodeStatus::FormatError: return FormatError(); + case DecodeStatus::ChecksumError: return ChecksumError(); + default: return {}; + } +} + } diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 5588cf9cb1..b993a13bc7 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -17,15 +17,6 @@ namespace ZXing { -static Error Status2Error(DecodeStatus s) -{ - switch (s) { - case DecodeStatus::FormatError: return FormatError(); - case DecodeStatus::ChecksumError: return ChecksumError(); - default: return {}; - } -} - Result::Result(DecodeStatus status) : _error(Status2Error(status)) {} Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, @@ -41,9 +32,9 @@ Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFor {} Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format) - : _format(decodeResult.errorCode() == DecodeStatus::NotFound ? BarcodeFormat::None : format), + : _format(decodeResult.content().symbology.code == 0 ? BarcodeFormat::None : format), _content(std::move(decodeResult).content()), - _error(Status2Error(decodeResult.errorCode())), + _error(std::move(decodeResult).error()), _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), diff --git a/core/src/pdf417/PDFScanningDecoder.cpp b/core/src/pdf417/PDFScanningDecoder.cpp index d946fa0a16..c73ab8e3fa 100644 --- a/core/src/pdf417/PDFScanningDecoder.cpp +++ b/core/src/pdf417/PDFScanningDecoder.cpp @@ -612,7 +612,7 @@ static DecoderResult CreateDecoderResultFromAmbiguousValues(int ecLevel, std::ve codewords[ambiguousIndexes[i]] = ambiguousIndexValues[i][ambiguousIndexCount[i]]; } auto result = DecodeCodewords(codewords, ecLevel, erasureArray); - if (result.errorCode() != DecodeStatus::ChecksumError) { + if (result.error() != Error::Checksum) { return result; } diff --git a/test/unit/aztec/AZDecoderTest.cpp b/test/unit/aztec/AZDecoderTest.cpp index 175b041405..b1b7c5cc24 100644 --- a/test/unit/aztec/AZDecoderTest.cpp +++ b/test/unit/aztec/AZDecoderTest.cpp @@ -96,7 +96,7 @@ TEST(AZDecoderTest, DecodeTooManyErrors) , 'X', true); DecoderResult result = parse(std::move(bits), true, 16, 4); - EXPECT_EQ(result.errorCode(), DecodeStatus::FormatError); + EXPECT_EQ(result.error(), Error::Format); } TEST(AZDecoderTest, DecodeTooManyErrors2) @@ -132,7 +132,7 @@ TEST(AZDecoderTest, DecodeTooManyErrors2) , 'X', true); DecoderResult result = parse(std::move(bits), true, 16, 4); - EXPECT_EQ(result.errorCode(), DecodeStatus::FormatError); + EXPECT_EQ(result.error(), Error::Format); } // Helper taking bit string to call GetEncodedData() diff --git a/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp b/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp index c6b6e8de64..1e03a453dd 100644 --- a/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp +++ b/test/unit/datamatrix/DMDecodedBitStreamParserTest.cpp @@ -54,13 +54,13 @@ TEST(DMDecodeTest, Ascii) TEST(DMDecodeTest, AsciiError) { // ASCII err on invalid code word - EXPECT_EQ(parse({66, 250, 68}).errorCode(), DecodeStatus::FormatError); + EXPECT_EQ(parse({66, 250, 68}).error(), Error::Format); // ASCII err on invalid code word at end (currently failing) - EXPECT_EQ(parse({66, 67, 68, 250}).errorCode(), DecodeStatus::FormatError); + EXPECT_EQ(parse({66, 67, 68, 250}).error(), Error::Format); // ASCII accept extra (illegal) unlatch at end - EXPECT_EQ(parse({66, 67, 68, 254}).errorCode(), DecodeStatus::NoError); + EXPECT_FALSE(parse({66, 67, 68, 254}).error()); } // Most of the following examples are taken from the DMHighLevelEncodeTest.cpp tests. diff --git a/test/unit/qrcode/MQRDecoderTest.cpp b/test/unit/qrcode/MQRDecoderTest.cpp index 34bf5add5e..25e7f700de 100644 --- a/test/unit/qrcode/MQRDecoderTest.cpp +++ b/test/unit/qrcode/MQRDecoderTest.cpp @@ -35,7 +35,7 @@ TEST(MQRDecoderTest, MQRCodeM3L) 88, false); const auto result = Decode(bitMatrix); - EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); + EXPECT_TRUE(result.isValid()); } TEST(MQRDecoderTest, MQRCodeM3M) @@ -58,7 +58,7 @@ TEST(MQRDecoderTest, MQRCodeM3M) 88, false); const auto result = Decode(bitMatrix); - EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); + EXPECT_TRUE(result.isValid()); } TEST(MQRDecoderTest, MQRCodeM1) @@ -76,7 +76,7 @@ TEST(MQRDecoderTest, MQRCodeM1) "X XXXXXX X\n", 88, false); const auto result = Decode(bitMatrix); - EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); + EXPECT_TRUE(result.isValid()); EXPECT_EQ(L"123", result.text()); } @@ -95,7 +95,7 @@ TEST(MQRDecoderTest, MQRCodeM1Error4Bits) "X XXXXXXX \n", 88, false); const auto result = Decode(bitMatrix); - EXPECT_EQ(DecodeStatus::ChecksumError, result.errorCode()); + EXPECT_EQ(Error::Checksum, result.error()); EXPECT_TRUE(result.text().empty()); } @@ -120,5 +120,5 @@ TEST(MQRDecoderTest, MQRCodeM4) "X XXXXXXX X X X\n", 88, false); const auto result = Decode(bitMatrix); - EXPECT_EQ(DecodeStatus::NoError, result.errorCode()); + EXPECT_TRUE(result.isValid()); } From af45a12e4f1d7aeed9339e24a54e7792f8e665aa Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 1 Jul 2022 01:27:24 +0200 Subject: [PATCH 132/180] Result: introduce default constructor for "empty" / "not-found" result --- core/src/MultiFormatReader.cpp | 4 ++-- core/src/ReadBarcode.cpp | 2 +- core/src/Result.cpp | 2 +- core/src/Result.h | 1 + core/src/aztec/AZReader.cpp | 2 +- core/src/datamatrix/DMReader.cpp | 4 ++-- core/src/maxicode/MCReader.cpp | 4 ++-- core/src/oned/ODCodabarReader.cpp | 10 +++++----- core/src/oned/ODCode128Reader.cpp | 12 ++++++------ core/src/oned/ODCode39Reader.cpp | 10 +++++----- core/src/oned/ODCode93Reader.cpp | 10 +++++----- core/src/oned/ODDataBarExpandedReader.cpp | 6 +++--- core/src/oned/ODDataBarReader.cpp | 2 +- core/src/oned/ODITFReader.cpp | 6 +++--- core/src/oned/ODMultiUPCEANReader.cpp | 6 +++--- core/src/oned/ODReader.cpp | 6 +++--- core/src/pdf417/PDFReader.cpp | 8 ++++---- core/src/qrcode/QRReader.cpp | 6 +++--- example/ZXingQtReader.h | 2 +- example/ZXingReader.cpp | 2 +- test/unit/ThresholdBinarizerTest.cpp | 3 +-- 21 files changed, 54 insertions(+), 54 deletions(-) diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index 38cdaf1c20..d52e91d2af 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -48,13 +48,13 @@ MultiFormatReader::~MultiFormatReader() = default; Result MultiFormatReader::read(const BinaryBitmap& image) const { - Result r(DecodeStatus::NotFound); + Result r; for (const auto& reader : _readers) { r = reader->decode(image); if (r.isValid()) return r; } - return _hints.returnErrors() ? r : Result(DecodeStatus::NotFound); + return _hints.returnErrors() ? r : Result(); } Results MultiFormatReader::readMultiple(const BinaryBitmap& image, int maxSymbols) const diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 9f95eb4688..39e3ee768a 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -122,7 +122,7 @@ Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) // HACK: use the maxNumberOfSymbols value as a switch to ReadBarcodes to enable the downscaling // see python and android wrapper auto ress = ReadBarcodes(_iv, DecodeHints(hints).setMaxNumberOfSymbols(1)); - return ress.empty() ? Result(DecodeStatus::NotFound) : ress.front(); + return ress.empty() ? Result() : ress.front(); } else { LumImage lum; ImageView iv = SetupLumImageView(_iv, lum, hints); diff --git a/core/src/Result.cpp b/core/src/Result.cpp index b993a13bc7..ae166486ae 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -147,7 +147,7 @@ bool Result::operator==(const Result& o) const Result MergeStructuredAppendSequence(const Results& results) { if (results.empty()) - return Result(DecodeStatus::NotFound); + return {}; std::list allResults(results.begin(), results.end()); allResults.sort([](const Result& r1, const Result& r2) { return r1.sequenceIndex() < r2.sequenceIndex(); }); diff --git a/core/src/Result.h b/core/src/Result.h index 41adcda416..273216799c 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -30,6 +30,7 @@ using Position = QuadrilateralI; class Result { public: + Result() noexcept = default; explicit Result(DecodeStatus status); // 1D convenience constructor diff --git a/core/src/aztec/AZReader.cpp b/core/src/aztec/AZReader.cpp index de452c6a6e..236ff4761f 100644 --- a/core/src/aztec/AZReader.cpp +++ b/core/src/aztec/AZReader.cpp @@ -24,7 +24,7 @@ Reader::decode(const BinaryBitmap& image) const { auto binImg = image.getBitMatrix(); if (binImg == nullptr) { - return Result(DecodeStatus::NotFound); + return {}; } DetectorResult detectResult = Detect(*binImg, false, _hints.isPure()); diff --git a/core/src/datamatrix/DMReader.cpp b/core/src/datamatrix/DMReader.cpp index ed3b93979c..c34ea646c8 100644 --- a/core/src/datamatrix/DMReader.cpp +++ b/core/src/datamatrix/DMReader.cpp @@ -26,11 +26,11 @@ Result Reader::decode(const BinaryBitmap& image) const #else auto binImg = image.getBitMatrix(); if (binImg == nullptr) - return Result(DecodeStatus::NotFound); + return {}; auto detectorResult = Detect(*binImg, _hints.tryHarder(), _hints.tryRotate(), _hints.isPure()); if (!detectorResult.isValid()) - return Result(DecodeStatus::NotFound); + return {}; return Result(Decode(detectorResult.bits()), std::move(detectorResult).position(), BarcodeFormat::DataMatrix); #endif diff --git a/core/src/maxicode/MCReader.cpp b/core/src/maxicode/MCReader.cpp index 441bb529bd..fc0834efb6 100644 --- a/core/src/maxicode/MCReader.cpp +++ b/core/src/maxicode/MCReader.cpp @@ -48,13 +48,13 @@ Reader::decode(const BinaryBitmap& image) const { auto binImg = image.getBitMatrix(); if (binImg == nullptr) { - return Result(DecodeStatus::NotFound); + return {}; } //TODO: this only works with effectively 'pure' barcodes. Needs proper detector. BitMatrix bits = ExtractPureBits(*binImg); if (bits.empty()) { - return Result(DecodeStatus::NotFound); + return {}; } return Result(Decode(bits), {}, BarcodeFormat::MaxiCode); diff --git a/core/src/oned/ODCodabarReader.cpp b/core/src/oned/ODCodabarReader.cpp index 0ebec78088..119911d1a9 100644 --- a/core/src/oned/ODCodabarReader.cpp +++ b/core/src/oned/ODCodabarReader.cpp @@ -61,7 +61,7 @@ CodabarReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr(next, minCharCount * CHAR_LEN, IsLeftGuard); if (!next.isValid()) - return Result(DecodeStatus::NotFound); + return {}; int xStart = next.pixelsInFront(); int maxInterCharacterSpace = next.sum() / 2; // spec actually says 1 narrow space, width/2 is about 4 @@ -71,22 +71,22 @@ CodabarReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr= CODE_START_A) @@ -260,13 +260,13 @@ Result Code128Reader::decodePattern(int rowNumber, PatternView& next, std::uniqu } if (Size(rawCodes) < minCharCount - 1) // stop code is missing in rawCodes - return Result(DecodeStatus::NotFound); + return {}; // check termination bar (is present and not wider than about 2 modules) and quiet zone (next is now 13 modules // wide, require at least 8) next = next.subView(0, CHAR_LEN + 1); if (!next.isValid() || next[CHAR_LEN] > next.sum(CHAR_LEN) / 4 || !next.hasQuietZoneAfter(QUIET_ZONE/13)) - return Result(DecodeStatus::NotFound); + return {}; int checksum = rawCodes.front(); for (int i = 1; i < Size(rawCodes) - 1; ++i) diff --git a/core/src/oned/ODCode39Reader.cpp b/core/src/oned/ODCode39Reader.cpp index caf4d76f18..469b61628c 100644 --- a/core/src/oned/ODCode39Reader.cpp +++ b/core/src/oned/ODCode39Reader.cpp @@ -94,10 +94,10 @@ Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique next = FindLeftGuard(next, minCharCount * CHAR_LEN, START_PATTERN, QUIET_ZONE_SCALE * 12); if (!next.isValid()) - return Result(DecodeStatus::NotFound); + return {}; if (!isStartOrStopSymbol(DecodeNarrowWidePattern(next, CHARACTER_ENCODINGS, ALPHABET))) // read off the start pattern - return Result(DecodeStatus::NotFound); + return {}; int xStart = next.pixelsInFront(); int maxInterCharacterSpace = next.sum() / 2; // spec actually says 1 narrow space, width/2 is about 4 @@ -108,18 +108,18 @@ Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique do { // check remaining input width and inter-character space if (!next.skipSymbol() || !next.skipSingle(maxInterCharacterSpace)) - return Result(DecodeStatus::NotFound); + return {}; txt += DecodeNarrowWidePattern(next, CHARACTER_ENCODINGS, ALPHABET); if (txt.back() == 0) - return Result(DecodeStatus::NotFound); + return {}; } while (!isStartOrStopSymbol(txt.back())); txt.pop_back(); // remove asterisk // check txt length and whitespace after the last char. See also FindStartPattern. if (Size(txt) < minCharCount - 2 || !next.hasQuietZoneAfter(QUIET_ZONE_SCALE)) - return Result(DecodeStatus::NotFound); + return {}; if (_validateCheckSum) { auto checkDigit = txt.back(); diff --git a/core/src/oned/ODCode93Reader.cpp b/core/src/oned/ODCode93Reader.cpp index 7b3552d519..4cc499205d 100644 --- a/core/src/oned/ODCode93Reader.cpp +++ b/core/src/oned/ODCode93Reader.cpp @@ -87,7 +87,7 @@ Result Code93Reader::decodePattern(int rowNumber, PatternView& next, std::unique next = FindLeftGuard(next, minCharCount * CHAR_LEN, IsStartGuard); if (!next.isValid()) - return Result(DecodeStatus::NotFound); + return {}; int xStart = next.pixelsInFront(); @@ -97,22 +97,22 @@ Result Code93Reader::decodePattern(int rowNumber, PatternView& next, std::unique do { // check remaining input width if (!next.skipSymbol()) - return Result(DecodeStatus::NotFound); + return {}; txt += LookupBitPattern(OneToFourBitPattern(next), CHARACTER_ENCODINGS, ALPHABET); if (txt.back() == 0) - return Result(DecodeStatus::NotFound); + return {}; } while (txt.back() != '*'); txt.pop_back(); // remove asterisk if (Size(txt) < minCharCount - 2) - return Result(DecodeStatus::NotFound); + return {}; // check termination bar (is present and not wider than about 2 modules) and quiet zone next = next.subView(0, CHAR_LEN + 1); if (!next.isValid() || next[CHAR_LEN] > next.sum(CHAR_LEN) / 4 || !next.hasQuietZoneAfter(QUIET_ZONE_SCALE)) - return Result(DecodeStatus::NotFound); + return {}; if (!CheckChecksums(txt)) return Result(DecodeStatus::ChecksumError); diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index 2260931761..6e8d59e92f 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -360,16 +360,16 @@ Result DataBarExpandedReader::decodePattern(int rowNumber, PatternView& view, // L R L R | r | l if (!Insert(allPairs, ReadRowOfPairs(view, rowNumber))) - return Result(DecodeStatus::NotFound); + return {}; auto pairs = FindValidSequence(allPairs); if (pairs.empty()) - return Result(DecodeStatus::NotFound); + return {}; #endif auto txt = DecodeExpandedBits(BuildBitArray(pairs)); if (txt.empty()) - return Result(DecodeStatus::NotFound); + return {}; RemovePairs(allPairs, pairs); diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index a39ec9f741..88865b6e3d 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -211,7 +211,7 @@ Result DataBarReader::decodePattern(int rowNumber, PatternView& next, // guaratee progress (see loop in ODReader.cpp) next = {}; - return Result(DecodeStatus::NotFound); + return {}; } } // namespace ZXing::OneD diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index 3de5d5206c..2e4db3f3f3 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -37,7 +37,7 @@ Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_pt next = FindLeftGuard(next, 4 + minCharCount/2 + 3, START_PATTERN_, minQuietZone); if (!next.isValid()) - return Result(DecodeStatus::NotFound); + return {}; std::string txt; txt.reserve(20); @@ -71,10 +71,10 @@ Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_pt next = next.subView(0, 3); if (Size(txt) < minCharCount || !next.isValid()) - return Result(DecodeStatus::NotFound); + return {}; if (!IsRightGuard(next, STOP_PATTERN_1, minQuietZone) && !IsRightGuard(next, STOP_PATTERN_2, minQuietZone)) - return Result(DecodeStatus::NotFound); + return {}; if (_validateCheckSum && !GTIN::IsCheckDigitValid(txt)) return Result(DecodeStatus::ChecksumError); diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index 92b59d379a..22da7ad2cf 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -265,7 +265,7 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u next = FindLeftGuard(next, minSize, END_PATTERN, QUIET_ZONE_LEFT); if (!next.isValid()) - return Result(DecodeStatus::NotFound); + return {}; PartialResult res; auto begin = next; @@ -273,7 +273,7 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u if (!(((_hints.hasFormat(BarcodeFormat::EAN13 | BarcodeFormat::UPCA)) && EAN13(res, begin)) || (_hints.hasFormat(BarcodeFormat::EAN8) && EAN8(res, begin)) || (_hints.hasFormat(BarcodeFormat::UPCE) && UPCE(res, begin)))) - return Result(DecodeStatus::NotFound); + return {}; if (!GTIN::IsCheckDigitValid(res.format == BarcodeFormat::UPCE ? UPCEANCommon::ConvertUPCEtoUPCA(res.txt) : res.txt)) return Result(DecodeStatus::ChecksumError); @@ -307,7 +307,7 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u next = res.end; if (_hints.eanAddOnSymbol() == EanAddOnSymbol::Require && !addOnRes.isValid()) - return Result(DecodeStatus::NotFound); + return {}; return {res.txt, rowNumber, begin.pixelsInFront(), res.end.pixelsTillEnd(), res.format, symbologyIdentifier}; } diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index a09858a192..c6877f3e24 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -170,7 +170,7 @@ static Results DoDecode(const std::vector>& readers, other.setPosition(points); other.incrementLineCount(); // clear the result, so we don't insert it again below - result = Result(DecodeStatus::NotFound); + result = Result(); break; } } @@ -209,7 +209,7 @@ static Results DoDecode(const std::vector>& readers, for (auto a = res.begin(); a != res.end(); ++a) for (auto b = std::next(a); b != res.end(); ++b) if (HaveIntersectingBoundingBoxes(a->position(), b->position())) - *(a->lineCount() < b->lineCount() ? a : b) = Result(DecodeStatus::NotFound); + *(a->lineCount() < b->lineCount() ? a : b) = Result(); //TODO: C++20 res.erase_if() it = std::remove_if(res.begin(), res.end(), [](auto&& r) { return r.format() == BarcodeFormat::None; }); @@ -227,7 +227,7 @@ Reader::decode(const BinaryBitmap& image) const if (result.empty() && _hints.tryRotate()) result = DoDecode(_readers, image, _hints.tryHarder(), true, _hints.isPure(), 1, _hints.minLineCount(), _hints.returnErrors()); - return result.empty() ? Result(DecodeStatus::NotFound) : result.front(); + return result.empty() ? Result() : result.front(); } Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index e731760916..9a54180ff2 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -255,7 +255,7 @@ static Result DecodePure(const BinaryBitmap& image_) { auto pimage = image_.getBitMatrix(); if (!pimage) - return Result(DecodeStatus::NotFound); + return {}; auto& image = *pimage; #ifdef PRINT_DEBUG @@ -264,7 +264,7 @@ static Result DecodePure(const BinaryBitmap& image_) int left, top, width, height; if (!image.findBoundingBox(left, top, width, height, 9) || (width < 3 * 17 && height < 3 * 17)) - return Result(DecodeStatus::NotFound); + return {}; int right = left + width - 1; int bottom = top + height - 1; @@ -283,7 +283,7 @@ static Result DecodePure(const BinaryBitmap& image_) } if (!info) - return Result(DecodeStatus::NotFound); + return {}; auto codeWords = ReadCodeWords(cur, info); @@ -311,7 +311,7 @@ Reader::decode(const BinaryBitmap& image) const } Results results = DoDecode(image, false, _hints.returnErrors()); - return results.empty() ? Result(DecodeStatus::NotFound) : results.front(); + return results.empty() ? Result() : results.front(); } Results Reader::decode(const BinaryBitmap& image, [[maybe_unused]] int maxSymbols) const diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index a7f2676509..ba5c6d7478 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -26,13 +26,13 @@ Result Reader::decode(const BinaryBitmap& image) const #if 1 if (!_hints.isPure()) { auto res = decode(image, 1); - return res.empty() ? Result(DecodeStatus::NotFound) : res.front(); + return res.empty() ? Result() : res.front(); } #endif auto binImg = image.getBitMatrix(); if (binImg == nullptr) { - return Result(DecodeStatus::NotFound); + return {}; } DetectorResult detectorResult; @@ -42,7 +42,7 @@ Result Reader::decode(const BinaryBitmap& image) const detectorResult = DetectPureMQR(*binImg); if (!detectorResult.isValid()) - return Result(DecodeStatus::NotFound); + return {}; auto decoderResult = Decode(detectorResult.bits()); auto position = detectorResult.position(); diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index 58998c20c8..c2155dff9e 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -110,7 +110,7 @@ class Result : private ZXing::Result Position _position; public: - Result() : ZXing::Result(ZXing::DecodeStatus::NotFound) {} // required for qmetatype machinery + Result() = default; // required for qmetatype machinery explicit Result(ZXing::Result&& r) : ZXing::Result(std::move(r)) { _text = QString::fromStdString(ZXing::Result::text()); diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 42dda3837e..f134809807 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -160,7 +160,7 @@ int main(int argc, char* argv[]) // if we did not find anything, insert a dummy to produce some output for each file if (results.empty()) - results.emplace_back(DecodeStatus::NotFound); + results.emplace_back(); allResults.insert(allResults.end(), results.begin(), results.end()); if (filePath == filePaths.back()) { diff --git a/test/unit/ThresholdBinarizerTest.cpp b/test/unit/ThresholdBinarizerTest.cpp index aee132bdcc..d6695ba404 100644 --- a/test/unit/ThresholdBinarizerTest.cpp +++ b/test/unit/ThresholdBinarizerTest.cpp @@ -46,7 +46,6 @@ TEST(ThresholdBinarizerTest, PatternRowClear) BitMatrix bits; DecodeHints hints; std::vector buf; - Result result(DecodeStatus::NotFound); // Test that ThresholdBinarizer::getPatternRow() clears row first (same as GlobalHistogramBinarizer) // Following was failing due to OneD::DoDecode() accumulating bars in loop when using ThresholdBinarizer @@ -98,7 +97,7 @@ TEST(ThresholdBinarizerTest, PatternRowClear) hints.setFormats(BarcodeFormat::DataBarExpanded); OneD::Reader reader(hints); - result = reader.decode(ThresholdBinarizer(getImageView(buf, bits), 0x7F)); + Result result = reader.decode(ThresholdBinarizer(getImageView(buf, bits), 0x7F)); EXPECT_TRUE(result.isValid()); EXPECT_EQ(result.text(), "(91)12345678901234567890123456789012345678901234567890123456789012345678"); } From 55dd6b6db6328d706a4c64da3cbfdc378b9eb955 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 1 Jul 2022 01:45:20 +0200 Subject: [PATCH 133/180] Result: introduce FirstOrDefault helper for `Results` to `Result` conv --- core/src/ReadBarcode.cpp | 3 +-- core/src/Result.h | 6 ++++++ core/src/oned/ODReader.cpp | 2 +- core/src/pdf417/PDFReader.cpp | 3 +-- core/src/qrcode/QRReader.cpp | 3 +-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 39e3ee768a..5b62ff0d87 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -121,8 +121,7 @@ Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) if (hints.maxNumberOfSymbols() == 1) { // HACK: use the maxNumberOfSymbols value as a switch to ReadBarcodes to enable the downscaling // see python and android wrapper - auto ress = ReadBarcodes(_iv, DecodeHints(hints).setMaxNumberOfSymbols(1)); - return ress.empty() ? Result() : ress.front(); + return FirstOrDefault(ReadBarcodes(_iv, DecodeHints(hints).setMaxNumberOfSymbols(1))); } else { LumImage lum; ImageView iv = SetupLumImageView(_iv, lum, hints); diff --git a/core/src/Result.h b/core/src/Result.h index 273216799c..49485de189 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -173,6 +173,12 @@ class Result using Results = std::vector; +// Consider this an internal function that can change/disappear anytime without notice +inline Result FirstOrDefault(Results&& results) +{ + return results.empty() ? Result() : std::move(results.front()); +} + /** * @brief Merge a list of Results from one Structured Append sequence to a single result */ diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index c6877f3e24..afbd698485 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -227,7 +227,7 @@ Reader::decode(const BinaryBitmap& image) const if (result.empty() && _hints.tryRotate()) result = DoDecode(_readers, image, _hints.tryHarder(), true, _hints.isPure(), 1, _hints.minLineCount(), _hints.returnErrors()); - return result.empty() ? Result() : result.front(); + return FirstOrDefault(std::move(result)); } Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index 9a54180ff2..3cdab9aa2b 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -310,8 +310,7 @@ Reader::decode(const BinaryBitmap& image) const // currently the best option to deal with 'aliased' input like e.g. 03-aliased.png } - Results results = DoDecode(image, false, _hints.returnErrors()); - return results.empty() ? Result() : results.front(); + return FirstOrDefault(DoDecode(image, false, _hints.returnErrors())); } Results Reader::decode(const BinaryBitmap& image, [[maybe_unused]] int maxSymbols) const diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index ba5c6d7478..7b681e9f2e 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -25,8 +25,7 @@ Result Reader::decode(const BinaryBitmap& image) const { #if 1 if (!_hints.isPure()) { - auto res = decode(image, 1); - return res.empty() ? Result() : res.front(); + return FirstOrDefault(decode(image, 1)); } #endif From 610e53c39c3cf5254de5c2a2cedfca694efb521b Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 1 Jul 2022 01:53:42 +0200 Subject: [PATCH 134/180] c++: code style improvements (remove braces around single line if blocks) --- core/src/aztec/AZDetector.cpp | 10 ++-- core/src/aztec/AZReader.cpp | 9 ++-- core/src/datamatrix/DMDetector.cpp | 6 +-- core/src/maxicode/MCReader.cpp | 9 ++-- .../oned/rss/ODRSSExpandedBinaryDecoder.cpp | 50 ++++++++----------- core/src/qrcode/QRReader.cpp | 6 +-- 6 files changed, 35 insertions(+), 55 deletions(-) diff --git a/core/src/aztec/AZDetector.cpp b/core/src/aztec/AZDetector.cpp index 453509b29c..7c56af7829 100644 --- a/core/src/aztec/AZDetector.cpp +++ b/core/src/aztec/AZDetector.cpp @@ -496,23 +496,19 @@ DetectorResult Detect(const BitMatrix& image, bool isMirror, bool isPure) std::array bullsEyeCorners; bool compact = false; int nbCenterLayers = 0; - if (!GetBullsEyeCorners(image, pCenter, bullsEyeCorners, compact, nbCenterLayers)) { + if (!GetBullsEyeCorners(image, pCenter, bullsEyeCorners, compact, nbCenterLayers)) return {}; - } - if (isMirror) { + if (isMirror) std::swap(bullsEyeCorners[0], bullsEyeCorners[2]); - } // 3. Get the size of the matrix and other parameters from the bull's eye int nbLayers = 0; int nbDataBlocks = 0; bool readerInit = false; int shift = 0; - if (!ExtractParameters(image, bullsEyeCorners, compact, nbCenterLayers, nbLayers, nbDataBlocks, readerInit, - shift)) { + if (!ExtractParameters(image, bullsEyeCorners, compact, nbCenterLayers, nbLayers, nbDataBlocks, readerInit, shift)) return {}; - } // 4. Sample the grid return {SampleGrid(image, bullsEyeCorners[(shift + 0) % 4], bullsEyeCorners[(shift + 1) % 4], diff --git a/core/src/aztec/AZReader.cpp b/core/src/aztec/AZReader.cpp index 236ff4761f..95e43ad8d8 100644 --- a/core/src/aztec/AZReader.cpp +++ b/core/src/aztec/AZReader.cpp @@ -23,22 +23,19 @@ Result Reader::decode(const BinaryBitmap& image) const { auto binImg = image.getBitMatrix(); - if (binImg == nullptr) { + if (binImg == nullptr) return {}; - } DetectorResult detectResult = Detect(*binImg, false, _hints.isPure()); DecoderResult decodeResult = DecodeStatus::NotFound; - if (detectResult.isValid()) { + if (detectResult.isValid()) decodeResult = Decode(detectResult); - } //TODO: don't start detection all over again, just to swap 2 corner points if (!decodeResult.isValid()) { detectResult = Detect(*binImg, true, _hints.isPure()); - if (detectResult.isValid()) { + if (detectResult.isValid()) decodeResult = Decode(detectResult); - } } return Result(std::move(decodeResult), std::move(detectResult).position(), BarcodeFormat::Aztec); diff --git a/core/src/datamatrix/DMDetector.cpp b/core/src/datamatrix/DMDetector.cpp index 7eb9dc20c6..47078d6986 100644 --- a/core/src/datamatrix/DMDetector.cpp +++ b/core/src/datamatrix/DMDetector.cpp @@ -249,9 +249,8 @@ static void OrderByBestPatterns(const ResultPoint*& p0, const ResultPoint*& p1, static DetectorResult DetectOld(const BitMatrix& image) { ResultPoint pointA, pointB, pointC, pointD; - if (!DetectWhiteRect(image, pointA, pointB, pointC, pointD)) { + if (!DetectWhiteRect(image, pointA, pointB, pointC, pointD)) return {}; - } // Point A and D are across the diagonal from one another, // as are B and C. Figure out which are the solid black lines @@ -300,9 +299,8 @@ static DetectorResult DetectOld(const BitMatrix& image) } } - if (bottomRight == nullptr || bottomLeft == nullptr || topLeft == nullptr) { + if (bottomRight == nullptr || bottomLeft == nullptr || topLeft == nullptr) return {}; - } // Bottom left is correct but top left and bottom right might be switched // Use the dot product trick to sort them out diff --git a/core/src/maxicode/MCReader.cpp b/core/src/maxicode/MCReader.cpp index fc0834efb6..a03e8c76de 100644 --- a/core/src/maxicode/MCReader.cpp +++ b/core/src/maxicode/MCReader.cpp @@ -25,9 +25,8 @@ namespace ZXing::MaxiCode { static BitMatrix ExtractPureBits(const BitMatrix& image) { int left, top, width, height; - if (!image.findBoundingBox(left, top, width, height, BitMatrixParser::MATRIX_WIDTH)) { + if (!image.findBoundingBox(left, top, width, height, BitMatrixParser::MATRIX_WIDTH)) return {}; - } // Now just read off the bits BitMatrix result(BitMatrixParser::MATRIX_WIDTH, BitMatrixParser::MATRIX_HEIGHT); @@ -47,15 +46,13 @@ Result Reader::decode(const BinaryBitmap& image) const { auto binImg = image.getBitMatrix(); - if (binImg == nullptr) { + if (binImg == nullptr) return {}; - } //TODO: this only works with effectively 'pure' barcodes. Needs proper detector. BitMatrix bits = ExtractPureBits(*binImg); - if (bits.empty()) { + if (bits.empty()) return {}; - } return Result(Decode(bits), {}, BarcodeFormat::MaxiCode); } diff --git a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp index 1ebe6237f9..5c513c2cd6 100644 --- a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp +++ b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp @@ -104,9 +104,9 @@ static std::string DecodeAI01AndOtherAIs(const BitArray& bits) buffer.append(std::to_string(firstGtinDigit)); AI01EncodeCompressedGtinWithoutAI(buffer, bits, HEADER_SIZE + 4, initialGtinPosition); - if (StatusIsOK(DecodeAppIdAllCodes(bits, HEADER_SIZE + 44, -1, buffer))) { + if (StatusIsOK(DecodeAppIdAllCodes(bits, HEADER_SIZE + 44, -1, buffer))) return buffer; - } + return {}; } @@ -114,9 +114,9 @@ static std::string DecodeAnyAI(const BitArray& bits) { static const int HEADER_SIZE = 2 + 1 + 2; std::string buffer; - if (StatusIsOK(DecodeAppIdAllCodes(bits, HEADER_SIZE, -1, buffer))) { + if (StatusIsOK(DecodeAppIdAllCodes(bits, HEADER_SIZE, -1, buffer))) return buffer; - } + return {}; } @@ -125,9 +125,8 @@ static std::string DecodeAI013103(const BitArray& bits) static const int HEADER_SIZE = 4 + 1; static const int WEIGHT_SIZE = 15; - if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE) { + if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE) return {}; - } std::string buffer; AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); @@ -145,9 +144,8 @@ static std::string DecodeAI01320x(const BitArray& bits) static const int HEADER_SIZE = 4 + 1; static const int WEIGHT_SIZE = 15; - if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE) { + if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE) return {}; - } std::string buffer; AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); @@ -166,9 +164,8 @@ static std::string DecodeAI01392x(const BitArray& bits) static const int HEADER_SIZE = 5 + 1 + 2; static const int LAST_DIGIT_SIZE = 2; - if (bits.size() < HEADER_SIZE + AI01_GTIN_SIZE) { + if (bits.size() < HEADER_SIZE + AI01_GTIN_SIZE) return {}; - } std::string buffer; AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); @@ -193,9 +190,8 @@ static std::string DecodeAI01393x(const BitArray& bits) static const int LAST_DIGIT_SIZE = 2; static const int FIRST_THREE_DIGITS_SIZE = 10; - if (bits.size() < HEADER_SIZE + AI01_GTIN_SIZE) { + if (bits.size() < HEADER_SIZE + AI01_GTIN_SIZE) return {}; - } std::string buffer; AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); @@ -207,12 +203,12 @@ static std::string DecodeAI01393x(const BitArray& bits) buffer.push_back(')'); int firstThreeDigits = ToInt(bits, HEADER_SIZE + AI01_GTIN_SIZE + LAST_DIGIT_SIZE, FIRST_THREE_DIGITS_SIZE); - if (firstThreeDigits / 100 == 0) { + if (firstThreeDigits / 100 == 0) buffer.push_back('0'); - } - if (firstThreeDigits / 10 == 0) { + + if (firstThreeDigits / 10 == 0) buffer.push_back('0'); - } + buffer.append(std::to_string(firstThreeDigits)); int pos = HEADER_SIZE + AI01_GTIN_SIZE + LAST_DIGIT_SIZE + FIRST_THREE_DIGITS_SIZE; @@ -230,9 +226,8 @@ static std::string DecodeAI013x0x1x(const BitArray& bits, const char* firstAIdig static const int WEIGHT_SIZE = 20; static const int DATE_SIZE = 16; - if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE + DATE_SIZE) { + if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE + DATE_SIZE) return {}; - } std::string buffer; AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); @@ -262,17 +257,17 @@ static std::string DecodeAI013x0x1x(const BitArray& bits, const char* firstAIdig numericDate /= 12; int year = numericDate; - if (year / 10 == 0) { + if (year / 10 == 0) buffer.push_back('0'); - } + buffer.append(std::to_string(year)); - if (month / 10 == 0) { + if (month / 10 == 0) buffer.push_back('0'); - } + buffer.append(std::to_string(month)); - if (day / 10 == 0) { + if (day / 10 == 0) buffer.push_back('0'); - } + buffer.append(std::to_string(day)); } @@ -281,12 +276,11 @@ static std::string DecodeAI013x0x1x(const BitArray& bits, const char* firstAIdig std::string DecodeExpandedBits(const BitArray& bits) { - if (bits.get(1)) { + if (bits.get(1)) return DecodeAI01AndOtherAIs(bits); - } - if (!bits.get(2)) { + + if (!bits.get(2)) return DecodeAnyAI(bits); - } int fourBitEncodationMethod = ToInt(bits, 1, 4); diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index 7b681e9f2e..7d825f7f82 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -24,15 +24,13 @@ namespace ZXing::QRCode { Result Reader::decode(const BinaryBitmap& image) const { #if 1 - if (!_hints.isPure()) { + if (!_hints.isPure()) return FirstOrDefault(decode(image, 1)); - } #endif auto binImg = image.getBitMatrix(); - if (binImg == nullptr) { + if (binImg == nullptr) return {}; - } DetectorResult detectorResult; if (_hints.hasFormat(BarcodeFormat::QRCode)) From 0be27beb2318d41642308994851f492f63d4f7dc Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 1 Jul 2022 02:01:33 +0200 Subject: [PATCH 135/180] Result: fix gcc build regression (noexcept default constructor) --- core/src/Result.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/Result.h b/core/src/Result.h index 49485de189..befd92e3b8 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -30,7 +30,7 @@ using Position = QuadrilateralI; class Result { public: - Result() noexcept = default; + Result() = default; explicit Result(DecodeStatus status); // 1D convenience constructor From 94a4c443999f0bc705061fc5cb41a75bc396d821 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 1 Jul 2022 09:39:11 +0200 Subject: [PATCH 136/180] Error: switch PDFDecodedBitStreamParser to exception based error handling Format errors in ReedSolomon corrected bits are very exceptional. See also other matrix decoders. --- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 154 +++++++----------- test/unit/pdf417/PDF417DecoderTest.cpp | 40 ++--- 2 files changed, 74 insertions(+), 120 deletions(-) diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index b481549f29..f7f493d300 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -256,7 +256,7 @@ static int ProcessTextECI(std::vector& textCompactionData, int& index, cons * @param result The data in the character set encoding. * @return The next index into the codeword array. */ -static int TextCompaction(DecodeStatus& status, const std::vector& codewords, int codeIndex, Content& result) +static int TextCompaction(const std::vector& codewords, int codeIndex, Content& result) { // 2 characters per codeword std::vector textCompactionData((codewords[0] - codeIndex) * 2, 0); @@ -293,10 +293,9 @@ static int TextCompaction(DecodeStatus& status, const std::vector& codeword codeIndex = ProcessTextECI(textCompactionData, index, codewords, codeIndex, code); break; default: - if (!TerminatesCompaction(code)) { - status = DecodeStatus::FormatError; - return codeIndex; - } + if (!TerminatesCompaction(code)) + throw FormatError(); + codeIndex--; end = true; break; @@ -311,7 +310,7 @@ static int TextCompaction(DecodeStatus& status, const std::vector& codeword * Helper for Byte Compaction to look ahead and count 5-codeword batches and trailing bytes, with some checking of * format errors. */ -static int CountByteBatches(DecodeStatus& status, int mode, const std::vector& codewords, int codeIndex, int& trailingCount) +static int CountByteBatches(int mode, const std::vector& codewords, int codeIndex, int& trailingCount) { int count = 0; trailingCount = 0; @@ -319,26 +318,22 @@ static int CountByteBatches(DecodeStatus& status, int mode, const std::vector= TEXT_COMPACTION_MODE_LATCH) { - if (mode == BYTE_COMPACTION_MODE_LATCH_6 && count && count % 5) { - status = DecodeStatus::FormatError; - return 0; - } + if (mode == BYTE_COMPACTION_MODE_LATCH_6 && count && count % 5) + throw FormatError(); + if (IsECI(code)) { codeIndex += code == ECI_GENERAL_PURPOSE ? 2 : 1; continue; } - if (!TerminatesCompaction(code)) { - status = DecodeStatus::FormatError; - return 0; - } + if (!TerminatesCompaction(code)) + throw FormatError(); break; } count++; } - if (codeIndex > codewords[0]) { - status = DecodeStatus::FormatError; - return 0; - } + if (codeIndex > codewords[0]) + throw FormatError(); + if (count == 0) return 0; @@ -349,10 +344,8 @@ static int CountByteBatches(DecodeStatus& status, int mode, const std::vector& codewords, int codeIndex, Con * @param result The data in the character set encoding. * @return The next index into the codeword array. */ -static int ByteCompaction(DecodeStatus& status, int mode, const std::vector& codewords, int codeIndex, - Content& result) +static int ByteCompaction(int mode, const std::vector& codewords, int codeIndex, Content& result) { // Count number of 5-codeword batches and trailing bytes int trailingCount; - int batches = CountByteBatches(status, mode, codewords, codeIndex, trailingCount); - - if (StatusIsError(status)) - return codeIndex; + int batches = CountByteBatches(mode, codewords, codeIndex, trailingCount); // Deal with initial ECIs codeIndex = ProcessByteECIs(codewords, codeIndex, result); @@ -463,7 +452,7 @@ Decode the above codewords involves Remove leading 1 => Result is 000213298174000 */ -static DecodeStatus DecodeBase900toBase10(const std::vector& codewords, int count, std::string& resultString) +static std::string DecodeBase900toBase10(const std::vector& codewords, int count) { // Table containing values for the exponent of 900. static const auto EXP900 = []() { @@ -479,12 +468,11 @@ static DecodeStatus DecodeBase900toBase10(const std::vector& codewords, int for (int i = 0; i < count; i++) result += EXP900[count - i - 1] * codewords[i]; - resultString = result.toString(); - if (!resultString.empty() && resultString.front() == '1') { - resultString = resultString.substr(1); - return DecodeStatus::NoError; - } - return DecodeStatus::FormatError; + std::string resultString = result.toString(); + if (!resultString.empty() && resultString.front() == '1') + return resultString.substr(1); + + throw FormatError(); } @@ -498,7 +486,7 @@ static DecodeStatus DecodeBase900toBase10(const std::vector& codewords, int * @param encoding Currently active character encoding. * @return The next index into the codeword array. */ -static int NumericCompaction(DecodeStatus& status, const std::vector& codewords, int codeIndex, Content& result) +static int NumericCompaction(const std::vector& codewords, int codeIndex, Content& result) { int count = 0; bool end = false; @@ -512,21 +500,15 @@ static int NumericCompaction(DecodeStatus& status, const std::vector& codew // As operating in Basic Channel Mode (i.e. not embedding backslashed ECIs and doubling backslashes) // allow ECIs anywhere in Numeric Compaction (i.e. ISO/IEC 15438:2015 5.5.3.4 doesn't apply). if (count > 0) { - std::string tmp; - status = DecodeBase900toBase10(numericCodewords, count, tmp); - if (StatusIsError(status)) - return codeIndex; - - result += tmp; + result += DecodeBase900toBase10(numericCodewords, count); count = 0; } codeIndex = ProcessECI(codewords, codeIndex, codewords[0], code, result); continue; } - if (!TerminatesCompaction(code)) { - status = DecodeStatus::FormatError; - return codeIndex; - } + if (!TerminatesCompaction(code)) + throw FormatError(); + codeIndex--; end = true; } @@ -543,12 +525,7 @@ static int NumericCompaction(DecodeStatus& status, const std::vector& codew // current Numeric Compaction mode grouping as described in 5.4.4.2, // and then to start a new one grouping. if (count > 0) { - std::string tmp; - status = DecodeBase900toBase10(numericCodewords, count, tmp); - if (StatusIsError(status)) - return codeIndex; - - result += tmp; + result += DecodeBase900toBase10(numericCodewords, count); count = 0; } } @@ -559,14 +536,14 @@ static int NumericCompaction(DecodeStatus& status, const std::vector& codew /* * Helper to deal with optional text fields in Macros. */ -static int DecodeMacroOptionalTextField(DecodeStatus& status, const std::vector& codewords, int codeIndex, std::string& field) +static int DecodeMacroOptionalTextField(const std::vector& codewords, int codeIndex, std::string& field) { Content result; // Each optional field begins with an implied reset to ECI 2 (Annex H.2.3). ECI 2 is ASCII for 0-127, and Cp437 // for non-ASCII (128-255). Text optional fields can contain ECIs. result.defaultCharset = "Cp437"; - codeIndex = TextCompaction(status, codewords, codeIndex, result); + codeIndex = TextCompaction(codewords, codeIndex, result); // Converting to UTF-8 (backward-incompatible change for non-ASCII chars) field = result.utf8(); @@ -577,15 +554,14 @@ static int DecodeMacroOptionalTextField(DecodeStatus& status, const std::vector< /* * Helper to deal with optional numeric fields in Macros. */ -static int DecodeMacroOptionalNumericField(DecodeStatus& status, const std::vector& codewords, int codeIndex, - uint64_t& field) +static int DecodeMacroOptionalNumericField(const std::vector& codewords, int codeIndex, uint64_t& field) { Content result; // Each optional field begins with an implied reset to ECI 2 (Annex H.2.3). ECI 2 is ASCII for 0-127, and Cp437 // for non-ASCII (128-255). Text optional fields can contain ECIs. result.defaultCharset = "Cp437"; - codeIndex = NumericCompaction(status, codewords, codeIndex, result); + codeIndex = NumericCompaction(codewords, codeIndex, result); field = std::stoll(result.utf8()); @@ -593,21 +569,17 @@ static int DecodeMacroOptionalNumericField(DecodeStatus& status, const std::vect } ZXING_EXPORT_TEST_ONLY -DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, DecoderResultExtra& resultMetadata, - int& next) +int DecodeMacroBlock(const std::vector& codewords, int codeIndex, DecoderResultExtra& resultMetadata) { // we must have at least two bytes left for the segment index if (codeIndex + NUMBER_OF_SEQUENCE_CODEWORDS > codewords[0]) - return DecodeStatus::FormatError; + throw FormatError(); std::vector segmentIndexArray(NUMBER_OF_SEQUENCE_CODEWORDS); for (int i = 0; i < NUMBER_OF_SEQUENCE_CODEWORDS; i++, codeIndex++) segmentIndexArray[i] = codewords[codeIndex]; - std::string strBuf; - DecodeStatus status = DecodeBase900toBase10(segmentIndexArray, NUMBER_OF_SEQUENCE_CODEWORDS, strBuf); - if (StatusIsError(status)) - return status; + std::string strBuf = DecodeBase900toBase10(segmentIndexArray, NUMBER_OF_SEQUENCE_CODEWORDS); resultMetadata.setSegmentIndex(std::stoi(strBuf)); @@ -636,47 +608,47 @@ DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, switch (codewords[codeIndex]) { case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME: { std::string fileName; - codeIndex = DecodeMacroOptionalTextField(status, codewords, codeIndex + 1, fileName); + codeIndex = DecodeMacroOptionalTextField(codewords, codeIndex + 1, fileName); resultMetadata.setFileName(fileName); break; } case MACRO_PDF417_OPTIONAL_FIELD_SENDER: { std::string sender; - codeIndex = DecodeMacroOptionalTextField(status, codewords, codeIndex + 1, sender); + codeIndex = DecodeMacroOptionalTextField(codewords, codeIndex + 1, sender); resultMetadata.setSender(sender); break; } case MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE: { std::string addressee; - codeIndex = DecodeMacroOptionalTextField(status, codewords, codeIndex + 1, addressee); + codeIndex = DecodeMacroOptionalTextField(codewords, codeIndex + 1, addressee); resultMetadata.setAddressee(addressee); break; } case MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT: { uint64_t segmentCount; - codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, segmentCount); + codeIndex = DecodeMacroOptionalNumericField(codewords, codeIndex + 1, segmentCount); resultMetadata.setSegmentCount(static_cast(segmentCount)); break; } case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP: { uint64_t timestamp; - codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, timestamp); + codeIndex = DecodeMacroOptionalNumericField(codewords, codeIndex + 1, timestamp); resultMetadata.setTimestamp(timestamp); break; } case MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM: { uint64_t checksum; - codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, checksum); + codeIndex = DecodeMacroOptionalNumericField(codewords, codeIndex + 1, checksum); resultMetadata.setChecksum(static_cast(checksum)); break; } case MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE: { uint64_t fileSize; - codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, fileSize); + codeIndex = DecodeMacroOptionalNumericField(codewords, codeIndex + 1, fileSize); resultMetadata.setFileSize(fileSize); break; } - default: status = DecodeStatus::FormatError; break; + default: throw FormatError(); } break; } @@ -685,10 +657,7 @@ DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, resultMetadata.setLastSegment(true); break; } - default: status = DecodeStatus::FormatError; break; - } - if (StatusIsError(status)) { - return status; + default: throw FormatError(); } } @@ -702,8 +671,7 @@ DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, codewords.begin() + optionalFieldsStart + optionalFieldsLength)); } - next = codeIndex; - return DecodeStatus::NoError; + return codeIndex; } DecoderResult @@ -715,25 +683,24 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel) bool readerInit = false; auto resultMetadata = std::make_shared(); int codeIndex = 1; - DecodeStatus status = DecodeStatus::NoError; - while (codeIndex < codewords[0] && status == DecodeStatus::NoError) { + while (codeIndex < codewords[0]) { int code = codewords[codeIndex++]; switch (code) { case TEXT_COMPACTION_MODE_LATCH: - codeIndex = TextCompaction(status, codewords, codeIndex, result); + codeIndex = TextCompaction(codewords, codeIndex, result); break; case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: // This should only be encountered once in this loop, when default Text Compaction mode applies // (see default case below) - codeIndex = TextCompaction(status, codewords, codeIndex - 1, result); + codeIndex = TextCompaction(codewords, codeIndex - 1, result); break; case BYTE_COMPACTION_MODE_LATCH: case BYTE_COMPACTION_MODE_LATCH_6: - codeIndex = ByteCompaction(status, code, codewords, codeIndex, result); + codeIndex = ByteCompaction(code, codewords, codeIndex, result); break; case NUMERIC_COMPACTION_MODE_LATCH: - codeIndex = NumericCompaction(status, codewords, codeIndex, result); + codeIndex = NumericCompaction(codewords, codeIndex, result); break; case ECI_CHARSET: case ECI_GENERAL_PURPOSE: @@ -741,45 +708,42 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel) codeIndex = ProcessECI(codewords, codeIndex, codewords[0], code, result); break; case BEGIN_MACRO_PDF417_CONTROL_BLOCK: - status = DecodeMacroBlock(codewords, codeIndex, *resultMetadata, codeIndex); + codeIndex = DecodeMacroBlock(codewords, codeIndex, *resultMetadata); break; case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: case MACRO_PDF417_TERMINATOR: // Should not see these outside a macro block - status = DecodeStatus::FormatError; + throw FormatError(); break; case READER_INIT: if (codeIndex != 2) // Must be first codeword after symbol length (ISO/IEC 15438:2015 5.4.1.4) - status = DecodeStatus::FormatError; + throw FormatError(); else readerInit = true; break; case LINKAGE_EANUCC: if (codeIndex != 2) // Must be first codeword after symbol length (GS1 Composite ISO/IEC 24723:2010 4.3) - status = DecodeStatus::FormatError; + throw FormatError(); // TODO: handle else case break; case LINKAGE_OTHER: // Allowed to treat as invalid by ISO/IEC 24723:2010 5.4.1.5 and 5.4.6.1 when in Basic Channel Mode - status = DecodeStatus::FormatError; // TODO: add NotSupported error + throw FormatError(); // TODO: add NotSupported error break; default: if (code >= TEXT_COMPACTION_MODE_LATCH) { // Reserved codewords (all others in switch) // Allowed to treat as invalid by ISO/IEC 24723:2010 5.4.6.1 when in Basic Channel Mode - status = DecodeStatus::FormatError; // TODO: add NotSupported error + throw FormatError(); // TODO: add NotSupported error } else { // Default mode is Text Compaction mode Alpha sub-mode (ISO/IEC 15438:2015 5.4.2.1) - codeIndex = TextCompaction(status, codewords, codeIndex - 1, result); + codeIndex = TextCompaction(codewords, codeIndex - 1, result); } break; } } - if (StatusIsError(status)) - return status; - if (result.empty() && resultMetadata->segmentIndex() == -1) - return DecodeStatus::FormatError; + return FormatError(); StructuredAppendInfo sai; if (resultMetadata->segmentIndex() > -1) { diff --git a/test/unit/pdf417/PDF417DecoderTest.cpp b/test/unit/pdf417/PDF417DecoderTest.cpp index 55b902cd3f..c08f83ab0a 100644 --- a/test/unit/pdf417/PDF417DecoderTest.cpp +++ b/test/unit/pdf417/PDF417DecoderTest.cpp @@ -11,9 +11,9 @@ #include "gtest/gtest.h" -namespace ZXing { namespace Pdf417 { - DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, DecoderResultExtra& resultMetadata, int& next); -}} +namespace ZXing::Pdf417 { +int DecodeMacroBlock(const std::vector& codewords, int codeIndex, DecoderResultExtra& resultMetadata); +} using namespace ZXing; using namespace ZXing::Pdf417; @@ -21,7 +21,11 @@ using namespace ZXing::Pdf417; // Shorthand for Decode() static DecoderResult parse(const std::vector& codewords, int ecLevel = 0) { - return DecodedBitStreamParser::Decode(codewords, ecLevel); + try { + return DecodedBitStreamParser::Decode(codewords, ecLevel); + } catch (Error e) { + return e; + } } /** @@ -33,10 +37,8 @@ TEST(PDF417DecoderTest, StandardSample1) // we should never reach these 1000, 1000, 1000 }; - int next = 0; DecoderResultExtra resultMetadata; - auto status = DecodeMacroBlock(sampleCodes, 2, resultMetadata, next); - EXPECT_EQ(status, DecodeStatus::NoError); + DecodeMacroBlock(sampleCodes, 2, resultMetadata); EXPECT_EQ(0, resultMetadata.segmentIndex()); EXPECT_EQ("017053", resultMetadata.fileId()); @@ -65,10 +67,8 @@ TEST(PDF417DecoderTest, StandardSample2) // we should never reach these 1000, 1000, 1000 }; - int next = 0; DecoderResultExtra resultMetadata; - auto status = DecodeMacroBlock(sampleCodes, 2, resultMetadata, next); - EXPECT_EQ(status, DecodeStatus::NoError); + DecodeMacroBlock(sampleCodes, 2, resultMetadata); EXPECT_EQ(3, resultMetadata.segmentIndex()); EXPECT_EQ("017053", resultMetadata.fileId()); @@ -95,10 +95,8 @@ TEST(PDF417DecoderTest, StandardSample3) { std::vector sampleCodes = { 7, 928, 111, 100, 100, 200, 300 }; - int next = 0; DecoderResultExtra resultMetadata; - auto status = DecodeMacroBlock(sampleCodes, 2, resultMetadata, next); - EXPECT_EQ(status, DecodeStatus::NoError); + DecodeMacroBlock(sampleCodes, 2, resultMetadata); EXPECT_EQ(0, resultMetadata.segmentIndex()); EXPECT_EQ("100200300", resultMetadata.fileId()); @@ -117,10 +115,8 @@ TEST(PDF417DecoderTest, SampleWithFilename) 599, 923, 1, 111, 102, 98, 311, 355, 522, 920, 779, 40, 628, 33, 749, 267, 506, 213, 928, 465, 248, 493, 72, 780, 699, 780, 493, 755, 84, 198, 628, 368, 156, 198, 809, 19, 113 }; - int next = 0; DecoderResultExtra resultMetadata; - auto status = DecodeMacroBlock(sampleCodes, 3, resultMetadata, next); - EXPECT_EQ(status, DecodeStatus::NoError); + DecodeMacroBlock(sampleCodes, 3, resultMetadata); EXPECT_EQ(0, resultMetadata.segmentIndex()); EXPECT_EQ("000252021086", resultMetadata.fileId()); @@ -142,10 +138,8 @@ TEST(PDF417DecoderTest, SampleWithNumericValues) std::vector sampleCodes = { 25, 477, 928, 111, 100, 0, 252, 21, 86, 923, 2, 2, 0, 1, 0, 0, 0, 923, 5, 130, 923, 6, 1, 500, 13 }; - int next = 0; DecoderResultExtra resultMetadata; - auto status = DecodeMacroBlock(sampleCodes, 3, resultMetadata, next); - EXPECT_EQ(status, DecodeStatus::NoError); + DecodeMacroBlock(sampleCodes, 3, resultMetadata); EXPECT_EQ(0, resultMetadata.segmentIndex()); EXPECT_EQ("000252021086", resultMetadata.fileId()); @@ -167,10 +161,8 @@ TEST(PDF417DecoderTest, SampleWithMacroTerminatorOnly) { std::vector sampleCodes = { 7, 477, 928, 222, 198, 0, 922 }; - int next = 0; DecoderResultExtra resultMetadata; - auto status = DecodeMacroBlock(sampleCodes, 3, resultMetadata, next); - EXPECT_EQ(status, DecodeStatus::NoError); + DecodeMacroBlock(sampleCodes, 3, resultMetadata); EXPECT_EQ(99998, resultMetadata.segmentIndex()); EXPECT_EQ("000", resultMetadata.fileId()); @@ -522,10 +514,8 @@ TEST(PDF417DecoderTest, ECIMacroOptionalNumeric) std::vector sampleCodes = { 19, 477, 928, 111, 100, 0, 252, 21, 86, 923, 5, 15, 369, 753, 190, 927, 25, 124, 745 }; - int next = 0; DecoderResultExtra resultMetadata; - auto status = DecodeMacroBlock(sampleCodes, 3, resultMetadata, next); - EXPECT_EQ(status, DecodeStatus::NoError); + DecodeMacroBlock(sampleCodes, 3, resultMetadata); EXPECT_EQ(0, resultMetadata.segmentIndex()); EXPECT_EQ("000252021086", resultMetadata.fileId()); From 4186c76257e595748ec2bd11f70d06760265a416 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 1 Jul 2022 09:43:29 +0200 Subject: [PATCH 137/180] Error: switch to `DecoderResult(Error)` constructor from `DecodeStatus` --- core/src/DecoderResult.h | 4 +--- core/src/aztec/AZDecoder.cpp | 6 +++--- core/src/aztec/AZReader.cpp | 2 +- core/src/datamatrix/DMDecoder.cpp | 10 +++++----- core/src/maxicode/MCDecoder.cpp | 8 ++++---- core/src/pdf417/PDFScanningDecoder.cpp | 27 ++++++++++++++------------ core/src/qrcode/QRDecoder.cpp | 12 ++++++------ 7 files changed, 35 insertions(+), 34 deletions(-) diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index e6682df153..7c54337f55 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -8,7 +8,6 @@ #include "ByteArray.h" #include "Content.h" -#include "DecodeStatus.h" #include "Error.h" #include "StructuredAppend.h" #include "ZXContainerAlgorithms.h" @@ -38,14 +37,13 @@ class DecoderResult DecoderResult& operator=(const DecoderResult &) = delete; public: - DecoderResult(DecodeStatus status) : _error(Status2Error(status)) {} + DecoderResult() = default; DecoderResult(Error error) : _error(error) {} DecoderResult(ByteArray&& rawBytes, Content&& bytes = {}) : _rawBytes(std::move(rawBytes)), _content(std::move(bytes)) { _numBits = 8 * Size(_rawBytes); } - DecoderResult() = default; DecoderResult(DecoderResult&&) noexcept = default; DecoderResult& operator=(DecoderResult&&) = default; diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index ebb7c6c083..1fb152515f 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -307,11 +307,11 @@ DecoderResult Decode(const BitArray& bits) try { DecodeContent(bits, res); } catch (const std::out_of_range&) { // see ReadBits() - return DecodeStatus::FormatError; + return FormatError(); } if (res.bytes.empty()) - return DecodeStatus::FormatError; + return FormatError(); // Check for Structured Append - need 4 5-bit words, beginning with ML UL, ending with index and count bool haveStructuredAppend = Size(bits) > 20 && ToInt(bits, 0, 5) == 29 // latch to MIXED (from UPPER) @@ -351,7 +351,7 @@ DecoderResult Decode(const DetectorResult& detectorResult) BitArray bits = CorrectBits(detectorResult, ExtractBits(detectorResult)); if (!bits.size()) - return DecodeStatus::FormatError; + return FormatError(); return Decode(bits).setReaderInit(detectorResult.readerInit()); } diff --git a/core/src/aztec/AZReader.cpp b/core/src/aztec/AZReader.cpp index 95e43ad8d8..56f45040e7 100644 --- a/core/src/aztec/AZReader.cpp +++ b/core/src/aztec/AZReader.cpp @@ -27,7 +27,7 @@ Reader::decode(const BinaryBitmap& image) const return {}; DetectorResult detectResult = Detect(*binImg, false, _hints.isPure()); - DecoderResult decodeResult = DecodeStatus::NotFound; + DecoderResult decodeResult; if (detectResult.isValid()) decodeResult = Decode(detectResult); diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 8284833b17..186d291ca8 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -352,7 +352,7 @@ DecoderResult Decode(ByteArray&& bytes, const bool isDMRE) #ifndef NDEBUG printf("DMDecoder error: %s\n", e.what()); #endif - return DecodeStatus::FormatError; + return FormatError(); } result.append(resultTrailer); @@ -396,17 +396,17 @@ static DecoderResult DoDecode(const BitMatrix& bits) // Construct a parser and read version, error-correction level const Version* version = VersionForDimensionsOf(bits); if (version == nullptr) - return DecodeStatus::FormatError; + return FormatError(); // Read codewords ByteArray codewords = CodewordsFromBitMatrix(bits); if (codewords.empty()) - return DecodeStatus::FormatError; + return FormatError(); // Separate into data blocks std::vector dataBlocks = GetDataBlocks(codewords, *version); if (dataBlocks.empty()) - return DecodeStatus::FormatError; + return FormatError(); // Count total number of data bytes ByteArray resultBytes(TransformReduce(dataBlocks, 0, [](const auto& db) { return db.numDataCodewords; })); @@ -418,7 +418,7 @@ static DecoderResult DoDecode(const BitMatrix& bits) ByteArray& codewordBytes = dataBlock.codewords; int numDataCodewords = dataBlock.numDataCodewords; if (!CorrectErrors(codewordBytes, numDataCodewords)) - return DecodeStatus::ChecksumError; + return ChecksumError(); for (int i = 0; i < numDataCodewords; i++) { // De-interlace data blocks. diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 30a6b87913..cbf875ac71 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -305,7 +305,7 @@ DecoderResult Decode(const BitMatrix& bits) ByteArray codewords = BitMatrixParser::ReadCodewords(bits); if (!CorrectErrors(codewords, 0, 10, 10, ALL)) - return DecodeStatus::ChecksumError; + return ChecksumError(); int mode = codewords[0] & 0x0F; ByteArray datawords; @@ -317,15 +317,15 @@ DecoderResult Decode(const BitMatrix& bits) if (CorrectErrors(codewords, 20, 84, 40, EVEN) && CorrectErrors(codewords, 20, 84, 40, ODD)) datawords.resize(94, 0); else - return DecodeStatus::ChecksumError; + return ChecksumError(); break; case 5: // Full ECC if (CorrectErrors(codewords, 20, 68, 56, EVEN) && CorrectErrors(codewords, 20, 68, 56, ODD)) datawords.resize(78, 0); else - return DecodeStatus::ChecksumError; + return ChecksumError(); break; - default: return DecodeStatus::FormatError; + default: return FormatError(); } std::copy_n(codewords.begin(), 10, datawords.begin()); diff --git a/core/src/pdf417/PDFScanningDecoder.cpp b/core/src/pdf417/PDFScanningDecoder.cpp index c73ab8e3fa..895404ca1b 100644 --- a/core/src/pdf417/PDFScanningDecoder.cpp +++ b/core/src/pdf417/PDFScanningDecoder.cpp @@ -570,20 +570,23 @@ static bool VerifyCodewordCount(std::vector& codewords, int numECCodewords) DecoderResult DecodeCodewords(std::vector& codewords, int ecLevel, const std::vector& erasures) { - if (codewords.empty()) { - return DecodeStatus::FormatError; - } + if (codewords.empty()) + return FormatError(); int numECCodewords = 1 << (ecLevel + 1); int correctedErrorsCount = 0; if (!CorrectErrors(codewords, erasures, numECCodewords, correctedErrorsCount)) - return DecodeStatus::ChecksumError; + return ChecksumError(); if (!VerifyCodewordCount(codewords, numECCodewords)) - return DecodeStatus::FormatError; + return FormatError(); // Decode the codewords - return DecodedBitStreamParser::Decode(codewords, ecLevel); + try { + return DecodedBitStreamParser::Decode(codewords, ecLevel); + } catch (Error e) { + return e; + } } @@ -617,7 +620,7 @@ static DecoderResult CreateDecoderResultFromAmbiguousValues(int ecLevel, std::ve } if (ambiguousIndexCount.empty()) { - return DecodeStatus::ChecksumError; + return ChecksumError(); } for (size_t i = 0; i < ambiguousIndexCount.size(); i++) { if (ambiguousIndexCount[i] < Size(ambiguousIndexValues[i]) - 1) { @@ -627,12 +630,12 @@ static DecoderResult CreateDecoderResultFromAmbiguousValues(int ecLevel, std::ve else { ambiguousIndexCount[i] = 0; if (i == ambiguousIndexCount.size() - 1) { - return DecodeStatus::ChecksumError; + return ChecksumError(); } } } } - return DecodeStatus::ChecksumError; + return ChecksumError(); } @@ -640,7 +643,7 @@ static DecoderResult CreateDecoderResult(DetectionResult& detectionResult) { auto barcodeMatrix = CreateBarcodeMatrix(detectionResult); if (!AdjustCodewordCount(detectionResult, barcodeMatrix)) { - return DecodeStatus::NotFound; + return {}; } std::vector erasures; std::vector codewords(detectionResult.barcodeRowCount() * detectionResult.barcodeColumnCount(), 0); @@ -678,7 +681,7 @@ ScanningDecoder::Decode(const BitMatrix& image, const Nullable& ima { BoundingBox boundingBox; if (!BoundingBox::Create(image.width(), image.height(), imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight, boundingBox)) { - return DecodeStatus::NotFound; + return {}; } Nullable leftRowIndicatorColumn, rightRowIndicatorColumn; @@ -691,7 +694,7 @@ ScanningDecoder::Decode(const BitMatrix& image, const Nullable& ima rightRowIndicatorColumn = GetRowIndicatorColumn(image, boundingBox, imageTopRight, false, minCodewordWidth, maxCodewordWidth); } if (!Merge(leftRowIndicatorColumn, rightRowIndicatorColumn, detectionResult)) { - return DecodeStatus::NotFound; + return {}; } if (i == 0 && detectionResult.getBoundingBox() != nullptr && (detectionResult.getBoundingBox().value().minY() < boundingBox.minY() || detectionResult.getBoundingBox().value().maxY() > boundingBox.maxY())) { boundingBox = detectionResult.getBoundingBox(); diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 90d80b2723..1af47dd5dc 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -333,7 +333,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo #ifndef NDEBUG printf("QRDecoder error: %s\n", e.what()); #endif - return DecodeStatus::FormatError; + return FormatError(); } return DecoderResult(std::move(bytes), std::move(result)) @@ -345,22 +345,22 @@ DecoderResult Decode(const BitMatrix& bits) { const Version* pversion = ReadVersion(bits); if (!pversion) - return DecodeStatus::FormatError; + return FormatError(); const Version& version = *pversion; auto formatInfo = ReadFormatInformation(bits, version.isMicroQRCode()); if (!formatInfo.isValid()) - return DecodeStatus::FormatError; + return FormatError(); // Read codewords ByteArray codewords = ReadCodewords(bits, version, formatInfo); if (codewords.empty()) - return DecodeStatus::FormatError; + return FormatError(); // Separate into data blocks std::vector dataBlocks = DataBlock::GetDataBlocks(codewords, version, formatInfo.ecLevel); if (dataBlocks.empty()) - return DecodeStatus::FormatError; + return FormatError(); // Count total number of data bytes const auto op = [](auto totalBytes, const auto& dataBlock){ return totalBytes + dataBlock.numDataCodewords();}; @@ -375,7 +375,7 @@ DecoderResult Decode(const BitMatrix& bits) int numDataCodewords = dataBlock.numDataCodewords(); if (!CorrectErrors(codewordBytes, numDataCodewords)) - return DecodeStatus::ChecksumError; + return ChecksumError(); resultIterator = std::copy_n(codewordBytes.begin(), numDataCodewords, resultIterator); } From 24b737365358bdb0e404e65cb70d0071362159f7 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 1 Jul 2022 16:17:16 +0200 Subject: [PATCH 138/180] GS1: introduce general purpose `HRIFromGS1` function This a cleaned up and generalized (handle `` delimiters) version of what was formerly implemented as part of the DataBar decoder. --- core/CMakeLists.txt | 4 +- .../rss/ODRSSFieldParser.cpp => GS1.cpp} | 96 ++-- core/src/GS1.h | 14 + core/src/oned/rss/ODRSSFieldParser.h | 20 - .../src/oned/rss/ODRSSGenericAppIdDecoder.cpp | 18 +- test/unit/CMakeLists.txt | 2 +- test/unit/GS1Test.cpp | 346 +++++++++++++++ test/unit/oned/rss/ODRSSFieldParserTest.cpp | 420 ------------------ 8 files changed, 421 insertions(+), 499 deletions(-) rename core/src/{oned/rss/ODRSSFieldParser.cpp => GS1.cpp} (65%) create mode 100644 core/src/GS1.h delete mode 100644 core/src/oned/rss/ODRSSFieldParser.h create mode 100644 test/unit/GS1Test.cpp delete mode 100644 test/unit/oned/rss/ODRSSFieldParserTest.cpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index d59ed2d818..fa5df9058d 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -101,6 +101,8 @@ if (BUILD_READERS) src/GlobalHistogramBinarizer.cpp src/GridSampler.h src/GridSampler.cpp + src/GS1.h + src/GS1.cpp src/HybridBinarizer.h src/HybridBinarizer.cpp src/ImageView.h @@ -313,8 +315,6 @@ if (BUILD_READERS) set (ONED_RSS_FILES ${ONED_RSS_FILES} src/oned/rss/ODRSSExpandedBinaryDecoder.h src/oned/rss/ODRSSExpandedBinaryDecoder.cpp - src/oned/rss/ODRSSFieldParser.h - src/oned/rss/ODRSSFieldParser.cpp src/oned/rss/ODRSSGenericAppIdDecoder.h src/oned/rss/ODRSSGenericAppIdDecoder.cpp ) diff --git a/core/src/oned/rss/ODRSSFieldParser.cpp b/core/src/GS1.cpp similarity index 65% rename from core/src/oned/rss/ODRSSFieldParser.cpp rename to core/src/GS1.cpp index 6551cf1acf..ab0ab598bc 100644 --- a/core/src/oned/rss/ODRSSFieldParser.cpp +++ b/core/src/GS1.cpp @@ -1,27 +1,30 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors +* Copyright 2022 Axel Waggershauser */ // SPDX-License-Identifier: Apache-2.0 -#include "ODRSSFieldParser.h" +#include "GS1.h" -#include "DecodeStatus.h" #include "ZXContainerAlgorithms.h" -#include -#include -#include -#include - -namespace ZXing::OneD::DataBar { - - //private static final Object VARIABLE_LENGTH = new Object(); +namespace ZXing { struct AiInfo { - const char* aiPrefix; - int fieldSize; // if negative, the length is variable and abs(length) give the max size + std::string_view aiPrefix; + int _fieldSize; // if negative, the length is variable and abs(length) give the max size + + bool isVariableLength() const noexcept { return _fieldSize < 0; } + int fieldSize() const noexcept { return std::abs(_fieldSize); } + int aiSize() const + { + if ((aiPrefix[0] == '3' && Contains("1234569", aiPrefix[1])) || aiPrefix == "703" || aiPrefix == "723") + return 4; + else + return Size(aiPrefix); + } }; // GS1 General Specifications Release 22.0 (Jan 22, 2022) @@ -230,48 +233,49 @@ static const AiInfo aiInfos[] = { { "8200", -70 }, }; -static size_t -AiSize(const char* aiPrefix) +std::string HRIFromGS1(const std::string& gs1) { - if ((aiPrefix[0] == '3' && Contains("1234569", aiPrefix[1])) || std::string(aiPrefix) == "703" - || std::string(aiPrefix) == "723") - return 4; - else - return strlen(aiPrefix); -} + //TODO: c++20 + auto starts_with = [](std::string_view str, std::string_view pre) { return str.substr(0, pre.size()) == pre; }; + constexpr char GS = 29; // GS character (29 / 0x1D) + std::string_view rem = gs1; + std::string res; -DecodeStatus -ParseFieldsInGeneralPurpose(const std::string &rawInfo, std::string& result) -{ - if (rawInfo.empty()) { - return DecodeStatus::NoError; - } + while (rem.size()) { + const AiInfo* i = FindIf(aiInfos, [&](const AiInfo& i) { return starts_with(rem, i.aiPrefix); }); + if (i == std::end(aiInfos)) + return {}; - auto starts_with = - [](const std::string& str, const char* pre) { return strncmp(pre, str.data(), strlen(pre)) == 0; }; + int aiSize = i->aiSize(); + if (Size(rem) < aiSize) + return {}; - const AiInfo* aiInfo = FindIf(aiInfos, [&](const AiInfo& i) { return starts_with(rawInfo, i.aiPrefix); }); - if (aiInfo == std::end(aiInfos)) - return DecodeStatus::NotFound; + res += '('; + res += rem.substr(0, aiSize); + res += ')'; + rem.remove_prefix(aiSize); - size_t aiSize = AiSize(aiInfo->aiPrefix); + int fieldSize = i->fieldSize(); + if (i->isVariableLength()) { + auto gsPos = rem.find(GS); + fieldSize = std::min(gsPos == std::string_view::npos ? Size(rem) : static_cast(gsPos), fieldSize); + } + if (fieldSize == 0 || Size(rem) < fieldSize) + return {}; - // require at least one character in the variable field size case - if (rawInfo.length() < aiSize + std::max(1, aiInfo->fieldSize)) - return DecodeStatus::NotFound; + res += rem.substr(0, fieldSize); + rem.remove_prefix(fieldSize); - size_t fieldSize = aiInfo->fieldSize >= 0 - ? size_t(aiInfo->fieldSize) // fixed - : std::min(rawInfo.length() - aiSize, size_t(-aiInfo->fieldSize)); // variable + if (Size(rem) && rem.front() == GS) { + if (i->isVariableLength()) + rem.remove_prefix(1); + else + return {}; + } + } - auto ai = rawInfo.substr(0, aiSize); - auto field = rawInfo.substr(aiSize, fieldSize); - auto remaining = rawInfo.substr(aiSize + fieldSize); - std::string parsedRemaining; - auto status = ParseFieldsInGeneralPurpose(remaining, parsedRemaining); - result = '(' + ai + ')' + field + parsedRemaining; - return status; + return res; } -} // namespace ZXing::OneD::DataBar +} // namespace ZXing diff --git a/core/src/GS1.h b/core/src/GS1.h new file mode 100644 index 0000000000..50b579a8a9 --- /dev/null +++ b/core/src/GS1.h @@ -0,0 +1,14 @@ +/* + * Copyright 2022 Axel Waggershauser + */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +namespace ZXing { + +std::string HRIFromGS1(const std::string& gs1); + +} // namespace ZXing diff --git a/core/src/oned/rss/ODRSSFieldParser.h b/core/src/oned/rss/ODRSSFieldParser.h deleted file mode 100644 index 0d24839429..0000000000 --- a/core/src/oned/rss/ODRSSFieldParser.h +++ /dev/null @@ -1,20 +0,0 @@ -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -*/ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace ZXing { - -enum class DecodeStatus; - -namespace OneD::DataBar { - -DecodeStatus ParseFieldsInGeneralPurpose(const std::string &rawInfo, std::string& result); - -} // namespace OneD::DataBar -} // namespace ZXing diff --git a/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp b/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp index 5064abfd71..eee7c9e4e7 100644 --- a/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp +++ b/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp @@ -8,7 +8,7 @@ #include "BitArray.h" #include "DecodeStatus.h" -#include "ODRSSFieldParser.h" +#include "GS1.h" #include #include @@ -409,7 +409,6 @@ ParseBlocks(const BitArray& bits, ParsingState& state, std::string& buffer) ParseAlphaBlock(bits, state, buffer) : (state.encoding == ParsingState::ISO_IEC_646 ? ParseIsoIec646Block(bits, state, buffer) : - // else ParseNumericBlock(bits, state, buffer)); if (result.isValid() || initialPosition == state.position) { @@ -460,14 +459,16 @@ DecodeAppIdAllCodes(const BitArray& bits, int pos, int remainingValue, std::stri while (true) { state.position = pos; DecodedInformation info = DoDecodeGeneralPurposeField(state, bits, remaining); - std::string parsedFields; - auto status = ParseFieldsInGeneralPurpose(info.newString, parsedFields); - if (StatusIsError(status)) { - if (result.empty() && remaining.empty()){ + if (pos == info.newPosition || info.newString.empty()) // No step forward! + break; + + std::string parsedFields = HRIFromGS1(info.newString); + if (parsedFields.empty()) { + if (result.empty() && remaining.empty()) { result = info.newString; return DecodeStatus::NoError; } else - return status; + return DecodeStatus::FormatError; } result += parsedFields; if (info.isRemaining()) { @@ -477,9 +478,6 @@ DecodeAppIdAllCodes(const BitArray& bits, int pos, int remainingValue, std::stri remaining.clear(); } - if (pos == info.newPosition) {// No step forward! - break; - } pos = info.newPosition; }; return DecodeStatus::NoError; diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 96ddb9b60e..45f609c1f5 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -15,6 +15,7 @@ add_executable (UnitTest CharacterSetECITest.cpp ContentTest.cpp GTINTest.cpp + GS1Test.cpp ReedSolomonTest.cpp TextDecoderTest.cpp TextUtfEncodingTest.cpp @@ -45,7 +46,6 @@ add_executable (UnitTest oned/ODUPCAWriterTest.cpp oned/ODUPCEWriterTest.cpp oned/rss/ODRSSExpandedBinaryDecoderTest.cpp - oned/rss/ODRSSFieldParserTest.cpp qrcode/MQRDecoderTest.cpp qrcode/QRBitMatrixParserTest.cpp qrcode/QRDataMaskTest.cpp diff --git a/test/unit/GS1Test.cpp b/test/unit/GS1Test.cpp new file mode 100644 index 0000000000..122e09228a --- /dev/null +++ b/test/unit/GS1Test.cpp @@ -0,0 +1,346 @@ +/* + * Copyright 2022 gitlost + */ +// SPDX-License-Identifier: Apache-2.0 + +#include "GS1.h" + +#include "gtest/gtest.h" + +using namespace ZXing; + +TEST(HRIFromGS1, Single) +{ + // 2-digit AIs + + // Fixed length + EXPECT_EQ(HRIFromGS1("00123456789012345678"), "(00)123456789012345678"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("0012345678901234567"), ""); + EXPECT_EQ(HRIFromGS1("001234567890123456789"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("16123456"), "(16)123456"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("1612345"), ""); + EXPECT_EQ(HRIFromGS1("161234567"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("2212345678901234567890"), "(22)12345678901234567890"); + EXPECT_EQ(HRIFromGS1("221234567890123456789"), "(22)1234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("22123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("91123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"), + "(91)123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("9112345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"), + "(91)12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("911234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("99123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"), + "(99)123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("9912345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"), + "(99)12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("991234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"), ""); + + // 3-digit AIs + + EXPECT_EQ(HRIFromGS1("310"), ""); // incomplete prefix + + // Max length + EXPECT_EQ(HRIFromGS1("2351234567890123456789012345678"), "(235)1234567890123456789012345678"); + EXPECT_EQ(HRIFromGS1("235123456789012345678901234567"), "(235)123456789012345678901234567"); + // Too long + EXPECT_EQ(HRIFromGS1("23512345678901234567890123456789"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("24312345678901234567890"), "(243)12345678901234567890"); + EXPECT_EQ(HRIFromGS1("2431234567890123456789"), "(243)1234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("243123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("253123456789012345678901234567890"), "(253)123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("25312345678901234567890123456789"), "(253)12345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("2531234567890123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("2551234567890123456789012345"), "(255)1234567890123456789012345"); + EXPECT_EQ(HRIFromGS1("255123456789012345678901234"), "(255)123456789012345678901234"); + // Too long + EXPECT_EQ(HRIFromGS1("25512345678901234567890123456"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("4151234567890123"), "(415)1234567890123"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("415123456789012"), ""); + EXPECT_EQ(HRIFromGS1("41512345678901234"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("4171234567890123"), "(417)1234567890123"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("417123456789012"), ""); + EXPECT_EQ(HRIFromGS1("41712345678901234"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("421123456789012"), "(421)123456789012"); + EXPECT_EQ(HRIFromGS1("42112345678901"), "(421)12345678901"); + // Too long + EXPECT_EQ(HRIFromGS1("4211234567890123"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("425123456789012345"), "(425)123456789012345"); + EXPECT_EQ(HRIFromGS1("42512345678901234"), "(425)12345678901234"); + // Too long + EXPECT_EQ(HRIFromGS1("4251234567890123456"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("427123"), "(427)123"); + EXPECT_EQ(HRIFromGS1("42712"), "(427)12"); + // Too long + EXPECT_EQ(HRIFromGS1("4271234"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("71012345678901234567890"), "(710)12345678901234567890"); + EXPECT_EQ(HRIFromGS1("7101234567890123456789"), "(710)1234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("710123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("71512345678901234567890"), "(715)12345678901234567890"); + EXPECT_EQ(HRIFromGS1("7151234567890123456789"), "(715)1234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("715123456789012345678901"), ""); + + // 4-digit variable 4th + + // Fixed length + EXPECT_EQ(HRIFromGS1("3370123456"), "(3370)123456"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("337012345"), ""); + EXPECT_EQ(HRIFromGS1("33701234567"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("3375123456"), "(3375)123456"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("33751234567"), ""); + EXPECT_EQ(HRIFromGS1("337512345"), ""); + + // EXPECT_EQ(ParseFieldsInGeneralPurpose("3376123456"), // Allow although > max 3375 + + // Fixed length + EXPECT_EQ(HRIFromGS1("39401234"), "(3940)1234"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("394012345"), ""); + EXPECT_EQ(HRIFromGS1("3940123"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("39431234"), "(3943)1234"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("394312345"), ""); + EXPECT_EQ(HRIFromGS1("3943123"), ""); + + // EXPECT_EQ(ParseFieldsInGeneralPurpose("39441234"), // Allow although > max 3943 + + // Fixed length + EXPECT_EQ(HRIFromGS1("3950123456"), "(3950)123456"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("39501234567"), ""); + EXPECT_EQ(HRIFromGS1("395012345"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("3955123456"), "(3955)123456"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("39551234567"), ""); + EXPECT_EQ(HRIFromGS1("395512345"), ""); + + // EXPECT_EQ(ParseFieldsInGeneralPurpose("3956123456"), // Allow although > max 3955 + + // Max length + EXPECT_EQ(HRIFromGS1("7230123456789012345678901234567890"), "(7230)123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("723012345678901234567890123456789"), "(7230)12345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("72301234567890123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("7239123456789012345678901234567890"), "(7239)123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("723912345678901234567890123456789"), "(7239)12345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("72391234567890123456789012345678901"), ""); + + // 4-digit AIs + + // Max length + EXPECT_EQ(HRIFromGS1("430012345678901234567890123456789012345"), "(4300)12345678901234567890123456789012345"); + EXPECT_EQ(HRIFromGS1("43001234567890123456789012345678901234"), "(4300)1234567890123456789012345678901234"); + // Too long + EXPECT_EQ(HRIFromGS1("4300123456789012345678901234567890123456"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("430712"), "(4307)12"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("4307123"), ""); + EXPECT_EQ(HRIFromGS1("43071"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("4308123456789012345678901234567890"), "(4308)123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("430812345678901234567890123456789"), "(4308)12345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("43081234567890123456789012345678901"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("431712"), "(4317)12"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("4317123"), ""); + EXPECT_EQ(HRIFromGS1("43171"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("431812345678901234567890"), "(4318)12345678901234567890"); + EXPECT_EQ(HRIFromGS1("43181234567890123456789"), "(4318)1234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("4318123456789012345678901"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("43211"), "(4321)1"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("432112"), ""); + EXPECT_EQ(HRIFromGS1("4321"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("4326123456"), "(4326)123456"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("43261234567"), ""); + EXPECT_EQ(HRIFromGS1("432612345"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("70041234"), "(7004)1234"); + EXPECT_EQ(HRIFromGS1("7004123"), "(7004)123"); + // Too long + EXPECT_EQ(HRIFromGS1("700412345"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("7006123456"), "(7006)123456"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("70061234567"), ""); + EXPECT_EQ(HRIFromGS1("700612345"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("701012"), "(7010)12"); + EXPECT_EQ(HRIFromGS1("70101"), "(7010)1"); + // Too long + EXPECT_EQ(HRIFromGS1("7010123"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("702012345678901234567890"), "(7020)12345678901234567890"); + EXPECT_EQ(HRIFromGS1("70201234567890123456789"), "(7020)1234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("7020123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("7023123456789012345678901234567890"), "(7023)123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("702312345678901234567890123456789"), "(7023)12345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("70231234567890123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("70401234"), "(7040)1234"); + EXPECT_EQ(HRIFromGS1("704012345"), ""); + // Too long + EXPECT_EQ(HRIFromGS1("7040123"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("724012345678901234567890"), "(7240)12345678901234567890"); + EXPECT_EQ(HRIFromGS1("72401234567890123456789"), "(7240)1234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("7240123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("80071234567890123456789012345678901234"), "(8007)1234567890123456789012345678901234"); + EXPECT_EQ(HRIFromGS1("8007123456789012345678901234567890123"), "(8007)123456789012345678901234567890123"); + // Too long + EXPECT_EQ(HRIFromGS1("800712345678901234567890123456789012345"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("800912345678901234567890123456789012345678901234567890"), + + "(8009)12345678901234567890123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("80091234567890123456789012345678901234567890123456789"), + + "(8009)1234567890123456789012345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("8009123456789012345678901234567890123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("80131234567890123456789012345"), "(8013)1234567890123456789012345"); + EXPECT_EQ(HRIFromGS1("8013123456789012345678901234"), "(8013)123456789012345678901234"); + // Too long + EXPECT_EQ(HRIFromGS1("801312345678901234567890123456"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("8017123456789012345678"), "(8017)123456789012345678"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("80171234567890123456789"), ""); + EXPECT_EQ(HRIFromGS1("801712345678901234567"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("80191234567890"), "(8019)1234567890"); + EXPECT_EQ(HRIFromGS1("8019123456789"), "(8019)123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("801912345678901"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("8026123456789012345678"), "(8026)123456789012345678"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("80261234567890123456789"), ""); + EXPECT_EQ(HRIFromGS1("802612345678901234567"), ""); + + // Non-existing + EXPECT_EQ(HRIFromGS1("8100123456"), ""); + EXPECT_EQ(HRIFromGS1("81011234567890"), ""); + EXPECT_EQ(HRIFromGS1("810212"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("81101234567890123456789012345678901234567890123456789012345678901234567890"), + "(8110)1234567890123456789012345678901234567890123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("8110123456789012345678901234567890123456789012345678901234567890123456789"), + "(8110)123456789012345678901234567890123456789012345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("811012345678901234567890123456789012345678901234567890123456789012345678901"), ""); + + // Fixed length + EXPECT_EQ(HRIFromGS1("81111234"), "(8111)1234"); + // Incorrect lengths + EXPECT_EQ(HRIFromGS1("811112345"), ""); + EXPECT_EQ(HRIFromGS1("8111123"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("81121234567890123456789012345678901234567890123456789012345678901234567890"), + "(8112)1234567890123456789012345678901234567890123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("8112123456789012345678901234567890123456789012345678901234567890123456789"), + "(8112)123456789012345678901234567890123456789012345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("811212345678901234567890123456789012345678901234567890123456789012345678901"), ""); + + // Max length + EXPECT_EQ(HRIFromGS1("82001234567890123456789012345678901234567890123456789012345678901234567890"), + "(8200)1234567890123456789012345678901234567890123456789012345678901234567890"); + EXPECT_EQ(HRIFromGS1("8200123456789012345678901234567890123456789012345678901234567890123456789"), + "(8200)123456789012345678901234567890123456789012345678901234567890123456789"); + // Too long + EXPECT_EQ(HRIFromGS1("820012345678901234567890123456789012345678901234567890123456789012345678901"), ""); +} + +TEST(HRIFromGS1, MultiFixed) +{ + EXPECT_EQ(HRIFromGS1("81111234430712"), "(8111)1234(4307)12"); +} + +TEST(HRIFromGS1, MultiVariable) +{ + EXPECT_EQ(HRIFromGS1("70041234\x1d""81111234"), "(7004)1234(8111)1234"); +} diff --git a/test/unit/oned/rss/ODRSSFieldParserTest.cpp b/test/unit/oned/rss/ODRSSFieldParserTest.cpp deleted file mode 100644 index a6cf3ffd79..0000000000 --- a/test/unit/oned/rss/ODRSSFieldParserTest.cpp +++ /dev/null @@ -1,420 +0,0 @@ -/* -* Copyright 2022 gitlost -*/ -// SPDX-License-Identifier: Apache-2.0 - -#include "oned/rss/ODRSSFieldParser.h" - -#include "DecodeStatus.h" - -#include "gtest/gtest.h" - -using namespace ZXing; -using namespace ZXing::OneD::DataBar; - -TEST(ODRSSFieldParserTest, ParseFieldsInGeneralPurpose) -{ - std::string result; - - // 2-digit AIs - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("00123456789012345678", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(00)123456789012345678"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("0012345678901234567", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("001234567890123456789", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("16123456", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(16)123456"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("1612345", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("161234567", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("2212345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(22)12345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("221234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(22)1234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("22123456789012345678901", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("9112345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(91)123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("9112345678901234567890123456789012345678901234567890" - "123456789012345678901234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(91)12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("9112345678901234567890123456789012345678901234567890" - "12345678901234567890123456789012345678901", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("9912345678901234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(99)123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("9912345678901234567890123456789012345678901234567890" - "123456789012345678901234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(99)12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("9912345678901234567890123456789012345678901234567890" - "12345678901234567890123456789012345678901", result), DecodeStatus::NotFound); - - // 3-digit AIs - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("2351234567890123456789012345678", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(235)1234567890123456789012345678"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("235123456789012345678901234567", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(235)123456789012345678901234567"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("23512345678901234567890123456789", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("24312345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(243)12345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("2431234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(243)1234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("243123456789012345678901", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("253123456789012345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(253)123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("25312345678901234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(253)12345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("2531234567890123456789012345678901", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("2551234567890123456789012345", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(255)1234567890123456789012345"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("255123456789012345678901234", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(255)123456789012345678901234"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("25512345678901234567890123456", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("4151234567890123", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(415)1234567890123"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("415123456789012", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("41512345678901234", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("4171234567890123", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(417)1234567890123"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("417123456789012", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("41712345678901234", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("421123456789012", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(421)123456789012"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("42112345678901", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(421)12345678901"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("4211234567890123", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("425123456789012345", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(425)123456789012345"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("42512345678901234", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(425)12345678901234"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("4251234567890123456", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("427123", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(427)123"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("42712", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(427)12"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("4271234", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("71012345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(710)12345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("7101234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(710)1234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("710123456789012345678901", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("71512345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(715)12345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("7151234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(715)1234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("715123456789012345678901", result), DecodeStatus::NotFound); - - // 4-digit variable 4th - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("3370123456", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(3370)123456"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("337012345", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("33701234567", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("3375123456", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(3375)123456"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("33751234567", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("337512345", result), DecodeStatus::NotFound); - - EXPECT_EQ(ParseFieldsInGeneralPurpose("3376123456", result), DecodeStatus::NoError); // Allow although > max 3375 - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("39401234", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(3940)1234"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("394012345", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("3940123", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("39431234", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(3943)1234"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("394312345", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("3943123", result), DecodeStatus::NotFound); - - EXPECT_EQ(ParseFieldsInGeneralPurpose("39441234", result), DecodeStatus::NoError); // Allow although > max 3943 - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("3950123456", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(3950)123456"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("39501234567", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("395012345", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("3955123456", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(3955)123456"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("39551234567", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("395512345", result), DecodeStatus::NotFound); - - EXPECT_EQ(ParseFieldsInGeneralPurpose("3956123456", result), DecodeStatus::NoError); // Allow although > max 3955 - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("7230123456789012345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7230)123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("723012345678901234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7230)12345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("72301234567890123456789012345678901", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("7239123456789012345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7239)123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("723912345678901234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7239)12345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("72391234567890123456789012345678901", result), DecodeStatus::NotFound); - - // 4-digit AIs - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("430012345678901234567890123456789012345", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(4300)12345678901234567890123456789012345"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("43001234567890123456789012345678901234", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(4300)1234567890123456789012345678901234"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("4300123456789012345678901234567890123456", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("430712", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(4307)12"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("4307123", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("43071", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("4308123456789012345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(4308)123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("430812345678901234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(4308)12345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("43081234567890123456789012345678901", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("431712", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(4317)12"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("4317123", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("43171", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("431812345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(4318)12345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("43181234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(4318)1234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("4318123456789012345678901", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("43211", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(4321)1"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("432112", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("4321", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("4326123456", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(4326)123456"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("43261234567", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("432612345", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("70041234", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7004)1234"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("7004123", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7004)123"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("700412345", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("7006123456", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7006)123456"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("70061234567", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("700612345", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("701012", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7010)12"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("70101", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7010)1"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("7010123", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("702012345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7020)12345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("70201234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7020)1234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("7020123456789012345678901", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("7023123456789012345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7023)123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("702312345678901234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7023)12345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("70231234567890123456789012345678901", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("70401234", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7040)1234"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("704012345", result), DecodeStatus::NotFound); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("7040123", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("724012345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7240)12345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("72401234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(7240)1234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("7240123456789012345678901", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("80071234567890123456789012345678901234", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8007)1234567890123456789012345678901234"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("8007123456789012345678901234567890123", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8007)123456789012345678901234567890123"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("800712345678901234567890123456789012345", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("800912345678901234567890123456789012345678901234567890", result), - DecodeStatus::NoError); - EXPECT_EQ(result, "(8009)12345678901234567890123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("80091234567890123456789012345678901234567890123456789", result), - DecodeStatus::NoError); - EXPECT_EQ(result, "(8009)1234567890123456789012345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("8009123456789012345678901234567890123456789012345678901", result), - DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("80131234567890123456789012345", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8013)1234567890123456789012345"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("8013123456789012345678901234", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8013)123456789012345678901234"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("801312345678901234567890123456", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("8017123456789012345678", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8017)123456789012345678"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("80171234567890123456789", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("801712345678901234567", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("80191234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8019)1234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("8019123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8019)123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("801912345678901", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("8026123456789012345678", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8026)123456789012345678"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("80261234567890123456789", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("802612345678901234567", result), DecodeStatus::NotFound); - - // Non-existing - EXPECT_EQ(ParseFieldsInGeneralPurpose("8100123456", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("81011234567890", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("810212", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("811012345678901234567890123456789012345678901234567890" - "12345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8110)1234567890123456789012345678901234567890123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("811012345678901234567890123456789012345678901234567890" - "1234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8110)123456789012345678901234567890123456789012345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("811012345678901234567890123456789012345678901234567890" - "123456789012345678901", result), DecodeStatus::NotFound); - - // Fixed length - EXPECT_EQ(ParseFieldsInGeneralPurpose("81111234", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8111)1234"); - // Incorrect lengths - EXPECT_EQ(ParseFieldsInGeneralPurpose("811112345", result), DecodeStatus::NotFound); - EXPECT_EQ(ParseFieldsInGeneralPurpose("8111123", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("811212345678901234567890123456789012345678901234567890" - "12345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8112)1234567890123456789012345678901234567890123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("811212345678901234567890123456789012345678901234567890" - "1234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8112)123456789012345678901234567890123456789012345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("811212345678901234567890123456789012345678901234567890" - "123456789012345678901", result), DecodeStatus::NotFound); - - // Max length - EXPECT_EQ(ParseFieldsInGeneralPurpose("820012345678901234567890123456789012345678901234567890" - "12345678901234567890", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8200)1234567890123456789012345678901234567890123456789012345678901234567890"); - EXPECT_EQ(ParseFieldsInGeneralPurpose("820012345678901234567890123456789012345678901234567890" - "1234567890123456789", result), DecodeStatus::NoError); - EXPECT_EQ(result, "(8200)123456789012345678901234567890123456789012345678901234567890123456789"); - // Too long - EXPECT_EQ(ParseFieldsInGeneralPurpose("820012345678901234567890123456789012345678901234567890" - "123456789012345678901", result), DecodeStatus::NotFound); -} From e69f1be2ea9a91cbc4b69fd1a69dad428c2eb79c Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 2 Jul 2022 21:50:28 +0200 Subject: [PATCH 139/180] BitArrayView: is to BitArray what BitSource is to ByteArray --- core/src/BitArray.h | 53 ++++++++++++++++++++++++++++-------- core/src/aztec/AZDecoder.cpp | 16 +++++------ 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/core/src/BitArray.h b/core/src/BitArray.h index 355c76eea3..b531389f4d 100644 --- a/core/src/BitArray.h +++ b/core/src/BitArray.h @@ -363,17 +363,6 @@ int ToInt(const ARRAY& a) return pattern; } -inline int ReadBits(BitArray::Range& bits, int n) -{ - assert(n <= 32); - if (n > bits.size()) - throw std::out_of_range("ReadBits(BitArray::Range&) out of range."); - int res = 0; - for (; n > 0; --n, bits.begin++) - AppendBit(res, *bits.begin); - return res; -} - template >> T ToInt(const BitArray& bits, int pos = 0, int count = 8 * sizeof(T)) { @@ -402,4 +391,46 @@ std::vector ToInts(const BitArray& bits, int wordSize, int totalWords, int of return res; } +class BitArrayView +{ + const BitArray& bits; + BitArray::Iterator cur; + +public: + BitArrayView(const BitArray& bits) : bits(bits), cur(bits.begin()) {} + + BitArrayView& skipBits(int n) + { + if (n > bits.size()) + throw std::out_of_range("BitArrayView::skipBits() out of range."); + cur += n; + return *this; + } + + int peakBits(int n) const + { + assert(n <= 32); + if (n > bits.size()) + throw std::out_of_range("BitArrayView::peakBits() out of range."); + int res = 0; + for (auto i = cur; n > 0; --n, i++) + AppendBit(res, *i); + return res; + } + + int readBits(int n) + { + int res = peakBits(n); + cur += n; + return res; + } + + int size() const + { + return bits.end() - cur; + } + + explicit operator bool() const { return size(); } +}; + } // ZXing diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index 1fb152515f..e5c5d34c78 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -209,11 +209,11 @@ static const char* GetCharacter(Table table, int code) /** * See ISO/IEC 24778:2008 Section 10.1 */ -static ECI ParseECIValue(BitArray::Range& bits, const int flg) +static ECI ParseECIValue(BitArrayView& bits, const int flg) { int eci = 0; for (int i = 0; i < flg; i++) - eci = 10 * eci + ReadBits(bits, 4) - 2; + eci = 10 * eci + bits.readBits(4) - 2; return ECI(eci); } @@ -254,20 +254,20 @@ static void DecodeContent(const BitArray& bits, Content& res) Table latchTable = Table::UPPER; // table most recently latched to Table shiftTable = Table::UPPER; // table to use for the next read - auto remBits = bits.range(); + auto remBits = BitArrayView(bits); while (remBits.size() >= (shiftTable == Table::DIGIT ? 4 : 5)) { // see ISO/IEC 24778:2008 7.3.1.2 regarding padding bits if (shiftTable == Table::BINARY) { - int length = ReadBits(remBits, 5); + int length = remBits.readBits(5); if (length == 0) - length = ReadBits(remBits, 11) + 31; + length = remBits.readBits(11) + 31; for (int i = 0; i < length; i++) - res.push_back(ReadBits(remBits, 8)); + res.push_back(remBits.readBits(8)); // Go back to whatever mode we had been in shiftTable = latchTable; } else { int size = shiftTable == Table::DIGIT ? 4 : 5; - int code = ReadBits(remBits, size); + int code = remBits.readBits(size); const char* str = GetCharacter(shiftTable, code); if (std::strncmp(str, "CTRL_", 5) == 0) { // Table changes @@ -279,7 +279,7 @@ static void DecodeContent(const BitArray& bits, Content& res) if (str[6] == 'L') latchTable = shiftTable; } else if (std::strcmp(str, "FLGN") == 0) { - int flg = ReadBits(remBits, 3); + int flg = remBits.readBits(3); if (flg == 0) { // FNC1 res.push_back(29); // May be removed at end if first/second FNC1 } else if (flg <= 6) { From e52ef0e98e2be446edc57c41819faa175fe3c71b Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 2 Jul 2022 23:10:02 +0200 Subject: [PATCH 140/180] DataBar: reimplement `DecodeExpandedBits` * separate HRI generation from bit stream decoding * remove `DecodeStatus` usage * drop remaining reference to RSS --- core/CMakeLists.txt | 16 +- core/src/GS1.cpp | 8 +- core/src/oned/ODDataBarExpandedBitDecoder.cpp | 262 ++++++++++ ...ecoder.h => ODDataBarExpandedBitDecoder.h} | 3 +- core/src/oned/ODDataBarExpandedReader.cpp | 5 +- .../oned/rss/ODRSSExpandedBinaryDecoder.cpp | 313 ----------- .../src/oned/rss/ODRSSGenericAppIdDecoder.cpp | 491 ------------------ core/src/oned/rss/ODRSSGenericAppIdDecoder.h | 22 - test/blackbox/BlackboxTestRunner.cpp | 12 +- test/unit/CMakeLists.txt | 2 +- ...pp => ODDataBarExpandedBitDecoderTest.cpp} | 11 +- 11 files changed, 290 insertions(+), 855 deletions(-) create mode 100644 core/src/oned/ODDataBarExpandedBitDecoder.cpp rename core/src/oned/{rss/ODRSSExpandedBinaryDecoder.h => ODDataBarExpandedBitDecoder.h} (80%) delete mode 100644 core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp delete mode 100644 core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp delete mode 100644 core/src/oned/rss/ODRSSGenericAppIdDecoder.h rename test/unit/oned/{rss/ODRSSExpandedBinaryDecoderTest.cpp => ODDataBarExpandedBitDecoderTest.cpp} (88%) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index fa5df9058d..4088c65288 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -271,6 +271,8 @@ if (BUILD_READERS) src/oned/ODDataBarCommon.cpp src/oned/ODDataBarReader.h src/oned/ODDataBarReader.cpp + src/oned/ODDataBarExpandedBitDecoder.h + src/oned/ODDataBarExpandedBitDecoder.cpp src/oned/ODDataBarExpandedReader.h src/oned/ODDataBarExpandedReader.cpp src/oned/ODITFReader.h @@ -309,18 +311,6 @@ if (BUILD_WRITERS) endif() -set (ONED_RSS_FILES -) -if (BUILD_READERS) - set (ONED_RSS_FILES ${ONED_RSS_FILES} - src/oned/rss/ODRSSExpandedBinaryDecoder.h - src/oned/rss/ODRSSExpandedBinaryDecoder.cpp - src/oned/rss/ODRSSGenericAppIdDecoder.h - src/oned/rss/ODRSSGenericAppIdDecoder.cpp - ) -endif() - - set (PDF417_FILES ) if (BUILD_READERS) @@ -442,7 +432,6 @@ source_group (Sources\\aztec FILES ${AZTEC_FILES}) source_group (Sources\\datamatrix FILES ${DATAMATRIX_FILES}) source_group (Sources\\maxicode FILES ${MAXICODE_FILES}) source_group (Sources\\oned FILES ${ONED_FILES}) -source_group (Sources\\oned\\rss FILES ${ONED_RSS_FILES}) source_group (Sources\\pdf417 FILES ${PDF417_FILES}) source_group (Sources\\qrcode FILES ${QRCODE_FILES}) source_group (Sources\\textcodec FILES ${TEXT_CODEC_FILES}) @@ -457,7 +446,6 @@ add_library (ZXing ${DATAMATRIX_FILES} ${MAXICODE_FILES} ${ONED_FILES} - ${ONED_RSS_FILES} ${PDF417_FILES} ${QRCODE_FILES} ${TEXT_CODEC_FILES} diff --git a/core/src/GS1.cpp b/core/src/GS1.cpp index ab0ab598bc..ab6b94d5d4 100644 --- a/core/src/GS1.cpp +++ b/core/src/GS1.cpp @@ -259,7 +259,12 @@ std::string HRIFromGS1(const std::string& gs1) int fieldSize = i->fieldSize(); if (i->isVariableLength()) { auto gsPos = rem.find(GS); +#if 1 fieldSize = std::min(gsPos == std::string_view::npos ? Size(rem) : static_cast(gsPos), fieldSize); +#else + // TODO: ignore the 'max field size' part for now as it breaks rssexpanded-3/13.png? + fieldSize = gsPos == std::string_view::npos ? Size(rem) : static_cast(gsPos); +#endif } if (fieldSize == 0 || Size(rem) < fieldSize) return {}; @@ -268,7 +273,8 @@ std::string HRIFromGS1(const std::string& gs1) rem.remove_prefix(fieldSize); if (Size(rem) && rem.front() == GS) { - if (i->isVariableLength()) + // TODO: we have DataBar samples where the fixed-length 422 ends with a GS, sigh... + if (i->isVariableLength() || i->aiPrefix == "422") rem.remove_prefix(1); else return {}; diff --git a/core/src/oned/ODDataBarExpandedBitDecoder.cpp b/core/src/oned/ODDataBarExpandedBitDecoder.cpp new file mode 100644 index 0000000000..c31e7f681f --- /dev/null +++ b/core/src/oned/ODDataBarExpandedBitDecoder.cpp @@ -0,0 +1,262 @@ +/* +* Copyright 2016 Nu-book Inc. +* Copyright 2016 ZXing authors +* Copyright 2022 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "ODDataBarExpandedBitDecoder.h" + +#include "BitArray.h" +#include "Error.h" +#include "GTIN.h" + +#include + +namespace ZXing::OneD::DataBar { + +constexpr char GS = 29; // FNC1 + +static std::string DecodeGeneralPurposeBits(BitArrayView& bits) +{ + enum State { NUMERIC, ALPHA, ISO_IEC_646 }; + State state = NUMERIC; + std::string res; + + auto decode5Bits = [](State& state, std::string& res, BitArrayView& bits) { + int v = bits.readBits(5); + if (v == 4) { + state = state == ALPHA ? ISO_IEC_646 : ALPHA; + } else if (v == 15) { // FNC1 + latch to Numeric + res.push_back(GS); + state = NUMERIC; + // Allow for some generators incorrectly placing a numeric latch "000" after an FNC1 + if (bits.size() >= 7 && bits.peakBits(7) < 8) + bits.skipBits(3); + } else { + res.push_back(v + 43); + } + }; + + auto isPadding = [](State state, BitArrayView& bits) { + bool res = (state == NUMERIC) ? bits.size() < 4 + : bits.size() < 5 && (0b00100 >> (5 - bits.size()) == bits.peakBits(bits.size())); + if (res) + bits.skipBits(bits.size()); + return res; + }; + + while(bits.size() >= 3) { + switch(state) { + case NUMERIC: + if (isPadding(state, bits)) + break; + if (bits.size() < 7) { + int v = bits.readBits(4); + if (v > 0) + res.push_back('0' + v - 1); + } else if (bits.peakBits(4) == 0) { + bits.skipBits(4); + state = ALPHA; + } else { + int v = bits.readBits(7); + for (int digit : {(v - 8) / 11, (v - 8) % 11}) + res.push_back(digit == 10 ? GS : '0' + digit); + } + break; + case ALPHA: + if (isPadding(state, bits)) + break; + if (bits.peakBits(1) == 1) { + constexpr char const* lut58to62 = R"(*,-./)"; + int v = bits.readBits(6); + if (v < 58) + res.push_back(v + 33); + else if (v < 63) + res.push_back(lut58to62[v - 58]); + else + throw FormatError(); + } else if (bits.peakBits(3) == 0) { + bits.skipBits(3); + state = NUMERIC; + } else { + decode5Bits(state, res, bits); + } + break; + case ISO_IEC_646: + if (isPadding(state, bits)) + break; + if (bits.peakBits(3) == 0) { + bits.skipBits(3); + state = NUMERIC; + } else { + int v = bits.peakBits(5); + if (v < 16) { + decode5Bits(state, res, bits); + } else if (v < 29) { + v = bits.readBits(7); + res.push_back(v < 90 ? v + 1 : v + 7); + } else { + constexpr char const* lut232to252 = R"(!"%&'()*+,-./:;<=>?_ )"; + v = bits.readBits(8); + if (v < 232 || 252 < v) + throw FormatError(); + res.push_back(lut232to252[v - 232]); + } + } + break; + } + } + + // in NUMERIC encodation there might be a trailing FNC1 that needs to be ignored + if (res.size() && res.back() == GS) + res.pop_back(); + + return res; +} + +static void AppendNDigits(std::string& s, int v, int n) +{ + int div = std::pow(10, n-1); + while (v / div == 0 && div > 1) { + s.push_back('0'); + div /= 10; + } + s.append(std::to_string(v)); +} + +static std::string DecodeCompressedGTIN(std::string prefix, BitArrayView& bits) +{ + for (int i = 0; i < 4; ++i) + AppendNDigits(prefix, bits.readBits(10), 3); + + prefix.push_back(GTIN::ComputeCheckDigit(prefix.substr(2))); + + return prefix; +} + +static std::string DecodeAI01GTIN(BitArrayView& bits) +{ + return DecodeCompressedGTIN("019", bits); +} + +static std::string DecodeAI01AndOtherAIs(BitArrayView& bits) +{ + bits.skipBits(2); // Variable length symbol bit field + + auto header = DecodeCompressedGTIN("01" + std::to_string(bits.readBits(4)), bits); + auto trailer = DecodeGeneralPurposeBits(bits); + if (trailer.empty()) + return {}; + + return header + trailer; +} + +static std::string DecodeAnyAI(BitArrayView& bits) +{ + bits.skipBits(2); // Variable length symbol bit field + + return DecodeGeneralPurposeBits(bits); +} + +static std::string DecodeAI013103(BitArrayView& bits) +{ + std::string buffer = DecodeAI01GTIN(bits); + buffer.append("3103"); + AppendNDigits(buffer, bits.readBits(15), 6); + + return buffer; +} + +static std::string DecodeAI01320x(BitArrayView& bits) +{ + std::string buffer = DecodeAI01GTIN(bits); + int weight = bits.readBits(15); + buffer.append(weight < 10000 ? "3202" : "3203"); + AppendNDigits(buffer, weight < 10000 ? weight : weight - 10000, 6); + + return buffer; +} + +static std::string DecodeAI0139yx(BitArrayView& bits, char y) +{ + bits.skipBits(2); // Variable length symbol bit field + + std::string buffer = DecodeAI01GTIN(bits); + buffer.append("39"); + buffer.push_back(y); + buffer.append(std::to_string(bits.readBits(2))); + + if (y == '3') + AppendNDigits(buffer, bits.readBits(10), 3); + + auto trailer = DecodeGeneralPurposeBits(bits); + if (trailer.empty()) + return {}; + + return buffer + trailer; +} + +static std::string DecodeAI013x0x1x(BitArrayView& bits, const char* aiPrefix, const char* dateCode) +{ + std::string buffer = DecodeAI01GTIN(bits); + buffer.append(aiPrefix); + + int weight = bits.readBits(20); + buffer.append(std::to_string(weight / 100000)); + AppendNDigits(buffer, weight % 100000, 6); + + int date = bits.readBits(16); + if (date != 38400) { + buffer.append(dateCode); + + int day = date % 32; + date /= 32; + int month = date % 12 + 1; + date /= 12; + int year = date; + + AppendNDigits(buffer, year, 2); + AppendNDigits(buffer, month, 2); + AppendNDigits(buffer, day, 2); + } + + return buffer; +} + +std::string DecodeExpandedBits(const BitArray& _bits) +{ + auto bits = BitArrayView(_bits); + bits.readBits(1); // skip linkage bit + + if (bits.peakBits(1) == 1) + return DecodeAI01AndOtherAIs(bits.skipBits(1)); + + if (bits.peakBits(2) == 0) + return DecodeAnyAI(bits.skipBits(2)); + + switch (bits.peakBits(4)) { + case 4: return DecodeAI013103(bits.skipBits(4)); + case 5: return DecodeAI01320x(bits.skipBits(4)); + } + + switch (bits.peakBits(5)) { + case 12: return DecodeAI0139yx(bits.skipBits(5), '2'); + case 13: return DecodeAI0139yx(bits.skipBits(5), '3'); + } + + switch (bits.readBits(7)) { + case 56: return DecodeAI013x0x1x(bits, "310", "11"); + case 57: return DecodeAI013x0x1x(bits, "320", "11"); + case 58: return DecodeAI013x0x1x(bits, "310", "13"); + case 59: return DecodeAI013x0x1x(bits, "320", "13"); + case 60: return DecodeAI013x0x1x(bits, "310", "15"); + case 61: return DecodeAI013x0x1x(bits, "320", "15"); + case 62: return DecodeAI013x0x1x(bits, "310", "17"); + case 63: return DecodeAI013x0x1x(bits, "320", "17"); + } + + return {}; +} + +} // namespace ZXing::OneD::DataBar diff --git a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.h b/core/src/oned/ODDataBarExpandedBitDecoder.h similarity index 80% rename from core/src/oned/rss/ODRSSExpandedBinaryDecoder.h rename to core/src/oned/ODDataBarExpandedBitDecoder.h index 2c708b4354..71653a6659 100644 --- a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.h +++ b/core/src/oned/ODDataBarExpandedBitDecoder.h @@ -1,6 +1,5 @@ /* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors +* Copyright 2022 Axel Waggershauser */ // SPDX-License-Identifier: Apache-2.0 diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index 6e8d59e92f..be1c1247c1 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -9,10 +9,11 @@ #include "BarcodeFormat.h" #include "DecoderResult.h" +#include "GS1.h" #include "ODDataBarCommon.h" +#include "ODDataBarExpandedBitDecoder.h" #include "Result.h" #include "TextDecoder.h" -#include "rss/ODRSSExpandedBinaryDecoder.h" #include #include @@ -368,6 +369,8 @@ Result DataBarExpandedReader::decodePattern(int rowNumber, PatternView& view, #endif auto txt = DecodeExpandedBits(BuildBitArray(pairs)); + // TODO: remove this to make it return standard conform content -> needs lots of blackbox test fixes + txt = HRIFromGS1(txt); if (txt.empty()) return {}; diff --git a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp b/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp deleted file mode 100644 index 5c513c2cd6..0000000000 --- a/core/src/oned/rss/ODRSSExpandedBinaryDecoder.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -*/ -// SPDX-License-Identifier: Apache-2.0 - -/* -* These authors would like to acknowledge the Spanish Ministry of Industry, -* Tourism and Trade, for the support in the project TSI020301-2008-2 -* "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled -* Mobile Dynamic Environments", led by Treelogic -* ( http://www.treelogic.com/ ): -* -* http://www.piramidepse.com/ -*/ - -#include "ODRSSExpandedBinaryDecoder.h" - -#include "BitArray.h" -#include "DecodeStatus.h" -#include "ODRSSGenericAppIdDecoder.h" - -#include - -namespace ZXing::OneD::DataBar { - -static const int AI01_GTIN_SIZE = 40; - -static void AI01AppendCheckDigit(std::string& buffer, int currentPos) -{ - int checkDigit = 0; - for (int i = 0; i < 13; i++) { - int digit = buffer[i + currentPos] - '0'; - checkDigit += (i & 0x01) == 0 ? 3 * digit : digit; - } - - checkDigit = 10 - (checkDigit % 10); - if (checkDigit == 10) { - checkDigit = 0; - } - buffer.append(std::to_string(checkDigit)); -} - -static void AI01EncodeCompressedGtinWithoutAI(std::string& buffer, const BitArray& bits, int currentPos, int initialBufferPosition) -{ - for (int i = 0; i < 4; ++i) { - int currentBlock = ToInt(bits, currentPos + 10 * i, 10); - if (currentBlock / 100 == 0) { - buffer.push_back('0'); - } - if (currentBlock / 10 == 0) { - buffer.push_back('0'); - } - buffer.append(std::to_string(currentBlock)); - } - AI01AppendCheckDigit(buffer, initialBufferPosition); -} - -static void AI01EncodeCompressedGtin(std::string& buffer, const BitArray& bits, int currentPos) -{ - buffer.append("(01)"); - int initialPosition = Size(buffer); - buffer.push_back('9'); - AI01EncodeCompressedGtinWithoutAI(buffer, bits, currentPos, initialPosition); -} - -using AddWeightCodeFunc = const std::function; -using CheckWeightFunc = const std::function; - -static void AI01EncodeCompressedWeight(std::string& buffer, const BitArray& bits, int currentPos, int weightSize, - const AddWeightCodeFunc& addWeightCode, const CheckWeightFunc& checkWeight) -{ - int originalWeightNumeric = ToInt(bits, currentPos, weightSize); - addWeightCode(buffer, originalWeightNumeric); - - int weightNumeric = checkWeight(originalWeightNumeric); - - int currentDivisor = 100000; - for (int i = 0; i < 5; ++i) { - if (weightNumeric / currentDivisor == 0) { - buffer.push_back('0'); - } - currentDivisor /= 10; - } - buffer.append(std::to_string(weightNumeric)); -} - -/** -* @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es) -* @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es) -*/ -static std::string DecodeAI01AndOtherAIs(const BitArray& bits) -{ - static const int HEADER_SIZE = 1 + 1 + 2; // first bit encodes the linkage flag, the second one is the encodation - // method, and the other two are for the variable length - - if (bits.size() < HEADER_SIZE + 44) - return {}; - - std::string buffer; - buffer.append("(01)"); - int initialGtinPosition = Size(buffer); - int firstGtinDigit = ToInt(bits, HEADER_SIZE, 4); - buffer.append(std::to_string(firstGtinDigit)); - - AI01EncodeCompressedGtinWithoutAI(buffer, bits, HEADER_SIZE + 4, initialGtinPosition); - if (StatusIsOK(DecodeAppIdAllCodes(bits, HEADER_SIZE + 44, -1, buffer))) - return buffer; - - return {}; -} - -static std::string DecodeAnyAI(const BitArray& bits) -{ - static const int HEADER_SIZE = 2 + 1 + 2; - std::string buffer; - if (StatusIsOK(DecodeAppIdAllCodes(bits, HEADER_SIZE, -1, buffer))) - return buffer; - - return {}; -} - -static std::string DecodeAI013103(const BitArray& bits) -{ - static const int HEADER_SIZE = 4 + 1; - static const int WEIGHT_SIZE = 15; - - if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE) - return {}; - - std::string buffer; - AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); - AI01EncodeCompressedWeight(buffer, bits, HEADER_SIZE + AI01_GTIN_SIZE, WEIGHT_SIZE, - // addWeightCode - [](std::string& buf, int) { buf.append("(3103)"); }, - // checkWeight - [](int weight) { return weight; }); - - return buffer; -} - -static std::string DecodeAI01320x(const BitArray& bits) -{ - static const int HEADER_SIZE = 4 + 1; - static const int WEIGHT_SIZE = 15; - - if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE) - return {}; - - std::string buffer; - AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); - AI01EncodeCompressedWeight( - buffer, bits, HEADER_SIZE + AI01_GTIN_SIZE, WEIGHT_SIZE, - // addWeightCode - [](std::string& buf, int weight) { buf.append(weight < 10000 ? "(3202)" : "(3203)"); }, - // checkWeight - [](int weight) { return weight < 10000 ? weight : weight - 10000; }); - - return buffer; -} - -static std::string DecodeAI01392x(const BitArray& bits) -{ - static const int HEADER_SIZE = 5 + 1 + 2; - static const int LAST_DIGIT_SIZE = 2; - - if (bits.size() < HEADER_SIZE + AI01_GTIN_SIZE) - return {}; - - std::string buffer; - AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); - - int lastAIdigit = ToInt(bits, HEADER_SIZE + AI01_GTIN_SIZE, LAST_DIGIT_SIZE); - buffer.append("(392"); - buffer.append(std::to_string(lastAIdigit)); - buffer.push_back(')'); - - int pos = HEADER_SIZE + AI01_GTIN_SIZE + LAST_DIGIT_SIZE; - int remainingValue = -1; - if (StatusIsOK(DecodeAppIdGeneralPurposeField(bits, pos, remainingValue, buffer)) - && StatusIsOK(DecodeAppIdAllCodes(bits, pos, remainingValue, buffer))) { - return buffer; - } - return {}; -} - -static std::string DecodeAI01393x(const BitArray& bits) -{ - static const int HEADER_SIZE = 5 + 1 + 2; - static const int LAST_DIGIT_SIZE = 2; - static const int FIRST_THREE_DIGITS_SIZE = 10; - - if (bits.size() < HEADER_SIZE + AI01_GTIN_SIZE) - return {}; - - std::string buffer; - AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); - - int lastAIdigit = ToInt(bits, HEADER_SIZE + AI01_GTIN_SIZE, LAST_DIGIT_SIZE); - - buffer.append("(393"); - buffer.append(std::to_string(lastAIdigit)); - buffer.push_back(')'); - - int firstThreeDigits = ToInt(bits, HEADER_SIZE + AI01_GTIN_SIZE + LAST_DIGIT_SIZE, FIRST_THREE_DIGITS_SIZE); - if (firstThreeDigits / 100 == 0) - buffer.push_back('0'); - - if (firstThreeDigits / 10 == 0) - buffer.push_back('0'); - - buffer.append(std::to_string(firstThreeDigits)); - - int pos = HEADER_SIZE + AI01_GTIN_SIZE + LAST_DIGIT_SIZE + FIRST_THREE_DIGITS_SIZE; - int remainingValue = -1; - if (StatusIsOK(DecodeAppIdGeneralPurposeField(bits, pos, remainingValue, buffer)) - && StatusIsOK(DecodeAppIdAllCodes(bits, pos, remainingValue, buffer))) { - return buffer; - } - return {}; -} - -static std::string DecodeAI013x0x1x(const BitArray& bits, const char* firstAIdigits, const char* dateCode) -{ - static const int HEADER_SIZE = 7 + 1; - static const int WEIGHT_SIZE = 20; - static const int DATE_SIZE = 16; - - if (bits.size() != HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE + DATE_SIZE) - return {}; - - std::string buffer; - AI01EncodeCompressedGtin(buffer, bits, HEADER_SIZE); - AI01EncodeCompressedWeight(buffer, bits, HEADER_SIZE + AI01_GTIN_SIZE, WEIGHT_SIZE, - // addWeightCode - [firstAIdigits](std::string& buf, int weight) { - buf.push_back('('); - buf.append(firstAIdigits); - buf.append(std::to_string(weight / 100000)); - buf.push_back(')'); - }, - // checkWeight - [](int weight) { - return weight % 100000; - }); - - // encode compressed date - int numericDate = ToInt(bits, HEADER_SIZE + AI01_GTIN_SIZE + WEIGHT_SIZE, DATE_SIZE); - if (numericDate != 38400) { - buffer.push_back('('); - buffer.append(dateCode); - buffer.push_back(')'); - - int day = numericDate % 32; - numericDate /= 32; - int month = numericDate % 12 + 1; - numericDate /= 12; - int year = numericDate; - - if (year / 10 == 0) - buffer.push_back('0'); - - buffer.append(std::to_string(year)); - if (month / 10 == 0) - buffer.push_back('0'); - - buffer.append(std::to_string(month)); - if (day / 10 == 0) - buffer.push_back('0'); - - buffer.append(std::to_string(day)); - } - - return buffer; -} - -std::string DecodeExpandedBits(const BitArray& bits) -{ - if (bits.get(1)) - return DecodeAI01AndOtherAIs(bits); - - if (!bits.get(2)) - return DecodeAnyAI(bits); - - int fourBitEncodationMethod = ToInt(bits, 1, 4); - - switch (fourBitEncodationMethod) { - case 4: return DecodeAI013103(bits); - case 5: return DecodeAI01320x(bits); - } - - int fiveBitEncodationMethod = ToInt(bits, 1, 5); - switch (fiveBitEncodationMethod) { - case 12: return DecodeAI01392x(bits); - case 13: return DecodeAI01393x(bits); - } - - int sevenBitEncodationMethod = ToInt(bits, 1, 7); - switch (sevenBitEncodationMethod) { - case 56: return DecodeAI013x0x1x(bits, "310", "11"); - case 57: return DecodeAI013x0x1x(bits, "320", "11"); - case 58: return DecodeAI013x0x1x(bits, "310", "13"); - case 59: return DecodeAI013x0x1x(bits, "320", "13"); - case 60: return DecodeAI013x0x1x(bits, "310", "15"); - case 61: return DecodeAI013x0x1x(bits, "320", "15"); - case 62: return DecodeAI013x0x1x(bits, "310", "17"); - case 63: return DecodeAI013x0x1x(bits, "320", "17"); - } - - return {}; -} - -} // namespace ZXing::OneD::DataBar diff --git a/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp b/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp deleted file mode 100644 index eee7c9e4e7..0000000000 --- a/core/src/oned/rss/ODRSSGenericAppIdDecoder.cpp +++ /dev/null @@ -1,491 +0,0 @@ -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -*/ -// SPDX-License-Identifier: Apache-2.0 - -#include "ODRSSGenericAppIdDecoder.h" - -#include "BitArray.h" -#include "DecodeStatus.h" -#include "GS1.h" - -#include -#include -#include - -namespace ZXing::OneD::DataBar { - -struct DecodedValue -{ - int newPosition = std::numeric_limits::max(); - - DecodedValue() = default; - explicit DecodedValue(int np) : newPosition(np) {} - bool isValid() const { return newPosition != std::numeric_limits::max(); } -}; - -/** -* @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es) -* @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es) -*/ -struct DecodedChar : public DecodedValue -{ - static const char FNC1 = '$'; // It's not in Alphanumeric neither in ISO/IEC 646 charset - - char value = '\0'; - - DecodedChar() = default; - DecodedChar(int np, char c) : DecodedValue(np), value(c) {} - - bool isFNC1() const { return value == FNC1; } -}; - -/** -* @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es) -* @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es) -*/ -struct DecodedInformation : public DecodedValue -{ - std::string newString; - int remainingValue = -1; - - DecodedInformation() = default; - DecodedInformation(int np, std::string s) : DecodedValue(np), newString(std::move(s)) {} - DecodedInformation(int np, std::string s, int r) : DecodedValue(np), newString(std::move(s)), remainingValue(r) {} - - bool isRemaining() const { return remainingValue >= 0; } -}; - -/** -* @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es) -* @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es) -*/ -struct DecodedNumeric : public DecodedValue -{ - static const int FNC1 = 10; - - int firstDigit = 0; - int secondDigit = 0; - - DecodedNumeric() = default; - DecodedNumeric(int newPosition, int first, int second) : DecodedValue(newPosition), firstDigit(first), secondDigit(second) { - if (firstDigit < 0 || firstDigit > 10 || secondDigit < 0 || secondDigit > 10) { - *this = DecodedNumeric(); - } - } - - int value() const { - return firstDigit * 10 + secondDigit; - } - - bool isFirstDigitFNC1() const { - return firstDigit == FNC1; - } - - bool isSecondDigitFNC1() const { - return secondDigit == FNC1; - } - - bool isAnyFNC1() const { - return firstDigit == FNC1 || secondDigit == FNC1; - } - -private: -}; - -struct ParsingState -{ - enum State { NUMERIC, ALPHA, ISO_IEC_646 }; - - int position = 0; - State encoding = NUMERIC; -}; - -static bool -IsStillAlpha(const BitArray& bits, int pos) -{ - if (pos + 5 > bits.size()) { - return false; - } - - // We now check if it's a valid 5-bit value (0..9 and FNC1) - int fiveBitValue = ToInt(bits, pos, 5); - if (fiveBitValue >= 5 && fiveBitValue < 16) { - return true; - } - - if (pos + 6 > bits.size()) { - return false; - } - - int sixBitValue = ToInt(bits, pos, 6); - return sixBitValue >= 16 && sixBitValue < 63; // 63 not included -} - -static bool -IsStillIsoIec646(const BitArray& bits, int pos) -{ - if (pos + 5 > bits.size()) { - return false; - } - - int fiveBitValue = ToInt(bits, pos, 5); - if (fiveBitValue >= 5 && fiveBitValue < 16) { - return true; - } - - if (pos + 7 > bits.size()) { - return false; - } - - int sevenBitValue = ToInt(bits, pos, 7); - if (sevenBitValue >= 64 && sevenBitValue < 116) { - return true; - } - - if (pos + 8 > bits.size()) { - return false; - } - - int eightBitValue = ToInt(bits, pos, 8); - return eightBitValue >= 232 && eightBitValue < 253; -} - -static bool -IsStillNumeric(const BitArray& bits, int pos) -{ - // It's numeric if it still has 7 positions - // and one of the first 4 bits is "1". - if (pos + 7 > bits.size()) { - return pos + 4 <= bits.size(); - } - auto bitIter = bits.iterAt(pos); - for (int i = 0; i < 4; ++i, ++bitIter) { - if (*bitIter) { - return true; - } - } - return false; -} - -static DecodedChar -DecodeAlphanumeric(const BitArray& bits, int pos) -{ - int fiveBitValue = ToInt(bits, pos, 5); - if (fiveBitValue == 15) { - return DecodedChar(pos + 5, DecodedChar::FNC1); - } - - if (fiveBitValue >= 5 && fiveBitValue < 15) { - return DecodedChar(pos + 5, (char)('0' + fiveBitValue - 5)); - } - - int sixBitValue = ToInt(bits, pos, 6); - - if (sixBitValue >= 32 && sixBitValue < 58) { - return DecodedChar(pos + 6, (char)(sixBitValue + 33)); - } - - if (sixBitValue < 58 || sixBitValue > 62) - throw std::runtime_error("Decoding invalid alphanumeric value"); - - constexpr char const* lut58to62 = R"(*,-./)"; - char c = lut58to62[sixBitValue - 58]; - - return DecodedChar(pos + 6, c); -} - -static bool -IsAlphaTo646ToAlphaLatch(const BitArray& bits, int pos) -{ - if (pos + 1 > bits.size()) { - return false; - } - for (int i = 0; i < 5 && i + pos < bits.size(); ++i) { - if (i == 2) { - if (!bits.get(pos + 2)) { - return false; - } - } - else if (bits.get(pos + i)) { - return false; - } - } - return true; -} - -static bool -IsAlphaOr646ToNumericLatch(const BitArray& bits, int pos) -{ - // Next is alphanumeric if there are 3 positions and they are all zeros - if (pos + 3 > bits.size()) { - return false; - } - auto bitIter = bits.iterAt(pos); - for (int i = 0; i < 3; ++i, ++bitIter) { - if (*bitIter) { - return false; - } - } - return true; -} - -static bool -IsNumericToAlphaNumericLatch(const BitArray& bits, int pos) -{ - // Next is alphanumeric if there are 4 positions and they are all zeros, or - // if there is a subset of this just before the end of the symbol - if (pos + 1 > bits.size()) { - return false; - } - - auto bitIter = bits.iterAt(pos); - for (int i = 0; i < 4 && i + pos < bits.size(); ++i, ++bitIter) { - if (*bitIter) { - return false; - } - } - return true; -} - -static DecodedInformation -ParseAlphaBlock(const BitArray& bits, ParsingState& state, std::string& buffer) -{ - while (IsStillAlpha(bits, state.position)) { - DecodedChar alpha = DecodeAlphanumeric(bits, state.position); - state.position = alpha.newPosition; - - if (alpha.isFNC1()) { - // Allow for some generators incorrectly placing a numeric latch "000" after an FNC1 - if (state.position + 7 < bits.size() && ToInt(bits, state.position, 7) < 8) { - state.position += 3; - } - state.encoding = ParsingState::NUMERIC; // FNC1 latches to numeric encodation - return DecodedInformation(state.position, buffer); //end of the char block - } - buffer.push_back(alpha.value); - } - - if (IsAlphaOr646ToNumericLatch(bits, state.position)) { - state.position += 3; - state.encoding = ParsingState::NUMERIC; - } - else if (IsAlphaTo646ToAlphaLatch(bits, state.position)) { - if (state.position + 5 < bits.size()) { - state.position += 5; - } - else { - state.position = bits.size(); - } - state.encoding = ParsingState::ISO_IEC_646; - } - return DecodedInformation(); -} - -static DecodedChar -DecodeIsoIec646(const BitArray& bits, int pos) -{ - int fiveBitValue = ToInt(bits,pos, 5); - if (fiveBitValue == 15) { - return DecodedChar(pos + 5, DecodedChar::FNC1); - } - - if (fiveBitValue >= 5 && fiveBitValue < 15) { - return DecodedChar(pos + 5, (char)('0' + fiveBitValue - 5)); - } - - int sevenBitValue = ToInt(bits, pos, 7); - - if (sevenBitValue >= 64 && sevenBitValue < 90) { - return DecodedChar(pos + 7, (char)(sevenBitValue + 1)); - } - - if (sevenBitValue >= 90 && sevenBitValue < 116) { - return DecodedChar(pos + 7, (char)(sevenBitValue + 7)); - } - - int eightBitValue = ToInt(bits, pos, 8); - if (eightBitValue < 232 || eightBitValue > 252) - throw std::runtime_error("Decoding invalid ISO-IEC-646 value"); - - constexpr char const* lut232to252 = R"(!"%&'()*+,-./:;<=>?_ )"; - char c = lut232to252[eightBitValue - 232]; - - return DecodedChar(pos + 8, c); -} - -static DecodedInformation -ParseIsoIec646Block(const BitArray& bits, ParsingState& state, std::string& buffer) -{ - while (IsStillIsoIec646(bits, state.position)) { - DecodedChar iso = DecodeIsoIec646(bits, state.position); - state.position = iso.newPosition; - if (iso.isFNC1()) { - // Allow for some generators incorrectly placing a numeric latch "000" after an FNC1 - if (state.position + 7 < bits.size() && ToInt(bits, state.position, 7) < 8) { - state.position += 3; - } - state.encoding = ParsingState::NUMERIC; // FNC1 latches to numeric encodation - return DecodedInformation(state.position, buffer); - } - buffer.push_back(iso.value); - } - - if (IsAlphaOr646ToNumericLatch(bits, state.position)) { - state.position += 3;; - state.encoding = ParsingState::NUMERIC; - } - else if (IsAlphaTo646ToAlphaLatch(bits, state.position)) { - if (state.position + 5 < bits.size()) { - state.position += 5; - } - else { - state.position = bits.size(); - } - state.encoding = ParsingState::ALPHA; - } - return DecodedInformation(); -} - -static DecodedNumeric -DecodeNumeric(const BitArray& bits, int pos) -{ - if (pos + 7 > bits.size()) { - int numeric = ToInt(bits, pos, 4); - if (numeric == 0) { - return DecodedNumeric(bits.size(), DecodedNumeric::FNC1, DecodedNumeric::FNC1); - } - return DecodedNumeric(bits.size(), numeric - 1, DecodedNumeric::FNC1); - } - int numeric = ToInt(bits, pos, 7); - int digit1 = (numeric - 8) / 11; - int digit2 = (numeric - 8) % 11; - - return DecodedNumeric(pos + 7, digit1, digit2); -} - -static DecodedInformation -ParseNumericBlock(const BitArray& bits, ParsingState& state, std::string& buffer) -{ - while (IsStillNumeric(bits, state.position)) { - DecodedNumeric numeric = DecodeNumeric(bits, state.position); - if (!numeric.isValid()) - break; - state.position = numeric.newPosition; - - if (numeric.isFirstDigitFNC1()) { - DecodedInformation information; - if (numeric.isSecondDigitFNC1()) { - return DecodedInformation(state.position, buffer); - } - else { - return DecodedInformation(state.position, buffer, numeric.secondDigit); - } - } - - buffer.append(std::to_string(numeric.firstDigit)); - if (numeric.isSecondDigitFNC1()) { - return DecodedInformation(state.position, buffer); - } - buffer.append(std::to_string(numeric.secondDigit)); - } - - if (IsNumericToAlphaNumericLatch(bits, state.position)) { - state.encoding = ParsingState::ALPHA; - state.position += 4; - } - return DecodedInformation(); -} - - -static DecodedInformation -ParseBlocks(const BitArray& bits, ParsingState& state, std::string& buffer) -{ - while (true) { - int initialPosition = state.position; - auto result = - state.encoding == ParsingState::ALPHA ? - ParseAlphaBlock(bits, state, buffer) : - (state.encoding == ParsingState::ISO_IEC_646 ? - ParseIsoIec646Block(bits, state, buffer) : - ParseNumericBlock(bits, state, buffer)); - if (result.isValid() || initialPosition == state.position) - { - return result; - } - } -} - -static DecodedInformation -DoDecodeGeneralPurposeField(ParsingState& state, const BitArray& bits, std::string prefix) -{ - DecodedInformation lastDecoded = ParseBlocks(bits, state, prefix); - if (lastDecoded.isValid() && lastDecoded.isRemaining()) { - return DecodedInformation(state.position, prefix, lastDecoded.remainingValue); - } - return DecodedInformation(state.position, prefix); -} - -DecodeStatus -DecodeAppIdGeneralPurposeField(const BitArray& bits, int& pos, int& remainingValue, std::string& result) -{ - try - { - ParsingState state; - state.position = pos; - DecodedInformation info = DoDecodeGeneralPurposeField(state, bits, std::string()); - result += info.newString; - pos = state.position; - remainingValue = info.remainingValue; - return DecodeStatus::NoError; - } - catch (const std::exception &) - { - } - return DecodeStatus::FormatError; -} - -DecodeStatus -DecodeAppIdAllCodes(const BitArray& bits, int pos, int remainingValue, std::string& result) -{ - try - { - ParsingState state; - std::string remaining; - if (remainingValue != -1) { - remaining = std::to_string(remainingValue); - } - while (true) { - state.position = pos; - DecodedInformation info = DoDecodeGeneralPurposeField(state, bits, remaining); - if (pos == info.newPosition || info.newString.empty()) // No step forward! - break; - - std::string parsedFields = HRIFromGS1(info.newString); - if (parsedFields.empty()) { - if (result.empty() && remaining.empty()) { - result = info.newString; - return DecodeStatus::NoError; - } else - return DecodeStatus::FormatError; - } - result += parsedFields; - if (info.isRemaining()) { - remaining = std::to_string(info.remainingValue); - } - else { - remaining.clear(); - } - - pos = info.newPosition; - }; - return DecodeStatus::NoError; - } - catch (const std::exception &) - { - } - return DecodeStatus::FormatError; -} - -} // namespace ZXing::OneD::DataBar diff --git a/core/src/oned/rss/ODRSSGenericAppIdDecoder.h b/core/src/oned/rss/ODRSSGenericAppIdDecoder.h deleted file mode 100644 index b6fe69485c..0000000000 --- a/core/src/oned/rss/ODRSSGenericAppIdDecoder.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -*/ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace ZXing { - -class BitArray; -enum class DecodeStatus; - -namespace OneD::DataBar { - -DecodeStatus DecodeAppIdGeneralPurposeField(const BitArray& bits, int& pos, int& remainingValue, std::string& result); -DecodeStatus DecodeAppIdAllCodes(const BitArray& bits, int pos, const int remainingValue, std::string& result); - -} // namespace OneD::DataBar -} // namespace ZXing diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 7f9a1d7ab9..bd12204987 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -525,14 +525,16 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("rssexpanded-3", "DataBarExpanded", 118, { - { 118, 118, 0 }, - { 118, 118, 180 }, - { 118, 0, pure }, + // TODO: See HRIFromGS1. 13.png and 66.png are seemingly invalid symbols + { 116, 116, 0 }, + { 116, 116, 180 }, + { 116, 0, pure }, }); runTests("rssexpandedstacked-1", "DataBarExpanded", 65, { - { 60, 65, 0 }, - { 60, 65, 180 }, + // TODO: See HRIFromGS1. 13.png is seemingly invalid symbol + { 60, 64, 0 }, + { 60, 64, 180 }, { 60, 0, pure }, }); diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 45f609c1f5..82503c0c90 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -45,7 +45,7 @@ add_executable (UnitTest oned/ODITFWriterTest.cpp oned/ODUPCAWriterTest.cpp oned/ODUPCEWriterTest.cpp - oned/rss/ODRSSExpandedBinaryDecoderTest.cpp + oned/ODDataBarExpandedBitDecoderTest.cpp qrcode/MQRDecoderTest.cpp qrcode/QRBitMatrixParserTest.cpp qrcode/QRDataMaskTest.cpp diff --git a/test/unit/oned/rss/ODRSSExpandedBinaryDecoderTest.cpp b/test/unit/oned/ODDataBarExpandedBitDecoderTest.cpp similarity index 88% rename from test/unit/oned/rss/ODRSSExpandedBinaryDecoderTest.cpp rename to test/unit/oned/ODDataBarExpandedBitDecoderTest.cpp index c11b7528ef..27e6902a63 100644 --- a/test/unit/oned/rss/ODRSSExpandedBinaryDecoderTest.cpp +++ b/test/unit/oned/ODDataBarExpandedBitDecoderTest.cpp @@ -5,7 +5,8 @@ #include "BitArray.h" #include "BitArrayUtility.h" -#include "oned/rss/ODRSSExpandedBinaryDecoder.h" +#include "GS1.h" +#include "oned/ODDataBarExpandedBitDecoder.h" #include "gtest/gtest.h" @@ -13,10 +14,10 @@ using namespace ZXing; static std::string parse(std::string bitStr) { - return OneD::DataBar::DecodeExpandedBits(Utility::ParseBitArray(bitStr, '1')); + return HRIFromGS1(OneD::DataBar::DecodeExpandedBits(Utility::ParseBitArray(bitStr, '1'))); } -TEST(ODRSSExpandedBinaryDecoderTest, FNC1NumericLatch) +TEST(ODDataBarExpandedBitDecoderTest, FNC1NumericLatch) { std::string result; @@ -37,7 +38,7 @@ TEST(ODRSSExpandedBinaryDecoderTest, FNC1NumericLatch) EXPECT_EQ(result, "(10)12((422)123"); } -TEST(ODRSSExpandedBinaryDecoderTest, DecodeAI01392x) +TEST(ODDataBarExpandedBitDecoderTest, DecodeAI01392x) { std::string result; @@ -53,7 +54,7 @@ TEST(ODRSSExpandedBinaryDecoderTest, DecodeAI01392x) EXPECT_EQ(result, "(01)90012345678908(3929)12345678901234(20)01"); } -TEST(ODRSSExpandedBinaryDecoderTest, DecodeAI01393x) +TEST(ODDataBarExpandedBitDecoderTest, DecodeAI01393x) { std::string result; From 18797e2c09fc3ac0d51d032e4a75c280425f25f7 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 2 Jul 2022 23:10:47 +0200 Subject: [PATCH 141/180] test: remove duplicated blackbox test files from rssexpandedstacked --- test/blackbox/BlackboxTestRunner.cpp | 6 +++--- test/samples/rssexpandedstacked-2/13.png | Bin 334 -> 0 bytes test/samples/rssexpandedstacked-2/13.txt | 1 - test/samples/rssexpandedstacked-2/19.png | Bin 258 -> 0 bytes test/samples/rssexpandedstacked-2/19.txt | 1 - test/samples/rssexpandedstacked-2/20.png | Bin 249 -> 0 bytes test/samples/rssexpandedstacked-2/20.txt | 1 - test/samples/rssexpandedstacked-2/22.png | Bin 330 -> 0 bytes test/samples/rssexpandedstacked-2/22.txt | 1 - test/samples/rssexpandedstacked-2/23.png | Bin 301 -> 0 bytes test/samples/rssexpandedstacked-2/23.txt | 1 - 11 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 test/samples/rssexpandedstacked-2/13.png delete mode 100644 test/samples/rssexpandedstacked-2/13.txt delete mode 100644 test/samples/rssexpandedstacked-2/19.png delete mode 100644 test/samples/rssexpandedstacked-2/19.txt delete mode 100644 test/samples/rssexpandedstacked-2/20.png delete mode 100644 test/samples/rssexpandedstacked-2/20.txt delete mode 100644 test/samples/rssexpandedstacked-2/22.png delete mode 100644 test/samples/rssexpandedstacked-2/22.txt delete mode 100644 test/samples/rssexpandedstacked-2/23.png delete mode 100644 test/samples/rssexpandedstacked-2/23.txt diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index bd12204987..fdda8ff254 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -538,9 +538,9 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set { 60, 0, pure }, }); - runTests("rssexpandedstacked-2", "DataBarExpanded", 7, { - { 2, 7, 0 }, - { 2, 7, 180 }, + runTests("rssexpandedstacked-2", "DataBarExpanded", 2, { + { 2, 2, 0 }, + { 2, 2, 180 }, }); runTests("qrcode-1", "QRCode", 16, { diff --git a/test/samples/rssexpandedstacked-2/13.png b/test/samples/rssexpandedstacked-2/13.png deleted file mode 100644 index 21dc744571328db490eb75af6570d58a5032b906..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334 zcmeAS@N?(olHy`uVBq!ia0vp^XBZe5w=n`44ARp>e1IHLPZ!6K3dXnBcb`f%&7jN`K}v8=R{sUQ~Cby`d^!Ci(bV!ooi}DOVp%p9T0xy=Ox7J2{ z{w--4Ri5E{G4r_lq;K9*-;L8I&bCtG7gqqFC!D@|2x~d=5>XU@yuD} ze=Cl8TZ!!z-yRow_4an5_bR<82JV~nT;j3PwZ7-Gz?=6#He+j;Iz S|66ea6i}Y7elF{r5}E*5K$EEe diff --git a/test/samples/rssexpandedstacked-2/13.txt b/test/samples/rssexpandedstacked-2/13.txt deleted file mode 100644 index 0429d93c29..0000000000 --- a/test/samples/rssexpandedstacked-2/13.txt +++ /dev/null @@ -1 +0,0 @@ -(01)90012345678908(3922)795888888888888888888888888888888888888888888888888888 \ No newline at end of file diff --git a/test/samples/rssexpandedstacked-2/19.png b/test/samples/rssexpandedstacked-2/19.png deleted file mode 100644 index f21183e01b4f5edd2879569c9ea68e1ec0b1d227..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 258 zcmeAS@N?(olHy`uVBq!ia0vp^XBZe5l^B5xhPeq0l0fRDr;B4q1>@VRwpnhD9Bzq; zx483MXM`2JZhG4gEx1hd%yg^&EAL;n%8v1M+yQQEKH5}GOzTV zdlTaxp6PjSZH)i5?Im{a*-w4Bo4#%LHt(6w-n>b@KdIx!!-dIWrzhEVM};kKZ~$qn zx@gb;`~2+SFO$okr}leaFD{$>`g@7nbzQm2Q(tBtZ%ONIwE!9Kut5Cp=MDe&?9A_* zH*wOd7hAtPDZBV{+7UlZVO4eg*t?cjryXPhDpL@6;hO((Z_Cl*C>DE==R95gT-G@y GGywn&B5?cw diff --git a/test/samples/rssexpandedstacked-2/19.txt b/test/samples/rssexpandedstacked-2/19.txt deleted file mode 100644 index 8f0841f935..0000000000 --- a/test/samples/rssexpandedstacked-2/19.txt +++ /dev/null @@ -1 +0,0 @@ -(01)12345678901231(10)UNIVERSITY-OF-DEUSTO \ No newline at end of file diff --git a/test/samples/rssexpandedstacked-2/20.png b/test/samples/rssexpandedstacked-2/20.png deleted file mode 100644 index 7f5d8326efaef78decb6f99829514503b2702cf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^XBZe5l^B5xhPeq0l0fQ!r;B4q1>@VRmaA9|INTmK zJe9uFl^}iM8uPZMXu(Tdc6nc)SzrC1xu128;9;-h8NE}I9t&i7+ih}G5a3{8id|7X zseCmz`%|CJPmgMP_jvs)zrHf^#;WL!$gZa)3zwRB#xd${i1nyEEvx0hWDesXGW%7onbolpy2>^y8&{ne1IH5PZ!6K3dXlrtxshr^0+>1 zSo zbxoA3D!y^uTzu>7)aYv~UubsjnsnHHN$;JvJQB}d#Boi%xIn$hMQNgk$|dJ~zTdx| zeF#jw@S#rmVe#_Zm-3T6=e!HQw0PEPmq#(qJ12R7OsslwH!=R6jgHm3b8lCtnt9%l zF8ipsG=F_YKzC)Re#os)cU8dZlx)Ae+n}Fc*KcON`DJJP?+L4V_psm4F0lZ5WunIt a`xorF^ID!>=E%?i1(K($pUXO@geCy@VR)~9q7c#eK_ zh&s5I;EI;nY`yhC)K8!C3j!C_sV$X?=x{Cd#3)ZIJWFg zds~A8NMqH7*hKwoyV@w3-fM>~ce`X-o&7lR+J=X_r_a%yw#RwJjjS`AK*JRTUO1Mw z?p;&4y7 Date: Sat, 2 Jul 2022 23:17:45 +0200 Subject: [PATCH 142/180] GS1: accept GS after every field according to spec See also https://github.com/nu-book/zxing-cpp/issues/338#issuecomment-1172957337 --- core/src/GS1.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/core/src/GS1.cpp b/core/src/GS1.cpp index ab6b94d5d4..50c25debd7 100644 --- a/core/src/GS1.cpp +++ b/core/src/GS1.cpp @@ -272,13 +272,10 @@ std::string HRIFromGS1(const std::string& gs1) res += rem.substr(0, fieldSize); rem.remove_prefix(fieldSize); - if (Size(rem) && rem.front() == GS) { - // TODO: we have DataBar samples where the fixed-length 422 ends with a GS, sigh... - if (i->isVariableLength() || i->aiPrefix == "422") - rem.remove_prefix(1); - else - return {}; - } + // See General Specification v22.0 Section 7.8.6.3: "...the processing routine SHALL tolerate a single separator character + // immediately following any element string, whether necessary or not..." + if (Size(rem) && rem.front() == GS) + rem.remove_prefix(1); } return res; From 747f8d97c965172e8ca0d21ce2ff1d2b80937795 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 3 Jul 2022 00:47:14 +0200 Subject: [PATCH 143/180] Error: remove all remaining internal use of `DecodeStatus` Checksum and Format errors are now passed with fully decoded and located 1D symbols. For the 2D symbols, this is still a TODO. deprecate `DecodeStatus` related helper functions. --- core/src/DecodeStatus.h | 6 +++--- core/src/DecoderResult.h | 2 +- core/src/Result.cpp | 18 +++++++++--------- core/src/Result.h | 2 +- core/src/oned/ODCode128Reader.cpp | 9 +++++---- core/src/oned/ODCode39Reader.cpp | 10 ++++++---- core/src/oned/ODCode93Reader.cpp | 9 +++++---- core/src/oned/ODDataBarExpandedReader.cpp | 2 +- core/src/oned/ODITFReader.cpp | 5 +++-- core/src/oned/ODMultiUPCEANReader.cpp | 5 +++-- core/src/pdf417/PDFDetector.h | 1 - 11 files changed, 37 insertions(+), 32 deletions(-) diff --git a/core/src/DecodeStatus.h b/core/src/DecodeStatus.h index 5c8fba4a47..80a52ba3c3 100644 --- a/core/src/DecodeStatus.h +++ b/core/src/DecodeStatus.h @@ -15,17 +15,17 @@ enum class DecodeStatus ChecksumError, }; -inline bool StatusIsOK(DecodeStatus status) +[[deprecated]] inline bool StatusIsOK(DecodeStatus status) { return status == DecodeStatus::NoError; } -inline bool StatusIsError(DecodeStatus status) +[[deprecated]] inline bool StatusIsError(DecodeStatus status) { return status != DecodeStatus::NoError; } -inline const char* ToString(DecodeStatus status) +[[deprecated]] inline const char* ToString(DecodeStatus status) { constexpr const char* names[] = {"NoError", "NotFound", "FormatError", "ChecksumError"}; return names[static_cast(status)]; diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 7c54337f55..9340da4182 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -39,7 +39,7 @@ class DecoderResult public: DecoderResult() = default; DecoderResult(Error error) : _error(error) {} - DecoderResult(ByteArray&& rawBytes, Content&& bytes = {}) : _rawBytes(std::move(rawBytes)), _content(std::move(bytes)) + DecoderResult(ByteArray&& rawBytes, Content&& bytes) : _rawBytes(std::move(rawBytes)), _content(std::move(bytes)) { _numBits = 8 * Size(_rawBytes); } diff --git a/core/src/Result.cpp b/core/src/Result.cpp index ae166486ae..4a1daa6ab7 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -19,11 +19,11 @@ namespace ZXing { Result::Result(DecodeStatus status) : _error(Status2Error(status)) {} -Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, - SymbologyIdentifier si, ByteArray&& rawBytes, bool readerInit, const std::string& ai) - : - _format(format), +Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, Error error, + ByteArray&& rawBytes, bool readerInit, const std::string& ai) + : _format(format), _content({ByteArray(text)}, si, ai), + _error(error), _position(Line(y, xStart, xStop)), _rawBytes(std::move(rawBytes)), _numBits(Size(_rawBytes) * 8), @@ -152,11 +152,6 @@ Result MergeStructuredAppendSequence(const Results& results) std::list allResults(results.begin(), results.end()); allResults.sort([](const Result& r1, const Result& r2) { return r1.sequenceIndex() < r2.sequenceIndex(); }); - if (allResults.back().sequenceSize() != Size(allResults) || - !std::all_of(allResults.begin(), allResults.end(), - [&](Result& it) { return it.sequenceId() == allResults.front().sequenceId(); })) - return Result(DecodeStatus::FormatError); - Result res = allResults.front(); for (auto i = std::next(allResults.begin()); i != allResults.end(); ++i) res._content.append(i->_content); @@ -164,6 +159,11 @@ Result MergeStructuredAppendSequence(const Results& results) res._position = {}; res._sai.index = -1; + if (allResults.back().sequenceSize() != Size(allResults) || + !std::all_of(allResults.begin(), allResults.end(), + [&](Result& it) { return it.sequenceId() == allResults.front().sequenceId(); })) + res._error = FormatError("sequenceIDs not matching during structured append sequence merging"); + return res; } diff --git a/core/src/Result.h b/core/src/Result.h index befd92e3b8..6c7ed47da1 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -34,7 +34,7 @@ class Result explicit Result(DecodeStatus status); // 1D convenience constructor - Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, + Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, Error error = {}, ByteArray&& rawBytes = {}, bool readerInit = false, const std::string& ai = {}); Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format); diff --git a/core/src/oned/ODCode128Reader.cpp b/core/src/oned/ODCode128Reader.cpp index bb7ab97964..42fa421b79 100644 --- a/core/src/oned/ODCode128Reader.cpp +++ b/core/src/oned/ODCode128Reader.cpp @@ -252,9 +252,9 @@ Result Code128Reader::decodePattern(int rowNumber, PatternView& next, std::uniqu if (code == CODE_STOP) break; if (code >= CODE_START_A) - return Result(DecodeStatus::FormatError); + return {}; if (!raw2txt.decode(code)) - return Result(DecodeStatus::FormatError); + return {}; rawCodes.push_back(static_cast(code)); } @@ -268,15 +268,16 @@ Result Code128Reader::decodePattern(int rowNumber, PatternView& next, std::uniqu if (!next.isValid() || next[CHAR_LEN] > next.sum(CHAR_LEN) / 4 || !next.hasQuietZoneAfter(QUIET_ZONE/13)) return {}; + Error error; int checksum = rawCodes.front(); for (int i = 1; i < Size(rawCodes) - 1; ++i) checksum += i * rawCodes[i]; // the last code is the checksum: if (checksum % 103 != rawCodes.back()) - return Result(DecodeStatus::ChecksumError); + error = ChecksumError(); int xStop = next.pixelsTillEnd(); - return Result(raw2txt.text(), rowNumber, xStart, xStop, BarcodeFormat::Code128, raw2txt.symbologyIdentifier(), + return Result(raw2txt.text(), rowNumber, xStart, xStop, BarcodeFormat::Code128, raw2txt.symbologyIdentifier(), error, std::move(rawCodes), raw2txt.readerInit(), raw2txt.applicationIndicator()); } diff --git a/core/src/oned/ODCode39Reader.cpp b/core/src/oned/ODCode39Reader.cpp index 469b61628c..cadaf784a2 100644 --- a/core/src/oned/ODCode39Reader.cpp +++ b/core/src/oned/ODCode39Reader.cpp @@ -7,6 +7,7 @@ #include "ODCode39Reader.h" #include "DecodeHints.h" +#include "DecoderResult.h" #include "Result.h" #include "ZXContainerAlgorithms.h" @@ -121,23 +122,24 @@ Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique if (Size(txt) < minCharCount - 2 || !next.hasQuietZoneAfter(QUIET_ZONE_SCALE)) return {}; + Error error; if (_validateCheckSum) { auto checkDigit = txt.back(); txt.pop_back(); int checksum = TransformReduce(txt, 0, [](char c) { return IndexOf(ALPHABET, c); }); if (checkDigit != ALPHABET[checksum % 43]) - return Result(DecodeStatus::ChecksumError); + error = ChecksumError(); } - if (_extendedMode && !DecodeExtendedCode39AndCode93(txt, "$%/+")) - return Result(DecodeStatus::FormatError); + if (!error && _extendedMode && !DecodeExtendedCode39AndCode93(txt, "$%/+")) + error = FormatError("Decoding extended Code39/Code93 failed"); // Symbology identifier modifiers ISO/IEC 16388:2007 Annex C Table C.1 constexpr const char symbologyModifiers[4] = { '0', '3' /*checksum*/, '4' /*extended*/, '7' /*checksum,extended*/ }; SymbologyIdentifier symbologyIdentifier = {'A', symbologyModifiers[(int)_extendedMode * 2 + (int)_validateCheckSum]}; int xStop = next.pixelsTillEnd(); - return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::Code39, symbologyIdentifier); + return Result(std::move(txt), rowNumber, xStart, xStop, BarcodeFormat::Code39, symbologyIdentifier, error); } } // namespace ZXing::OneD diff --git a/core/src/oned/ODCode93Reader.cpp b/core/src/oned/ODCode93Reader.cpp index 4cc499205d..a2668f9af6 100644 --- a/core/src/oned/ODCode93Reader.cpp +++ b/core/src/oned/ODCode93Reader.cpp @@ -114,20 +114,21 @@ Result Code93Reader::decodePattern(int rowNumber, PatternView& next, std::unique if (!next.isValid() || next[CHAR_LEN] > next.sum(CHAR_LEN) / 4 || !next.hasQuietZoneAfter(QUIET_ZONE_SCALE)) return {}; + Error error; if (!CheckChecksums(txt)) - return Result(DecodeStatus::ChecksumError); + error = ChecksumError(); // Remove checksum digits txt.resize(txt.size() - 2); - if (!DecodeExtendedCode39AndCode93(txt, "abcd")) - return Result(DecodeStatus::FormatError); + if (!error && !DecodeExtendedCode39AndCode93(txt, "abcd")) + error = FormatError("Decoding extended Code39/Code93 failed"); // Symbology identifier ISO/IEC 15424:2008 4.4.10 no modifiers SymbologyIdentifier symbologyIdentifier = {'G', '0'}; int xStop = next.pixelsTillEnd(); - return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::Code93, symbologyIdentifier); + return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::Code93, symbologyIdentifier, error); } } // namespace ZXing::OneD diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index be1c1247c1..fb89a596ef 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -341,7 +341,7 @@ Result DataBarExpandedReader::decodePattern(int rowNumber, PatternView& view, auto pairs = ReadRowOfPairs(view, rowNumber); if (pairs.empty() || !ChecksumIsValid(pairs)) - return Result(DecodeStatus::NotFound); + return {}; #else if (!state) state.reset(new DBERState); diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index 2e4db3f3f3..a251d65fe7 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -76,8 +76,9 @@ Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_pt if (!IsRightGuard(next, STOP_PATTERN_1, minQuietZone) && !IsRightGuard(next, STOP_PATTERN_2, minQuietZone)) return {}; + Error error; if (_validateCheckSum && !GTIN::IsCheckDigitValid(txt)) - return Result(DecodeStatus::ChecksumError); + error = ChecksumError(); // Symbology identifier ISO/IEC 16390:2007 Annex C Table C.1 // See also GS1 General Specifications 5.1.3 Figure 5.1.3-2 @@ -87,7 +88,7 @@ Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_pt symbologyIdentifier.modifier = '1'; // Modulo 10 symbol check character validated and transmitted int xStop = next.pixelsTillEnd(); - return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::ITF, symbologyIdentifier); + return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::ITF, symbologyIdentifier, error); } } // namespace ZXing::OneD diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index 22da7ad2cf..c1aa0e4104 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -275,8 +275,9 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u (_hints.hasFormat(BarcodeFormat::UPCE) && UPCE(res, begin)))) return {}; + Error error; if (!GTIN::IsCheckDigitValid(res.format == BarcodeFormat::UPCE ? UPCEANCommon::ConvertUPCEtoUPCA(res.txt) : res.txt)) - return Result(DecodeStatus::ChecksumError); + error = ChecksumError(); // If UPC-A was a requested format and we detected a EAN-13 code with a leading '0', then we drop the '0' and call it // a UPC-A code. @@ -309,7 +310,7 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u if (_hints.eanAddOnSymbol() == EanAddOnSymbol::Require && !addOnRes.isValid()) return {}; - return {res.txt, rowNumber, begin.pixelsInFront(), res.end.pixelsTillEnd(), res.format, symbologyIdentifier}; + return Result(res.txt, rowNumber, begin.pixelsInFront(), res.end.pixelsTillEnd(), res.format, symbologyIdentifier, error); } } // namespace ZXing::OneD diff --git a/core/src/pdf417/PDFDetector.h b/core/src/pdf417/PDFDetector.h index ab375ff1c1..d3cc9bdf79 100644 --- a/core/src/pdf417/PDFDetector.h +++ b/core/src/pdf417/PDFDetector.h @@ -17,7 +17,6 @@ namespace ZXing { class BitMatrix; class BinaryBitmap; -enum class DecodeStatus; namespace Pdf417 { From 61063aad85afb8f7ef610e48aa17d64344e228af Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 01:20:03 +0200 Subject: [PATCH 144/180] DecodeHints: deprecate unused `hasNoFormat` --- core/src/DecodeHints.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 558dfc57be..d8c76c16e5 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -146,7 +146,7 @@ class DecodeHints [[deprecated]] DecodeHints& setAssumeCode39CheckDigit(bool v) { return setValidateCode39CheckSum(v); } bool hasFormat(BarcodeFormats f) const noexcept { return _formats.testFlags(f) || _formats.empty(); } - bool hasNoFormat() const noexcept { return _formats.empty(); } + [[deprecated]] bool hasNoFormat() const noexcept { return _formats.empty(); } }; } // ZXing From 233a04a6d3ee72cd417469da10257600b057d553 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 01:21:37 +0200 Subject: [PATCH 145/180] Error: fix `returnErrors` behavior for DataMatrix --- core/src/Reader.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/Reader.h b/core/src/Reader.h index bdc8cd0b1e..716d26b94d 100644 --- a/core/src/Reader.h +++ b/core/src/Reader.h @@ -6,6 +6,7 @@ #pragma once +#include "DecodeHints.h" #include "Result.h" namespace ZXing { @@ -27,7 +28,7 @@ class Reader // WARNING: this API is experimental and may change/disappear virtual Results decode(const BinaryBitmap& image, [[maybe_unused]] int maxSymbols) const { auto res = decode(image); - return res.isValid() ? Results{std::move(res)} : Results{}; + return res.isValid() || (_hints.returnErrors() && res.format() != BarcodeFormat::None) ? Results{std::move(res)} : Results{}; } }; From bd6d69b5b3859fc12b2d5e5f6230bf42b8f44aa8 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 01:24:00 +0200 Subject: [PATCH 146/180] DataMatrix: pass already parsed version to `CodewordsFromBitMatrix` --- core/src/datamatrix/DMBitLayout.cpp | 10 +++------- core/src/datamatrix/DMBitLayout.h | 4 +++- core/src/datamatrix/DMDecoder.cpp | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/core/src/datamatrix/DMBitLayout.cpp b/core/src/datamatrix/DMBitLayout.cpp index 1acffdb662..c0b0cddc7c 100644 --- a/core/src/datamatrix/DMBitLayout.cpp +++ b/core/src/datamatrix/DMBitLayout.cpp @@ -170,15 +170,11 @@ static BitMatrix ExtractDataBits(const Version& version, const BitMatrix& bits) * * @return bytes encoded within the Data Matrix Code */ -ByteArray CodewordsFromBitMatrix(const BitMatrix& bits) +ByteArray CodewordsFromBitMatrix(const BitMatrix& bits, const Version& version) { - const Version* version = VersionForDimensionsOf(bits); - if (version == nullptr) - return {}; - - BitMatrix dataBits = ExtractDataBits(*version, bits); + BitMatrix dataBits = ExtractDataBits(version, bits); - ByteArray result(version->totalCodewords()); + ByteArray result(version.totalCodewords()); auto codeword = result.begin(); VisitMatrix(dataBits.height(), dataBits.width(), [&codeword, &dataBits](const BitPosArray& bitPos) { diff --git a/core/src/datamatrix/DMBitLayout.h b/core/src/datamatrix/DMBitLayout.h index 395ba23ffd..92f8e291f3 100644 --- a/core/src/datamatrix/DMBitLayout.h +++ b/core/src/datamatrix/DMBitLayout.h @@ -12,8 +12,10 @@ class ByteArray; namespace DataMatrix { +class Version; + BitMatrix BitMatrixFromCodewords(const ByteArray& codewords, int width, int height); -ByteArray CodewordsFromBitMatrix(const BitMatrix& bits); +ByteArray CodewordsFromBitMatrix(const BitMatrix& bits, const Version& version); } // namespace DataMatrix } // namespace ZXing diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 186d291ca8..75da4309ae 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -399,7 +399,7 @@ static DecoderResult DoDecode(const BitMatrix& bits) return FormatError(); // Read codewords - ByteArray codewords = CodewordsFromBitMatrix(bits); + ByteArray codewords = CodewordsFromBitMatrix(bits, *version); if (codewords.empty()) return FormatError(); From d2dc44b6facae5d6f9a5ac6fae2b978ebe860f34 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 01:26:05 +0200 Subject: [PATCH 147/180] Error: replace Error subclasses with macros, implementing tracing support Use `__FILE__` and `__LINE__` to report location of error. --- core/src/Error.h | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/core/src/Error.h b/core/src/Error.h index 4b63dd8952..6eeeb0f001 100644 --- a/core/src/Error.h +++ b/core/src/Error.h @@ -18,8 +18,16 @@ class Error Type type() const noexcept { return _type; } const std::string& msg() const noexcept { return _msg; } operator bool() const noexcept { return _type != Type::None; } + std::string location() const noexcept + { + return _file.empty() ? "" : _file.substr(_file.find_last_of("/\\") + 1) + ":" + std::to_string(_line); + } Error() = default; + Error(Type type, std::string msg = {}) : _type(type), _msg(std::move(msg)) {} + Error(std::string file, int line, Type type, std::string msg = {}) + : _type(type), _msg(std::move(msg)), _file(std::move(file)), _line(line) + {} static constexpr auto Format = Type::Format; static constexpr auto Checksum = Type::Checksum; @@ -28,8 +36,8 @@ class Error protected: Type _type = Type::None; std::string _msg; - - Error(Type type, std::string msg) : _type(type), _msg(std::move(msg)) {} + std::string _file; + int _line = -1; }; inline bool operator==(const Error& e, Error::Type t) noexcept { return e.type() == t; } @@ -37,17 +45,9 @@ inline bool operator!=(const Error& e, Error::Type t) noexcept { return !(e == t inline bool operator==(Error::Type t, const Error& e) noexcept { return e.type() == t; } inline bool operator!=(Error::Type t, const Error& e) noexcept { return !(t == e); } -class FormatError : public Error -{ -public: - FormatError(std::string msg = {}) : Error(Format, std::move(msg)) {} -}; - -class ChecksumError : public Error -{ -public: - ChecksumError(std::string msg = {}) : Error(Checksum, std::move(msg)) {} -}; +#define FormatError(...) Error(__FILE__, __LINE__, Error::Format, ##__VA_ARGS__) +#define ChecksumError(...) Error(__FILE__, __LINE__, Error::Checksum, ##__VA_ARGS__) +#define UnsupportedError(...) Error(__FILE__, __LINE__, Error::Unsupported, ##__VA_ARGS__) inline std::string ToString(const Error& e) { @@ -55,6 +55,8 @@ inline std::string ToString(const Error& e) std::string ret = name[static_cast(e.type())]; if (!e.msg().empty()) ret += " (" + e.msg() + ")"; + if (!e.location().empty()) + ret += " @ " + e.location(); return ret; } @@ -62,8 +64,8 @@ inline std::string ToString(const Error& e) inline Error Status2Error(DecodeStatus s) { switch (s) { - case DecodeStatus::FormatError: return FormatError(); - case DecodeStatus::ChecksumError: return ChecksumError(); + case DecodeStatus::FormatError: return Error(Error::Format); + case DecodeStatus::ChecksumError: return Error(Error::Checksum); default: return {}; } } From f2e9e5e73006759662deb9c2e5b3b1ddcb91fd8e Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 09:46:06 +0200 Subject: [PATCH 148/180] Error: add some `FormatError` messages and use `UnsupportedError` --- core/src/DecoderResult.h | 2 +- core/src/aztec/AZDecoder.cpp | 20 ++++----- core/src/datamatrix/DMDecoder.cpp | 41 +++++++++---------- core/src/maxicode/MCDecoder.cpp | 2 +- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 4 +- core/src/qrcode/QRDecoder.cpp | 37 ++++++++--------- test/unit/aztec/AZDecoderTest.cpp | 4 +- 7 files changed, 53 insertions(+), 57 deletions(-) diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 9340da4182..34822a7791 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -38,7 +38,7 @@ class DecoderResult public: DecoderResult() = default; - DecoderResult(Error error) : _error(error) {} + DecoderResult(Error error) : _error(std::move(error)) {} DecoderResult(ByteArray&& rawBytes, Content&& bytes) : _rawBytes(std::move(rawBytes)), _content(std::move(bytes)) { _numBits = 8 * Size(_rawBytes); diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index e5c5d34c78..1a71e0022f 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -143,12 +143,12 @@ static BitArray CorrectBits(const DetectorResult& ddata, const BitArray& rawbits int numECCodewords = numCodewords - numDataCodewords; if (numCodewords < numDataCodewords) - return {}; + throw FormatError("Invalid number of code words"); auto dataWords = ToInts(rawbits, codewordSize, numCodewords, Size(rawbits) % codewordSize); if (!ReedSolomonDecode(*gf, dataWords, numECCodewords)) - return {}; + throw ChecksumError(); // drop the ECCodewords from the dataWords array dataWords.resize(numDataCodewords); @@ -306,12 +306,12 @@ DecoderResult Decode(const BitArray& bits) try { DecodeContent(bits, res); - } catch (const std::out_of_range&) { // see ReadBits() + } catch (const std::exception&) { // see BitArrayView::readBits return FormatError(); } if (res.bytes.empty()) - return FormatError(); + return FormatError("Empty symbol content"); // Check for Structured Append - need 4 5-bit words, beginning with ML UL, ending with index and count bool haveStructuredAppend = Size(bits) > 20 && ToInt(bits, 0, 5) == 29 // latch to MIXED (from UPPER) @@ -348,12 +348,12 @@ DecoderResult Decode(const BitArray& bits) DecoderResult Decode(const DetectorResult& detectorResult) { - BitArray bits = CorrectBits(detectorResult, ExtractBits(detectorResult)); - - if (!bits.size()) - return FormatError(); - - return Decode(bits).setReaderInit(detectorResult.readerInit()); + try { + auto bits = CorrectBits(detectorResult, ExtractBits(detectorResult)); + return Decode(bits).setReaderInit(detectorResult.readerInit()); + } catch (Error e) { + return e; + } } } // namespace ZXing::Aztec diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 75da4309ae..a82440c5be 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -161,7 +161,7 @@ static void DecodeC40OrTextSegment(BitSource& bits, Content& result, Mode mode) else if (cValue < 40) // Size(BASIC_SET_CHARS) result.push_back(upperShift(BASIC_SET_CHARS[cValue])); else - throw std::runtime_error("invalid value in C40 or Text segment"); + throw FormatError("invalid value in C40 or Text segment"); break; case 1: result.push_back(upperShift(cValue)); break; case 2: @@ -170,7 +170,7 @@ static void DecodeC40OrTextSegment(BitSource& bits, Content& result, Mode mode) else if (cValue == 30) // Upper Shift upperShift.set = true; else - throw std::runtime_error("invalid value in C40 or Text segment"); + throw FormatError("invalid value in C40 or Text segment"); break; case 3: if (mode == Mode::C40) @@ -178,9 +178,9 @@ static void DecodeC40OrTextSegment(BitSource& bits, Content& result, Mode mode) else if (cValue < Size(TEXT_SHIFT3_SET_CHARS)) result.push_back(upperShift(TEXT_SHIFT3_SET_CHARS[cValue])); else - throw std::runtime_error("invalid value in C40 or Text segment"); + throw FormatError("invalid value in C40 or Text segment"); break; - default: throw std::runtime_error("invalid value in C40 or Text segment"); ; + default: throw FormatError("invalid value in C40 or Text segment"); ; } } } @@ -196,7 +196,7 @@ static void DecodeAnsiX12Segment(BitSource& bits, Content& result) // X12 segment terminator , separator *, sub-element separator >, space static const char segChars[4] = {'\r', '*', '>', ' '}; if (cValue < 0) - throw std::runtime_error("invalid value in AnsiX12 segment"); + throw FormatError("invalid value in AnsiX12 segment"); else if (cValue < 4) result.push_back(segChars[cValue]); else if (cValue < 14) // 0 - 9 @@ -204,7 +204,7 @@ static void DecodeAnsiX12Segment(BitSource& bits, Content& result) else if (cValue < 40) // A - Z result.push_back((char)(cValue + 51)); else - throw std::runtime_error("invalid value in AnsiX12 segment"); + throw FormatError("invalid value in AnsiX12 segment"); } } } @@ -262,7 +262,7 @@ static void DecodeBase256Segment(BitSource& bits, Content& result) // We're seeing NegativeArraySizeException errors from users. if (count < 0) - throw std::runtime_error("invalid count in Base256 segment"); + throw FormatError("invalid count in Base256 segment"); result.reserve(count); for (int i = 0; i < count; i++) { @@ -277,6 +277,7 @@ DecoderResult Decode(ByteArray&& bytes, const bool isDMRE) { BitSource bits(bytes); Content result; + Error error; result.symbology = {'d', '1', 3}; // ECC 200 (ISO 16022:2006 Annex N Table N.1) std::string resultTrailer; @@ -292,7 +293,7 @@ DecoderResult Decode(ByteArray&& bytes, const bool isDMRE) while (!done && bits.available() >= 8) { int oneByte = bits.readBits(8); switch (oneByte) { - case 0: throw std::runtime_error("invalid 0 code word"); + case 0: throw FormatError("invalid 0 code word"); case 129: done = true; break; // Pad -> we are done, ignore the rest of the bits case 230: DecodeC40OrTextSegment(bits, result, Mode::C40); break; case 231: DecodeBase256Segment(bits, result); break; @@ -309,13 +310,13 @@ DecoderResult Decode(ByteArray&& bytes, const bool isDMRE) break; case 233: // Structured Append if (!firstCodeword) // Must be first ISO 16022:2006 5.6.1 - throw std::runtime_error("structured append tag must be first code word"); + throw FormatError("structured append tag must be first code word"); ParseStructuredAppend(bits, sai); firstFNC1Position = 5; break; case 234: // Reader Programming if (!firstCodeword) // Must be first ISO 16022:2006 5.2.4.9 - throw std::runtime_error("reader programming tag must be first code word"); + throw FormatError("reader programming tag must be first code word"); readerInit = true; break; case 235: upperShift.set = true; break; // Upper Shift (shift to Extended ASCII) @@ -343,16 +344,13 @@ DecoderResult Decode(ByteArray&& bytes, const bool isDMRE) // work around encoders that use unlatch to ASCII as last code word (ask upstream) if (oneByte == 254 && bits.available() == 0) break; - throw std::runtime_error("invalid code word"); + throw FormatError("invalid code word"); } } firstCodeword = false; } - } catch (const std::exception& e) { -#ifndef NDEBUG - printf("DMDecoder error: %s\n", e.what()); -#endif - return FormatError(); + } catch (Error e) { + error = std::move(e); } result.append(resultTrailer); @@ -360,8 +358,9 @@ DecoderResult Decode(ByteArray&& bytes, const bool isDMRE) result.symbology.modifier += isDMRE * 6; return DecoderResult(std::move(bytes), std::move(result)) - .setStructuredAppend(sai) - .setReaderInit(readerInit); + .setError(std::move(error)) + .setStructuredAppend(sai) + .setReaderInit(readerInit); } } // namespace DecodedBitStreamParser @@ -396,17 +395,17 @@ static DecoderResult DoDecode(const BitMatrix& bits) // Construct a parser and read version, error-correction level const Version* version = VersionForDimensionsOf(bits); if (version == nullptr) - return FormatError(); + return FormatError("Invalid matrix dimension"); // Read codewords ByteArray codewords = CodewordsFromBitMatrix(bits, *version); if (codewords.empty()) - return FormatError(); + return FormatError("Invalid number of code words"); // Separate into data blocks std::vector dataBlocks = GetDataBlocks(codewords, *version); if (dataBlocks.empty()) - return FormatError(); + return FormatError("Invalid number of data blocks"); // Count total number of data bytes ByteArray resultBytes(TransformReduce(dataBlocks, 0, [](const auto& db) { return db.numDataCodewords; })); diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index cbf875ac71..058527cf19 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -325,7 +325,7 @@ DecoderResult Decode(const BitMatrix& bits) else return ChecksumError(); break; - default: return FormatError(); + default: return FormatError("Invalid mode"); } std::copy_n(codewords.begin(), 10, datawords.begin()); diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index f7f493d300..631af0b07d 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -728,12 +728,12 @@ DecodedBitStreamParser::Decode(const std::vector& codewords, int ecLevel) break; case LINKAGE_OTHER: // Allowed to treat as invalid by ISO/IEC 24723:2010 5.4.1.5 and 5.4.6.1 when in Basic Channel Mode - throw FormatError(); // TODO: add NotSupported error + throw UnsupportedError("LINKAGE_OTHER, see ISO/IEC 24723:2010 5.4.1.5"); break; default: if (code >= TEXT_COMPACTION_MODE_LATCH) { // Reserved codewords (all others in switch) // Allowed to treat as invalid by ISO/IEC 24723:2010 5.4.6.1 when in Basic Channel Mode - throw FormatError(); // TODO: add NotSupported error + throw UnsupportedError("TEXT_COMPACTION_MODE_LATCH, see ISO/IEC 24723:2010 5.4.6.1"); } else { // Default mode is Text Compaction mode Alpha sub-mode (ISO/IEC 15438:2015 5.4.2.1) codeIndex = TextCompaction(codewords, codeIndex - 1, result); diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 1af47dd5dc..cfb94a774e 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -176,7 +176,7 @@ static void DecodeNumericSegment(BitSource& bits, int count, Content& result) // Each 10 bits encodes three digits int threeDigitsBits = bits.readBits(10); if (threeDigitsBits >= 1000) - throw std::runtime_error("Invalid value in numeric segment"); + throw FormatError("Invalid value in numeric segment"); result += ToAlphaNumericChar(threeDigitsBits / 100); result += ToAlphaNumericChar((threeDigitsBits / 10) % 10); @@ -188,7 +188,7 @@ static void DecodeNumericSegment(BitSource& bits, int count, Content& result) // Two digits left over to read, encoded in 7 bits int twoDigitsBits = bits.readBits(7); if (twoDigitsBits >= 100) - throw std::runtime_error("Invalid value in numeric segment"); + throw FormatError("Invalid value in numeric segment"); result += ToAlphaNumericChar(twoDigitsBits / 10); result += ToAlphaNumericChar(twoDigitsBits % 10); @@ -196,7 +196,7 @@ static void DecodeNumericSegment(BitSource& bits, int count, Content& result) // One digit left over to read int digitBits = bits.readBits(4); if (digitBits >= 10) - throw std::runtime_error("Invalid value in numeric segment"); + throw FormatError("Invalid value in numeric segment"); result += ToAlphaNumericChar(digitBits); } @@ -219,7 +219,7 @@ static ECI ParseECIValue(BitSource& bits) int secondThirdBytes = bits.readBits(16); return ECI(((firstByte & 0x1F) << 16) | secondThirdBytes); } - throw std::runtime_error("ParseECIValue: invalid value"); + throw FormatError("ParseECIValue: invalid value"); } /** @@ -257,6 +257,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo { BitSource bits(bytes); Content result; + Error error; result.symbology = {'Q', '1', 1}; StructuredAppendInfo structuredAppend; const int modeBitLength = CodecModeBitsLength(version); @@ -273,13 +274,13 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo switch (mode) { case CodecMode::FNC1_FIRST_POSITION: // if (!result.empty()) // uncomment to enforce specification -// throw std::runtime_error("GS1 Indicator (FNC1 in first position) at illegal position"); +// throw FormatError("GS1 Indicator (FNC1 in first position) at illegal position"); result.symbology.modifier = '3'; result.applicationIndicator = "GS1"; // In Alphanumeric mode undouble doubled percents and treat single percent as break; case CodecMode::FNC1_SECOND_POSITION: if (!result.empty()) - throw std::runtime_error("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 = '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 < 10) // "00-09" @@ -289,7 +290,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222)) // "A-Za-z" result += static_cast(appInd - 100); else - throw std::runtime_error("Invalid AIM Application Indicator"); + throw FormatError("Invalid AIM Application Indicator"); result.applicationIndicator = result.bytes.asString(); // see also above break; case CodecMode::STRUCTURED_APPEND: @@ -307,7 +308,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo // 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 std::runtime_error("Unsupported HANZI subset"); + throw FormatError("Unsupported HANZI subset"); int count = bits.readBits(CharacterCountBits(mode, version)); DecodeHanziSegment(bits, count, result); break; @@ -321,22 +322,18 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo 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 std::runtime_error("Invalid CodecMode"); + default: throw FormatError("Invalid CodecMode"); } break; } } } - } - catch (const std::exception& e) - { -#ifndef NDEBUG - printf("QRDecoder error: %s\n", e.what()); -#endif - return FormatError(); + } catch (Error e) { + error = std::move(e); } return DecoderResult(std::move(bytes), std::move(result)) + .setError(std::move(error)) .setEcLevel(ToString(ecLevel)) .setStructuredAppend(structuredAppend); } @@ -345,22 +342,22 @@ DecoderResult Decode(const BitMatrix& bits) { const Version* pversion = ReadVersion(bits); if (!pversion) - return FormatError(); + return FormatError("Invalid version"); const Version& version = *pversion; auto formatInfo = ReadFormatInformation(bits, version.isMicroQRCode()); if (!formatInfo.isValid()) - return FormatError(); + return FormatError("Invalid format informatino"); // Read codewords ByteArray codewords = ReadCodewords(bits, version, formatInfo); if (codewords.empty()) - return FormatError(); + return FormatError("Failed to read codewords"); // Separate into data blocks std::vector dataBlocks = DataBlock::GetDataBlocks(codewords, version, formatInfo.ecLevel); if (dataBlocks.empty()) - return FormatError(); + return FormatError("Failed to get data blocks"); // Count total number of data bytes const auto op = [](auto totalBytes, const auto& dataBlock){ return totalBytes + dataBlock.numDataCodewords();}; diff --git a/test/unit/aztec/AZDecoderTest.cpp b/test/unit/aztec/AZDecoderTest.cpp index b1b7c5cc24..43f39c4872 100644 --- a/test/unit/aztec/AZDecoderTest.cpp +++ b/test/unit/aztec/AZDecoderTest.cpp @@ -96,7 +96,7 @@ TEST(AZDecoderTest, DecodeTooManyErrors) , 'X', true); DecoderResult result = parse(std::move(bits), true, 16, 4); - EXPECT_EQ(result.error(), Error::Format); + EXPECT_EQ(result.error(), Error::Checksum); } TEST(AZDecoderTest, DecodeTooManyErrors2) @@ -132,7 +132,7 @@ TEST(AZDecoderTest, DecodeTooManyErrors2) , 'X', true); DecoderResult result = parse(std::move(bits), true, 16, 4); - EXPECT_EQ(result.error(), Error::Format); + EXPECT_EQ(result.error(), Error::Checksum); } // Helper taking bit string to call GetEncodedData() From 5abaacf334f8944942a4740bffb6d917988ffe08 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 09:47:05 +0200 Subject: [PATCH 149/180] Error: make bool case explicit for now --- core/src/Error.h | 2 +- example/ZXingReader.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/Error.h b/core/src/Error.h index 6eeeb0f001..43ff156bf2 100644 --- a/core/src/Error.h +++ b/core/src/Error.h @@ -17,7 +17,7 @@ class Error enum class Type { None, Format, Checksum, Unsupported }; Type type() const noexcept { return _type; } const std::string& msg() const noexcept { return _msg; } - operator bool() const noexcept { return _type != Type::None; } + explicit operator bool() const noexcept { return _type != Type::None; } std::string location() const noexcept { return _file.empty() ? "" : _file.substr(_file.find_last_of("/\\") + 1) + ":" + std::to_string(_line); diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index f134809807..3532fb3280 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -172,7 +172,7 @@ int main(int argc, char* argv[]) for (auto&& result : results) { if (!outPath.empty()) - drawRect(image, result.position(), result.error()); + drawRect(image, result.position(), bool(result.error())); ret |= static_cast(result.error().type()); From a29c3174dcb02e1e46df334c4a303d5784672d58 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 09:47:36 +0200 Subject: [PATCH 150/180] Error: add unit tests --- test/unit/CMakeLists.txt | 1 + test/unit/ErrorTest.cpp | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 test/unit/ErrorTest.cpp diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 82503c0c90..871bac6f76 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable (UnitTest BitHacksTest.cpp CharacterSetECITest.cpp ContentTest.cpp + ErrorTest.cpp GTINTest.cpp GS1Test.cpp ReedSolomonTest.cpp diff --git a/test/unit/ErrorTest.cpp b/test/unit/ErrorTest.cpp new file mode 100644 index 0000000000..66eb6ac611 --- /dev/null +++ b/test/unit/ErrorTest.cpp @@ -0,0 +1,43 @@ +/* +* Copyright 2022 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "Error.h" + +#include "gtest/gtest.h" + +using namespace ZXing; + +TEST(ErrorTest, Default) +{ + Error e; + + EXPECT_FALSE(e); + EXPECT_EQ(e.type(), Error::Type::None); + EXPECT_EQ(e.msg().empty(), true); + EXPECT_EQ(e.location().empty(), true); +} + +TEST(ErrorTest, Empty) +{ + Error e = ChecksumError(); + + EXPECT_TRUE(e); + EXPECT_EQ(e.type(), Error::Type::Checksum); + EXPECT_EQ(e.type(), Error::Checksum); + EXPECT_EQ(e, Error::Checksum); + EXPECT_EQ(Error::Checksum, e); + EXPECT_EQ(e.msg().empty(), true); + EXPECT_EQ(e.location().empty(), false); +} + +TEST(ErrorTest, WithMsg) +{ + Error e = FormatError("something is wrong"); int line = __LINE__; + + EXPECT_TRUE(e); + EXPECT_EQ(e, Error::Format); + EXPECT_EQ(e.msg(), "something is wrong"); + EXPECT_EQ(e.location(), "ErrorTest.cpp:" + std::to_string(line)); +} From 430880c046fcf856c010c2017e5be46f2bacdc6f Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 10:13:21 +0200 Subject: [PATCH 151/180] ODReader: fix #350 (failed 1d symbol merging) --- core/src/Result.cpp | 3 +++ core/src/oned/ODReader.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 4a1daa6ab7..e7f102e663 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -135,6 +135,9 @@ bool Result::operator==(const Result& o) const if (BarcodeFormats(BarcodeFormat::TwoDCodes).testFlag(format())) return IsInside(Center(o.position()), position()); + // 1D comparisons only implemented for this->lineCount == 1 + assert(lineCount() == 1); + // if one line is less than half the length of the other away from the // latter, we consider it to belong to the same symbol auto dTop = maxAbsComponent(o.position().topLeft() - position().topLeft()); diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index afbd698485..4d5f2f374e 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -154,7 +154,7 @@ static Results DoDecode(const std::vector>& readers, // check if we know this code already for (auto& other : res) { - if (other == result) { + if (result == other) { // merge the position information auto dTop = maxAbsComponent(other.position().topLeft() - result.position().topLeft()); auto dBot = maxAbsComponent(other.position().bottomLeft() - result.position().topLeft()); From be97c41a479c0100fe1be799b2521ce9b7fe7c57 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 11:02:22 +0200 Subject: [PATCH 152/180] Error: fix compile regression on gcc-9 (##__VA_ARGS_ -> __VA_OPT__) --- core/src/Error.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/Error.h b/core/src/Error.h index 43ff156bf2..a405eb8fe9 100644 --- a/core/src/Error.h +++ b/core/src/Error.h @@ -45,9 +45,10 @@ inline bool operator!=(const Error& e, Error::Type t) noexcept { return !(e == t inline bool operator==(Error::Type t, const Error& e) noexcept { return e.type() == t; } inline bool operator!=(Error::Type t, const Error& e) noexcept { return !(t == e); } -#define FormatError(...) Error(__FILE__, __LINE__, Error::Format, ##__VA_ARGS__) -#define ChecksumError(...) Error(__FILE__, __LINE__, Error::Checksum, ##__VA_ARGS__) -#define UnsupportedError(...) Error(__FILE__, __LINE__, Error::Unsupported, ##__VA_ARGS__) +// see https://en.wikipedia.org/wiki/Variadic_macro_in_the_C_preprocessor#Support regarding __VA_OPT__ +#define FormatError(...) Error(__FILE__, __LINE__, Error::Format __VA_OPT__(,) __VA_ARGS__) +#define ChecksumError(...) Error(__FILE__, __LINE__, Error::Checksum __VA_OPT__(,) __VA_ARGS__) +#define UnsupportedError(...) Error(__FILE__, __LINE__, Error::Unsupported __VA_OPT__(,) __VA_ARGS__) inline std::string ToString(const Error& e) { From 53da58625dcd519b7c10e2349a83c9bd10105cf7 Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Mon, 4 Jul 2022 10:19:37 +0200 Subject: [PATCH 153/180] gcc13: add missing header file Include Fixes: #348 --- core/src/textcodec/JPTextEncoder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/textcodec/JPTextEncoder.cpp b/core/src/textcodec/JPTextEncoder.cpp index 26cf744d8f..d909ad666f 100644 --- a/core/src/textcodec/JPTextEncoder.cpp +++ b/core/src/textcodec/JPTextEncoder.cpp @@ -37,6 +37,8 @@ #include "JPTextEncoder.h" +#include + /* * This data is derived from Unicode 1.1, * JIS X 0208 (1990) to Unicode mapping table version 0.9 . From 991cbbd0ab26c02f192879846e597d4847477bee Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 11:23:01 +0200 Subject: [PATCH 154/180] Error: fix compile regression (2nd attempt, since wikipedia was wrong...) --- core/src/Error.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/Error.h b/core/src/Error.h index a405eb8fe9..0b7ee85351 100644 --- a/core/src/Error.h +++ b/core/src/Error.h @@ -45,10 +45,9 @@ inline bool operator!=(const Error& e, Error::Type t) noexcept { return !(e == t inline bool operator==(Error::Type t, const Error& e) noexcept { return e.type() == t; } inline bool operator!=(Error::Type t, const Error& e) noexcept { return !(t == e); } -// see https://en.wikipedia.org/wiki/Variadic_macro_in_the_C_preprocessor#Support regarding __VA_OPT__ -#define FormatError(...) Error(__FILE__, __LINE__, Error::Format __VA_OPT__(,) __VA_ARGS__) -#define ChecksumError(...) Error(__FILE__, __LINE__, Error::Checksum __VA_OPT__(,) __VA_ARGS__) -#define UnsupportedError(...) Error(__FILE__, __LINE__, Error::Unsupported __VA_OPT__(,) __VA_ARGS__) +#define FormatError(...) Error(__FILE__, __LINE__, Error::Format, std::string(__VA_ARGS__)) +#define ChecksumError(...) Error(__FILE__, __LINE__, Error::Checksum, std::string(__VA_ARGS__)) +#define UnsupportedError(...) Error(__FILE__, __LINE__, Error::Unsupported, std::string(__VA_ARGS__)) inline std::string ToString(const Error& e) { From ee75cdece23beeb983e5b1f9bc42641f65e53c1b Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 16:23:35 +0200 Subject: [PATCH 155/180] example: move OpenCV example from `ReadBarcode` to `ReadBarcodes` --- example/ZXingOpenCV.cpp | 6 +++--- example/ZXingOpenCV.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example/ZXingOpenCV.cpp b/example/ZXingOpenCV.cpp index d4c3e4c06f..21411cf779 100644 --- a/example/ZXingOpenCV.cpp +++ b/example/ZXingOpenCV.cpp @@ -21,9 +21,9 @@ int main() else while (waitKey(25) != 27) { cap >> image; - auto res = ReadBarcode(image); - if (res.isValid()) - DrawResult(image, res); + auto results = ReadBarcodes(image); + for (auto& r : results) + DrawResult(image, r); imshow("Display window", image); } diff --git a/example/ZXingOpenCV.h b/example/ZXingOpenCV.h index 8dbfb2545d..3987458b43 100644 --- a/example/ZXingOpenCV.h +++ b/example/ZXingOpenCV.h @@ -28,9 +28,9 @@ inline ZXing::ImageView ImageViewFromMat(const cv::Mat& image) return {image.data, image.cols, image.rows, fmt}; } -inline ZXing::Result ReadBarcode(const cv::Mat& image, const ZXing::DecodeHints& hints = {}) +inline ZXing::Results ReadBarcodes(const cv::Mat& image, const ZXing::DecodeHints& hints = {}) { - return ZXing::ReadBarcode(ImageViewFromMat(image), hints); + return ZXing::ReadBarcodes(ImageViewFromMat(image), hints); } inline void DrawResult(cv::Mat& img, ZXing::Result res) @@ -42,5 +42,5 @@ inline void DrawResult(cv::Mat& img, ZXing::Result res) int npts = contour.size(); cv::polylines(img, &pts, &npts, 1, true, CV_RGB(0, 255, 0)); - cv::putText(img, res.text(), cv::Point(10, 20), cv::FONT_HERSHEY_DUPLEX, 0.5, CV_RGB(0, 255, 0)); + cv::putText(img, res.text(), zx2cv(pos[3]) + cv::Point(0, 20), cv::FONT_HERSHEY_DUPLEX, 0.5, CV_RGB(0, 255, 0)); } From 080dd4a4bdac0121ff70dd5da2d844c467aee4bd Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 17:13:51 +0200 Subject: [PATCH 156/180] ImageView: include `ImageView.h` instead of `ReadBarcode.h` --- core/src/BinaryBitmap.h | 2 +- test/blackbox/ImageLoader.cpp | 2 +- test/blackbox/ImageLoader.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/BinaryBitmap.h b/core/src/BinaryBitmap.h index 0337d24abd..433e510aed 100644 --- a/core/src/BinaryBitmap.h +++ b/core/src/BinaryBitmap.h @@ -7,7 +7,7 @@ #pragma once -#include "ReadBarcode.h" +#include "ImageView.h" #include #include diff --git a/test/blackbox/ImageLoader.cpp b/test/blackbox/ImageLoader.cpp index 69d1d7c303..1f1f073421 100644 --- a/test/blackbox/ImageLoader.cpp +++ b/test/blackbox/ImageLoader.cpp @@ -7,7 +7,7 @@ #include "ImageLoader.h" #include "BinaryBitmap.h" -#include "ReadBarcode.h" +#include "ImageView.h" #include #include diff --git a/test/blackbox/ImageLoader.h b/test/blackbox/ImageLoader.h index 29469ce989..1d96e71c3d 100644 --- a/test/blackbox/ImageLoader.h +++ b/test/blackbox/ImageLoader.h @@ -5,7 +5,7 @@ #pragma once -#include "ReadBarcode.h" +#include "ImageView.h" #include "ZXFilesystem.h" namespace ZXing::Test::ImageLoader { From 244147c08ff1c1f1b7310652ce4403f85144dfe9 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 17:15:35 +0200 Subject: [PATCH 157/180] WASM: use `ReadBarcodes` internally (actually using `tryDownscale`) --- wrappers/wasm/BarcodeReader.cpp | 52 +++++++++++++-------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/wrappers/wasm/BarcodeReader.cpp b/wrappers/wasm/BarcodeReader.cpp index 7efee44486..2b9c0b46a0 100644 --- a/wrappers/wasm/BarcodeReader.cpp +++ b/wrappers/wasm/BarcodeReader.cpp @@ -23,7 +23,7 @@ struct ReadResult ZXing::Position position; }; -ReadResult readBarcodeFromImage(int bufferPtr, int bufferLength, bool tryHarder, std::string format) +ReadResult readBarcodeFromImageView(ZXing::ImageView iv, bool tryHarder, const std::string& format) { using namespace ZXing; try { @@ -32,18 +32,11 @@ ReadResult readBarcodeFromImage(int bufferPtr, int bufferLength, bool tryHarder, hints.setTryRotate(tryHarder); hints.setTryDownscale(tryHarder); hints.setFormats(BarcodeFormatsFromString(format)); + hints.setMaxNumberOfSymbols(1); - int width, height, channels; - std::unique_ptr buffer( - stbi_load_from_memory(reinterpret_cast(bufferPtr), bufferLength, &width, &height, - &channels, 4), - stbi_image_free); - if (buffer == nullptr) { - return { "", "", "Error loading image" }; - } - - auto result = ReadBarcode({buffer.get(), width, height, ImageFormat::RGBX}, hints); - if (result.isValid()) { + auto results = ReadBarcodes(iv, hints); + if (!results.empty()) { + auto& result = results.front(); return { ToString(result.format()), result.text(), "", result.position() }; } } @@ -56,30 +49,25 @@ ReadResult readBarcodeFromImage(int bufferPtr, int bufferLength, bool tryHarder, return {}; } -ReadResult readBarcodeFromPixmap(int bufferPtr, int imgWidth, int imgHeight, bool tryHarder, std::string format) +ReadResult readBarcodeFromImage(int bufferPtr, int bufferLength, bool tryHarder, std::string format) { using namespace ZXing; - try { - DecodeHints hints; - hints.setTryHarder(tryHarder); - hints.setTryRotate(tryHarder); - hints.setTryDownscale(tryHarder); - hints.setFormats(BarcodeFormatsFromString(format)); - auto result = - ReadBarcode({reinterpret_cast(bufferPtr), imgWidth, imgHeight, ImageFormat::RGBX}, hints); - - if (result.isValid()) { - return { ToString(result.format()), result.text(), "", result.position() }; - } + int width, height, channels; + std::unique_ptr buffer( + stbi_load_from_memory(reinterpret_cast(bufferPtr), bufferLength, &width, &height, &channels, 4), + stbi_image_free); + if (buffer == nullptr) { + return {"", "", "Error loading image"}; } - catch (const std::exception& e) { - return { "", "", e.what() }; - } - catch (...) { - return { "", "", "Unknown error" }; - } - return {}; + + return readBarcodeFromImageView({buffer.get(), width, height, ImageFormat::RGBX}, tryHarder, format); +} + +ReadResult readBarcodeFromPixmap(int bufferPtr, int imgWidth, int imgHeight, bool tryHarder, std::string format) +{ + using namespace ZXing; + return readBarcodeFromImageView({reinterpret_cast(bufferPtr), imgWidth, imgHeight, ImageFormat::RGBX}, tryHarder, format); } EMSCRIPTEN_BINDINGS(BarcodeReader) From e64432ca17e594fb806ec4233555db07dd7e3b23 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 17:40:18 +0200 Subject: [PATCH 158/180] fix build regression: missing include ( I'm getting sloppy :-( ) --- test/blackbox/TestReaderMain.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/blackbox/TestReaderMain.cpp b/test/blackbox/TestReaderMain.cpp index ebc4ed972e..22691a6864 100644 --- a/test/blackbox/TestReaderMain.cpp +++ b/test/blackbox/TestReaderMain.cpp @@ -6,6 +6,7 @@ #include "BlackboxTestRunner.h" #include "ImageLoader.h" +#include "ReadBarcode.h" #include "ZXContainerAlgorithms.h" #include "ZXFilesystem.h" From db1cc6129ddab042ab1417e5d53074691f6dcf58 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 17:55:28 +0200 Subject: [PATCH 159/180] android: use `ReadBarcodes` and remove Result::status() usage --- .../android/zxingcpp/src/main/cpp/BarcodeReader.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp index 571434cff1..8afd7a3d51 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp +++ b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp @@ -104,12 +104,10 @@ jstring Read(JNIEnv *env, ImageView image, jstring formats, jboolean tryHarder, .setTryHarder(tryHarder) .setTryRotate( tryRotate ) .setTryDownscale(tryDownscale) - .setMaxNumberOfSymbols(1); // see ReadBarcode implementation - -// return C2JString(env, ToString(DecodeStatus::NotFound)); + .setMaxNumberOfSymbols(1); auto startTime = std::chrono::high_resolution_clock::now(); - auto res = ReadBarcode(image, hints); + auto results = ReadBarcodes(image, hints); auto duration = std::chrono::high_resolution_clock::now() - startTime; // LOGD("time: %4d ms\n", (int)std::chrono::duration_cast(duration).count()); @@ -119,7 +117,8 @@ jstring Read(JNIEnv *env, ImageView image, jstring formats, jboolean tryHarder, auto time = std::to_wstring(std::chrono::duration_cast(duration).count()); env->SetObjectField(result, fidTime, C2JString(env, time)); - if (res.isValid()) { + if (!results.empty()) { + auto& res = results.front(); jbyteArray jByteArray = env->NewByteArray(res.bytes().size()); env->SetByteArrayRegion(jByteArray, 0, res.bytes().size(), (jbyte*)res.bytes().data()); jfieldID fidBytes = env->GetFieldID(clResult, "bytes", "[B"); @@ -145,7 +144,7 @@ jstring Read(JNIEnv *env, ImageView image, jstring formats, jboolean tryHarder, return C2JString(env, JavaBarcodeFormatName(res.format())); } else - return C2JString(env, ToString(res.status())); + return C2JString(env, "NotFound"); } catch (const std::exception& e) { return ThrowJavaException(env, e.what()); } catch (...) { From 544fa75175dc82f79a48724d3f2a30834f88ad8a Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 18:39:44 +0200 Subject: [PATCH 160/180] BarcodeFormat: deprecate `OneDCodes` and `TwoDCodes` The ISO specifications use the terms 'linear symbology' and 'matrix symbology'. So we are using the new names `LinearCodes` and `MatrixCodes`, which are also nicer to read than the old. --- core/src/BarcodeFormat.cpp | 4 ++-- core/src/BarcodeFormat.h | 8 +++++--- core/src/MultiFormatReader.cpp | 4 ++-- core/src/Result.cpp | 2 +- example/ZXingQt5CamReader.qml | 6 +++--- example/ZXingQt6CamReader.qml | 6 +++--- example/ZXingQtReader.h | 4 ++-- wrappers/python/zxing.cpp | 6 ++++-- 8 files changed, 22 insertions(+), 18 deletions(-) diff --git a/core/src/BarcodeFormat.cpp b/core/src/BarcodeFormat.cpp index 232bcb854f..0bc05be826 100644 --- a/core/src/BarcodeFormat.cpp +++ b/core/src/BarcodeFormat.cpp @@ -42,8 +42,8 @@ static BarcodeFormatName NAMES[] = { {BarcodeFormat::QRCode, "QRCode"}, {BarcodeFormat::UPCA, "UPC-A"}, {BarcodeFormat::UPCE, "UPC-E"}, - {BarcodeFormat::OneDCodes, "1D-Codes"}, - {BarcodeFormat::TwoDCodes, "2D-Codes"}, + {BarcodeFormat::LinearCodes, "Linear-Codes"}, + {BarcodeFormat::MatrixCodes, "Matrix-Codes"}, }; const char* ToString(BarcodeFormat format) diff --git a/core/src/BarcodeFormat.h b/core/src/BarcodeFormat.h index 186e52eee6..b12e332a77 100644 --- a/core/src/BarcodeFormat.h +++ b/core/src/BarcodeFormat.h @@ -39,9 +39,9 @@ enum class BarcodeFormat UPCE = (1 << 15), ///< UPC-E (1D) MicroQRCode = (1 << 16), ///< Micro QR Code (2D) - OneDCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, - TwoDCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, - Any = OneDCodes | TwoDCodes, + LinearCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, + MatrixCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, + Any = LinearCodes | MatrixCodes, // Deprecated names, kept for compatibility at the moment NONE [[deprecated]] = None, @@ -60,6 +60,8 @@ enum class BarcodeFormat RSS_EXPANDED [[deprecated]] = DataBarExpanded, UPC_A [[deprecated]] = UPCA, UPC_E [[deprecated]] = UPCE, + OneDCodes [[deprecated]] = LinearCodes, + TwoDCodes [[deprecated]] = MatrixCodes, _max = MicroQRCode, ///> implementation detail, don't use }; diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index d52e91d2af..442dd64488 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -24,7 +24,7 @@ MultiFormatReader::MultiFormatReader(const DecodeHints& hints) : _hints(hints) auto formats = hints.formats().empty() ? BarcodeFormat::Any : hints.formats(); // Put 1D readers upfront in "normal" mode - if (formats.testFlags(BarcodeFormat::OneDCodes) && !hints.tryHarder()) + if (formats.testFlags(BarcodeFormat::LinearCodes) && !hints.tryHarder()) _readers.emplace_back(new OneD::Reader(hints)); if (formats.testFlags(BarcodeFormat::QRCode | BarcodeFormat::MicroQRCode)) @@ -39,7 +39,7 @@ MultiFormatReader::MultiFormatReader(const DecodeHints& hints) : _hints(hints) _readers.emplace_back(new MaxiCode::Reader(hints)); // At end in "try harder" mode - if (formats.testFlags(BarcodeFormat::OneDCodes) && hints.tryHarder()) + if (formats.testFlags(BarcodeFormat::LinearCodes) && hints.tryHarder()) _readers.emplace_back(new OneD::Reader(hints)); } diff --git a/core/src/Result.cpp b/core/src/Result.cpp index e7f102e663..74c177cbc9 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -132,7 +132,7 @@ bool Result::operator==(const Result& o) const if (!(format() == o.format() && (bytes() == o.bytes() || error() || o.error()))) return false; - if (BarcodeFormats(BarcodeFormat::TwoDCodes).testFlag(format())) + if (BarcodeFormats(BarcodeFormat::MatrixCodes).testFlag(format())) return IsInside(Center(o.position()), position()); // 1D comparisons only implemented for this->lineCount == 1 diff --git a/example/ZXingQt5CamReader.qml b/example/ZXingQt5CamReader.qml index bf3f8d3ed2..467bb18a63 100644 --- a/example/ZXingQt5CamReader.qml +++ b/example/ZXingQt5CamReader.qml @@ -28,7 +28,7 @@ Window { BarcodeReader { id: barcodeReader - formats: (oneDSwitch.checked ? (ZXing.OneDCodes) : ZXing.None) | (twoDSwitch.checked ? (ZXing.TwoDCodes) : ZXing.None) + formats: (linearSwitch.checked ? (ZXing.LinearCodes) : ZXing.None) | (matrixSwitch.checked ? (ZXing.MatrixCodes) : ZXing.None) tryRotate: tryRotateSwitch.checked tryHarder: tryHarderSwitch.checked tryDownscale: tryDownscaleSwitch.checked @@ -139,8 +139,8 @@ Window { Switch {id: tryRotateSwitch; text: qsTr("Try Rotate"); checked: true } Switch {id: tryHarderSwitch; text: qsTr("Try Harder"); checked: true } Switch {id: tryDownscaleSwitch; text: qsTr("Try Downscale"); checked: true } - Switch {id: oneDSwitch; text: qsTr("1D Codes"); checked: true } - Switch {id: twoDSwitch; text: qsTr("2D Codes"); checked: true } + Switch {id: linearSwitch; text: qsTr("Linear Codes"); checked: true } + Switch {id: matrixSwitch; text: qsTr("Matrix Codes"); checked: true } } } } diff --git a/example/ZXingQt6CamReader.qml b/example/ZXingQt6CamReader.qml index 7c80e77855..9234ae1882 100644 --- a/example/ZXingQt6CamReader.qml +++ b/example/ZXingQt6CamReader.qml @@ -29,7 +29,7 @@ Window { id: barcodeReader videoSink: videoOutput.videoSink - formats: (oneDSwitch.checked ? (ZXing.OneDCodes) : ZXing.None) | (twoDSwitch.checked ? (ZXing.TwoDCodes) : ZXing.None) + formats: (linearSwitch.checked ? (ZXing.LinearCodes) : ZXing.None) | (matrixSwitch.checked ? (ZXing.MatrixCodes) : ZXing.None) tryRotate: tryRotateSwitch.checked tryHarder: tryHarderSwitch.checked tryDownscale: tryDownscaleSwitch.checked @@ -140,8 +140,8 @@ Window { Switch {id: tryRotateSwitch; text: qsTr("Try Rotate"); checked: true } Switch {id: tryHarderSwitch; text: qsTr("Try Harder"); checked: true } Switch {id: tryDownscaleSwitch; text: qsTr("Try Downscale"); checked: true } - Switch {id: oneDSwitch; text: qsTr("1D Codes"); checked: true } - Switch {id: twoDSwitch; text: qsTr("2D Codes"); checked: true } + Switch {id: linearSwitch; text: qsTr("Linear Codes"); checked: true } + Switch {id: matrixSwitch; text: qsTr("Matrix Codes"); checked: true } } } } diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index c2155dff9e..a43453c9aa 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -54,8 +54,8 @@ enum class BarcodeFormat UPCE = (1 << 15), ///< UPC-E (1D) MicroQRCode = (1 << 16), ///< Micro QR Code (2D) - OneDCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, - TwoDCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, + LinearCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, + MatrixCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, }; enum class ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI }; diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index 862d024947..b00c47d2bb 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -150,8 +150,10 @@ PYBIND11_MODULE(zxingcpp, m) .value("UPCE", BarcodeFormat::UPCE) // use upper case 'NONE' because 'None' is a reserved identifier in python .value("NONE", BarcodeFormat::None) - .value("OneDCodes", BarcodeFormat::OneDCodes) - .value("TwoDCodes", BarcodeFormat::TwoDCodes) + .value("OneDCodes", BarcodeFormat::LinearCodes) // deprecated, will be removed + .value("TwoDCodes", BarcodeFormat::MatrixCodes) // deprecated, will be removed + .value("LinearCodes", BarcodeFormat::LinearCodes) + .value("MatrixCodes", BarcodeFormat::MatrixCodes) .export_values() // see https://github.com/pybind/pybind11/issues/2221 .def("__or__", [](BarcodeFormat f1, BarcodeFormat f2){ return f1 | f2; }); From 725047ebeb3e2286b233bc473586d1a24885c6f0 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 4 Jul 2022 19:13:59 +0200 Subject: [PATCH 161/180] README: refer to `ReadBarcodes` and use new "Linear"/"Matrix" terminolgy --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ddaf4a862d..0508b63cac 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # ZXing-C++ -ZXing-C++ ("zebra crossing") is an open-source, multi-format 1D/2D barcode image processing library implemented in C++. +ZXing-C++ ("zebra crossing") is an open-source, multi-format linear/matrix barcode image processing library implemented in C++. It was originally ported from the Java [ZXing Library](https://github.com/zxing/zxing) but has been developed further and now includes many improvements in terms of quality and performance. It can both read and write barcodes in a number of formats. @@ -18,14 +18,14 @@ It was originally ported from the Java [ZXing Library](https://github.com/zxing/ ## Supported Formats -| 1D product | 1D industrial | 2D -| ---------- | ----------------- | -------------- -| UPC-A | Code 39 | QR Code -| UPC-E | Code 93 | Micro QR Code -| EAN-8 | Code 128 | Aztec -| EAN-13 | Codabar | DataMatrix -| DataBar | ITF | PDF417 -| | DataBar Expanded | MaxiCode (beta) +| Linear product | Linear industrial | Matrix | +|----------------|-------------------|--------------------| +| UPC-A | Code 39 | QR Code | +| UPC-E | Code 93 | Micro QR Code | +| EAN-8 | Code 128 | Aztec | +| EAN-13 | Codabar | DataMatrix | +| DataBar | DataBar Exanded | PDF417 | +| | ITF | MaxiCode (partial) | Note: DataBar used to be called RSS. DataBar is not supported for writing. @@ -34,7 +34,7 @@ Note: DataBar used to be called RSS. DataBar is not supported for writing. ### To read barcodes: As an example, have a look at [`ZXingReader.cpp`](example/ZXingReader.cpp). 1. Load your image into memory (3rd-party library required). -2. Call `ReadBarcode()` from [`ReadBarcode.h`](core/src/ReadBarcode.h), the simplest API to get a `Result`. +2. Call `ReadBarcodes()` from [`ReadBarcode.h`](core/src/ReadBarcode.h), the simplest API to get a list of `Result` objects. ### To write barcodes: As an example, have a look at [`ZXingWriter.cpp`](example/ZXingWriter.cpp). From e0592197f7dae0a0c9755a925d04f355abb4c634 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 00:55:19 +0200 Subject: [PATCH 162/180] BarcodeFormat: replace or remove 1D/2D terms in comments --- core/src/BarcodeFormat.h | 30 +++++++++++++++--------------- core/src/DecodeHints.h | 6 +++--- core/src/MultiFormatReader.cpp | 2 +- core/src/Result.cpp | 2 +- core/src/Result.h | 4 ++-- example/ZXingQtReader.h | 30 +++++++++++++++--------------- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/core/src/BarcodeFormat.h b/core/src/BarcodeFormat.h index b12e332a77..01263c375a 100644 --- a/core/src/BarcodeFormat.h +++ b/core/src/BarcodeFormat.h @@ -21,23 +21,23 @@ enum class BarcodeFormat // would not have been necessary to explicitly set the values to single bit constants. This has been done to ease // the interoperability with C-like interfaces, the python and the Qt wrapper. None = 0, ///< Used as a return value if no valid barcode has been detected - Aztec = (1 << 0), ///< Aztec (2D) - Codabar = (1 << 1), ///< Codabar (1D) - Code39 = (1 << 2), ///< Code39 (1D) - Code93 = (1 << 3), ///< Code93 (1D) - Code128 = (1 << 4), ///< Code128 (1D) + Aztec = (1 << 0), ///< Aztec + Codabar = (1 << 1), ///< Codabar + Code39 = (1 << 2), ///< Code39 + Code93 = (1 << 3), ///< Code93 + Code128 = (1 << 4), ///< Code128 DataBar = (1 << 5), ///< GS1 DataBar, formerly known as RSS 14 DataBarExpanded = (1 << 6), ///< GS1 DataBar Expanded, formerly known as RSS EXPANDED - DataMatrix = (1 << 7), ///< DataMatrix (2D) - EAN8 = (1 << 8), ///< EAN-8 (1D) - EAN13 = (1 << 9), ///< EAN-13 (1D) - ITF = (1 << 10), ///< ITF (Interleaved Two of Five) (1D) - MaxiCode = (1 << 11), ///< MaxiCode (2D) - PDF417 = (1 << 12), ///< PDF417 (1D) or (2D) - QRCode = (1 << 13), ///< QR Code (2D) - UPCA = (1 << 14), ///< UPC-A (1D) - UPCE = (1 << 15), ///< UPC-E (1D) - MicroQRCode = (1 << 16), ///< Micro QR Code (2D) + DataMatrix = (1 << 7), ///< DataMatrix + EAN8 = (1 << 8), ///< EAN-8 + EAN13 = (1 << 9), ///< EAN-13 + ITF = (1 << 10), ///< ITF (Interleaved Two of Five) + MaxiCode = (1 << 11), ///< MaxiCode + PDF417 = (1 << 12), ///< PDF417 + QRCode = (1 << 13), ///< QR Code + UPCA = (1 << 14), ///< UPC-A + UPCE = (1 << 15), ///< UPC-E + MicroQRCode = (1 << 16), ///< Micro QR Code LinearCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, MatrixCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index d8c76c16e5..05d051cd96 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -24,8 +24,8 @@ namespace ZXing { */ enum class Binarizer : unsigned char // needs to unsigned for the bitfield below to work, uint8_t fails as well { - LocalAverage, ///< T = average of neighboring pixels for 2D and GlobalHistogram for 1D (HybridBinarizer) - GlobalHistogram, ///< T = valley between the 2 largest peaks in the histogram (per line in 1D case) + LocalAverage, ///< T = average of neighboring pixels for matrix and GlobalHistogram for linear (HybridBinarizer) + GlobalHistogram, ///< T = valley between the 2 largest peaks in the histogram (per line in linear case) FixedThreshold, ///< T = 127 BoolCast, ///< T = 0, fastest possible }; @@ -105,7 +105,7 @@ class DecodeHints // WARNING: this API is experimental and may change/disappear ZX_PROPERTY(uint8_t, downscaleFactor, setDownscaleFactor) - /// The number of scan lines in a 1D barcode that have to be equal to accept the result, default is 2 + /// The number of scan lines in a linear barcode that have to be equal to accept the result, default is 2 ZX_PROPERTY(uint8_t, minLineCount, setMinLineCount) /// The maximum number of symbols (barcodes) to detect / look for in the image with ReadBarcodes diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index 442dd64488..f445232a47 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -23,7 +23,7 @@ MultiFormatReader::MultiFormatReader(const DecodeHints& hints) : _hints(hints) { auto formats = hints.formats().empty() ? BarcodeFormat::Any : hints.formats(); - // Put 1D readers upfront in "normal" mode + // Put linear readers upfront in "normal" mode if (formats.testFlags(BarcodeFormat::LinearCodes) && !hints.tryHarder()) _readers.emplace_back(new OneD::Reader(hints)); diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 74c177cbc9..d406f975b3 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -135,7 +135,7 @@ bool Result::operator==(const Result& o) const if (BarcodeFormats(BarcodeFormat::MatrixCodes).testFlag(format())) return IsInside(Center(o.position()), position()); - // 1D comparisons only implemented for this->lineCount == 1 + // linear symbology comparisons only implemented for this->lineCount == 1 assert(lineCount() == 1); // if one line is less than half the length of the other away from the diff --git a/core/src/Result.h b/core/src/Result.h index 6c7ed47da1..cd6397944d 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -33,7 +33,7 @@ class Result Result() = default; explicit Result(DecodeStatus status); - // 1D convenience constructor + // linear symbology convenience constructor Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, Error error = {}, ByteArray&& rawBytes = {}, bool readerInit = false, const std::string& ai = {}); @@ -145,7 +145,7 @@ class Result bool readerInit() const { return _readerInit; } /** - * @brief How many lines have been detected with this code (applies only to 1D symbologies) + * @brief How many lines have been detected with this code (applies only to linear symbologies) */ int lineCount() const { return _lineCount; } diff --git a/example/ZXingQtReader.h b/example/ZXingQtReader.h index a43453c9aa..a181a4b1a3 100644 --- a/example/ZXingQtReader.h +++ b/example/ZXingQtReader.h @@ -36,23 +36,23 @@ Q_NAMESPACE enum class BarcodeFormat { None = 0, ///< Used as a return value if no valid barcode has been detected - Aztec = (1 << 0), ///< Aztec (2D) - Codabar = (1 << 1), ///< Codabar (1D) - Code39 = (1 << 2), ///< Code39 (1D) - Code93 = (1 << 3), ///< Code93 (1D) - Code128 = (1 << 4), ///< Code128 (1D) + Aztec = (1 << 0), ///< Aztec + Codabar = (1 << 1), ///< Codabar + Code39 = (1 << 2), ///< Code39 + Code93 = (1 << 3), ///< Code93 + Code128 = (1 << 4), ///< Code128 DataBar = (1 << 5), ///< GS1 DataBar, formerly known as RSS 14 DataBarExpanded = (1 << 6), ///< GS1 DataBar Expanded, formerly known as RSS EXPANDED - DataMatrix = (1 << 7), ///< DataMatrix (2D) - EAN8 = (1 << 8), ///< EAN-8 (1D) - EAN13 = (1 << 9), ///< EAN-13 (1D) - ITF = (1 << 10), ///< ITF (Interleaved Two of Five) (1D) - MaxiCode = (1 << 11), ///< MaxiCode (2D) - PDF417 = (1 << 12), ///< PDF417 (1D) or (2D) - QRCode = (1 << 13), ///< QR Code (2D) - UPCA = (1 << 14), ///< UPC-A (1D) - UPCE = (1 << 15), ///< UPC-E (1D) - MicroQRCode = (1 << 16), ///< Micro QR Code (2D) + DataMatrix = (1 << 7), ///< DataMatrix + EAN8 = (1 << 8), ///< EAN-8 + EAN13 = (1 << 9), ///< EAN-13 + ITF = (1 << 10), ///< ITF (Interleaved Two of Five) + MaxiCode = (1 << 11), ///< MaxiCode + PDF417 = (1 << 12), ///< PDF417 or + QRCode = (1 << 13), ///< QR Code + UPCA = (1 << 14), ///< UPC-A + UPCE = (1 << 15), ///< UPC-E + MicroQRCode = (1 << 16), ///< Micro QR Code LinearCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, MatrixCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode, From c8c5d1853d96a365bec26f68c689f8057c84d3e8 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 00:56:41 +0200 Subject: [PATCH 163/180] DecodeHints: deprecate unused `allowedLengths` property --- core/src/DecodeHints.h | 8 ++++---- core/src/oned/ODITFReader.cpp | 11 +---------- core/src/oned/ODITFReader.h | 8 +++----- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 05d051cd96..6f0343edab 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -52,7 +52,6 @@ class DecodeHints EanAddOnSymbol _eanAddOnSymbol : 2; std::string _characterSet; - std::vector _allowedLengths; BarcodeFormats _formats = BarcodeFormat::None; uint16_t _downscaleThreshold = 500; uint8_t _downscaleFactor = 3; @@ -114,9 +113,6 @@ class DecodeHints /// Specifies fallback character set to use instead of auto-detecting it (when applicable) ZX_PROPERTY(std::string, characterSet, setCharacterSet) - /// Allowed lengths of encoded data -- reject anything else.. - ZX_PROPERTY(std::vector, allowedLengths, setAllowedLengths) - /// If true, the Code-39 reader will try to read extended mode. ZX_PROPERTY(bool, tryCode39ExtendedMode, setTryCode39ExtendedMode) @@ -141,6 +137,10 @@ class DecodeHints [[deprecated]] bool assumeGS1() const noexcept { return true; } [[deprecated]] DecodeHints& setAssumeGS1(bool v [[maybe_unused]]) { return *this; } + /// NOTE: has not been in effect since at least 1.2 and no one noticed. + [[deprecated]] std::vector allowedLengths() const noexcept { return {}; } + [[deprecated]] DecodeHints& setAllowedLengths(const std::vector v [[maybe_unused]]) { return *this; } + /// NOTE: use validateCode39CheckSum [[deprecated]] bool assumeCode39CheckDigit() const noexcept { return validateCode39CheckSum(); } [[deprecated]] DecodeHints& setAssumeCode39CheckDigit(bool v) { return setValidateCode39CheckSum(v); } diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index a251d65fe7..f7c1805815 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -15,16 +15,7 @@ namespace ZXing::OneD { -/** Valid ITF lengths. Anything longer than the largest value is also allowed. */ -constexpr auto DEFAULT_ALLOWED_LENGTHS = { 6, 8, 10, 12, 14 }; - -ITFReader::ITFReader(const DecodeHints& hints) : - _allowedLengths(hints.allowedLengths()), - _validateCheckSum(hints.validateITFCheckSum()) -{ - if (_allowedLengths.empty()) - _allowedLengths.assign(DEFAULT_ALLOWED_LENGTHS.begin(), DEFAULT_ALLOWED_LENGTHS.end()); -} +ITFReader::ITFReader(const DecodeHints& hints) : _validateCheckSum(hints.validateITFCheckSum()) {} constexpr auto START_PATTERN_ = FixedPattern<4, 4>{1, 1, 1, 1}; constexpr auto STOP_PATTERN_1 = FixedPattern<3, 4>{2, 1, 1}; diff --git a/core/src/oned/ODITFReader.h b/core/src/oned/ODITFReader.h index 0277c52a7d..c3bf45fba8 100644 --- a/core/src/oned/ODITFReader.h +++ b/core/src/oned/ODITFReader.h @@ -20,11 +20,10 @@ namespace OneD { *

Implements decoding of the ITF format, or Interleaved Two of Five.

* *

This Reader will scan ITF barcodes of certain lengths only. -* At the moment it reads length 6, 8, 10, 12, 14, 16, 18, 20, 24, and 44 as these have appeared "in the wild". Not all -* lengths are scanned, especially shorter ones, to avoid false positives. This in turn is due to a lack of -* required checksum function.

+* At the moment it reads length >= 6. Not all lengths are scanned, especially shorter ones, to avoid false positives. +* This in turn is due to a lack of required checksum function.

* -*

The checksum is optional and is only applied by this Reader if the validateITFCheckSum hint is given.

+*

The checksum is optional and is only checked if the validateITFCheckSum hint is given.

* *

http://en.wikipedia.org/wiki/Interleaved_2_of_5 * is a great reference for Interleaved 2 of 5 information.

@@ -36,7 +35,6 @@ class ITFReader : public RowReader Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; private: - std::vector _allowedLengths; bool _validateCheckSum; }; From 51217a10d5d379ec284e96be376ef9f16044d61e Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 01:20:41 +0200 Subject: [PATCH 164/180] DecodeHints: optimize struct layout to reduce size --- core/src/DecodeHints.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 6f0343edab..841bb24255 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -48,15 +48,15 @@ class DecodeHints bool _validateITFCheckSum : 1; bool _returnCodabarStartEnd : 1; bool _returnErrors : 1; - Binarizer _binarizer : 2; EanAddOnSymbol _eanAddOnSymbol : 2; + Binarizer _binarizer : 2; - std::string _characterSet; - BarcodeFormats _formats = BarcodeFormat::None; - uint16_t _downscaleThreshold = 500; - uint8_t _downscaleFactor = 3; uint8_t _minLineCount = 2; uint8_t _maxNumberOfSymbols = 0xff; + uint8_t _downscaleFactor = 3; + uint16_t _downscaleThreshold = 500; + BarcodeFormats _formats = BarcodeFormat::None; + std::string _characterSet; public: // bitfields don't get default initialized to 0 before c++20 @@ -70,8 +70,8 @@ class DecodeHints _validateITFCheckSum(0), _returnCodabarStartEnd(0), _returnErrors(0), - _binarizer(Binarizer::LocalAverage), - _eanAddOnSymbol(EanAddOnSymbol::Ignore) + _eanAddOnSymbol(EanAddOnSymbol::Ignore), + _binarizer(Binarizer::LocalAverage) {} #define ZX_PROPERTY(TYPE, GETTER, SETTER) \ From 4afba9ba0def489437874f8a5cf7e4d0140041db Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 01:28:20 +0200 Subject: [PATCH 165/180] DecodeHints: cleanup matrix reader headers --- core/src/aztec/AZReader.h | 11 ++--------- core/src/datamatrix/DMReader.h | 10 ++-------- core/src/maxicode/MCReader.h | 11 ++--------- core/src/pdf417/PDFReader.h | 10 ++-------- core/src/qrcode/QRReader.h | 11 ++--------- 5 files changed, 10 insertions(+), 43 deletions(-) diff --git a/core/src/aztec/AZReader.h b/core/src/aztec/AZReader.h index c0bab4fea4..0b8c2a6a29 100644 --- a/core/src/aztec/AZReader.h +++ b/core/src/aztec/AZReader.h @@ -8,13 +8,7 @@ #include "Reader.h" -#include - -namespace ZXing { - -class DecodeHints; - -namespace Aztec { +namespace ZXing::Aztec { class Reader : public ZXing::Reader { @@ -24,5 +18,4 @@ class Reader : public ZXing::Reader Result decode(const BinaryBitmap& image) const override; }; -} // Aztec -} // ZXing +} // namespace ZXing::Aztec diff --git a/core/src/datamatrix/DMReader.h b/core/src/datamatrix/DMReader.h index 1d6706072c..62f67142e1 100644 --- a/core/src/datamatrix/DMReader.h +++ b/core/src/datamatrix/DMReader.h @@ -7,13 +7,8 @@ #pragma once #include "Reader.h" -#include -namespace ZXing { - -class DecodeHints; - -namespace DataMatrix { +namespace ZXing::DataMatrix { class Reader : public ZXing::Reader { @@ -26,5 +21,4 @@ class Reader : public ZXing::Reader #endif }; -} // DataMatrix -} // ZXing +} // namespace ZXing::DataMatrix diff --git a/core/src/maxicode/MCReader.h b/core/src/maxicode/MCReader.h index 8f209c0ff7..f3cfd75a57 100644 --- a/core/src/maxicode/MCReader.h +++ b/core/src/maxicode/MCReader.h @@ -8,13 +8,7 @@ #include "Reader.h" -#include - -namespace ZXing { - -class DecodeHints; - -namespace MaxiCode { +namespace ZXing::MaxiCode { class Reader : public ZXing::Reader { @@ -24,5 +18,4 @@ class Reader : public ZXing::Reader Result decode(const BinaryBitmap& image) const override; }; -} // MaxiCode -} // ZXing +} // namespace ZXing::MaxiCode diff --git a/core/src/pdf417/PDFReader.h b/core/src/pdf417/PDFReader.h index 9d8be24824..c4d3272ee6 100644 --- a/core/src/pdf417/PDFReader.h +++ b/core/src/pdf417/PDFReader.h @@ -9,13 +9,8 @@ #include "Reader.h" #include -#include -namespace ZXing { - -class DecodeHints; - -namespace Pdf417 { +namespace ZXing::Pdf417 { /** * This implementation can detect and decode PDF417 codes in an image. @@ -33,5 +28,4 @@ class Reader : public ZXing::Reader [[deprecated]] std::list decodeMultiple(const BinaryBitmap& image) const; }; -} // Pdf417 -} // ZXing +} // namespace ZXing::Pdf417 diff --git a/core/src/qrcode/QRReader.h b/core/src/qrcode/QRReader.h index 5b86edf751..22d514e254 100644 --- a/core/src/qrcode/QRReader.h +++ b/core/src/qrcode/QRReader.h @@ -8,13 +8,7 @@ #include "Reader.h" -#include - -namespace ZXing { - -class DecodeHints; - -namespace QRCode { +namespace ZXing::QRCode { class Reader : public ZXing::Reader { @@ -25,5 +19,4 @@ class Reader : public ZXing::Reader Results decode(const BinaryBitmap& image, int maxSymbols) const override; }; -} // QRCode -} // ZXing +} // namespace ZXing::QRCode From 7c4bd7af454a555fbfedcb2c8bcdaf6ce6f593f6 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 02:30:58 +0200 Subject: [PATCH 166/180] DecodeHints: prevent to store reference to temporary in `Reader` --- core/src/DecodeHints.h | 5 +++-- core/src/Reader.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 841bb24255..9668a09848 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -76,7 +76,8 @@ class DecodeHints #define ZX_PROPERTY(TYPE, GETTER, SETTER) \ TYPE GETTER() const noexcept { return _##GETTER; } \ - DecodeHints& SETTER(TYPE v) { return _##GETTER = std::move(v), *this; } + DecodeHints& SETTER(TYPE v)& { return _##GETTER = std::move(v), *this; } \ + DecodeHints&& SETTER(TYPE v)&& { return _##GETTER = std::move(v), std::move(*this); } /// Specify a set of BarcodeFormats that should be searched for, the default is all supported formats. ZX_PROPERTY(BarcodeFormats, formats, setFormats) @@ -143,7 +144,7 @@ class DecodeHints /// NOTE: use validateCode39CheckSum [[deprecated]] bool assumeCode39CheckDigit() const noexcept { return validateCode39CheckSum(); } - [[deprecated]] DecodeHints& setAssumeCode39CheckDigit(bool v) { return setValidateCode39CheckSum(v); } + [[deprecated]] DecodeHints& setAssumeCode39CheckDigit(bool v) & { return setValidateCode39CheckSum(v); } bool hasFormat(BarcodeFormats f) const noexcept { return _formats.testFlags(f) || _formats.empty(); } [[deprecated]] bool hasNoFormat() const noexcept { return _formats.empty(); } diff --git a/core/src/Reader.h b/core/src/Reader.h index 716d26b94d..70f34e1d29 100644 --- a/core/src/Reader.h +++ b/core/src/Reader.h @@ -21,6 +21,7 @@ class Reader public: explicit Reader(const DecodeHints& hints) : _hints(hints) {} + explicit Reader(DecodeHints&& hints) = delete; virtual ~Reader() = default; virtual Result decode(const BinaryBitmap& image) const = 0; From 4eeaff2b85f0821da8d249187bb6b8f2c83f0288 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 02:32:37 +0200 Subject: [PATCH 167/180] DecodeHints: store reference in RowReader base class and use everywhere --- core/src/oned/ODCodabarReader.cpp | 7 +------ core/src/oned/ODCodabarReader.h | 15 ++++----------- core/src/oned/ODCode128Reader.h | 11 ++++------- core/src/oned/ODCode39Reader.cpp | 15 ++++----------- core/src/oned/ODCode39Reader.h | 17 ++++------------- core/src/oned/ODCode93Reader.h | 8 ++++---- core/src/oned/ODDataBarExpandedReader.cpp | 3 --- core/src/oned/ODDataBarExpandedReader.h | 11 +++-------- core/src/oned/ODDataBarReader.cpp | 3 --- core/src/oned/ODDataBarReader.h | 11 +++-------- core/src/oned/ODITFReader.cpp | 6 ++---- core/src/oned/ODITFReader.h | 17 ++++------------- core/src/oned/ODMultiUPCEANReader.cpp | 4 ---- core/src/oned/ODMultiUPCEANReader.h | 14 +++----------- core/src/oned/ODReader.cpp | 4 ++-- core/src/oned/ODRowReader.h | 7 ++++++- test/unit/oned/ODCodaBarWriterTest.cpp | 3 ++- test/unit/oned/ODCode128ReaderTest.cpp | 4 +++- test/unit/oned/ODCode128WriterTest.cpp | 4 +++- test/unit/oned/ODCode39ExtendedModeTest.cpp | 3 ++- test/unit/oned/ODCode93ReaderTest.cpp | 4 +++- 21 files changed, 57 insertions(+), 114 deletions(-) diff --git a/core/src/oned/ODCodabarReader.cpp b/core/src/oned/ODCodabarReader.cpp index 119911d1a9..b3fd40b0c3 100644 --- a/core/src/oned/ODCodabarReader.cpp +++ b/core/src/oned/ODCodabarReader.cpp @@ -30,11 +30,6 @@ static_assert(Size(ALPHABET) - 1 == Size(CHARACTER_ENCODINGS), "table size misma // some industries use a checksum standard but this is not part of the original codabar standard // for more information see : http://www.mecsw.com/specs/codabar.html -CodabarReader::CodabarReader(const DecodeHints& hints) -{ - _returnStartEnd = hints.returnCodabarStartEnd(); -} - // each character has 4 bars and 3 spaces constexpr int CHAR_LEN = 7; // quiet zone is half the width of a character symbol @@ -89,7 +84,7 @@ CodabarReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const override; + using RowReader::RowReader; -private: - bool _returnStartEnd; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const override; }; -} // OneD -} // ZXing +} // namespace ZXing::OneD diff --git a/core/src/oned/ODCode128Reader.h b/core/src/oned/ODCode128Reader.h index e555afa93e..60526fa469 100644 --- a/core/src/oned/ODCode128Reader.h +++ b/core/src/oned/ODCode128Reader.h @@ -8,17 +8,14 @@ #include "ODRowReader.h" -namespace ZXing { - -class DecodeHints; - -namespace OneD { +namespace ZXing::OneD { class Code128Reader : public RowReader { public: + using RowReader::RowReader; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; }; -} // OneD -} // ZXing +} // namespace ZXing::OneD diff --git a/core/src/oned/ODCode39Reader.cpp b/core/src/oned/ODCode39Reader.cpp index cadaf784a2..5eb916d6d2 100644 --- a/core/src/oned/ODCode39Reader.cpp +++ b/core/src/oned/ODCode39Reader.cpp @@ -75,17 +75,10 @@ DecodeExtendedCode39AndCode93(std::string& encoded, const char ctrl[4]) return true; } - -Code39Reader::Code39Reader(const DecodeHints& hints) : - _extendedMode(hints.tryCode39ExtendedMode()), - _validateCheckSum(hints.validateCode39CheckSum()) -{ -} - Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const { // minimal number of characters that must be present (including start, stop and checksum characters) - int minCharCount = _validateCheckSum ? 4 : 3; + int minCharCount = _hints.validateCode39CheckSum() ? 4 : 3; auto isStartOrStopSymbol = [](char c) { return c == '*'; }; // provide the indices with the narrow bars/spaces which have to be equally wide @@ -123,7 +116,7 @@ Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique return {}; Error error; - if (_validateCheckSum) { + if (_hints.validateCode39CheckSum()) { auto checkDigit = txt.back(); txt.pop_back(); int checksum = TransformReduce(txt, 0, [](char c) { return IndexOf(ALPHABET, c); }); @@ -131,12 +124,12 @@ Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique error = ChecksumError(); } - if (!error && _extendedMode && !DecodeExtendedCode39AndCode93(txt, "$%/+")) + if (!error && _hints.tryCode39ExtendedMode() && !DecodeExtendedCode39AndCode93(txt, "$%/+")) error = FormatError("Decoding extended Code39/Code93 failed"); // Symbology identifier modifiers ISO/IEC 16388:2007 Annex C Table C.1 constexpr const char symbologyModifiers[4] = { '0', '3' /*checksum*/, '4' /*extended*/, '7' /*checksum,extended*/ }; - SymbologyIdentifier symbologyIdentifier = {'A', symbologyModifiers[(int)_extendedMode * 2 + (int)_validateCheckSum]}; + SymbologyIdentifier symbologyIdentifier = {'A', symbologyModifiers[(int)_hints.tryCode39ExtendedMode() * 2 + (int)_hints.validateCode39CheckSum()]}; int xStop = next.pixelsTillEnd(); return Result(std::move(txt), rowNumber, xStart, xStop, BarcodeFormat::Code39, symbologyIdentifier, error); diff --git a/core/src/oned/ODCode39Reader.h b/core/src/oned/ODCode39Reader.h index 4bda353dc0..51b7139fe8 100644 --- a/core/src/oned/ODCode39Reader.h +++ b/core/src/oned/ODCode39Reader.h @@ -8,11 +8,7 @@ #include "ODRowReader.h" -namespace ZXing { - -class DecodeHints; - -namespace OneD { +namespace ZXing::OneD { class Code39Reader : public RowReader { @@ -22,14 +18,9 @@ class Code39Reader : public RowReader * or optionally attempt to decode "extended Code 39" sequences that are used to encode * the full ASCII character set. */ - explicit Code39Reader(const DecodeHints& hints); - - Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; + using RowReader::RowReader; -private: - bool _extendedMode; - bool _validateCheckSum; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; }; -} // OneD -} // ZXing +} // namespace ZXing::OneD diff --git a/core/src/oned/ODCode93Reader.h b/core/src/oned/ODCode93Reader.h index c0733533e3..b8ea6125d0 100644 --- a/core/src/oned/ODCode93Reader.h +++ b/core/src/oned/ODCode93Reader.h @@ -8,14 +8,14 @@ #include "ODRowReader.h" -namespace ZXing { -namespace OneD { +namespace ZXing::OneD { class Code93Reader : public RowReader { public: + using RowReader::RowReader; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; }; -} // OneD -} // ZXing +} // namespace ZXing::OneD diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index fb89a596ef..7565e50388 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -22,9 +22,6 @@ namespace ZXing::OneD { using namespace DataBar; -DataBarExpandedReader::DataBarExpandedReader(const DecodeHints&) {} -DataBarExpandedReader::~DataBarExpandedReader() = default; - static bool IsFinderPattern(int a, int b, int c, int d, int e) { return IsFinder(a, b, c, d, e) && (c > 3 * e); diff --git a/core/src/oned/ODDataBarExpandedReader.h b/core/src/oned/ODDataBarExpandedReader.h index 290482470a..65c523636c 100644 --- a/core/src/oned/ODDataBarExpandedReader.h +++ b/core/src/oned/ODDataBarExpandedReader.h @@ -7,24 +7,19 @@ #pragma once -#include "DecodeHints.h" #include "ODRowReader.h" -namespace ZXing { -namespace OneD { +namespace ZXing::OneD { /** * Decodes DataBarExpandedReader (formerly known as RSS) sybmols, including truncated and stacked variants. See ISO/IEC 24724:2006. */ class DataBarExpandedReader : public RowReader { - public: - explicit DataBarExpandedReader(const DecodeHints& hints); - ~DataBarExpandedReader() override; + using RowReader::RowReader; Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const override; }; -} // OneD -} // ZXing +} // namespace ZXing::OneD diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index 88865b6e3d..aa647ffb67 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -22,9 +22,6 @@ namespace ZXing::OneD { using namespace DataBar; -DataBarReader::DataBarReader(const DecodeHints&) {} -DataBarReader::~DataBarReader() = default; - static bool IsCharacterPair(PatternView v, int modsLeft, int modsRight) { float modSizeRef = ModSizeFinder(v); diff --git a/core/src/oned/ODDataBarReader.h b/core/src/oned/ODDataBarReader.h index 0e10cebc1d..0e64bb2f9a 100644 --- a/core/src/oned/ODDataBarReader.h +++ b/core/src/oned/ODDataBarReader.h @@ -7,24 +7,19 @@ #pragma once -#include "DecodeHints.h" #include "ODRowReader.h" -namespace ZXing { -namespace OneD { +namespace ZXing::OneD { /** * Decodes DataBar (formerly known as RSS) sybmols, including truncated and stacked variants. See ISO/IEC 24724:2006. */ class DataBarReader : public RowReader { - public: - explicit DataBarReader(const DecodeHints& hints); - ~DataBarReader() override; + using RowReader::RowReader; Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const override; }; -} // OneD -} // ZXing +} // namespace ZXing::OneD diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index f7c1805815..27145e29c5 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -15,8 +15,6 @@ namespace ZXing::OneD { -ITFReader::ITFReader(const DecodeHints& hints) : _validateCheckSum(hints.validateITFCheckSum()) {} - constexpr auto START_PATTERN_ = FixedPattern<4, 4>{1, 1, 1, 1}; constexpr auto STOP_PATTERN_1 = FixedPattern<3, 4>{2, 1, 1}; constexpr auto STOP_PATTERN_2 = FixedPattern<3, 5>{3, 1, 1}; @@ -68,14 +66,14 @@ Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_pt return {}; Error error; - if (_validateCheckSum && !GTIN::IsCheckDigitValid(txt)) + if (_hints.validateITFCheckSum() && !GTIN::IsCheckDigitValid(txt)) error = ChecksumError(); // Symbology identifier ISO/IEC 16390:2007 Annex C Table C.1 // See also GS1 General Specifications 5.1.3 Figure 5.1.3-2 SymbologyIdentifier symbologyIdentifier = {'I', '0'}; // No check character validation - if (_validateCheckSum || (txt.size() == 14 && GTIN::IsCheckDigitValid(txt))) // If no hint test if valid ITF-14 + if (_hints.validateITFCheckSum() || (txt.size() == 14 && GTIN::IsCheckDigitValid(txt))) // If no hint test if valid ITF-14 symbologyIdentifier.modifier = '1'; // Modulo 10 symbol check character validated and transmitted int xStop = next.pixelsTillEnd(); diff --git a/core/src/oned/ODITFReader.h b/core/src/oned/ODITFReader.h index c3bf45fba8..e71d0ea25e 100644 --- a/core/src/oned/ODITFReader.h +++ b/core/src/oned/ODITFReader.h @@ -8,13 +8,7 @@ #include "ODRowReader.h" -#include - -namespace ZXing { - -class DecodeHints; - -namespace OneD { +namespace ZXing::OneD { /** *

Implements decoding of the ITF format, or Interleaved Two of Five.

@@ -31,12 +25,9 @@ namespace OneD { class ITFReader : public RowReader { public: - explicit ITFReader(const DecodeHints& hints); - Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; + using RowReader::RowReader; -private: - bool _validateCheckSum; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; }; -} // OneD -} // ZXing +} // namespace ZXing::OneD diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index c1aa0e4104..bba83cdc23 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -16,10 +16,6 @@ namespace ZXing::OneD { -MultiUPCEANReader::MultiUPCEANReader(const DecodeHints& hints) : _hints(hints) {} - -MultiUPCEANReader::~MultiUPCEANReader() = default; - constexpr int CHAR_LEN = 4; constexpr auto END_PATTERN = FixedPattern<3, 3>{1, 1, 1}; diff --git a/core/src/oned/ODMultiUPCEANReader.h b/core/src/oned/ODMultiUPCEANReader.h index e4537ca287..9ec795d51c 100644 --- a/core/src/oned/ODMultiUPCEANReader.h +++ b/core/src/oned/ODMultiUPCEANReader.h @@ -6,12 +6,9 @@ #pragma once -#include "BarcodeFormat.h" -#include "DecodeHints.h" #include "ODRowReader.h" -namespace ZXing { -namespace OneD { +namespace ZXing::OneD { /** * @brief A reader that can read all available UPC/EAN formats. @@ -19,14 +16,9 @@ namespace OneD { class MultiUPCEANReader : public RowReader { public: - explicit MultiUPCEANReader(const DecodeHints& hints); - ~MultiUPCEANReader() override; + using RowReader::RowReader; Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; - -private: - DecodeHints _hints; }; -} // OneD -} // ZXing +} // namespace ZXing::OneD diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 4d5f2f374e..a616029413 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -36,9 +36,9 @@ Reader::Reader(const DecodeHints& hints) : ZXing::Reader(hints) if (formats.testFlag(BarcodeFormat::Code39)) _readers.emplace_back(new Code39Reader(hints)); if (formats.testFlag(BarcodeFormat::Code93)) - _readers.emplace_back(new Code93Reader()); + _readers.emplace_back(new Code93Reader(hints)); if (formats.testFlag(BarcodeFormat::Code128)) - _readers.emplace_back(new Code128Reader()); + _readers.emplace_back(new Code128Reader(hints)); if (formats.testFlag(BarcodeFormat::ITF)) _readers.emplace_back(new ITFReader(hints)); if (formats.testFlag(BarcodeFormat::Codabar)) diff --git a/core/src/oned/ODRowReader.h b/core/src/oned/ODRowReader.h index 9bf55fc9d2..98e61a22d7 100644 --- a/core/src/oned/ODRowReader.h +++ b/core/src/oned/ODRowReader.h @@ -8,7 +8,6 @@ #pragma once #include "BitArray.h" -#include "DecodeStatus.h" #include "Pattern.h" #include @@ -38,6 +37,7 @@ RSSExp.: v?-74d/?-41c namespace ZXing { +class DecodeHints; class Result; namespace OneD { @@ -48,7 +48,12 @@ namespace OneD { */ class RowReader { +protected: + const DecodeHints& _hints; + public: + explicit RowReader(const DecodeHints& hints) : _hints(hints) {} + explicit RowReader(DecodeHints&& hints) = delete; struct DecodingState { diff --git a/test/unit/oned/ODCodaBarWriterTest.cpp b/test/unit/oned/ODCodaBarWriterTest.cpp index f8662491fa..7294b5a942 100644 --- a/test/unit/oned/ODCodaBarWriterTest.cpp +++ b/test/unit/oned/ODCodaBarWriterTest.cpp @@ -53,7 +53,8 @@ TEST(ODCodaBarWriterTest, FullCircle) std::string text = "A0123456789-$:/.+A"; BitArray row; CodabarWriter().encode(text, 0, 0).getRow(0, row); - Result res = CodabarReader(DecodeHints().setReturnCodabarStartEnd(true)).decodeSingleRow(0, row); + auto hints = DecodeHints().setReturnCodabarStartEnd(true); + Result res = CodabarReader(hints).decodeSingleRow(0, row); EXPECT_EQ(text, res.utf8()); } diff --git a/test/unit/oned/ODCode128ReaderTest.cpp b/test/unit/oned/ODCode128ReaderTest.cpp index c22f655333..530da630b7 100644 --- a/test/unit/oned/ODCode128ReaderTest.cpp +++ b/test/unit/oned/ODCode128ReaderTest.cpp @@ -5,6 +5,7 @@ #include "oned/ODCode128Reader.h" +#include "DecodeHints.h" #include "Result.h" #include "gtest/gtest.h" @@ -25,7 +26,8 @@ static Result parse(const int startPattern, PatternRow row) row.insert(row.end(), { 2, 3, 3, 1, 1, 1, 2, 0 }); // Stop pattern std::unique_ptr state; - Code128Reader reader; + DecodeHints hints; + Code128Reader reader(hints); PatternView next(row); return reader.decodePattern(0, next, state); } diff --git a/test/unit/oned/ODCode128WriterTest.cpp b/test/unit/oned/ODCode128WriterTest.cpp index 0d25f605d4..a524164a5a 100644 --- a/test/unit/oned/ODCode128WriterTest.cpp +++ b/test/unit/oned/ODCode128WriterTest.cpp @@ -6,6 +6,7 @@ #include "oned/ODCode128Writer.h" #include "BitMatrixIO.h" +#include "DecodeHints.h" #include "Result.h" #include "oned/ODCode128Reader.h" @@ -38,7 +39,8 @@ static ZXing::Result Decode(const BitMatrix &matrix) { BitArray row; matrix.getRow(0, row); - return Code128Reader().decodeSingleRow(0, row); + DecodeHints hints; + return Code128Reader(hints).decodeSingleRow(0, row); } TEST(ODCode128Writer, EncodeWithFunc1) diff --git a/test/unit/oned/ODCode39ExtendedModeTest.cpp b/test/unit/oned/ODCode39ExtendedModeTest.cpp index ca172b4ec1..cbf307d3ba 100644 --- a/test/unit/oned/ODCode39ExtendedModeTest.cpp +++ b/test/unit/oned/ODCode39ExtendedModeTest.cpp @@ -17,7 +17,8 @@ using namespace ZXing::OneD; static std::string Decode(std::string_view encoded) { - Code39Reader sut(DecodeHints().setTryCode39ExtendedMode(true)); + auto hints = DecodeHints().setTryCode39ExtendedMode(true); + Code39Reader sut(hints); BitArray row = Utility::ParseBitArray(encoded, '1'); Result result = sut.decodeSingleRow(0, row); return result.utf8(); diff --git a/test/unit/oned/ODCode93ReaderTest.cpp b/test/unit/oned/ODCode93ReaderTest.cpp index b037f1acbc..f11a70ec79 100644 --- a/test/unit/oned/ODCode93ReaderTest.cpp +++ b/test/unit/oned/ODCode93ReaderTest.cpp @@ -7,6 +7,7 @@ #include "oned/ODCode93Reader.h" #include "BitArray.h" #include "BitArrayUtility.h" +#include "DecodeHints.h" #include "Result.h" #include "gtest/gtest.h" @@ -16,7 +17,8 @@ using namespace ZXing::OneD; static std::string Decode(std::string_view input) { - Code93Reader sut; + DecodeHints hints; + Code93Reader sut(hints); auto row = Utility::ParseBitArray(input, '1'); auto result = sut.decodeSingleRow(0, row); return result.utf8(); From 838ff6cc52de0d6ccaf365dcee8716283eb59c04 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 02:34:04 +0200 Subject: [PATCH 168/180] ByteArray: add copyright line --- core/src/ByteArray.h | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/ByteArray.h b/core/src/ByteArray.h index ad87c2961c..a31f42c1ed 100644 --- a/core/src/ByteArray.h +++ b/core/src/ByteArray.h @@ -1,5 +1,6 @@ /* * Copyright 2016 Nu-book Inc. +* Copyright 2022 Axel Waggershauser */ // SPDX-License-Identifier: Apache-2.0 From 2dfb65ac8ac77135b9e7ff2174f7da2a71d9fe60 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 02:34:26 +0200 Subject: [PATCH 169/180] Content: add missing includes (IWYU) --- core/src/Content.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/Content.h b/core/src/Content.h index cce0b541d2..0acd6c2761 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -7,6 +7,9 @@ #include "ByteArray.h" +#include +#include + namespace ZXing { enum class ECI : int; From caab6edcf6c99a0146e33d1c1dbbfa3b792312e2 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 10:22:02 +0200 Subject: [PATCH 170/180] Content: introduce `text(TextMode)` See https://github.com/nu-book/zxing-cpp/issues/338#issuecomment-1174741554 --- core/src/Content.cpp | 27 ++++++++++++++++----------- core/src/Content.h | 8 +++++--- core/src/Result.cpp | 2 +- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index ae0ac9157b..3829abe140 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -7,6 +7,7 @@ #include "CharacterSet.h" #include "ECI.h" +#include "GS1.h" #include "TextDecoder.h" #include "TextUtfEncoding.h" #include "ZXContainerAlgorithms.h" @@ -137,19 +138,23 @@ std::wstring Content::render(bool withECI) const return res; } -std::wstring Content::utf16() const +std::string Content::text(TextMode mode) const { - return render(false); -} - -std::string Content::utf8() const -{ - return TextUtfEncoding::ToUtf8(render(false)); -} + switch(mode) { + case TextMode::Utf8: return TextUtfEncoding::ToUtf8(render(false)); + case TextMode::Utf8ECI: return TextUtfEncoding::ToUtf8(render(true)); + case TextMode::HRI: + if (applicationIndicator == "GS1") + return HRIFromGS1(text(TextMode::Utf8)); + else if (type() == ContentType::Text) + return text(TextMode::Utf8); + else + return text(TextMode::Escaped); + case TextMode::Hex: return ToHex(bytes); + case TextMode::Escaped: return TextUtfEncoding::ToUtf8(render(false), true); + } -std::string Content::utf8ECI() const -{ - return TextUtfEncoding::ToUtf8(render(true)); + return {}; // silence compiler warning } ByteArray Content::bytesECI() const diff --git a/core/src/Content.h b/core/src/Content.h index 0acd6c2761..d330ca1312 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -16,6 +16,7 @@ enum class ECI : int; enum class CharacterSet; enum class ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI }; +enum class TextMode { Utf8, Utf8ECI, HRI, Hex, Escaped }; std::string ToString(ContentType type); @@ -74,9 +75,10 @@ class Content bool empty() const { return bytes.empty(); } bool canProcess() const; - std::wstring utf16() const; - std::string utf8() const; - std::string utf8ECI() const; + std::string text(TextMode mode) const; + std::wstring utf16() const { return render(false); } + std::string utf8() const { return text(TextMode::Utf8); } + ByteArray bytesECI() const; CharacterSet guessEncoding() const; ContentType type() const; diff --git a/core/src/Result.cpp b/core/src/Result.cpp index d406f975b3..0b1e2ddf50 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -80,7 +80,7 @@ std::wstring Result::utf16() const std::string Result::utf8ECI() const { - return _content.utf8ECI(); + return _content.text(TextMode::Utf8ECI); } ContentType Result::contentType() const From 776a2b4f29cbc6c4410aa992080e391c63477a69 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 10:27:52 +0200 Subject: [PATCH 171/180] example: use the term 'bytes' instead of 'binary' in output option --- example/ZXingReader.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 3532fb3280..9ef814f7f9 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -38,7 +38,7 @@ static void PrintUsage(const char* exePath) << " -errors Include results with errors (like checksum error)\n" << " -1 Print only file name, content/error on one line per file/barcode (implies '-escape')\n" << " -escape Escape non-graphical characters in angle brackets\n" - << " -binary Write (only) the binary content of the symbol(s) to stdout\n" + << " -bytes Write (only) the bytes content of the symbol(s) to stdout\n" << " -pngout \n" << " Write a copy of the input image with barcodes outlined by a green line\n" << "\n" @@ -49,7 +49,7 @@ static void PrintUsage(const char* exePath) std::cout << "Formats can be lowercase, with or without '-', separated by ',' and/or '|'\n"; } -static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLine, bool& angleEscape, bool& binaryOutput, +static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLine, bool& angleEscape, bool& bytesOnly, std::vector& filePaths, std::string& outPath) { for (int i = 1; i < argc; ++i) { @@ -77,8 +77,8 @@ static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLi oneLine = true; } else if (strcmp(argv[i], "-escape") == 0) { angleEscape = true; - } else if (strcmp(argv[i], "-binary") == 0) { - binaryOutput = true; + } else if (strcmp(argv[i], "-bytes") == 0) { + bytesOnly = true; } else if (strcmp(argv[i], "-pngout") == 0) { if (++i == argc) return false; @@ -131,11 +131,11 @@ int main(int argc, char* argv[]) std::string outPath; bool oneLine = false; bool angleEscape = false; - bool binaryOutput = false; + bool bytesOnly = false; int ret = 0; - if (!ParseOptions(argc, argv, hints, oneLine, angleEscape, binaryOutput, filePaths, outPath)) { + if (!ParseOptions(argc, argv, hints, oneLine, angleEscape, bytesOnly, filePaths, outPath)) { PrintUsage(argv[0]); return -1; } @@ -176,7 +176,7 @@ int main(int argc, char* argv[]) ret |= static_cast(result.error().type()); - if (binaryOutput) { + if (bytesOnly) { std::cout.write(reinterpret_cast(result.bytes().data()), result.bytes().size()); continue; } From 7a597ff0c9c0e8af6dca4c41dd0fcff4090f8d41 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 22:57:28 +0200 Subject: [PATCH 172/180] Result: remove now unused `Result(DecodeStatus)` constructor --- core/src/Error.h | 12 ------------ core/src/Result.cpp | 2 -- core/src/Result.h | 1 - 3 files changed, 15 deletions(-) diff --git a/core/src/Error.h b/core/src/Error.h index 0b7ee85351..207caf3d5d 100644 --- a/core/src/Error.h +++ b/core/src/Error.h @@ -5,8 +5,6 @@ #pragma once -#include "DecodeStatus.h" - #include namespace ZXing { @@ -60,14 +58,4 @@ inline std::string ToString(const Error& e) return ret; } -// transitional helper function -inline Error Status2Error(DecodeStatus s) -{ - switch (s) { - case DecodeStatus::FormatError: return Error(Error::Format); - case DecodeStatus::ChecksumError: return Error(Error::Checksum); - default: return {}; - } -} - } diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 0b1e2ddf50..c4a489c610 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -17,8 +17,6 @@ namespace ZXing { -Result::Result(DecodeStatus status) : _error(Status2Error(status)) {} - Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, Error error, ByteArray&& rawBytes, bool readerInit, const std::string& ai) : _format(format), diff --git a/core/src/Result.h b/core/src/Result.h index cd6397944d..5469a0370f 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -31,7 +31,6 @@ class Result { public: Result() = default; - explicit Result(DecodeStatus status); // linear symbology convenience constructor Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, Error error = {}, From 5a700741eef8546ad265283b14b49571c2368767 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 23:52:30 +0200 Subject: [PATCH 173/180] DMReader: fix compile regression in c++20 --- core/src/datamatrix/DMReader.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/datamatrix/DMReader.cpp b/core/src/datamatrix/DMReader.cpp index c34ea646c8..06d21a9cce 100644 --- a/core/src/datamatrix/DMReader.cpp +++ b/core/src/datamatrix/DMReader.cpp @@ -21,8 +21,7 @@ namespace ZXing::DataMatrix { Result Reader::decode(const BinaryBitmap& image) const { #ifdef __cpp_impl_coroutine - auto results = decode(image, 1); - return results.empty() ? Result(DecodeStatus::NotFound) : results.front(); + return FirstOrDefault(decode(image, 1)); #else auto binImg = image.getBitMatrix(); if (binImg == nullptr) From b3e94b8ca5f5697929dc5d9e57fd1dc4ba777d85 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 5 Jul 2022 23:53:51 +0200 Subject: [PATCH 174/180] ReadBarcode: code cosmetic and TODO comment --- core/src/ReadBarcode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 5b62ff0d87..6b1e22df97 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -121,7 +121,7 @@ Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) if (hints.maxNumberOfSymbols() == 1) { // HACK: use the maxNumberOfSymbols value as a switch to ReadBarcodes to enable the downscaling // see python and android wrapper - return FirstOrDefault(ReadBarcodes(_iv, DecodeHints(hints).setMaxNumberOfSymbols(1))); + return FirstOrDefault(ReadBarcodes(_iv, hints)); } else { LumImage lum; ImageView iv = SetupLumImageView(_iv, lum, hints); @@ -150,7 +150,7 @@ Results ReadBarcodes(const ImageView& _iv, const DecodeHints& hints) if (iv.width() != _iv.width()) r.setPosition(Scale(r.position(), _iv.width() / iv.width())); if (!Contains(results, r)) { - results.push_back(std::move(r)); + results.push_back(std::move(r)); // TODO: keep the one with no error instead of the first found --maxSymbols; } } From d3f18b8316e3eef33c81c91e068ef0e4b37256c2 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 6 Jul 2022 10:43:44 +0200 Subject: [PATCH 175/180] PDF417: support `tryRotate` (fix #349) This comes at the cost of about 2% performance decrease in the falsepositive use case. --- core/src/BitMatrix.cpp | 18 ++++++++---- core/src/BitMatrix.h | 15 +++++++++- core/src/pdf417/PDFDetector.cpp | 41 ++++++++++++++++++---------- core/src/pdf417/PDFDetector.h | 3 +- core/src/pdf417/PDFReader.cpp | 21 ++++++++++---- test/blackbox/BlackboxTestRunner.cpp | 12 +++++--- 6 files changed, 78 insertions(+), 32 deletions(-) diff --git a/core/src/BitMatrix.cpp b/core/src/BitMatrix.cpp index d9449cfbe3..d0497ff394 100644 --- a/core/src/BitMatrix.cpp +++ b/core/src/BitMatrix.cpp @@ -196,9 +196,9 @@ BitMatrix::getBottomRightOnBit(int& right, int& bottom) const constexpr BitMatrix::data_t BitMatrix::SET_V; constexpr BitMatrix::data_t BitMatrix::UNSET_V; -void BitMatrix::getPatternRow(int r, PatternRow& p_row) const +template +void GetPatternRow(BitMatrix::Row b_row, int row_size, PatternRow& p_row) { - auto b_row = row(r); #if 0 p_row.reserve(64); p_row.clear(); @@ -216,11 +216,11 @@ void BitMatrix::getPatternRow(int r, PatternRow& p_row) const if (BitMatrix::isSet(*lastPos)) p_row.push_back(0); // last value is number of white pixels, here 0 #else - p_row.resize(width() + 2); + p_row.resize(row_size + 2); std::fill(p_row.begin(), p_row.end(), 0); - auto* bitPos = b_row.begin(); - auto* intPos = p_row.data(); + auto bitPos = b_row.begin(); + auto intPos = p_row.data(); intPos += BitMatrix::isSet(*bitPos); // first value is number of white pixels, here 0 @@ -236,6 +236,14 @@ void BitMatrix::getPatternRow(int r, PatternRow& p_row) const p_row.resize(intPos - p_row.data() + 1); #endif } + +void BitMatrix::getPatternRow(int r, PatternRow& p_row, bool transpose) const +{ + if (transpose) + GetPatternRow(col(r), height(), p_row); + else + GetPatternRow(row(r), width(), p_row); +} #endif BitMatrix Inflate(BitMatrix&& input, int width, int height, int quietZone) diff --git a/core/src/BitMatrix.h b/core/src/BitMatrix.h index a01f20a4f4..13c34fd875 100644 --- a/core/src/BitMatrix.h +++ b/core/src/BitMatrix.h @@ -104,6 +104,19 @@ class BitMatrix }; Row row(int y) { return {_bits.data() + y * _width, _bits.data() + (y + 1) * _width}; } Row row(int y) const { return {_bits.data() + y * _width, _bits.data() + (y + 1) * _width}; } + + struct ColIter + { + const data_t* pos; + int stride; + + data_t operator*() const { return *pos; } + data_t operator[](int i) const { return *(pos + i * stride); } + ColIter& operator++() { return pos += stride, *this; } + bool operator<(const ColIter& rhs) const { return pos < rhs.pos; } + }; + Row col(int x) const { return {{_bits.data() + x, _width}, {_bits.data() + x + _height * _width, _width}}; } + #endif /** @@ -232,7 +245,7 @@ class BitMatrix bool getBottomRightOnBit(int &right, int& bottom) const; #ifdef ZX_FAST_BIT_STORAGE - void getPatternRow(int r, std::vector& p_row) const; + void getPatternRow(int r, std::vector& p_row, bool transpose = false) const; #endif /** diff --git a/core/src/pdf417/PDFDetector.cpp b/core/src/pdf417/PDFDetector.cpp index 003b4b4654..97835f13c5 100644 --- a/core/src/pdf417/PDFDetector.cpp +++ b/core/src/pdf417/PDFDetector.cpp @@ -304,15 +304,16 @@ static std::list, 8>> DetectBarcode(const BitMa } #ifdef ZX_FAST_BIT_STORAGE -bool HasStartPattern(const BitMatrix& m) +bool HasStartPattern(const BitMatrix& m, bool rotate90) { constexpr FixedPattern<8, 17> START_PATTERN = { 8, 1, 1, 1, 1, 1, 1, 3 }; constexpr int minSymbolWidth = 3*8+1; // compact symbol PatternRow row; + int end = rotate90 ? m.width() : m.height(); - for (int r = ROW_STEP; r < m.height(); r += ROW_STEP) { - m.getPatternRow(r, row); + for (int r = ROW_STEP; r < end; r += ROW_STEP) { + m.getPatternRow(r, row, rotate90); if (FindLeftGuard(row, minSymbolWidth, START_PATTERN, 2).isValid()) return true; @@ -332,7 +333,7 @@ bool HasStartPattern(const BitMatrix& m) * @param multiple if true, then the image is searched for multiple codes. If false, then at most one code will * be found and returned */ -Detector::Result Detector::Detect(const BinaryBitmap& image, bool multiple) +Detector::Result Detector::Detect(const BinaryBitmap& image, bool multiple, bool tryRotate) { // construct a 'dummy' shared pointer, just be able to pass it up the call chain in DecodeStatus // TODO: reimplement PDF Detector @@ -340,23 +341,33 @@ Detector::Result Detector::Detect(const BinaryBitmap& image, bool multiple) if (!binImg) return {}; + Result result; + + for (int rotate90 = false; rotate90 <= tryRotate && result.points.empty(); ++rotate90) { #if defined(ZX_FAST_BIT_STORAGE) - if (!HasStartPattern(*binImg)) - return {}; + if (!HasStartPattern(*binImg, rotate90)) + continue; #endif + result.rotation = 90 * rotate90; + if (rotate90) { + auto newBits = std::make_shared(binImg->copy()); + newBits->rotate90(); + binImg = newBits; + } - auto barcodeCoordinates = DetectBarcode(*binImg, multiple); - if (barcodeCoordinates.empty()) { - auto newBits = std::make_shared(binImg->copy()); - newBits->rotate180(); - binImg = newBits; - barcodeCoordinates = DetectBarcode(*binImg, multiple); + result.points = DetectBarcode(*binImg, multiple); + if (result.points.empty()) { + auto newBits = std::make_shared(binImg->copy()); + newBits->rotate180(); + binImg = newBits; + result.points = DetectBarcode(*binImg, multiple); + result.rotation += 180; + } } - if (barcodeCoordinates.empty()) + + if (result.points.empty()) return {}; - Result result; - result.points = barcodeCoordinates; result.bits = binImg; return result; } diff --git a/core/src/pdf417/PDFDetector.h b/core/src/pdf417/PDFDetector.h index d3cc9bdf79..1316c0a52c 100644 --- a/core/src/pdf417/PDFDetector.h +++ b/core/src/pdf417/PDFDetector.h @@ -35,9 +35,10 @@ class Detector { std::shared_ptr bits; std::list, 8>> points; + int rotation; }; - static Result Detect(const BinaryBitmap& image, bool multiple); + static Result Detect(const BinaryBitmap& image, bool multiple, bool tryRotate); }; } // Pdf417 diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index 3cdab9aa2b..947b21e95c 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -66,19 +66,28 @@ static int GetMaxCodewordWidth(const std::array, 8>& p) std::max(GetMaxWidth(p[1], p[5]), GetMaxWidth(p[7], p[3]) * CodewordDecoder::MODULES_IN_CODEWORD / MODULES_IN_STOP_PATTERN)); } -static Results DoDecode(const BinaryBitmap& image, bool multiple, bool returnErrors) +static Results DoDecode(const BinaryBitmap& image, bool multiple, bool tryRotate, bool returnErrors) { - Detector::Result detectorResult = Detector::Detect(image, multiple); + Detector::Result detectorResult = Detector::Detect(image, multiple, tryRotate); if (detectorResult.points.empty()) return {}; + auto rotate = [res = detectorResult](PointI p) { + switch(res.rotation) { + case 90: return PointI(res.bits->height() - p.y - 1, p.x); + case 180: return PointI(res.bits->width() - p.x - 1, res.bits->height() - p.y - 1); + case 270: return PointI(p.y, res.bits->width() - p.x - 1); + } + return p; + }; + Results results; for (const auto& points : detectorResult.points) { DecoderResult decoderResult = ScanningDecoder::Decode(*detectorResult.bits, points[4], points[5], points[6], points[7], GetMinCodewordWidth(points), GetMaxCodewordWidth(points)); if (decoderResult.isValid(returnErrors)) { - auto point = [&](int i) { return points[i].value(); }; + auto point = [&](int i) { return rotate(PointI(points[i].value())); }; Result result(std::move(decoderResult), {point(0), point(2), point(3), point(1)}, BarcodeFormat::PDF417); results.push_back(result); if (!multiple) @@ -310,18 +319,18 @@ Reader::decode(const BinaryBitmap& image) const // currently the best option to deal with 'aliased' input like e.g. 03-aliased.png } - return FirstOrDefault(DoDecode(image, false, _hints.returnErrors())); + return FirstOrDefault(DoDecode(image, false, _hints.tryRotate(), _hints.returnErrors())); } Results Reader::decode(const BinaryBitmap& image, [[maybe_unused]] int maxSymbols) const { - return DoDecode(image, true, _hints.returnErrors()); + return DoDecode(image, true, _hints.tryRotate(), _hints.returnErrors()); } std::list Reader::decodeMultiple(const BinaryBitmap& image) const { - Results results = DoDecode(image, true, _hints.returnErrors()); + Results results = DoDecode(image, true, _hints.tryRotate(), _hints.returnErrors()); return std::list(results.begin(), results.end()); } diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index fdda8ff254..52038adcc7 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -600,21 +600,25 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("pdf417-1", "PDF417", 17, { - { 16, 16, 0 }, - { 1, 1, 90 }, - { 16, 16, 180 }, - { 1, 1, 270 }, + { 16, 17, 0 }, + { 1, 17, 90 }, + { 16, 17, 180 }, + { 1, 17, 270 }, { 17, 0, pure }, }); runTests("pdf417-2", "PDF417", 25, { { 25, 25, 0 }, + { 0, 25, 90 }, { 25, 25, 180 }, + { 0, 25, 270 }, }); runTests("pdf417-3", "PDF417", 16, { { 16, 16, 0 }, + { 0, 16, 90 }, { 16, 16, 180 }, + { 0, 16, 270 }, { 7, 0, pure }, }); From 0e863f16df743f0de82340e8b641d5f413fe6e82 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 7 Jul 2022 02:02:57 +0200 Subject: [PATCH 176/180] ZXAlgorithms: introduce narrow_cast from gsl -> fix a few warnings --- core/src/BitArray.h | 8 +++---- core/src/BitMatrixIO.cpp | 4 ++-- core/src/GS1.cpp | 4 ++-- core/src/GlobalHistogramBinarizer.cpp | 6 ++--- core/src/Pattern.h | 4 ++-- core/src/Result.cpp | 3 ++- core/src/ThresholdBinarizer.h | 4 ++-- core/src/ZXContainerAlgorithms.h | 13 +++++++---- core/src/aztec/AZDetector.cpp | 3 ++- core/src/datamatrix/DMDecoder.cpp | 2 +- core/src/datamatrix/DMEncoderContext.h | 2 +- core/src/datamatrix/DMHighLevelEncoder.cpp | 22 +++++++++---------- core/src/maxicode/MCDecoder.cpp | 2 +- core/src/oned/ODCode128Reader.cpp | 4 ++-- core/src/oned/ODRowReader.cpp | 4 ++-- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 4 ++-- core/src/qrcode/QRDecoder.cpp | 12 +++++----- core/src/qrcode/QRDetector.cpp | 2 +- core/src/qrcode/QREncoder.cpp | 2 +- core/src/qrcode/QRMaskUtil.cpp | 2 +- test/blackbox/BlackboxTestRunner.cpp | 2 +- test/unit/ThresholdBinarizerTest.cpp | 2 +- wrappers/python/zxing.cpp | 15 +++++-------- 23 files changed, 64 insertions(+), 62 deletions(-) diff --git a/core/src/BitArray.h b/core/src/BitArray.h index b531389f4d..6f4829aee8 100644 --- a/core/src/BitArray.h +++ b/core/src/BitArray.h @@ -31,7 +31,7 @@ struct Range { Iterator begin, end; explicit operator bool() const { return begin < end; } - int size() const { return static_cast(end - begin); } + int size() const { return narrow_cast(end - begin); } }; /** @@ -89,7 +89,7 @@ class BitArray int operator-(const Iterator& rhs) const { - return static_cast(_value - rhs._value) * 32 + (_mask >= rhs._mask + return narrow_cast(_value - rhs._value) * 32 + (_mask >= rhs._mask ? +BitHacks::CountBitsSet(_mask - rhs._mask) : -BitHacks::CountBitsSet(rhs._mask - _mask)); } @@ -272,7 +272,7 @@ class BitArray // Set allowClippedZone to false if clipping the zone at the image border is not acceptable. bool hasQuietZone(Iterator i, int signedZoneSize, bool allowClippedZone = true) const { - int index = static_cast(i - begin()); + int index = narrow_cast(i - begin()); if (signedZoneSize > 0) { if (!allowClippedZone && index + signedZoneSize >= size()) return false; @@ -427,7 +427,7 @@ class BitArrayView int size() const { - return bits.end() - cur; + return narrow_cast(bits.end() - cur); } explicit operator bool() const { return size(); } diff --git a/core/src/BitMatrixIO.cpp b/core/src/BitMatrixIO.cpp index 10a7c86306..0d4e54325d 100644 --- a/core/src/BitMatrixIO.cpp +++ b/core/src/BitMatrixIO.cpp @@ -64,8 +64,8 @@ BitMatrix ParseBitMatrix(const std::string& str, char one, bool expectSpace) return {}; int strStride = expectSpace ? 2 : 1; - int height = static_cast(str.length() / (lineLength + 1)); - int width = static_cast(lineLength / strStride); + int height = narrow_cast(str.length() / (lineLength + 1)); + int width = narrow_cast(lineLength / strStride); BitMatrix mat(width, height); for (int y = 0; y < height; ++y) { size_t offset = y * (lineLength + 1); diff --git a/core/src/GS1.cpp b/core/src/GS1.cpp index 50c25debd7..d083d8af0e 100644 --- a/core/src/GS1.cpp +++ b/core/src/GS1.cpp @@ -260,10 +260,10 @@ std::string HRIFromGS1(const std::string& gs1) if (i->isVariableLength()) { auto gsPos = rem.find(GS); #if 1 - fieldSize = std::min(gsPos == std::string_view::npos ? Size(rem) : static_cast(gsPos), fieldSize); + fieldSize = std::min(gsPos == std::string_view::npos ? Size(rem) : narrow_cast(gsPos), fieldSize); #else // TODO: ignore the 'max field size' part for now as it breaks rssexpanded-3/13.png? - fieldSize = gsPos == std::string_view::npos ? Size(rem) : static_cast(gsPos); + fieldSize = gsPos == std::string_view::npos ? Size(rem) : narrow_cast(gsPos); #endif } if (fieldSize == 0 || Size(rem) < fieldSize) diff --git a/core/src/GlobalHistogramBinarizer.cpp b/core/src/GlobalHistogramBinarizer.cpp index 000bbe9182..f09d040d22 100644 --- a/core/src/GlobalHistogramBinarizer.cpp +++ b/core/src/GlobalHistogramBinarizer.cpp @@ -30,7 +30,7 @@ static int EstimateBlackPoint(const std::array& buckets) { // Find the tallest peak in the histogram. auto firstPeakPos = std::max_element(buckets.begin(), buckets.end()); - int firstPeak = static_cast(firstPeakPos - buckets.begin()); + int firstPeak = narrow_cast(firstPeakPos - buckets.begin()); int firstPeakSize = *firstPeakPos; int maxBucketCount = firstPeakSize; @@ -99,7 +99,7 @@ bool GlobalHistogramBinarizer::getPatternRow(int row, int rotation, PatternRow& auto process = [&](bool val, const uint8_t* p) { if (val != lastVal) { - res.push_back(static_cast((p - lastPos) / pixStride)); + res.push_back(narrow_cast((p - lastPos) / pixStride)); lastVal = val; lastPos = p; } @@ -112,7 +112,7 @@ bool GlobalHistogramBinarizer::getPatternRow(int row, int rotation, PatternRow& bool backVal = *backPos < blackPoint; process(backVal, backPos); - res.push_back(static_cast((backPos - lastPos) / pixStride + 1)); + res.push_back(narrow_cast((backPos - lastPos) / pixStride + 1)); if (backVal) res.push_back(0); // last value is number of white pixels, here 0 diff --git a/core/src/Pattern.h b/core/src/Pattern.h index 2226169d15..1b2a112c9b 100644 --- a/core/src/Pattern.h +++ b/core/src/Pattern.h @@ -59,7 +59,7 @@ class PatternView int size() const { return _size; } // index is the number of bars and spaces from the first bar to the current position - int index() const { return static_cast(_data - (_base + 1)); } + int index() const { return narrow_cast(_data - (_base + 1)); } int pixelsInFront() const { return std::accumulate(_base, _data, 0); } int pixelsTillEnd() const { return std::accumulate(_base, _data + _size, 0) - 1; } bool isAtFirstBar() const { return _data == _base + 1; } @@ -114,7 +114,7 @@ class PatternView void extend() { - _size = std::max(0, static_cast(_end - _data)); + _size = std::max(0, narrow_cast(_end - _data)); } }; diff --git a/core/src/Result.cpp b/core/src/Result.cpp index c4a489c610..da29225cd1 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -9,6 +9,7 @@ #include "DecoderResult.h" #include "TextDecoder.h" #include "TextUtfEncoding.h" +#include "ZXContainerAlgorithms.h" #include #include @@ -94,7 +95,7 @@ bool Result::hasECI() const int Result::orientation() const { constexpr auto std_numbers_pi_v = 3.14159265358979323846; // TODO: c++20 - return std::lround(_position.orientation() * 180 / std_numbers_pi_v); + return narrow_cast(std::lround(_position.orientation() * 180 / std_numbers_pi_v)); } std::string Result::symbologyIdentifier() const diff --git a/core/src/ThresholdBinarizer.h b/core/src/ThresholdBinarizer.h index 620886a536..92cb1ffb56 100644 --- a/core/src/ThresholdBinarizer.h +++ b/core/src/ThresholdBinarizer.h @@ -35,13 +35,13 @@ class ThresholdBinarizer : public BinaryBitmap for (const uint8_t* p = begin; p < end; p += stride) { bool val = *p <= _threshold; if (val != lastVal) { - res.push_back(static_cast(p - lastPos) / stride); + res.push_back(narrow_cast(p - lastPos) / stride); lastVal = val; lastPos = p; } } - res.push_back(static_cast(end - lastPos) / stride); + res.push_back(narrow_cast(end - lastPos) / stride); if (*(end - stride) <= _threshold) res.push_back(0); // last value is number of white pixels, here 0 diff --git a/core/src/ZXContainerAlgorithms.h b/core/src/ZXContainerAlgorithms.h index 28a9b89a19..f04191f10d 100644 --- a/core/src/ZXContainerAlgorithms.h +++ b/core/src/ZXContainerAlgorithms.h @@ -14,6 +14,11 @@ namespace ZXing { +template +constexpr T narrow_cast(U&& u) noexcept { + return static_cast(std::forward(u)); +} + template auto Find(Container& c, const Value& v) -> decltype(std::begin(c)) { return std::find(std::begin(c), std::end(c), v); @@ -46,23 +51,23 @@ Value Reduce(const Container& c, Value v = Value{}, Op op = {}) { // see C++20 ssize template constexpr auto Size(const Container& c) -> decltype(c.size(), int()) { - return static_cast(c.size()); + return narrow_cast(c.size()); } template constexpr int Size(const T (&)[N]) noexcept { - return static_cast(N); + return narrow_cast(N); } template int IndexOf(const Container& c, const Value& v) { auto i = Find(c, v); - return i == std::end(c) ? -1 : static_cast(std::distance(std::begin(c), i)); + return i == std::end(c) ? -1 : narrow_cast(std::distance(std::begin(c), i)); } inline int IndexOf(const char* str, char c) { auto s = strchr(str, c); - return s != nullptr ? static_cast(s - str) : -1; + return s != nullptr ? narrow_cast(s - str) : -1; } template diff --git a/core/src/aztec/AZDetector.cpp b/core/src/aztec/AZDetector.cpp index 7c56af7829..d309ea6c7a 100644 --- a/core/src/aztec/AZDetector.cpp +++ b/core/src/aztec/AZDetector.cpp @@ -14,6 +14,7 @@ #include "ReedSolomonDecoder.h" #include "ResultPoint.h" #include "WhiteRectDetector.h" +#include "ZXContainerAlgorithms.h" #include #include @@ -23,7 +24,7 @@ namespace ZXing::Aztec { template ::value>::type> static int RoundToNearest(T x) { - return static_cast(std::lround(x)); + return narrow_cast(std::lround(x)); } static const int EXPECTED_CORNER_BITS[] = { diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index a82440c5be..69c5067208 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -268,7 +268,7 @@ static void DecodeBase256Segment(BitSource& bits, Content& result) for (int i = 0; i < count; i++) { // readBits(8) may fail, have seen this particular error in the wild, such as at // http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2 - result += static_cast(Unrandomize255State(bits.readBits(8), codewordPosition++)); + result += narrow_cast(Unrandomize255State(bits.readBits(8), codewordPosition++)); } } diff --git a/core/src/datamatrix/DMEncoderContext.h b/core/src/datamatrix/DMEncoderContext.h index ac5a81555f..dabcf0aef6 100644 --- a/core/src/datamatrix/DMEncoderContext.h +++ b/core/src/datamatrix/DMEncoderContext.h @@ -103,7 +103,7 @@ class EncoderContext } int totalMessageCharCount() const { - return static_cast(_msg.length() - _skipAtEnd); + return narrow_cast(_msg.length() - _skipAtEnd); } int remainingCharacters() const { diff --git a/core/src/datamatrix/DMHighLevelEncoder.cpp b/core/src/datamatrix/DMHighLevelEncoder.cpp index d030aa05be..0ace173816 100644 --- a/core/src/datamatrix/DMHighLevelEncoder.cpp +++ b/core/src/datamatrix/DMHighLevelEncoder.cpp @@ -116,7 +116,7 @@ static uint8_t Randomize253State(uint8_t ch, int codewordPosition) { int pseudoRandom = ((149 * codewordPosition) % 253) + 1; int tempVariable = ch + pseudoRandom; - return static_cast(tempVariable <= 254 ? tempVariable : (tempVariable - 254)); + return narrow_cast(tempVariable <= 254 ? tempVariable : (tempVariable - 254)); } static int FindMinimums(const std::array& intCharCounts, int min, std::array& mins) @@ -322,7 +322,7 @@ namespace ASCIIEncoder { static int DetermineConsecutiveDigitCount(const std::string& msg, int startpos) { auto begin = msg.begin() + startpos; - return static_cast(std::find_if_not(begin, msg.end(), IsDigit) - begin); + return narrow_cast(std::find_if_not(begin, msg.end(), IsDigit) - begin); } static uint8_t EncodeASCIIDigits(int digit1, int digit2) @@ -428,8 +428,8 @@ namespace C40Encoder { int c2 = sb.at(startPos + 1); int c3 = sb.at(startPos + 2); int v = (1600 * c1) + (40 * c2) + c3 + 1; - context.addCodeword(static_cast(v / 256)); - context.addCodeword(static_cast(v % 256)); + context.addCodeword(narrow_cast(v / 256)); + context.addCodeword(narrow_cast(v % 256)); } static void WriteNextTriplet(EncoderContext& context, std::string& buffer) @@ -494,7 +494,7 @@ namespace C40Encoder { int c = context.currentChar(); context.setCurrentPos(context.currentPos() + 1); int lastCharSize = encodeChar(c, buffer); - int unwritten = static_cast(buffer.length() / 3) * 2; + int unwritten = narrow_cast(buffer.length() / 3) * 2; int curCodewordCount = context.codewordCount() + unwritten; auto symbolInfo = context.updateSymbolInfo(curCodewordCount); int available = symbolInfo->dataCapacity() - curCodewordCount; @@ -682,17 +682,17 @@ namespace EdifactEncoder { int c4 = len >= 4 ? sb.at(startPos + 3) : 0; int v = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4; - int cw1 = (v >> 16) & 255; - int cw2 = (v >> 8) & 255; - int cw3 = v & 255; + uint8_t cw1 = (v >> 16) & 255; + uint8_t cw2 = (v >> 8) & 255; + uint8_t cw3 = v & 255; ByteArray res; res.reserve(3); - res.push_back(static_cast(cw1)); + res.push_back(cw1); if (len >= 2) { - res.push_back(static_cast(cw2)); + res.push_back(cw2); } if (len >= 3) { - res.push_back(static_cast(cw3)); + res.push_back(cw3); } return res; } diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 058527cf19..794409da26 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -52,7 +52,7 @@ static bool CorrectErrors(ByteArray& codewordBytes, int start, int dataCodewords // We don't care about errors in the error-correction codewords for (int i = 0; i < dataCodewords; i++) { if ((mode == ALL) || (i % 2 == (mode - 1))) - codewordBytes[i + start] = static_cast(codewordsInts[i / divisor]); + codewordBytes[i + start] = narrow_cast(codewordsInts[i / divisor]); } return true; diff --git a/core/src/oned/ODCode128Reader.cpp b/core/src/oned/ODCode128Reader.cpp index 42fa421b79..a90228de8f 100644 --- a/core/src/oned/ODCode128Reader.cpp +++ b/core/src/oned/ODCode128Reader.cpp @@ -237,7 +237,7 @@ Result Code128Reader::decodePattern(int rowNumber, PatternView& next, std::uniqu int xStart = next.pixelsInFront(); ByteArray rawCodes; rawCodes.reserve(20); - rawCodes.push_back(static_cast(startCode)); + rawCodes.push_back(narrow_cast(startCode)); Raw2TxtDecoder raw2txt(startCode); @@ -256,7 +256,7 @@ Result Code128Reader::decodePattern(int rowNumber, PatternView& next, std::uniqu if (!raw2txt.decode(code)) return {}; - rawCodes.push_back(static_cast(code)); + rawCodes.push_back(narrow_cast(code)); } if (Size(rawCodes) < minCharCount - 1) // stop code is missing in rawCodes diff --git a/core/src/oned/ODRowReader.cpp b/core/src/oned/ODRowReader.cpp index ef5bb3458f..339ec0d56d 100644 --- a/core/src/oned/ODRowReader.cpp +++ b/core/src/oned/ODRowReader.cpp @@ -21,10 +21,10 @@ Result RowReader::decodeSingleRow(int rowNumber, const BitArray& row) const if (*i) res.push_back(0); while ((i = row.getNextSetTo(i, !*i)) != row.end()) { - res.push_back(static_cast(i - li)); + res.push_back(narrow_cast(i - li)); li = i; } - res.push_back(static_cast(i - li)); + res.push_back(narrow_cast(i - li)); if (*(i-1)) res.push_back(0); diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index 631af0b07d..72f8989505 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -627,7 +627,7 @@ int DecodeMacroBlock(const std::vector& codewords, int codeIndex, DecoderRe case MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT: { uint64_t segmentCount; codeIndex = DecodeMacroOptionalNumericField(codewords, codeIndex + 1, segmentCount); - resultMetadata.setSegmentCount(static_cast(segmentCount)); + resultMetadata.setSegmentCount(narrow_cast(segmentCount)); break; } case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP: { @@ -639,7 +639,7 @@ int DecodeMacroBlock(const std::vector& codewords, int codeIndex, DecoderRe case MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM: { uint64_t checksum; codeIndex = DecodeMacroOptionalNumericField(codewords, codeIndex + 1, checksum); - resultMetadata.setChecksum(static_cast(checksum)); + resultMetadata.setChecksum(narrow_cast(checksum)); break; } case MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE: { diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index cfb94a774e..7465577c79 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -75,8 +75,8 @@ static void DecodeHanziSegment(BitSource& bits, int count, Content& result) // In the 0xB0A1 to 0xFAFE range assembledTwoBytes += 0x0A6A1; } - result += static_cast((assembledTwoBytes >> 8) & 0xFF); - result += static_cast(assembledTwoBytes & 0xFF); + result += narrow_cast((assembledTwoBytes >> 8) & 0xFF); + result += narrow_cast(assembledTwoBytes & 0xFF); count--; } } @@ -99,8 +99,8 @@ static void DecodeKanjiSegment(BitSource& bits, int count, Content& result) // In the 0xE040 to 0xEBBF range assembledTwoBytes += 0x0C140; } - result += static_cast(assembledTwoBytes >> 8); - result += static_cast(assembledTwoBytes); + result += narrow_cast(assembledTwoBytes >> 8); + result += narrow_cast(assembledTwoBytes); count--; } } @@ -111,7 +111,7 @@ static void DecodeByteSegment(BitSource& bits, int count, Content& result) result.reserve(count); for (int i = 0; i < count; i++) - result += static_cast(bits.readBits(8)); + result += narrow_cast(bits.readBits(8)); } static char ToAlphaNumericChar(int value) @@ -288,7 +288,7 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo else if (appInd < 100) // "10-99" result += std::to_string(appInd); else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222)) // "A-Za-z" - result += static_cast(appInd - 100); + result += narrow_cast(appInd - 100); else throw FormatError("Invalid AIM Application Indicator"); result.applicationIndicator = result.bytes.asString(); // see also above diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 2eb0e04c1a..0070718858 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -202,7 +202,7 @@ static DimensionEstimate EstimateDimension(const BitMatrix& image, PointF a, Poi auto moduleSize = (ms_a + ms_b) / 2; - int dimension = std::lround(distance(a, b) / moduleSize) + 7; + int dimension = narrow_cast(std::lround(distance(a, b) / moduleSize) + 7); int error = 1 - (dimension % 4); return {dimension + error, moduleSize, std::abs(error)}; diff --git a/core/src/qrcode/QREncoder.cpp b/core/src/qrcode/QREncoder.cpp index 2ce915fd0d..206e4cd70a 100644 --- a/core/src/qrcode/QREncoder.cpp +++ b/core/src/qrcode/QREncoder.cpp @@ -376,7 +376,7 @@ void GenerateECBytes(const ByteArray& dataBytes, int numEcBytes, ByteArray& ecBy ReedSolomonEncode(GenericGF::QRCodeField256(), message, numEcBytes); ecBytes.resize(numEcBytes); - std::transform(message.end() - numEcBytes, message.end(), ecBytes.begin(), [](auto c) { return static_cast(c); }); + std::transform(message.end() - numEcBytes, message.end(), ecBytes.begin(), [](auto c) { return narrow_cast(c); }); } diff --git a/core/src/qrcode/QRMaskUtil.cpp b/core/src/qrcode/QRMaskUtil.cpp index 0213730b2d..53c9b3ac58 100644 --- a/core/src/qrcode/QRMaskUtil.cpp +++ b/core/src/qrcode/QRMaskUtil.cpp @@ -138,7 +138,7 @@ static int ApplyMaskPenaltyRule4(const TritMatrix& matrix) auto numDarkCells = std::count_if(matrix.begin(), matrix.end(), [](Trit cell) { return cell; }); auto numTotalCells = matrix.size(); auto fivePercentVariances = std::abs(numDarkCells * 2 - numTotalCells) * 10 / numTotalCells; - return static_cast(fivePercentVariances * N4); + return narrow_cast(fivePercentVariances * N4); } // The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details. diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 52038adcc7..dc657ab667 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -156,7 +156,7 @@ static int totalImageLoadTime = 0; int timeSince(std::chrono::steady_clock::time_point startTime) { auto duration = std::chrono::steady_clock::now() - startTime; - return static_cast(std::chrono::duration_cast(duration).count()); + return narrow_cast(std::chrono::duration_cast(duration).count()); } // pre-load images into cache, so the disc io time does not end up in the timing measurement diff --git a/test/unit/ThresholdBinarizerTest.cpp b/test/unit/ThresholdBinarizerTest.cpp index d6695ba404..bcf8ab6839 100644 --- a/test/unit/ThresholdBinarizerTest.cpp +++ b/test/unit/ThresholdBinarizerTest.cpp @@ -13,7 +13,7 @@ using namespace ZXing; // Helper to parse a 0/1 string into a BitMatrix static BitMatrix ParseBitMatrix(const std::string& str, const int width) { - const int height = static_cast(str.length() / width); + const int height = narrow_cast(str.length() / width); BitMatrix mat(width, height); for (int y = 0; y < height; ++y) { diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index b00c47d2bb..e4997020c3 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -11,6 +11,7 @@ // Reader #include "ReadBarcode.h" +#include "ZXContainerAlgorithms.h" // Writer #include "BitMatrix.h" @@ -30,12 +31,6 @@ namespace py = pybind11; // Numpy array wrapper class for images (either BGR or GRAYSCALE) using Image = py::array_t; -template -OUT narrow(IN in) -{ - return static_cast(in); -} - std::ostream& operator<<(std::ostream& os, const Position& points) { for (const auto& p : points) os << p.x << "x" << p.y << " "; @@ -63,9 +58,9 @@ auto read_barcodes_impl(py::object _image, const BarcodeFormats& formats, bool t catch(...) { throw py::type_error("Unsupported type " + _type + ". Expect a PIL Image or numpy array"); } - const auto height = narrow(image.shape(0)); - const auto width = narrow(image.shape(1)); - auto channels = image.ndim() == 2 ? 1 : narrow(image.shape(2)); + const auto height = narrow_cast(image.shape(0)); + const auto width = narrow_cast(image.shape(1)); + auto channels = image.ndim() == 2 ? 1 : narrow_cast(image.shape(2)); ImageFormat imgfmt; if (_type.find("PIL.") != std::string::npos) { const auto mode = _image.attr("mode").cast(); @@ -117,7 +112,7 @@ Image write_barcode(BarcodeFormat format, std::string text, int width, int heigh auto r = result.mutable_unchecked<2>(); for (py::ssize_t y = 0; y < r.shape(0); y++) for (py::ssize_t x = 0; x < r.shape(1); x++) - r(y, x) = bitmap.get(narrow(x), narrow(y)) ? 0 : 255; + r(y, x) = bitmap.get(narrow_cast(x), narrow_cast(y)) ? 0 : 255; return result; } From 60944309c034395a228a61ec577ce89f2bf4baff Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 7 Jul 2022 02:10:20 +0200 Subject: [PATCH 177/180] ZXAlgorithms: rename `ZXContainerAlgoriths.h` to `ZXAlgorithms.h` --- core/CMakeLists.txt | 4 ++-- core/src/BarcodeFormat.cpp | 2 +- core/src/BitArray.h | 2 +- core/src/BitSource.cpp | 2 +- core/src/CharacterSet.cpp | 2 +- core/src/ConcentricFinder.h | 2 +- core/src/Content.cpp | 2 +- core/src/DecoderResult.h | 2 +- core/src/GS1.cpp | 2 +- core/src/GTIN.h | 2 +- core/src/GenericGFPoly.cpp | 2 +- core/src/GenericGFPoly.h | 2 +- core/src/HybridBinarizer.cpp | 2 +- core/src/Matrix.h | 2 +- core/src/Pattern.h | 2 +- core/src/Quadrilateral.h | 2 +- core/src/Result.cpp | 2 +- core/src/TextEncoder.cpp | 2 +- core/src/{ZXContainerAlgorithms.h => ZXAlgorithms.h} | 0 core/src/aztec/AZDetector.cpp | 2 +- core/src/aztec/AZHighLevelEncoder.cpp | 2 +- core/src/datamatrix/DMDataBlock.cpp | 2 +- core/src/datamatrix/DMDecoder.cpp | 2 +- core/src/datamatrix/DMEncoderContext.h | 2 +- core/src/datamatrix/DMHighLevelEncoder.cpp | 2 +- core/src/datamatrix/DMSymbolInfo.cpp | 2 +- core/src/oned/ODCodabarReader.cpp | 2 +- core/src/oned/ODCodabarWriter.cpp | 2 +- core/src/oned/ODCode128Reader.cpp | 2 +- core/src/oned/ODCode39Reader.cpp | 2 +- core/src/oned/ODCode39Writer.cpp | 2 +- core/src/oned/ODCode93Reader.cpp | 2 +- core/src/oned/ODCode93Writer.cpp | 2 +- core/src/oned/ODITFReader.cpp | 2 +- core/src/pdf417/PDFCodewordDecoder.cpp | 2 +- core/src/pdf417/PDFDetectionResult.cpp | 2 +- core/src/pdf417/PDFDetectionResultColumn.cpp | 2 +- core/src/pdf417/PDFEncoder.h | 2 +- core/src/pdf417/PDFHighLevelEncoder.cpp | 2 +- core/src/pdf417/PDFModulusPoly.h | 2 +- core/src/qrcode/QRCodecMode.cpp | 2 +- core/src/qrcode/QRDataBlock.cpp | 2 +- core/src/qrcode/QRDecoder.cpp | 2 +- test/blackbox/BlackboxTestRunner.cpp | 2 +- test/blackbox/TestReaderMain.cpp | 2 +- test/unit/datamatrix/DMHighLevelEncodeTest.cpp | 2 +- test/unit/pdf417/PDF417ErrorCorrectionTest.cpp | 2 +- wrappers/python/zxing.cpp | 2 +- 48 files changed, 48 insertions(+), 48 deletions(-) rename core/src/{ZXContainerAlgorithms.h => ZXAlgorithms.h} (100%) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 4088c65288..7382bcae71 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -75,11 +75,11 @@ set (COMMON_FILES src/TextUtfEncoding.h src/TextUtfEncoding.cpp src/TritMatrix.h + src/ZXAlgorithms.h src/ZXBigInteger.h src/ZXBigInteger.cpp src/ZXConfig.h src/ZXNullable.h - src/ZXContainerAlgorithms.h src/ZXTestSupport.h ) if (BUILD_READERS) @@ -148,8 +148,8 @@ set (PUBLIC_HEADERS src/Flags.h src/GTIN.h src/TextUtfEncoding.h + src/ZXAlgorithms.h src/ZXConfig.h - src/ZXContainerAlgorithms.h ) if (BUILD_READERS) set (PUBLIC_HEADERS ${PUBLIC_HEADERS} diff --git a/core/src/BarcodeFormat.cpp b/core/src/BarcodeFormat.cpp index 0bc05be826..e188f2908a 100644 --- a/core/src/BarcodeFormat.cpp +++ b/core/src/BarcodeFormat.cpp @@ -6,7 +6,7 @@ #include "BarcodeFormat.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/BitArray.h b/core/src/BitArray.h index 6f4829aee8..4c840262e6 100644 --- a/core/src/BitArray.h +++ b/core/src/BitArray.h @@ -8,7 +8,7 @@ #pragma once #include "ZXConfig.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #ifndef ZX_FAST_BIT_STORAGE #include "BitHacks.h" #endif diff --git a/core/src/BitSource.cpp b/core/src/BitSource.cpp index bc2771a932..3693e52607 100644 --- a/core/src/BitSource.cpp +++ b/core/src/BitSource.cpp @@ -7,7 +7,7 @@ #include "BitSource.h" #include "ByteArray.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include diff --git a/core/src/CharacterSet.cpp b/core/src/CharacterSet.cpp index 57ee86eb01..4c0c17bb16 100644 --- a/core/src/CharacterSet.cpp +++ b/core/src/CharacterSet.cpp @@ -6,7 +6,7 @@ #include "CharacterSet.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include diff --git a/core/src/ConcentricFinder.h b/core/src/ConcentricFinder.h index 8fe07d2945..ab86da8a1f 100644 --- a/core/src/ConcentricFinder.h +++ b/core/src/ConcentricFinder.h @@ -8,7 +8,7 @@ #include "BitMatrixCursor.h" #include "Pattern.h" #include "Quadrilateral.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 3829abe140..00ea9b3217 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -10,7 +10,7 @@ #include "GS1.h" #include "TextDecoder.h" #include "TextUtfEncoding.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" namespace ZXing { diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 34822a7791..2b916d0249 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -10,7 +10,7 @@ #include "Content.h" #include "Error.h" #include "StructuredAppend.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/GS1.cpp b/core/src/GS1.cpp index d083d8af0e..1b5994abfb 100644 --- a/core/src/GS1.cpp +++ b/core/src/GS1.cpp @@ -7,7 +7,7 @@ #include "GS1.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" namespace ZXing { diff --git a/core/src/GTIN.h b/core/src/GTIN.h index 9637be44ec..df9405c720 100644 --- a/core/src/GTIN.h +++ b/core/src/GTIN.h @@ -8,7 +8,7 @@ #pragma once #include "BarcodeFormat.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include diff --git a/core/src/GenericGFPoly.cpp b/core/src/GenericGFPoly.cpp index 8abe394e93..a2634f82d1 100644 --- a/core/src/GenericGFPoly.cpp +++ b/core/src/GenericGFPoly.cpp @@ -9,7 +9,7 @@ #include "GenericGF.h" #include "ZXConfig.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/GenericGFPoly.h b/core/src/GenericGFPoly.h index 11b03a70d6..70ce6f1a70 100644 --- a/core/src/GenericGFPoly.h +++ b/core/src/GenericGFPoly.h @@ -6,7 +6,7 @@ #pragma once -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/HybridBinarizer.cpp b/core/src/HybridBinarizer.cpp index bf14d51d6a..598bd2a860 100644 --- a/core/src/HybridBinarizer.cpp +++ b/core/src/HybridBinarizer.cpp @@ -10,7 +10,7 @@ #include "BitMatrixIO.h" #include "ByteArray.h" #include "Matrix.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/Matrix.h b/core/src/Matrix.h index a85ccf42c1..7b350ec735 100644 --- a/core/src/Matrix.h +++ b/core/src/Matrix.h @@ -7,7 +7,7 @@ #pragma once #include "Point.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/Pattern.h b/core/src/Pattern.h index 1b2a112c9b..f75bc7dbb0 100644 --- a/core/src/Pattern.h +++ b/core/src/Pattern.h @@ -5,7 +5,7 @@ #pragma once -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/Quadrilateral.h b/core/src/Quadrilateral.h index 9d3cea0201..a2620715a8 100644 --- a/core/src/Quadrilateral.h +++ b/core/src/Quadrilateral.h @@ -6,7 +6,7 @@ #pragma once #include "Point.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/Result.cpp b/core/src/Result.cpp index da29225cd1..00d70ae6aa 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -9,7 +9,7 @@ #include "DecoderResult.h" #include "TextDecoder.h" #include "TextUtfEncoding.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/TextEncoder.cpp b/core/src/TextEncoder.cpp index 21af9ee744..5ddde516bf 100644 --- a/core/src/TextEncoder.cpp +++ b/core/src/TextEncoder.cpp @@ -7,7 +7,7 @@ #include "CharacterSet.h" #include "TextUtfEncoding.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include "textcodec/Big5TextEncoder.h" #include "textcodec/GBTextEncoder.h" #include "textcodec/JPTextEncoder.h" diff --git a/core/src/ZXContainerAlgorithms.h b/core/src/ZXAlgorithms.h similarity index 100% rename from core/src/ZXContainerAlgorithms.h rename to core/src/ZXAlgorithms.h diff --git a/core/src/aztec/AZDetector.cpp b/core/src/aztec/AZDetector.cpp index d309ea6c7a..8f14fe07f6 100644 --- a/core/src/aztec/AZDetector.cpp +++ b/core/src/aztec/AZDetector.cpp @@ -14,7 +14,7 @@ #include "ReedSolomonDecoder.h" #include "ResultPoint.h" #include "WhiteRectDetector.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/aztec/AZHighLevelEncoder.cpp b/core/src/aztec/AZHighLevelEncoder.cpp index 0060050cd6..f17b5df637 100644 --- a/core/src/aztec/AZHighLevelEncoder.cpp +++ b/core/src/aztec/AZHighLevelEncoder.cpp @@ -9,7 +9,7 @@ #include "AZEncodingState.h" #include "AZToken.h" #include "BitArray.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/datamatrix/DMDataBlock.cpp b/core/src/datamatrix/DMDataBlock.cpp index 241a160ac5..710e67d087 100644 --- a/core/src/datamatrix/DMDataBlock.cpp +++ b/core/src/datamatrix/DMDataBlock.cpp @@ -7,7 +7,7 @@ #include "DMDataBlock.h" #include "DMVersion.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 69c5067208..6c30613221 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -17,7 +17,7 @@ #include "GenericGF.h" #include "ReedSolomonDecoder.h" #include "TextDecoder.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include "ZXTestSupport.h" #include diff --git a/core/src/datamatrix/DMEncoderContext.h b/core/src/datamatrix/DMEncoderContext.h index dabcf0aef6..ecd9b0b032 100644 --- a/core/src/datamatrix/DMEncoderContext.h +++ b/core/src/datamatrix/DMEncoderContext.h @@ -9,7 +9,7 @@ #include "ByteArray.h" #include "DMSymbolInfo.h" #include "DMSymbolShape.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/datamatrix/DMHighLevelEncoder.cpp b/core/src/datamatrix/DMHighLevelEncoder.cpp index 0ace173816..9741de5473 100644 --- a/core/src/datamatrix/DMHighLevelEncoder.cpp +++ b/core/src/datamatrix/DMHighLevelEncoder.cpp @@ -10,7 +10,7 @@ #include "CharacterSet.h" #include "DMEncoderContext.h" #include "TextEncoder.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/datamatrix/DMSymbolInfo.cpp b/core/src/datamatrix/DMSymbolInfo.cpp index 0a3c0ce848..f3505a69a5 100644 --- a/core/src/datamatrix/DMSymbolInfo.cpp +++ b/core/src/datamatrix/DMSymbolInfo.cpp @@ -7,7 +7,7 @@ #include "DMSymbolInfo.h" #include "DMSymbolShape.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include "ZXTestSupport.h" #include diff --git a/core/src/oned/ODCodabarReader.cpp b/core/src/oned/ODCodabarReader.cpp index b3fd40b0c3..f72b6ad44c 100644 --- a/core/src/oned/ODCodabarReader.cpp +++ b/core/src/oned/ODCodabarReader.cpp @@ -9,7 +9,7 @@ #include "DecodeHints.h" #include "Result.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/oned/ODCodabarWriter.cpp b/core/src/oned/ODCodabarWriter.cpp index 25c42edbfc..46a50569f9 100644 --- a/core/src/oned/ODCodabarWriter.cpp +++ b/core/src/oned/ODCodabarWriter.cpp @@ -8,7 +8,7 @@ #include "ODWriterHelper.h" #include "TextUtfEncoding.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/oned/ODCode128Reader.cpp b/core/src/oned/ODCode128Reader.cpp index a90228de8f..9f9f788e2f 100644 --- a/core/src/oned/ODCode128Reader.cpp +++ b/core/src/oned/ODCode128Reader.cpp @@ -8,7 +8,7 @@ #include "ODCode128Patterns.h" #include "Result.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/oned/ODCode39Reader.cpp b/core/src/oned/ODCode39Reader.cpp index 5eb916d6d2..6824075fe0 100644 --- a/core/src/oned/ODCode39Reader.cpp +++ b/core/src/oned/ODCode39Reader.cpp @@ -9,7 +9,7 @@ #include "DecodeHints.h" #include "DecoderResult.h" #include "Result.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include diff --git a/core/src/oned/ODCode39Writer.cpp b/core/src/oned/ODCode39Writer.cpp index d6732cd801..49347f3964 100644 --- a/core/src/oned/ODCode39Writer.cpp +++ b/core/src/oned/ODCode39Writer.cpp @@ -10,7 +10,7 @@ #include "ODWriterHelper.h" #include "TextEncoder.h" #include "TextUtfEncoding.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/oned/ODCode93Reader.cpp b/core/src/oned/ODCode93Reader.cpp index a2668f9af6..cfeaa0db3f 100644 --- a/core/src/oned/ODCode93Reader.cpp +++ b/core/src/oned/ODCode93Reader.cpp @@ -8,7 +8,7 @@ #include "ODCode93Reader.h" #include "Result.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/oned/ODCode93Writer.cpp b/core/src/oned/ODCode93Writer.cpp index fb2c26157c..9cca0771db 100644 --- a/core/src/oned/ODCode93Writer.cpp +++ b/core/src/oned/ODCode93Writer.cpp @@ -8,7 +8,7 @@ #include "ODWriterHelper.h" #include "TextUtfEncoding.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include "ZXTestSupport.h" #include diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index 27145e29c5..f2e8078196 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -9,7 +9,7 @@ #include "DecodeHints.h" #include "GTIN.h" #include "Result.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include diff --git a/core/src/pdf417/PDFCodewordDecoder.cpp b/core/src/pdf417/PDFCodewordDecoder.cpp index 9318156ad2..4b9d8c1094 100644 --- a/core/src/pdf417/PDFCodewordDecoder.cpp +++ b/core/src/pdf417/PDFCodewordDecoder.cpp @@ -6,7 +6,7 @@ #include "PDFCodewordDecoder.h" #include "BitArray.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/pdf417/PDFDetectionResult.cpp b/core/src/pdf417/PDFDetectionResult.cpp index c145a1be2e..b9f97b305a 100644 --- a/core/src/pdf417/PDFDetectionResult.cpp +++ b/core/src/pdf417/PDFDetectionResult.cpp @@ -6,7 +6,7 @@ #include "PDFDetectionResult.h" #include "PDFCodewordDecoder.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/pdf417/PDFDetectionResultColumn.cpp b/core/src/pdf417/PDFDetectionResultColumn.cpp index 8c85065889..4386746fda 100644 --- a/core/src/pdf417/PDFDetectionResultColumn.cpp +++ b/core/src/pdf417/PDFDetectionResultColumn.cpp @@ -7,7 +7,7 @@ #include "PDFDetectionResultColumn.h" #include "PDFBarcodeMetadata.h" #include "PDFBarcodeValue.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/pdf417/PDFEncoder.h b/core/src/pdf417/PDFEncoder.h index f58d1279ef..3f086dfd0d 100644 --- a/core/src/pdf417/PDFEncoder.h +++ b/core/src/pdf417/PDFEncoder.h @@ -7,7 +7,7 @@ #pragma once #include "CharacterSet.h" #include "PDFCompaction.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/pdf417/PDFHighLevelEncoder.cpp b/core/src/pdf417/PDFHighLevelEncoder.cpp index ea394d11c2..07f08b4421 100644 --- a/core/src/pdf417/PDFHighLevelEncoder.cpp +++ b/core/src/pdf417/PDFHighLevelEncoder.cpp @@ -11,7 +11,7 @@ #include "ECI.h" #include "TextEncoder.h" #include "ZXBigInteger.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/pdf417/PDFModulusPoly.h b/core/src/pdf417/PDFModulusPoly.h index ed44abe7d3..0de1c9132a 100644 --- a/core/src/pdf417/PDFModulusPoly.h +++ b/core/src/pdf417/PDFModulusPoly.h @@ -6,7 +6,7 @@ #pragma once -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/qrcode/QRCodecMode.cpp b/core/src/qrcode/QRCodecMode.cpp index 4937b1c858..9d018d64bd 100644 --- a/core/src/qrcode/QRCodecMode.cpp +++ b/core/src/qrcode/QRCodecMode.cpp @@ -7,7 +7,7 @@ #include "QRCodecMode.h" #include "QRVersion.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include #include diff --git a/core/src/qrcode/QRDataBlock.cpp b/core/src/qrcode/QRDataBlock.cpp index 0c63a52ef5..cedd8a303a 100644 --- a/core/src/qrcode/QRDataBlock.cpp +++ b/core/src/qrcode/QRDataBlock.cpp @@ -8,7 +8,7 @@ #include "QRErrorCorrectionLevel.h" #include "QRVersion.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" namespace ZXing::QRCode { diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 7465577c79..abe44ba8c5 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -20,7 +20,7 @@ #include "ReedSolomonDecoder.h" #include "StructuredAppend.h" #include "TextDecoder.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include "ZXTestSupport.h" #include diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index dc657ab667..be71b2f809 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -10,7 +10,7 @@ #include "ImageLoader.h" #include "ReadBarcode.h" #include "ThresholdBinarizer.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include "pdf417/PDFReader.h" #include "qrcode/QRReader.h" diff --git a/test/blackbox/TestReaderMain.cpp b/test/blackbox/TestReaderMain.cpp index 22691a6864..31f9063092 100644 --- a/test/blackbox/TestReaderMain.cpp +++ b/test/blackbox/TestReaderMain.cpp @@ -7,7 +7,7 @@ #include "BlackboxTestRunner.h" #include "ImageLoader.h" #include "ReadBarcode.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include "ZXFilesystem.h" #include diff --git a/test/unit/datamatrix/DMHighLevelEncodeTest.cpp b/test/unit/datamatrix/DMHighLevelEncodeTest.cpp index 3c0d437e53..5a8a57ef6e 100644 --- a/test/unit/datamatrix/DMHighLevelEncodeTest.cpp +++ b/test/unit/datamatrix/DMHighLevelEncodeTest.cpp @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "ByteArray.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include "datamatrix/DMHighLevelEncoder.h" #include "datamatrix/DMSymbolInfo.h" #include "datamatrix/DMSymbolShape.h" diff --git a/test/unit/pdf417/PDF417ErrorCorrectionTest.cpp b/test/unit/pdf417/PDF417ErrorCorrectionTest.cpp index 763d9e4dbf..b7def50056 100644 --- a/test/unit/pdf417/PDF417ErrorCorrectionTest.cpp +++ b/test/unit/pdf417/PDF417ErrorCorrectionTest.cpp @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "PseudoRandom.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" #include "gtest/gtest.h" diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index e4997020c3..1685777336 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -11,7 +11,7 @@ // Reader #include "ReadBarcode.h" -#include "ZXContainerAlgorithms.h" +#include "ZXAlgorithms.h" // Writer #include "BitMatrix.h" From 76828972cb7bef59c2bc3a5060fdac7ff0554dc4 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 7 Jul 2022 09:41:21 +0200 Subject: [PATCH 178/180] Result: minimize additions of new text related API Don't add new API until the debate in https://github.com/nu-book/zxing-cpp/issues/338#issuecomment-1174741554 has settled. --- core/src/Result.cpp | 2 ++ core/src/Result.h | 20 +++++++++++--------- example/ZXingReader.cpp | 1 - test/blackbox/BlackboxTestRunner.cpp | 2 +- test/unit/oned/ODCodaBarWriterTest.cpp | 2 +- test/unit/oned/ODCode128ReaderTest.cpp | 18 +++++++++--------- test/unit/oned/ODCode128WriterTest.cpp | 8 ++++---- test/unit/oned/ODCode39ExtendedModeTest.cpp | 2 +- test/unit/oned/ODCode39ReaderTest.cpp | 14 +++++++------- test/unit/oned/ODCode93ReaderTest.cpp | 2 +- wrappers/winrt/BarcodeReader.cpp | 11 +++-------- 11 files changed, 40 insertions(+), 42 deletions(-) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 00d70ae6aa..3ce1d103c7 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -77,10 +77,12 @@ std::wstring Result::utf16() const return _content.utf16(); } +#if 0 std::string Result::utf8ECI() const { return _content.text(TextMode::Utf8ECI); } +#endif ContentType Result::contentType() const { diff --git a/core/src/Result.h b/core/src/Result.h index 5469a0370f..1d60013f50 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -29,6 +29,15 @@ using Position = QuadrilateralI; */ class Result { + /** + * @brief utf8/utf16 is the bytes() content converted to utf8/16 based on ECI or guessed character set information + * + * Note: these two properties might only be available while transitioning text() from std::wstring to std::string. time will tell. + * see https://github.com/nu-book/zxing-cpp/issues/338 for a background discussion on the issue. + */ + std::string utf8() const; + std::wstring utf16() const; + public: Result() = default; @@ -56,15 +65,6 @@ class Result */ ByteArray bytesECI() const; - /** - * @brief utf8/utf16 is the bytes() content converted to utf8/16 based on ECI or guessed character set information - * - * Note: these two properties might only be available while transitioning text() from std::wstring to std::string. time will tell. - * see https://github.com/nu-book/zxing-cpp/issues/338 for a background discussion on the issue. - */ - std::string utf8() const; - std::wstring utf16() const; - #ifdef ZX_USE_UTF8 std::string text() const { return utf8(); } std::string ecLevel() const { return _ecLevel; } @@ -75,10 +75,12 @@ class Result std::wstring ecLevel() const { return {_ecLevel.begin(), _ecLevel.end()}; } #endif +#if 0 // disabled until final API decission is made /** * @brief utf8ECI is the standard content following the ECI protocol with every character set ECI segment transcoded to utf8 */ std::string utf8ECI() const; +#endif /** * @brief contentType gives a hint to the type of content found (Text/Binary/GS1/etc.) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 9ef814f7f9..163cf39858 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -206,7 +206,6 @@ int main(int argc, char* argv[]) } std::cout << "Text: \"" << (angleEscape ? escapeNonGraphical(result.text()) : result.text()) << "\"\n" - << "Utf8ECI: \"" << result.utf8ECI() << "\"\n" << "Bytes: " << ToHex(result.bytes()) << "\n" << "BytesECI: " << ToHex(result.bytesECI()) << "\n" << "Format: " << ToString(result.format()) << "\n" diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index be71b2f809..aec499faa4 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -136,7 +136,7 @@ static std::string checkResult(const fs::path& imgPath, std::string_view expecte } if (auto expected = readFile(".txt")) { - auto utf8Result = result.utf8(); + auto utf8Result = result.text(); return utf8Result != *expected ? fmt::format("Content mismatch: expected '{}' but got '{}'", *expected, utf8Result) : ""; } diff --git a/test/unit/oned/ODCodaBarWriterTest.cpp b/test/unit/oned/ODCodaBarWriterTest.cpp index 7294b5a942..9ae038db5e 100644 --- a/test/unit/oned/ODCodaBarWriterTest.cpp +++ b/test/unit/oned/ODCodaBarWriterTest.cpp @@ -55,7 +55,7 @@ TEST(ODCodaBarWriterTest, FullCircle) CodabarWriter().encode(text, 0, 0).getRow(0, row); auto hints = DecodeHints().setReturnCodabarStartEnd(true); Result res = CodabarReader(hints).decodeSingleRow(0, row); - EXPECT_EQ(text, res.utf8()); + EXPECT_EQ(text, res.text()); } TEST(ODCodaBarWriterTest, InvalidChars) diff --git a/test/unit/oned/ODCode128ReaderTest.cpp b/test/unit/oned/ODCode128ReaderTest.cpp index 530da630b7..7f3ad3e30d 100644 --- a/test/unit/oned/ODCode128ReaderTest.cpp +++ b/test/unit/oned/ODCode128ReaderTest.cpp @@ -39,7 +39,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 2, 2, 1, 2, 3, 1, 2, 2, 2, 1, 2, 2, 3, 1, 1, 2, 2, 2 }); auto result = parse('C', row); EXPECT_EQ(result.symbologyIdentifier(), "]C0"); - EXPECT_EQ(result.utf8(), "2001"); + EXPECT_EQ(result.text(), "2001"); } { @@ -47,7 +47,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 4, 1, 1, 1, 3, 1, 2, 2, 1, 2, 3, 1, 2, 2, 2, 1, 2, 2, 1, 3, 2, 1, 3, 1 }); auto result = parse('C', row); EXPECT_EQ(result.symbologyIdentifier(), "]C1"); - EXPECT_EQ(result.utf8(), "2001"); + EXPECT_EQ(result.text(), "2001"); } { @@ -55,7 +55,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 1, 1, 1, 3, 2, 3, 4, 1, 1, 1, 3, 1, 1, 3, 1, 1, 2, 3, 2, 1, 2, 3, 2, 1 }); auto result = parse('B', row); EXPECT_EQ(result.symbologyIdentifier(), "]C2"); - EXPECT_EQ(result.utf8(), "AB"); + EXPECT_EQ(result.text(), "AB"); } { @@ -63,7 +63,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 2, 1, 4, 1, 2, 1, 4, 1, 1, 1, 3, 1, 1, 3, 1, 1, 2, 3, 4, 2, 1, 2, 1, 1 }); auto result = parse('B', row); EXPECT_EQ(result.symbologyIdentifier(), "]C2"); - EXPECT_EQ(result.utf8(), "zB"); + EXPECT_EQ(result.text(), "zB"); } { @@ -71,7 +71,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 1, 1, 3, 1, 4, 1, 4, 1, 1, 1, 3, 1, 1, 1, 4, 1, 3, 1, 1, 1, 1, 3, 2, 3, 1, 2, 3, 1, 2, 2 }); auto result = parse('C', row); EXPECT_EQ(result.symbologyIdentifier(), "]C2"); - EXPECT_EQ(result.utf8(), "99A"); + EXPECT_EQ(result.text(), "99A"); } { @@ -79,7 +79,7 @@ TEST(ODCode128ReaderTest, SymbologyIdentifier) PatternRow row({ 2, 1, 2, 3, 2, 1, 4, 1, 1, 1, 3, 1, 1, 3, 1, 1, 2, 3, 3, 2, 2, 2, 1, 1 }); auto result = parse('B', row); EXPECT_EQ(result.symbologyIdentifier(), "]C0"); // Just ignoring, not giving FormatError - EXPECT_EQ(result.utf8(), "?\u001DB"); + EXPECT_EQ(result.text(), "?\u001DB"); } } @@ -90,7 +90,7 @@ TEST(ODCode128ReaderTest, ReaderInit) PatternRow row({ 1, 1, 1, 1, 4, 3, 1, 3, 1, 1, 4, 1 }); auto result = parse('C', row); EXPECT_FALSE(result.readerInit()); - EXPECT_EQ(result.utf8(), "92"); + EXPECT_EQ(result.text(), "92"); } { @@ -98,7 +98,7 @@ TEST(ODCode128ReaderTest, ReaderInit) PatternRow row({ 1, 1, 4, 3, 1, 1, 1, 1, 3, 1, 4, 1, 1, 1, 1, 1, 4, 3, 3, 3, 1, 1, 2, 1 }); auto result = parse('B', row); EXPECT_TRUE(result.readerInit()); - EXPECT_EQ(result.utf8(), "92"); + EXPECT_EQ(result.text(), "92"); } { @@ -106,6 +106,6 @@ TEST(ODCode128ReaderTest, ReaderInit) PatternRow row({ 3, 2, 1, 1, 2, 2, 1, 1, 4, 3, 1, 1, 2, 2, 3, 2, 1, 1, 1, 2, 1, 4, 2, 1 }); auto result = parse('B', row); EXPECT_TRUE(result.readerInit()); - EXPECT_EQ(result.utf8(), "92"); + EXPECT_EQ(result.text(), "92"); } } diff --git a/test/unit/oned/ODCode128WriterTest.cpp b/test/unit/oned/ODCode128WriterTest.cpp index a524164a5a..bc9db158be 100644 --- a/test/unit/oned/ODCode128WriterTest.cpp +++ b/test/unit/oned/ODCode128WriterTest.cpp @@ -98,7 +98,7 @@ TEST(ODCode128Writer, RoundtripGS1) auto encResult = Code128Writer().encode(toEncode, 0, 0); auto decResult = Decode(encResult); - auto actual = decResult.utf8(); + auto actual = decResult.text(); EXPECT_EQ(actual, expected); EXPECT_EQ(decResult.symbologyIdentifier(), "]C1"); } @@ -110,7 +110,7 @@ TEST(ODCode128Writer, RoundtripFNC1) auto encResult = Code128Writer().encode(toEncode, 0, 0); auto decResult = Decode(encResult); - auto actual = decResult.utf8(); + auto actual = decResult.text(); EXPECT_EQ(actual, expected); EXPECT_EQ(decResult.symbologyIdentifier(), "]C0"); } @@ -126,7 +126,7 @@ TEST(ODCode128Writer, EncodeSwitchCodesetFromAToB) auto actual = LineMatrixToString(encoded); EXPECT_EQ(actual, expected); - auto actualRoundTrip = Decode(encoded).utf8(); + auto actualRoundTrip = Decode(encoded).text(); EXPECT_EQ(actualRoundTrip, toEncode); } @@ -141,6 +141,6 @@ TEST(ODCode128Writer, EncodeSwitchCodesetFromBToA) auto actual = LineMatrixToString(encoded); EXPECT_EQ(actual, expected); - auto actualRoundTrip = Decode(encoded).utf8(); + auto actualRoundTrip = Decode(encoded).text(); EXPECT_EQ(actualRoundTrip, toEncode); } diff --git a/test/unit/oned/ODCode39ExtendedModeTest.cpp b/test/unit/oned/ODCode39ExtendedModeTest.cpp index cbf307d3ba..d25d7a6205 100644 --- a/test/unit/oned/ODCode39ExtendedModeTest.cpp +++ b/test/unit/oned/ODCode39ExtendedModeTest.cpp @@ -21,7 +21,7 @@ static std::string Decode(std::string_view encoded) Code39Reader sut(hints); BitArray row = Utility::ParseBitArray(encoded, '1'); Result result = sut.decodeSingleRow(0, row); - return result.utf8(); + return result.text(); } TEST(ODCode39ExtendedModeTest, Decode) diff --git a/test/unit/oned/ODCode39ReaderTest.cpp b/test/unit/oned/ODCode39ReaderTest.cpp index 893c2a008c..e33f587a0c 100644 --- a/test/unit/oned/ODCode39ReaderTest.cpp +++ b/test/unit/oned/ODCode39ReaderTest.cpp @@ -33,39 +33,39 @@ TEST(ODCode39ReaderTest, SymbologyIdentifier) PatternRow row({ 2, 1, 1, 1, 1, 2, 1, 1, 2 }); auto result = parse(row); EXPECT_EQ(result.symbologyIdentifier(), "]A0"); - EXPECT_EQ(result.utf8(), "A"); + EXPECT_EQ(result.text(), "A"); } { // "A" with checksum PatternRow row({ 2, 1, 1, 1, 1, 2, 1, 1, 2, 0, 2, 1, 1, 1, 1, 2, 1, 1, 2 }); auto result = parse(row, DecodeHints().setValidateCode39CheckSum(true)); EXPECT_EQ(result.symbologyIdentifier(), "]A3"); - EXPECT_EQ(result.utf8(), "A"); + EXPECT_EQ(result.text(), "A"); result = parse(row); EXPECT_EQ(result.symbologyIdentifier(), "]A0"); - EXPECT_EQ(result.utf8(), "AA"); + EXPECT_EQ(result.text(), "AA"); } { // Extended "a" PatternRow row({ 1, 2, 1, 1, 1, 2, 1, 2, 1, 0, 2, 1, 1, 1, 1, 2, 1, 1, 2 }); auto result = parse(row, DecodeHints().setTryCode39ExtendedMode(true)); EXPECT_EQ(result.symbologyIdentifier(), "]A4"); - EXPECT_EQ(result.utf8(), "a"); + EXPECT_EQ(result.text(), "a"); result = parse(row); EXPECT_EQ(result.symbologyIdentifier(), "]A0"); - EXPECT_EQ(result.utf8(), "+A"); + EXPECT_EQ(result.text(), "+A"); } { // Extended "a" with checksum PatternRow row({ 1, 2, 1, 1, 1, 2, 1, 2, 1, 0, 2, 1, 1, 1, 1, 2, 1, 1, 2, 0, 2, 1, 1, 2, 1, 1, 2, 1, 1 }); auto result = parse(row, DecodeHints().setTryCode39ExtendedMode(true).setValidateCode39CheckSum(true)); EXPECT_EQ(result.symbologyIdentifier(), "]A7"); - EXPECT_EQ(result.utf8(), "a"); + EXPECT_EQ(result.text(), "a"); result = parse(row); EXPECT_EQ(result.symbologyIdentifier(), "]A0"); - EXPECT_EQ(result.utf8(), "+A8"); + EXPECT_EQ(result.text(), "+A8"); } } diff --git a/test/unit/oned/ODCode93ReaderTest.cpp b/test/unit/oned/ODCode93ReaderTest.cpp index f11a70ec79..93d7557e3b 100644 --- a/test/unit/oned/ODCode93ReaderTest.cpp +++ b/test/unit/oned/ODCode93ReaderTest.cpp @@ -21,7 +21,7 @@ static std::string Decode(std::string_view input) Code93Reader sut(hints); auto row = Utility::ParseBitArray(input, '1'); auto result = sut.decodeSingleRow(0, row); - return result.utf8(); + return result.text(); } TEST(ODCode93ReaderTest, Decode) diff --git a/wrappers/winrt/BarcodeReader.cpp b/wrappers/winrt/BarcodeReader.cpp index 5d3360985e..7855bcd10f 100644 --- a/wrappers/winrt/BarcodeReader.cpp +++ b/wrappers/winrt/BarcodeReader.cpp @@ -149,15 +149,10 @@ BarcodeType BarcodeReader::ConvertNativeToRuntime(BarcodeFormat format) } } -static Platform::String^ ToPlatformString(const std::wstring& str) -{ - return ref new Platform::String(str.c_str(), (unsigned)str.length()); -} - static Platform::String^ ToPlatformString(const std::string& str) { - auto ptr = (const uint8_t*)str.data(); - return ToPlatformString(std::wstring(ptr, ptr + str.length())); + std::wstring wstr = TextUtfEncoding::FromUtf8(str); + return ref new Platform::String(wstr.c_str(), (unsigned)wstr.length()); } ReadResult^ @@ -193,7 +188,7 @@ BarcodeReader::Read(SoftwareBitmap^ bitmap, int cropWidth, int cropHeight) auto result = ReadBarcode(img, *m_hints); if (result.isValid()) { - return ref new ReadResult(ToPlatformString(ZXing::ToString(result.format())), ToPlatformString(result.utf16()), ConvertNativeToRuntime(result.format())); + return ref new ReadResult(ToPlatformString(ZXing::ToString(result.format())), ToPlatformString(result.text()), ConvertNativeToRuntime(result.format())); } } else { throw std::runtime_error("Failed to read bitmap's data"); From 4fe52f2f791c7f81c43f8eb0b1ca11c9d70772d6 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 7 Jul 2022 09:51:14 +0200 Subject: [PATCH 179/180] WINRT: replace usage of deprecated BarcodeFormat enum names --- wrappers/winrt/BarcodeReader.cpp | 60 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/wrappers/winrt/BarcodeReader.cpp b/wrappers/winrt/BarcodeReader.cpp index 7855bcd10f..84303c6a64 100644 --- a/wrappers/winrt/BarcodeReader.cpp +++ b/wrappers/winrt/BarcodeReader.cpp @@ -68,39 +68,39 @@ BarcodeFormat BarcodeReader::ConvertRuntimeToNative(BarcodeType type) { switch (type) { case BarcodeType::AZTEC: - return BarcodeFormat::AZTEC; + return BarcodeFormat::Aztec; case BarcodeType::CODABAR: - return BarcodeFormat::CODABAR; + return BarcodeFormat::Codabar; case BarcodeType::CODE_128: - return BarcodeFormat::CODE_128; + return BarcodeFormat::Code128; case BarcodeType::CODE_39: - return BarcodeFormat::CODE_39; + return BarcodeFormat::Code39; case BarcodeType::CODE_93: - return BarcodeFormat::CODE_93; + return BarcodeFormat::Code93; case BarcodeType::DATA_MATRIX: - return BarcodeFormat::DATA_MATRIX; + return BarcodeFormat::DataMatrix; case BarcodeType::EAN_13: - return BarcodeFormat::EAN_13; + return BarcodeFormat::EAN13; case BarcodeType::EAN_8: - return BarcodeFormat::EAN_8; + return BarcodeFormat::EAN8; case BarcodeType::ITF: return BarcodeFormat::ITF; case BarcodeType::MAXICODE: - return BarcodeFormat::MAXICODE; + return BarcodeFormat::MaxiCode; case BarcodeType::PDF_417: - return BarcodeFormat::PDF_417; + return BarcodeFormat::PDF417; case BarcodeType::QR_CODE: - return BarcodeFormat::QR_CODE; + return BarcodeFormat::QRCode; case BarcodeType::MICRO_QR_CODE: return BarcodeFormat::MicroQRCode; case BarcodeType::RSS_14: - return BarcodeFormat::RSS_14; + return BarcodeFormat::DataBar; case BarcodeType::RSS_EXPANDED: - return BarcodeFormat::RSS_EXPANDED; + return BarcodeFormat::DataBarExpanded; case BarcodeType::UPC_A: - return BarcodeFormat::UPC_A; + return BarcodeFormat::UPCA; case BarcodeType::UPC_E: - return BarcodeFormat::UPC_E; + return BarcodeFormat::UPCE; default: std::wstring typeAsString = type.ToString()->Begin(); throw std::invalid_argument("Unknown Barcode Type: " + TextUtfEncoding::ToUtf8(typeAsString)); @@ -110,39 +110,39 @@ BarcodeFormat BarcodeReader::ConvertRuntimeToNative(BarcodeType type) BarcodeType BarcodeReader::ConvertNativeToRuntime(BarcodeFormat format) { switch (format) { - case BarcodeFormat::AZTEC: + case BarcodeFormat::Aztec: return BarcodeType::AZTEC; - case BarcodeFormat::CODABAR: + case BarcodeFormat::Codabar: return BarcodeType::CODABAR; - case BarcodeFormat::CODE_128: + case BarcodeFormat::Code128: return BarcodeType::CODE_128; - case BarcodeFormat::CODE_39: + case BarcodeFormat::Code39: return BarcodeType::CODE_39; - case BarcodeFormat::CODE_93: + case BarcodeFormat::Code93: return BarcodeType::CODE_93; - case BarcodeFormat::DATA_MATRIX: + case BarcodeFormat::DataMatrix: return BarcodeType::DATA_MATRIX; - case BarcodeFormat::EAN_13: + case BarcodeFormat::EAN13: return BarcodeType::EAN_13; - case BarcodeFormat::EAN_8: + case BarcodeFormat::EAN8: return BarcodeType::EAN_8; case BarcodeFormat::ITF: return BarcodeType::ITF; - case BarcodeFormat::MAXICODE: + case BarcodeFormat::MaxiCode: return BarcodeType::MAXICODE; - case BarcodeFormat::PDF_417: + case BarcodeFormat::PDF417: return BarcodeType::PDF_417; - case BarcodeFormat::QR_CODE: + case BarcodeFormat::QRCode: return BarcodeType::QR_CODE; case BarcodeFormat::MicroQRCode: return BarcodeType::MICRO_QR_CODE; - case BarcodeFormat::RSS_14: + case BarcodeFormat::DataBar: return BarcodeType::RSS_14; - case BarcodeFormat::RSS_EXPANDED: + case BarcodeFormat::DataBarExpanded: return BarcodeType::RSS_EXPANDED; - case BarcodeFormat::UPC_A: + case BarcodeFormat::UPCA: return BarcodeType::UPC_A; - case BarcodeFormat::UPC_E: + case BarcodeFormat::UPCE: return BarcodeType::UPC_E; default: throw std::invalid_argument("Unknown Barcode Format "); From f89db69e34543c508f80e807535dab3012eea7ad Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 7 Jul 2022 10:59:58 +0200 Subject: [PATCH 180/180] release: bump version number from 1.3.0 to 1.4.0 --- CMakeLists.txt | 2 +- wrappers/python/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 50e2702c03..e947ce8dbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 3.14) -project (ZXing VERSION "1.3.0" LANGUAGES CXX) +project (ZXing VERSION "1.4.0" LANGUAGES CXX) option (BUILD_WRITERS "Build with writer support (encoders)" ON) option (BUILD_READERS "Build with reader support (decoders)" ON) diff --git a/wrappers/python/setup.py b/wrappers/python/setup.py index 873767a51d..eb47198d46 100644 --- a/wrappers/python/setup.py +++ b/wrappers/python/setup.py @@ -59,7 +59,7 @@ def build_extension(self, ext): # "local_scheme": "no-local-version", # "tag_regex": "v?([0-9]+.[0-9]+.[0-9]+)", # }, - version='1.3.0', + version='1.4.0', description='Python bindings for the zxing-cpp barcode library', long_description=long_description, long_description_content_type="text/markdown",

http://en.wikipedia.org/wiki/Interleaved_2_of_5 * is a great reference for Interleaved 2 of 5 information.