From a8ad5ccc8fd50cad8bf115442235cf860562b245 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 31 Dec 2024 12:49:26 +0100 Subject: [PATCH 001/104] CI: remove 32bit ARM part from publish job (fix regression) --- .github/workflows/publish-winrt.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/publish-winrt.yml b/.github/workflows/publish-winrt.yml index 41e38f94ce..880ad39a69 100644 --- a/.github/workflows/publish-winrt.yml +++ b/.github/workflows/publish-winrt.yml @@ -55,9 +55,6 @@ jobs: - uses: actions/download-artifact@v4 with: name: winrt-x64-artifacts - - uses: actions/download-artifact@v4 - with: - name: winrt-ARM-artifacts - uses: actions/download-artifact@v4 with: name: winrt-ARM64-artifacts From 0bb732b8bc747bc398647b6a3eb4597e623a219f Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 31 Dec 2024 15:59:13 +0100 Subject: [PATCH 002/104] Version: add ZXing::Version()/ZXing_Version() functions for runtime access See also #881. --- core/src/ZXingC.cpp | 10 ++++++++-- core/src/ZXingC.h | 2 ++ core/src/ZXingCpp.cpp | 16 ++++++++++++---- core/src/ZXingCpp.h | 10 ++++++---- wrappers/c/ZXingCTest.c | 2 +- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/core/src/ZXingC.cpp b/core/src/ZXingC.cpp index d5b8833a2b..3327abe6a8 100644 --- a/core/src/ZXingC.cpp +++ b/core/src/ZXingC.cpp @@ -7,12 +7,12 @@ #include "ZXingC.h" #include "ZXingCpp.h" +#include "Version.h" #include #include #include #include -#include #include using namespace ZXing; @@ -411,9 +411,15 @@ char* ZXing_LastErrorMsg() return copy(std::exchange(lastErrorMsg, {})); } +const char* ZXing_Version() +{ + return ZXING_VERSION_STR; +} + void ZXing_free(void* ptr) { - free(ptr); + if (ptr != ZXing_Version()) + free(ptr); } } // extern "C" diff --git a/core/src/ZXingC.h b/core/src/ZXingC.h index 2a5a279a02..282f528574 100644 --- a/core/src/ZXingC.h +++ b/core/src/ZXingC.h @@ -312,6 +312,8 @@ ZXing_Image* ZXing_WriteBarcodeToImage(const ZXing_Barcode* barcode, const ZXing /* ZXing_LastErrorMsg() returns NULL in case there is no last error and a copy of the string otherwise. */ char* ZXing_LastErrorMsg(); +const char* ZXing_Version(); + void ZXing_free(void* ptr); #ifdef __cplusplus diff --git a/core/src/ZXingCpp.cpp b/core/src/ZXingCpp.cpp index 28b2870ab4..782c5fc842 100644 --- a/core/src/ZXingCpp.cpp +++ b/core/src/ZXingCpp.cpp @@ -3,12 +3,20 @@ */ // SPDX-License-Identifier: Apache-2.0 -#ifdef ZXING_EXPERIMENTAL_API - #include "ZXingCpp.h" +#include "Version.h" + namespace ZXing { +const std::string& Version() +{ + static std::string res = ZXING_VERSION_STR; + return res; +} + +#ifdef ZXING_EXPERIMENTAL_API + BarcodeFormats SupportedBarcodeFormats(Operation op) { switch (op) { @@ -31,6 +39,6 @@ BarcodeFormats SupportedBarcodeFormats(Operation op) return {}; // unreachable code } -} // namespace ZXing - #endif // ZXING_EXPERIMENTAL_API + +} // namespace ZXing diff --git a/core/src/ZXingCpp.h b/core/src/ZXingCpp.h index 254d3d53cf..dd115823e0 100644 --- a/core/src/ZXingCpp.h +++ b/core/src/ZXingCpp.h @@ -9,10 +9,12 @@ #include "ReadBarcode.h" #include "WriteBarcode.h" -#ifdef ZXING_EXPERIMENTAL_API - namespace ZXing { +const std::string& Version(); + +#ifdef ZXING_EXPERIMENTAL_API + enum class Operation { Create, @@ -23,6 +25,6 @@ enum class Operation BarcodeFormats SupportedBarcodeFormats(Operation op = Operation::CreateOrRead); -} // namespace ZXing - #endif // ZXING_EXPERIMENTAL_API + +} // namespace ZXing diff --git a/wrappers/c/ZXingCTest.c b/wrappers/c/ZXingCTest.c index 6896523ad1..161c6dd5a1 100644 --- a/wrappers/c/ZXingCTest.c +++ b/wrappers/c/ZXingCTest.c @@ -16,7 +16,7 @@ int usage(char* pname) { - fprintf(stderr, "Usage: %s FILE [FORMATS]\n", pname); + fprintf(stderr, "ZXingCTest %s, usage: %s FILE [FORMATS]\n", ZXing_Version(), pname); return 1; } From 5a7ae447e0edadaa192181d40664c3e513820fa6 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 31 Dec 2024 17:42:48 +0100 Subject: [PATCH 003/104] ci: fix Swift package build regression --- Package.swift | 2 +- core/src/ZXingC.cpp | 5 +++++ core/src/ZXingCpp.cpp | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index b514af7cd4..5faf571d10 100644 --- a/Package.swift +++ b/Package.swift @@ -15,7 +15,7 @@ let package = Package( .target( name: "ZXingCppCore", path: "core/src", - exclude: ["libzint"], + exclude: ["libzint", "ZXingC.cpp", "ZXingCpp.cpp"], publicHeadersPath: ".", cxxSettings: [ .define("ZXING_READERS") diff --git a/core/src/ZXingC.cpp b/core/src/ZXingC.cpp index 3327abe6a8..9f2d342ecf 100644 --- a/core/src/ZXingC.cpp +++ b/core/src/ZXingC.cpp @@ -7,7 +7,12 @@ #include "ZXingC.h" #include "ZXingCpp.h" + +#if __has_include("Version.h") #include "Version.h" +#else // this is mainly a workaround for a missing autogenerated Version.h in the XCode/CocoaPods corner +#define ZXING_VERSION_STR "undefined" +#endif #include #include diff --git a/core/src/ZXingCpp.cpp b/core/src/ZXingCpp.cpp index 782c5fc842..d5fa34609b 100644 --- a/core/src/ZXingCpp.cpp +++ b/core/src/ZXingCpp.cpp @@ -5,7 +5,11 @@ #include "ZXingCpp.h" +#if __has_include("Version.h") #include "Version.h" +#else // this is mainly a workaround for a missing autogenerated Version.h in the XCode/CocoaPods corner +#define ZXING_VERSION_STR "undefined" +#endif namespace ZXing { From b9c62cc58589e9273fea9877f38ad76683f1662c Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 1 Jan 2025 14:48:38 +0100 Subject: [PATCH 004/104] zint: update auto-fetch URL to include DXFilmEdge support (latest rev) I thought to use the latest release 2.13.0 instead but that lacks support for in memory file generation which is crucial to our use case. --- core/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 658abfc5a0..fb19617bb8 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -536,7 +536,7 @@ if (ZXING_WRITERS_NEW) target_include_directories (ZXing PRIVATE "$") else() include(../zxing.cmake) - zxing_add_package(zint zint https://github.com/zint/zint.git 55a7369cd8c4a6b58bcd62f02a3a2d486952c897) + zxing_add_package(zint zint https://github.com/zint/zint.git 7a9fdd6cd00cd5bfd0082705d934c13ef84f25e1) target_link_libraries (ZXing PRIVATE zint) endif() endif() From d6068bcebeb8fd9f0d35a99b00d202be86a14dbe Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 1 Jan 2025 22:30:28 +0100 Subject: [PATCH 005/104] README: mention the `ZXING_WRITERS=NEW` build option --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 90eb314f39..112324a1da 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Thanks a lot for your contribution! [Note:] * DataBar used to be called RSS. - * DataBar, DX Film Edge, MaxiCode, Micro QR Code and rMQR Code are not supported for writing. + * DataBar, DX Film Edge, MaxiCode, Micro QR Code and rMQR Code are not supported for writing (unless the library is configured `ZXING_WRITERS=NEW` and `ZING_EXPERIMENTAL_API=ON`). * Building with only C++17 (see [CMakeLists.txt](https://github.com/zxing-cpp/zxing-cpp/blob/d4b0f502775857f257d13efd25fb840ece1bca3e/CMakeLists.txt#L45)) changes the behavior of the library: it then lacks support for DataBarLimited and multi-symbol and position independent detection for DataMatrix. ## Getting Started @@ -86,7 +86,7 @@ To see the full capability of the API, have a look at [`ZXingReader.cpp`](exampl 2. Call `encode()` with text content and the image size. This returns a [`BitMatrix`](core/src/BitMatrix.h) which is a binary image of the barcode where `true` == visual black and `false` == visual white. 3. Convert the bit matrix to your native image format. See also the `ToMatrix(BitMatrix&)` helper function. -As an example, have a look at [`ZXingWriter.cpp`](example/ZXingWriter.cpp). +As an example, have a look at [`ZXingWriter.cpp`](example/ZXingWriter.cpp). That file also contains example code showing the new `ZXING_EXPERIMENTAL_API` for writing barcodes. ## Web Demos - [Read barcodes](https://zxing-cpp.github.io/zxing-cpp/demo_reader.html) From d71b83b20490000fe7634e7f562364b67dcd3876 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 2 Jan 2025 09:52:32 +0100 Subject: [PATCH 006/104] CI: Update ZXingWinRT.nuspec (remove 32bit ARM) --- wrappers/winrt/nuget/ZXingWinRT.nuspec | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/wrappers/winrt/nuget/ZXingWinRT.nuspec b/wrappers/winrt/nuget/ZXingWinRT.nuspec index f6b61e8bf6..8115e9e936 100644 --- a/wrappers/winrt/nuget/ZXingWinRT.nuspec +++ b/wrappers/winrt/nuget/ZXingWinRT.nuspec @@ -15,8 +15,6 @@ zxing barcode scanner qrcode - - @@ -26,4 +24,4 @@ - \ No newline at end of file + From 746ef5139cd2b027f66c22f8aafc65c44d0f41b0 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 2 Jan 2025 10:00:29 +0100 Subject: [PATCH 007/104] CI: Update ZXingWinRT.nuspec (remove 32bit ARM, for real...) no comment... :-/ --- wrappers/winrt/nuget/ZXingWinRT.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/winrt/nuget/ZXingWinRT.nuspec b/wrappers/winrt/nuget/ZXingWinRT.nuspec index 8115e9e936..a98207f7a4 100644 --- a/wrappers/winrt/nuget/ZXingWinRT.nuspec +++ b/wrappers/winrt/nuget/ZXingWinRT.nuspec @@ -15,7 +15,7 @@ zxing barcode scanner qrcode - + From 8f95431dfad9890c34680bff1b7f69989e69d9fa Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 4 Jan 2025 02:03:36 +0100 Subject: [PATCH 008/104] C-API: correct wrong statement about default inclusion of the feature --- wrappers/c/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrappers/c/README.md b/wrappers/c/README.md index aa0d8c1a94..4381483d26 100644 --- a/wrappers/c/README.md +++ b/wrappers/c/README.md @@ -1,10 +1,10 @@ # C bindings for zxing-cpp -This is a preview/proposal for a C-API to zxing-cpp. If you have any comments or feedback, please have a look at https://github.com/zxing-cpp/zxing-cpp/discussions/583. +This is about the C-API of zxing-cpp. If you have any comments or feedback, please have a look at https://github.com/zxing-cpp/zxing-cpp/discussions/583. ## Installation -It is currently included in the default build to be trivially accessible for everyone. +To enable the C-API, the library needs to be configured with `cmake -DZING_C_API=ON`. Probably the easiest way to play with the C-API is to just modify the [ZXingCTest.c](https://github.com/zxing-cpp/zxing-cpp/blob/master/wrappers/c/ZXingCTest.c) file. From 63702f33b2e21c92c2b089fc6f5d9e92d70e74b5 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 4 Jan 2025 20:51:09 +0100 Subject: [PATCH 009/104] dotnet: target netstandard2.0 and net5.0 This fixes #884. --- wrappers/dotnet/ZXingCpp/ZXingCpp.cs | 13 +++++++++++++ wrappers/dotnet/ZXingCpp/ZXingCpp.csproj | 6 ++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/wrappers/dotnet/ZXingCpp/ZXingCpp.cs b/wrappers/dotnet/ZXingCpp/ZXingCpp.cs index 90eae5fed2..4b2b277890 100644 --- a/wrappers/dotnet/ZXingCpp/ZXingCpp.cs +++ b/wrappers/dotnet/ZXingCpp/ZXingCpp.cs @@ -6,6 +6,9 @@ namespace ZXingCpp { using System; +#if NETSTANDARD +using System.Text; +#endif using System.Runtime.InteropServices; using System.Collections.Generic; @@ -125,7 +128,17 @@ public static IntPtr CheckError(IntPtr ptr, string? msg = null) public static string MarshalAsString(IntPtr ptr) { ptr = CheckError(ptr, "ZXing C-API returned a NULL char*."); +#if NET string res = Marshal.PtrToStringUTF8(ptr) ?? ""; +#else + string res; + unsafe + { + int length = 0; + for (byte* i = (byte*)ptr; *i != 0; i++, length++); + res = Encoding.UTF8.GetString((byte*)ptr, length); + } +#endif ZXing_free(ptr); return res; } diff --git a/wrappers/dotnet/ZXingCpp/ZXingCpp.csproj b/wrappers/dotnet/ZXingCpp/ZXingCpp.csproj index 3db7fdd2a9..e80a7b80de 100644 --- a/wrappers/dotnet/ZXingCpp/ZXingCpp.csproj +++ b/wrappers/dotnet/ZXingCpp/ZXingCpp.csproj @@ -1,12 +1,14 @@  - netstandard2.1 + netstandard2.0;net5.0 + 9 + true enable ZXingCpp - 0.3.0-alpha + 0.4.0-alpha Axel Waggershauser zxing-cpp From 82806f5f92173b8cb4e1e9bee13a2d07a33fb69f Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 5 Jan 2025 23:41:29 +0100 Subject: [PATCH 010/104] c++: fix improper use of NDEBUG Thanks to Antonio Rojas for pointing it out to me. --- core/src/HybridBinarizer.cpp | 12 ++++++------ core/src/oned/ODDataBarCommon.h | 2 +- test/unit/datamatrix/DMEncodeDecodeTest.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/HybridBinarizer.cpp b/core/src/HybridBinarizer.cpp index 33562817a0..8f8a6f90cc 100644 --- a/core/src/HybridBinarizer.cpp +++ b/core/src/HybridBinarizer.cpp @@ -143,7 +143,7 @@ static std::shared_ptr CalculateMatrix(const uint8_t* __restrict lumi { auto matrix = std::make_shared(width, height); -#ifndef NDEBUG +#ifdef PRINT_DEBUG Matrix out(width, height); Matrix out2(width, height); #endif @@ -163,7 +163,7 @@ static std::shared_ptr CalculateMatrix(const uint8_t* __restrict lumi int average = sum / 25; ThresholdBlock(luminances, xoffset, yoffset, average, rowStride, *matrix); -#ifndef NDEBUG +#ifdef PRINT_DEBUG for (int yy = 0; yy < 8; ++yy) for (int xx = 0; xx < 8; ++xx) { out.set(xoffset + xx, yoffset + yy, blackPoints(x, y)); @@ -173,7 +173,7 @@ static std::shared_ptr CalculateMatrix(const uint8_t* __restrict lumi } } -#ifndef NDEBUG +#ifdef PRINT_DEBUG std::ofstream file("thresholds.pnm"); file << "P5\n" << out.width() << ' ' << out.height() << "\n255\n"; file.write(reinterpret_cast(out.data()), out.size()); @@ -260,7 +260,7 @@ static std::shared_ptr ThresholdImage(const ImageView iv, const Matri { auto matrix = std::make_shared(iv.width(), iv.height()); -#ifndef NDEBUG +#ifdef PRINT_DEBUG Matrix out(iv.width(), iv.height()); #endif @@ -270,7 +270,7 @@ static std::shared_ptr ThresholdImage(const ImageView iv, const Matri int xoffset = std::min(x * BLOCK_SIZE, iv.width() - BLOCK_SIZE); ThresholdBlock(iv.data(), xoffset, yoffset, thresholds(x, y), iv.rowStride(), *matrix); -#ifndef NDEBUG +#ifdef PRINT_DEBUG for (int yy = 0; yy < 8; ++yy) for (int xx = 0; xx < 8; ++xx) out.set(xoffset + xx, yoffset + yy, thresholds(x, y)); @@ -278,7 +278,7 @@ static std::shared_ptr ThresholdImage(const ImageView iv, const Matri } } -#ifndef NDEBUG +#ifdef PRINT_DEBUG std::ofstream file("thresholds_new.pnm"); file << "P5\n" << out.width() << ' ' << out.height() << "\n255\n"; file.write(reinterpret_cast(out.data()), out.size()); diff --git a/core/src/oned/ODDataBarCommon.h b/core/src/oned/ODDataBarCommon.h index cb937ebb17..88330ab580 100644 --- a/core/src/oned/ODDataBarCommon.h +++ b/core/src/oned/ODDataBarCommon.h @@ -37,7 +37,7 @@ inline bool IsFinder(int a, int b, int c, int d, int e) // (c < 5 + 10 * e) && (a < 2 + 4 * e) && (4 * a > n); -#if !defined(NDEBUG) && 0 +#if defined(PRINT_DEBUG) && 0 printf("["); for (bool v : {w + 5 > 9 * n, diff --git a/test/unit/datamatrix/DMEncodeDecodeTest.cpp b/test/unit/datamatrix/DMEncodeDecodeTest.cpp index 33f1587740..84238109eb 100644 --- a/test/unit/datamatrix/DMEncodeDecodeTest.cpp +++ b/test/unit/datamatrix/DMEncodeDecodeTest.cpp @@ -21,7 +21,7 @@ namespace { ASSERT_EQ(matrix.empty(), false); DecoderResult res = DataMatrix::Decode(matrix); -#ifndef NDEBUG +#ifdef PRINT_DEBUG if (!res.isValid() || data != res.text()) SaveAsPBM(matrix, "failed-datamatrix.pbm", 4); #endif From dff7da1ee4ac72343b9e74570352c381f093e9e1 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 7 Jan 2025 01:30:44 +0100 Subject: [PATCH 011/104] python: re-enable test_write_read_cycle_numpy on Windows With the current version 19.42.34435.0, it seems to work again. This 'fixes' #735. --- wrappers/python/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/python/test.py b/wrappers/python/test.py index f3d1282a79..655669ccb9 100644 --- a/wrappers/python/test.py +++ b/wrappers/python/test.py @@ -120,7 +120,7 @@ def test_write_read_cycle_buffer(self): self.check_res(zxingcpp.read_barcode(img), format, text) - @unittest.skipIf(not has_numpy or platform.system() == "Windows", "need numpy for read/write tests") + @unittest.skipIf(not has_numpy, "need numpy for read/write tests") def test_write_read_cycle_numpy(self): import numpy as np format = BF.QRCode From 149c40aa42dcdf43246b43ff282a2825c5c0541b Mon Sep 17 00:00:00 2001 From: Christian Schmitz Date: Fri, 10 Jan 2025 16:28:11 +0100 Subject: [PATCH 012/104] Changed thread_local to ZX_THREAD_LOCAL (#889) Change `thread_local` to `ZX_THREAD_LOCAL` so it can be disabled centrally (as it used to). --- core/src/GlobalHistogramBinarizer.cpp | 5 +++-- core/src/ZXConfig.h | 1 + core/src/ZXingC.cpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/GlobalHistogramBinarizer.cpp b/core/src/GlobalHistogramBinarizer.cpp index fb0b3ec26e..2777bb2283 100644 --- a/core/src/GlobalHistogramBinarizer.cpp +++ b/core/src/GlobalHistogramBinarizer.cpp @@ -8,6 +8,7 @@ #include "BitMatrix.h" #include "Pattern.h" +#include "ZXConfig.h" #include #include @@ -115,7 +116,7 @@ bool GlobalHistogramBinarizer::getPatternRow(int row, int rotation, PatternRow& // during the histogram calculation and during the sharpen+threshold operation. Additionally, if we // perform the ThresholdSharpened function on pixStride==1 data, the auto-vectorizer makes that part // 8x faster on an AVX2 cpu which easily recovers the extra cost that we pay for the copying. - thread_local std::vector line; + ZX_THREAD_LOCAL std::vector line; if (std::abs(buffer.pixStride()) > 4) { line.resize(lineView.size()); std::copy(lineView.begin(), lineView.end(), line.begin()); @@ -127,7 +128,7 @@ bool GlobalHistogramBinarizer::getPatternRow(int row, int rotation, PatternRow& if (threshold <= 0) return false; - thread_local std::vector binarized; + ZX_THREAD_LOCAL std::vector binarized; // the optimizer can generate a specialized version for pixStride==1 (non-rotated input) that is about 8x faster on AVX2 hardware if (lineView.begin().stride == 1) ThresholdSharpened(lineView, threshold, binarized); diff --git a/core/src/ZXConfig.h b/core/src/ZXConfig.h index d00d3df942..9190a1062e 100644 --- a/core/src/ZXConfig.h +++ b/core/src/ZXConfig.h @@ -9,6 +9,7 @@ // in e.g. the ReedSolomonDecoder. It is disabled by default. It can be enabled by modifying the following define. // Note: The Apple clang compiler until XCode 8 does not support c++11's thread_local. // The alternative 'static' makes the code thread unsafe. +// for Windows in Visual Studio 2019 on Intel 64-bit using thread_local causes a dependency to VCRUNTIME140_1.dll, so you need 2019 runtime DLLs instead of only 2015 version. #define ZX_THREAD_LOCAL thread_local // '' (nothing), 'thread_local' or 'static' // The Galoir Field abstractions used in Reed-Solomon error correction code can use more memory to eliminate a modulo diff --git a/core/src/ZXingC.cpp b/core/src/ZXingC.cpp index 9f2d342ecf..c502e2c639 100644 --- a/core/src/ZXingC.cpp +++ b/core/src/ZXingC.cpp @@ -7,6 +7,7 @@ #include "ZXingC.h" #include "ZXingCpp.h" +#include "ZXConfig.h" #if __has_include("Version.h") #include "Version.h" @@ -22,7 +23,7 @@ using namespace ZXing; -static thread_local std::string lastErrorMsg; +static ZX_THREAD_LOCAL std::string lastErrorMsg; static Barcodes emptyBarcodes{}; // used to prevent new heap allocation for each empty result template R transmute_cast(const T& v) noexcept From 7be9048e8176fba4f6ffa3dcf528282c6b0151a9 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 17 Jan 2025 08:53:40 +0100 Subject: [PATCH 013/104] README: fix typos `ZING` -> `ZXING`, fixes #891 --- README.md | 2 +- wrappers/c/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 112324a1da..0145bcda94 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Thanks a lot for your contribution! [Note:] * DataBar used to be called RSS. - * DataBar, DX Film Edge, MaxiCode, Micro QR Code and rMQR Code are not supported for writing (unless the library is configured `ZXING_WRITERS=NEW` and `ZING_EXPERIMENTAL_API=ON`). + * DataBar, DX Film Edge, MaxiCode, Micro QR Code and rMQR Code are not supported for writing (unless the library is configured `ZXING_WRITERS=NEW` and `ZXING_EXPERIMENTAL_API=ON`). * Building with only C++17 (see [CMakeLists.txt](https://github.com/zxing-cpp/zxing-cpp/blob/d4b0f502775857f257d13efd25fb840ece1bca3e/CMakeLists.txt#L45)) changes the behavior of the library: it then lacks support for DataBarLimited and multi-symbol and position independent detection for DataMatrix. ## Getting Started diff --git a/wrappers/c/README.md b/wrappers/c/README.md index 4381483d26..5de23e40bc 100644 --- a/wrappers/c/README.md +++ b/wrappers/c/README.md @@ -4,7 +4,7 @@ This is about the C-API of zxing-cpp. If you have any comments or feedback, plea ## Installation -To enable the C-API, the library needs to be configured with `cmake -DZING_C_API=ON`. +To enable the C-API, the library needs to be configured with `cmake -DZXING_C_API=ON`. Probably the easiest way to play with the C-API is to just modify the [ZXingCTest.c](https://github.com/zxing-cpp/zxing-cpp/blob/master/wrappers/c/ZXingCTest.c) file. From a920817b6fe0508cc4aca9003003c2812a78e935 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 31 Jan 2025 10:25:53 +0100 Subject: [PATCH 014/104] QRDetector: require the central FP square to be at least 3 pixels This is considered a fix for #888. --- core/src/qrcode/QRDetector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 37476843bc..1d9e7350de 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -43,7 +43,7 @@ PatternView FindPattern(const PatternView& view) { return FindLeftGuard(view, PATTERN.size(), [](const PatternView& view, int spaceInPixel) { // perform a fast plausability test for 1:1:3:1:1 pattern - if (view[2] < 2 * std::max(view[0], view[4]) || view[2] < std::max(view[1], view[3])) + if (view[2] < 3 || view[2] < 2 * std::max(view[0], view[4]) || view[2] < std::max(view[1], view[3])) return 0.; return IsPattern(view, PATTERN, spaceInPixel, 0.1); // the requires 4, here we accept almost 0 }); From 1ef992f9492af01027a58077957ecce23c8b7ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 13 Feb 2025 08:01:52 +0100 Subject: [PATCH 015/104] [Python] Support using system CMake (#902) Add `cmake` PyPI dependency only if the program is not found, and use the system version instead. This avoids unnecessary dependencies on third-party binary packages, and improves portability by using downstream-patched CMake version. --- wrappers/python/pyproject.toml | 1 - wrappers/python/setup.py | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/wrappers/python/pyproject.toml b/wrappers/python/pyproject.toml index 16484fe3ee..367d8e85f9 100644 --- a/wrappers/python/pyproject.toml +++ b/wrappers/python/pyproject.toml @@ -3,7 +3,6 @@ requires = [ "setuptools>=42", "setuptools_scm", "wheel", - "cmake>=3.15", "pybind11[global]", ] build-backend = "setuptools.build_meta" diff --git a/wrappers/python/setup.py b/wrappers/python/setup.py index d98aab2266..811dbbf8f2 100644 --- a/wrappers/python/setup.py +++ b/wrappers/python/setup.py @@ -1,3 +1,4 @@ +import json import os import platform import subprocess @@ -42,6 +43,20 @@ def build_extension(self, ext): subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp) +def get_setup_requires(): + try: + subp = subprocess.run(['cmake', '-E', 'capabilities'], stdout=subprocess.PIPE) + except OSError: + pass + else: + if subp.returncode == 0: + version = json.loads(subp.stdout).get('version', {}) + version_split = (version.get('major', 0), version.get('minor', 0)) + if version_split >= (3, 15): + return [] + return ['cmake>=3.15'] + + with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() @@ -76,6 +91,7 @@ def build_extension(self, ext): "Topic :: Multimedia :: Graphics", ], python_requires=">=3.6", + setup_requires=get_setup_requires(), ext_modules=[CMakeExtension('zxingcpp')], cmdclass=dict(build_ext=CMakeBuild), zip_safe=False, From 225e24f44b11a38797778927a4b2fdece5f03ecb Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 19 Feb 2025 23:28:12 +0100 Subject: [PATCH 016/104] publish-android: add comment on how to 'release' after publication --- .github/workflows/publish-android.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/publish-android.yml b/.github/workflows/publish-android.yml index afb3be26c4..24ab171b60 100644 --- a/.github/workflows/publish-android.yml +++ b/.github/workflows/publish-android.yml @@ -35,3 +35,6 @@ jobs: ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.OSSRH_USERNAME }} ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.OSSRH_PASSWORD }} run: ./gradlew publishReleasePublicationToSonatypeRepository + + +# To actually publish the the blob, login to https://s01.oss.sonatype.org/#stagingRepositories, then press "Close", then "Release". From dde0548a7e0f0c48545553f6fdd60bbd7a7e3dc1 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 19 Feb 2025 23:29:01 +0100 Subject: [PATCH 017/104] README: update current Sponsor infos --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0145bcda94..9dc75d7f5b 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/ax Named Sponsors: * [KURZ Digital Solutions GmbH & Co. KG](https://github.com/kurzdigital) * [Useful Sensors Inc](https://github.com/usefulsensors) -* [EUREKAM](https://eurekam.fr/) +* [synedra](https://synedra.com/) Thanks a lot for your contribution! From 37b847798a1af55d3a289a9516a751fcafae3c23 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 19 Feb 2025 23:38:41 +0100 Subject: [PATCH 018/104] android: switch SNAPSHOT version to 2.4.0 and update README See #897 and #833. --- wrappers/android/README.md | 4 ++-- wrappers/android/zxingcpp/build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wrappers/android/README.md b/wrappers/android/README.md index 0e10bb804d..f3a3ef1acd 100644 --- a/wrappers/android/README.md +++ b/wrappers/android/README.md @@ -4,8 +4,8 @@ The easiest way to use the library is to fetch if from _mavenCentral_. Simply add **one** of the following two lines ```gradle -implementation("io.github.zxing-cpp:android:2.2.0") -implementation("io.github.zxing-cpp:android:2.3.0-SNAPSHOT") +implementation("io.github.zxing-cpp:android:2.3.0") +implementation("io.github.zxing-cpp:android:2.4.0-SNAPSHOT") ``` to your `build.gradle.kts` file in the `dependencies` section. To access the SNAPSHOT version, you also need to add a separate repositories entry in your `build.cradle.kts` file: diff --git a/wrappers/android/zxingcpp/build.gradle.kts b/wrappers/android/zxingcpp/build.gradle.kts index 8cdb1d46c8..0a71d48006 100644 --- a/wrappers/android/zxingcpp/build.gradle.kts +++ b/wrappers/android/zxingcpp/build.gradle.kts @@ -58,7 +58,7 @@ dependencies { val publishSnapshot: String? by project group = "io.github.zxing-cpp" -version = "2.3.0" + if (publishSnapshot == "true") "-SNAPSHOT" else "" +version = "2.4.0" + if (publishSnapshot == "true") "-SNAPSHOT" else "" val javadocJar by tasks.registering(Jar::class) { archiveClassifier.set("javadoc") From b46534d9bca0ce1832ee192c80e7395b999341b7 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 20 Mar 2025 10:20:56 +0100 Subject: [PATCH 019/104] QRCode: improve detection rate for circular finder patterns Those are actually invalid according to the spec but quite popular. This is related to #910 and also #892, the latter still does not scan, though. --- core/src/ConcentricFinder.cpp | 10 ++++++++-- core/src/ConcentricFinder.h | 2 ++ core/src/qrcode/QRDetector.cpp | 6 ++++-- test/blackbox/BlackboxTestRunner.cpp | 10 +++++----- test/samples/qrcode-2/qr-circles-1.png | Bin 0 -> 1700 bytes test/samples/qrcode-2/qr-circles-1.txt | 1 + 6 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 test/samples/qrcode-2/qr-circles-1.png create mode 100644 test/samples/qrcode-2/qr-circles-1.txt diff --git a/core/src/ConcentricFinder.cpp b/core/src/ConcentricFinder.cpp index e21f65e664..e43dd38b79 100644 --- a/core/src/ConcentricFinder.cpp +++ b/core/src/ConcentricFinder.cpp @@ -156,8 +156,14 @@ static std::vector CollectRingPoints(const BitMatrix& image, PointF cent static std::optional FitQadrilateralToPoints(PointF center, std::vector& points) { auto dist2Center = [c = center](auto a, auto b) { return distance(a, c) < distance(b, c); }; + auto [minDistElem, maxDistElem] = std::minmax_element(points.begin(), points.end(), dist2Center); + + // check if points are on a circle: for a square the min/max ratio is 0.7, for a circle it is 1 + if (distance(center, *minDistElem) / distance(center, *maxDistElem) > 0.85) + return {}; + // 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::rotate(points.begin(), maxDistElem, points.end()); std::array corners; corners[0] = &points[0]; @@ -207,7 +213,7 @@ static bool QuadrilateralIsPlausibleSquare(const QuadrilateralF q, int lineIndex return m >= lineIndex * 2 && m > M / 3; } -static std::optional FitSquareToPoints(const BitMatrix& image, PointF center, int range, int lineIndex, bool backup) +std::optional FitSquareToPoints(const BitMatrix& image, PointF center, int range, int lineIndex, bool backup) { auto points = CollectRingPoints(image, center, range, lineIndex, backup); if (points.empty()) diff --git a/core/src/ConcentricFinder.h b/core/src/ConcentricFinder.h index 68f29b6d45..f8490e0c21 100644 --- a/core/src/ConcentricFinder.h +++ b/core/src/ConcentricFinder.h @@ -101,6 +101,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 FitSquareToPoints(const BitMatrix& image, PointF center, int range, int lineIndex, bool backup); + std::optional FindConcentricPatternCorners(const BitMatrix& image, PointF center, int range, int ringIndex); struct ConcentricPattern : public PointF diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 1d9e7350de..728a7db520 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -385,7 +385,7 @@ DetectorResult SampleQR(const BitMatrix& image, const FinderPatternSet& fp) } // otherwise the simple estimation used by upstream is used as a best guess fallback - if (!image.isIn(br)) { + if (!image.isIn(br) || !FitSquareToPoints(image, fp.bl, fp.bl.size, 2, false)) { br = fp.tr - fp.tl + fp.bl; brOffset = PointF(0, 0); } @@ -404,7 +404,9 @@ DetectorResult SampleQR(const BitMatrix& image, const FinderPatternSet& fp) dimension = version->dimension(); mod2Pix = Mod2Pix(dimension, brOffset, {fp.tl, fp.tr, br, fp.bl}); } -#if 1 + +#if 1 // finding and evaluating the alignment patterns to enable a tiled sampling of the symbol + auto& apM = version->alignmentPatternCenters(); // alignment pattern positions in modules auto apP = Matrix>(Size(apM), Size(apM)); // found/guessed alignment pattern positions in pixels const int N = Size(apM) - 1; diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 94bd072750..45f7073166 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -579,11 +579,11 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set { 16, 16, 270 }, }); - runTests("qrcode-2", "QRCode", 51, { - { 45, 48, 0 }, - { 45, 48, 90 }, - { 45, 48, 180 }, - { 45, 48, 270 }, + runTests("qrcode-2", "QRCode", 52, { + { 46, 49, 0 }, + { 46, 49, 90 }, + { 46, 49, 180 }, + { 46, 49, 270 }, { 22, 1, pure }, // the misread is the 'outer' symbol in 16.png }); diff --git a/test/samples/qrcode-2/qr-circles-1.png b/test/samples/qrcode-2/qr-circles-1.png new file mode 100644 index 0000000000000000000000000000000000000000..4a2ae9ee864148c2252bbbcd5fe0c959aa301115 GIT binary patch literal 1700 zcmV;V23z@wP)9glgM))rRaHPhKsY!!8X6h^004aJoudE%20=+gK~#9!?VP`l95ocjXZJ24 zbSD%E$}0{5-4+zkZlSH+t(#;;%IF z_>K3g(etyP{XF}Ij*gCwj*hn*i>vR~P*@4fi$bFS@I{*MS z?`PvaegSHL%e!1l%gyEB<9OcBr>A#MFNcr5yq+q5sU4(C>IqT@-rB4FJ-y!Io9Sm4 zG8WP$^#rK{ui%5JVScpuYE1Z9=7V%eJwfZts|I^p-fb}nj?@A33eLuaRYl_40rT$T zlO%pM?BeSr`eX2LJny&Di-}rp1`EgYF7vC(%Zk++aCpy?@cHCMej5Ls-1xmDeDoS0 zAopSm#9Qzn%~TiZ?$a^hyL7u)=MJm*LA@{Sm{%2ZlRGJ4m8GviVF(*f{WZ*ZI)Ft5=I7JOdQVi-Ozj~Wv`FV7r@ z_Jy=xWL{(Tls&WhKn3$U9^_Db0OAI=$6MFE=-}zV;pG*|gRC{*4w%0shyywyFV z%7RecN2@FdwOE46W}t&`JLpIFb4Gp5Co-~+4AV~P>t2!37|AflN`37wGMXncV7_Rt zgLpgW^TnLNXGtQ5-$)(ePkf#xa`=tZAqK~1iXw;KNL`+_)12YoJ>4=R0)J_PodhCNj59sMZmvyCxP8S@2j_DFmpZ>odS*+HKd z<~J8m9qw`8$#VhK;U4$iJQq+M?jinA%Jw=qy&d%ZLL}nPj-X%7^Cp$?S{Q8GJabbS z?+0LfTj!1d&I8UK(D4^i7mf^PAME%{sS8Jj6AN(G4O%Z(dmXg1gFYv` zjo`VS`HR=9--Xrb#Qt+=-(a85&jtw#FXqaXaAfG;%eY` z75-VF&H=n;Xs?5KJLumY%oEOk*;QUH&u*MJ8SJ0SWsMytRtPbKavAeSdmWtC4tfmX z-8pNZK6lTj{Ihqmd-sga|Nel+zH?65UI%ULpvMm86+dud&H9|%^WNEsx9f9m&+!B2 z%t4=X`}R6Gy&d#8;=2K|585|Uok3%S_Rdsi&^$r$1E@FDK^r^h-y_XCzcTf?nj5c^>_o9T;7m$Ub`X(YIV=xyI5-b-I5-b7pG_y2`%5dk9G}l;n0stL zzj7`Q-P>^nBfoOA4mb~TlpRFmR}RaA91hMp8V}B|%-@^&XVG4MW&Y;QKacnFEAw}U z{x=CmcH>|j@Ee5N0rx^M*b>{1#`ZH3pC|2=0&d5%GUxj#rGneBXX2bkrBpyW%!3@Q z1I~jSZ3nzc3qnMG<*+=+k?`^$>zq$ Date: Mon, 24 Mar 2025 09:47:15 +0000 Subject: [PATCH 020/104] AZDecoder: add flag `haveFNC1` and only erase `` if set. Fixes #916 The first byte is lost when recognizing the Aztec code --- core/src/aztec/AZDecoder.cpp | 43 +++++++++++++++++-------------- test/unit/aztec/AZDecoderTest.cpp | 17 ++++++++++++ 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index f38fbe3fef..46f96dda46 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -249,12 +249,13 @@ static StructuredAppendInfo ParseStructuredAppend(ByteArray& bytes) return sai; } -static void DecodeContent(const BitArray& bits, Content& res) +static void DecodeContent(const BitArray& bits, Content& res, bool &haveFNC1) { Table latchTable = Table::UPPER; // table most recently latched to Table shiftTable = Table::UPPER; // table to use for the next read auto remBits = BitArrayView(bits); + haveFNC1 = false; while (remBits.size() >= (shiftTable == Table::DIGIT ? 4 : 5)) { // see ISO/IEC 24778:2008 7.3.1.2 regarding padding bits if (shiftTable == Table::BINARY) { @@ -283,6 +284,7 @@ static void DecodeContent(const BitArray& bits, Content& res) } else if (std::strcmp(str, "FLGN") == 0) { int flg = remBits.readBits(3); if (flg == 0) { // FNC1 + haveFNC1 = true; res.push_back(29); // May be removed at end if first/second FNC1 } else if (flg <= 6) { // FLG(1) to FLG(6) ECI @@ -305,9 +307,10 @@ DecoderResult Decode(const BitArray& bits) { Content res; res.symbology = {'z', '0', 3}; + bool haveFNC1; try { - DecodeContent(bits, res); + DecodeContent(bits, res, haveFNC1); } catch (const std::exception&) { // see BitArrayView::readBits return FormatError(); } @@ -321,24 +324,24 @@ DecoderResult Decode(const BitArray& bits) 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.bytes.size() > 1 && res.bytes[0] == 29) { - res.symbology.modifier = '1'; // GS1 - res.symbology.aiFlag = AIFlag::GS1; - res.erase(0, 1); // Remove FNC1 - } 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 - res.symbology.aiFlag = AIFlag::AIM; - 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.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.symbology.aiFlag = AIFlag::AIM; - 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 (haveFNC1) { + if (res.bytes[0] == 29) { + res.symbology.modifier = '1'; // GS1 + res.symbology.aiFlag = AIFlag::GS1; + res.erase(0, 1); // Remove FNC1 + } 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 + res.symbology.aiFlag = AIFlag::AIM; + 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.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.symbology.aiFlag = AIFlag::AIM; + 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) diff --git a/test/unit/aztec/AZDecoderTest.cpp b/test/unit/aztec/AZDecoderTest.cpp index 2d85313e4a..b9db236e25 100644 --- a/test/unit/aztec/AZDecoderTest.cpp +++ b/test/unit/aztec/AZDecoderTest.cpp @@ -145,6 +145,23 @@ static DecoderResult getData(std::string_view bitStr) return Aztec::Decode(bits); } +TEST(AZDecoderTest, InitialGS) +{ + // Issue #916 The first byte is lost when recognizing the Aztec code + { + // Initial + auto data = getData("1111101000000111010101010100010000100101001110001011100111000101001111111111111"); + EXPECT_EQ(data.symbologyIdentifier(), "]z0"); + EXPECT_EQ(data.content().text(TextMode::Hex), "1D 55 10 94 E2 E7 14 FF"); + } + { + // Initial FNC1 (invalid GS1 data) + auto data = getData("00000000000001011011111001100001000010010100111000101110011100010100111111111111"); + EXPECT_EQ(data.symbologyIdentifier(), "]z1"); + EXPECT_EQ(data.content().text(TextMode::Hex), "55 10 94 E2 E7 14 FF"); + } +} + TEST(AZDecoderTest, SymbologyIdentifier) { { From a76458e66bc80a9245b2c8021b749e9f8b15c530 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 24 Mar 2025 14:58:26 +0100 Subject: [PATCH 021/104] PerspectiveTransform: add comment/link to doc --- core/src/PerspectiveTransform.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/PerspectiveTransform.h b/core/src/PerspectiveTransform.h index cc85fcb20c..3ac6699828 100644 --- a/core/src/PerspectiveTransform.h +++ b/core/src/PerspectiveTransform.h @@ -13,9 +13,11 @@ namespace ZXing { /** -*

This class implements a perspective transform in two dimensions. Given four source and four +* This class implements a perspective transform in two dimensions. Given four source and four * destination points, it will compute the transformation implied between them. The code is based -* directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.

+* directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56. +* +* See also e.g. https://math.stackexchange.com/a/339033 */ class PerspectiveTransform { From 8bfb03e8d76f9c5ec7e1e8f93210972350473520 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 24 Mar 2025 15:08:11 +0100 Subject: [PATCH 022/104] Pattern: slightly relax threshold for space component of short patterns There used to be a special case (`* 0.33` instead of `* 0.5`) for the space part of thresholds of short patterns (presumably targeted at QRCode finder patterns). I can't remember why I did this special casing. Anyway, removing it fixes #905 (which has basically failed because of 1 pixel) without any other measurable side effects. --- core/src/Pattern.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/Pattern.h b/core/src/Pattern.h index 891060b45e..cf6963c25f 100644 --- a/core/src/Pattern.h +++ b/core/src/Pattern.h @@ -184,7 +184,7 @@ double IsPattern(const PatternView& view, const FixedPattern& p if (minQuietZone && spaceInPixel < minQuietZone * modSize.space) return 0; - const BarAndSpace thr = {modSize[0] * .75 + .5, modSize[1] / (2 + (LEN < 6)) + .5}; + const BarAndSpace thr = {modSize[0] * .75 + .5, modSize[1] * .5 + .5}; for (int x = 0; x < LEN; ++x) if (std::abs(view[x] - pattern[x] * modSize[x]) > thr[x]) From 93175e071d1719c44c0ad1193539141eb846999f Mon Sep 17 00:00:00 2001 From: gitlost Date: Tue, 25 Mar 2025 09:04:23 +0000 Subject: [PATCH 023/104] AZDecoder: use `Content::erase()` in `ParseStructuredAppend()` to keep ECI positions in sync Content: fix `erase()`/`insert()` typos `pos` -> `e.pos` SymbologyIdentifier: allow for modifier >= 10 in `toString()` AZDecoderTest: add helper `check_si()` for `SymbologyIdentifier()` and add ECI test cases --- core/src/Content.cpp | 4 +- core/src/Content.h | 3 +- core/src/aztec/AZDecoder.cpp | 9 +-- test/unit/aztec/AZDecoderTest.cpp | 123 ++++++++++++++---------------- 4 files changed, 65 insertions(+), 74 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index d0f2e80a16..7468cac947 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -81,7 +81,7 @@ void Content::erase(int pos, int n) bytes.erase(bytes.begin() + pos, bytes.begin() + pos + n); for (auto& e : encodings) if (e.pos > pos) - pos -= n; + e.pos -= n; } void Content::insert(int pos, const std::string& str) @@ -89,7 +89,7 @@ void Content::insert(int pos, const std::string& str) bytes.insert(bytes.begin() + pos, str.begin(), str.end()); for (auto& e : encodings) if (e.pos > pos) - pos += Size(str); + e.pos += Size(str); } bool Content::canProcess() const diff --git a/core/src/Content.h b/core/src/Content.h index 99e5a01e71..ab5823b60e 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -28,7 +28,8 @@ struct SymbologyIdentifier std::string toString(bool hasECI = false) const { - return code ? ']' + std::string(1, code) + static_cast(modifier + eciModifierOffset * hasECI) : std::string(); + int modVal = (modifier >= 'A' ? modifier - 'A' + 10 : modifier - '0') + eciModifierOffset * hasECI; + return code ? ']' + std::string(1, code) + static_cast((modVal >= 10 ? 'A' - 10 : '0') + modVal) : std::string(); } }; diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index 46f96dda46..844be46894 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -220,9 +220,9 @@ static ECI ParseECIValue(BitArrayView& bits, const int flg) /** * See ISO/IEC 24778:2008 Section 8 */ -static StructuredAppendInfo ParseStructuredAppend(ByteArray& bytes) +static StructuredAppendInfo ParseStructuredAppend(Content& res) { - std::string text(bytes.begin(), bytes.end()); + std::string text(res.bytes.begin(), res.bytes.end()); StructuredAppendInfo sai; std::string::size_type i = 0; @@ -243,8 +243,7 @@ static StructuredAppendInfo ParseStructuredAppend(ByteArray& bytes) if (sai.count == 1 || sai.count <= sai.index) // If info doesn't make sense sai.count = 0; // Choose to mark count as unknown - text.erase(0, i + 2); // Remove - bytes = ByteArray(text); + res.erase(0, i + 2); // Remove return sai; } @@ -322,7 +321,7 @@ DecoderResult Decode(const BitArray& bits) 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.bytes) : StructuredAppendInfo(); + StructuredAppendInfo sai = haveStructuredAppend ? ParseStructuredAppend(res) : StructuredAppendInfo(); if (haveFNC1) { if (res.bytes[0] == 29) { diff --git a/test/unit/aztec/AZDecoderTest.cpp b/test/unit/aztec/AZDecoderTest.cpp index b9db236e25..32728a9b1b 100644 --- a/test/unit/aztec/AZDecoderTest.cpp +++ b/test/unit/aztec/AZDecoderTest.cpp @@ -162,83 +162,74 @@ TEST(AZDecoderTest, InitialGS) } } +// Shorthand to check SymbologyIdentifier result +static void check_si(int line, const DecoderResult& res, const std::string& si, const std::string& text, + int sa_index = -1, int sa_count = -1, + const std::string& textECI = {}, const std::string& bytesECI = {}) +{ + EXPECT_EQ(res.symbologyIdentifier(), si) << "line:" << line; + EXPECT_EQ(res.content().text(TextMode::Plain), text) << "line:" << line; + EXPECT_EQ(res.structuredAppend().index, sa_index) << "line:" << line; + EXPECT_EQ(res.structuredAppend().count, sa_count) << "line:" << line; + if (!textECI.empty()) + EXPECT_EQ(res.content().text(TextMode::ECI), textECI) << "line:" << line; + if (!bytesECI.empty()) + EXPECT_EQ(ToHex(res.content().bytesECI()), bytesECI) << "line:" << line; +} + TEST(AZDecoderTest, SymbologyIdentifier) { - { - // Plain - auto data = getData("00010"); - EXPECT_EQ(data.symbologyIdentifier(), "]z0"); - EXPECT_EQ(data.text(), L"A"); - } + // Plain + check_si(__LINE__, getData("00010"), "]z0", "A", -1, -1, "]z3\\000026A", "5D 7A 33 41"); - { - // GS1 ("PS FLGN(0) DL (20)01") - auto data = getData("0000000000000111100100001000100011"); - EXPECT_EQ(data.symbologyIdentifier(), "]z1"); - EXPECT_EQ(data.text(), L"2001"); - } + // GS1 ("PS FLGN(0) DL (20)01") + check_si(__LINE__, getData("0000000000000111100100001000100011"), "]z1", "2001"); - { - // AIM ("A PS FLGN(0) B") - auto data = getData("00010000000000000000011"); - EXPECT_EQ(data.symbologyIdentifier(), "]z2"); - EXPECT_EQ(data.text(), L"AB"); - } + // AIM ("A PS FLGN(0) B") + check_si(__LINE__, getData("00010000000000000000011"), "]z2", "AB"); - { - // AIM ("DL 99 UL PS FLGN(0) B") - auto data = getData("11110101110111110000000000000000011"); - EXPECT_EQ(data.symbologyIdentifier(), "]z2"); - EXPECT_EQ(data.text(), L"99B"); - } + // AIM ("DL 99 UL PS FLGN(0) B") + check_si(__LINE__, getData("11110101110111110000000000000000011"), "]z2", "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.structuredAppend().index, 0); - EXPECT_EQ(data.structuredAppend().count, 4); - } + // Structured Append (no ID) ("UL ML A D A") + check_si(__LINE__, getData("1110111101000100010100010"), "]z6", "A", 0, 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.structuredAppend().index, 0); - EXPECT_EQ(data.structuredAppend().count, 4); - } + // Structured Append (no ID) with GS1 ("UL ML A D PS FLGN(0) DL (20)01") + check_si(__LINE__, getData("111011110100010001010000000000000111100100001000100011"), "]z7", "2001", 0, 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.structuredAppend().index, 0); - EXPECT_EQ(data.structuredAppend().count, 4); - } + // Structured Append (no ID) with AIM ("UL ML A D A PS FLGN(0) B") + check_si(__LINE__, getData("1110111101000100010100010000000000000000011"), "]z8", "AB", 0, 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" - } + // Plain with FNC1 not in first/second position ("A B PS FLGN(0) C") + check_si(__LINE__, getData("0001000011000000000000000100"), "]z0", "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" - } + // Plain with FNC1 not in first/second position ("A B C PS FLGN(0) D") + check_si(__LINE__, getData("000100001100100000000000000000101"), "]z0", "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" - } + // Plain with FNC1 not in first/second position ("DL 1 UL PS FLGN(0) A") + check_si(__LINE__, getData("1111000111110000000000000000010"), "]z0", "1\u001DA"); // "1D" + + // ECI 3 with Plain - `res.symbologyIdentifier()` would be "]z3" if used `toString(hasECI())` + check_si(__LINE__, getData("0000000000001010100010"), "]z0", "A", -1, -1, "]z3\\000026A", "5D 7A 33 5C 30 30 30 30 30 33 41"); + + // ECI 3 with Plain, showing doubled backslash and ISO/IEC 8859-1 `bytesECI()` - "]z3" ditto + check_si(__LINE__, getData("000000000000101010001011101101011110100011111110000111101001"), "]z0", "A\\Bé", -1, -1, + "]z3\\000026A\\\\Bé", "5D 7A 33 5C 30 30 30 30 30 33 41 5C 5C 42 E9"); + + // ECI 3 with GS1 - "]z4" ditto + check_si(__LINE__, getData("000000000000000000000000010101111100100001000100011"), "]z1", "2001"); + + // ECI 3 with AIM - "]z5" ditto + check_si(__LINE__, getData("0000000000001010100010000000000000000011"), "]z2", "AB"); + + // ECI 3 with Structured Append (no ID) - "]z9" ditto + check_si(__LINE__, getData("111011110100010001010000000000001010100010"), "]z6", "A", 0, 4); + + // ECI 3 with Structured Append (no ID) with GS1 - "]zA" ditto + check_si(__LINE__, getData("11101111010001000101000000000000000000000000010101111100100001000100011"), "]z7", "2001", 0, 4, "]zA\\0000262001"); + + // ECI 3 with Structured Append (no ID) with AIM - "]zB" ditto + check_si(__LINE__, getData("111011110100010001010000000000001010100010000000000000000011"), "]z8", "AB", 0, 4, "]zB\\000026AB"); } // Helper taking 5-bit word array to call GetEncodedData() From 1bd094e3c5a2f2057991fe62ab5a0f2451b1d2d5 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 31 Mar 2025 10:41:08 +0200 Subject: [PATCH 024/104] Content: API cleanup and simplification (operator+= -> push_back/append) also: * string& -> string_view * ByteArray& -> span --- core/src/Content.cpp | 2 +- core/src/Content.h | 17 ++++++++++++----- core/src/datamatrix/DMDecoder.cpp | 2 +- core/src/pdf417/PDFDecoder.cpp | 2 +- core/src/qrcode/QRDecoder.cpp | 16 ++++++++-------- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 7468cac947..1b93420535 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -84,7 +84,7 @@ void Content::erase(int pos, int n) e.pos -= n; } -void Content::insert(int pos, const std::string& str) +void Content::insert(int pos, std::string_view str) { bytes.insert(bytes.begin() + pos, str.begin(), str.end()); for (auto& e : encodings) diff --git a/core/src/Content.h b/core/src/Content.h index ab5823b60e..c95e5ab436 100644 --- a/core/src/Content.h +++ b/core/src/Content.h @@ -8,8 +8,13 @@ #include "ByteArray.h" #include "CharacterSet.h" #include "ReaderOptions.h" +#include "ZXAlgorithms.h" +#if __has_include() // c++20 +#include +#endif #include +#include #include namespace ZXing { @@ -63,15 +68,17 @@ class Content void reserve(int count) { bytes.reserve(bytes.size() + count); } 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 push_back(int val) { bytes.push_back(narrow_cast(val)); } + void append(std::string_view str) { bytes.insert(bytes.end(), str.begin(), str.end()); } +#ifdef __cpp_lib_span + void append(std::span ba) { bytes.insert(bytes.end(), ba.begin(), ba.end()); } +#else void append(const ByteArray& ba) { bytes.insert(bytes.end(), ba.begin(), ba.end()); } +#endif void append(const Content& other); - void operator+=(char val) { push_back(val); } - void operator+=(const std::string& str) { append(str); } - void erase(int pos, int n); - void insert(int pos, const std::string& str); + void insert(int pos, std::string_view str); bool empty() const { return bytes.empty(); } bool canProcess() const; diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 7bbf157ff2..5433aac5a2 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -265,7 +265,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 += narrow_cast(Unrandomize255State(bits.readBits(8), codewordPosition++)); + result.push_back(Unrandomize255State(bits.readBits(8), codewordPosition++)); } } diff --git a/core/src/pdf417/PDFDecoder.cpp b/core/src/pdf417/PDFDecoder.cpp index ce8d13711a..1f0f738e3a 100644 --- a/core/src/pdf417/PDFDecoder.cpp +++ b/core/src/pdf417/PDFDecoder.cpp @@ -490,7 +490,7 @@ static int NumericCompaction(const std::vector& codewords, int codeIndex, C codeIndex++; } if (count > 0 && (count == MAX_NUMERIC_CODEWORDS || codeIndex == codewords[0] || code >= TEXT_COMPACTION_MODE_LATCH)) { - result += DecodeBase900toBase10(codewords, codeIndex, count); + result.append(DecodeBase900toBase10(codewords, codeIndex, count)); count = 0; } diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 6ec7d87ed4..068337d85c 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -73,8 +73,8 @@ static void DecodeHanziSegment(BitSource& bits, int count, Content& result) // In the 0xB0A1 to 0xFAFE range assembledTwoBytes += 0x0A6A1; } - result += narrow_cast((assembledTwoBytes >> 8) & 0xFF); - result += narrow_cast(assembledTwoBytes & 0xFF); + result.push_back((assembledTwoBytes >> 8) & 0xFF); + result.push_back(assembledTwoBytes & 0xFF); count--; } } @@ -97,8 +97,8 @@ static void DecodeKanjiSegment(BitSource& bits, int count, Content& result) // In the 0xE040 to 0xEBBF range assembledTwoBytes += 0x0C140; } - result += narrow_cast(assembledTwoBytes >> 8); - result += narrow_cast(assembledTwoBytes); + result.push_back(assembledTwoBytes >> 8); + result.push_back(assembledTwoBytes); count--; } } @@ -109,7 +109,7 @@ static void DecodeByteSegment(BitSource& bits, int count, Content& result) result.reserve(count); for (int i = 0; i < count; i++) - result += narrow_cast(bits.readBits(8)); + result.push_back(bits.readBits(8)); } static char ToAlphaNumericChar(int value) @@ -161,7 +161,7 @@ static void DecodeAlphanumericSegment(BitSource& bits, int count, Content& resul } result.switchEncoding(CharacterSet::ISO8859_1); - result += buffer; + result.append(buffer); } static void DecodeNumericSegment(BitSource& bits, int count, Content& result) @@ -262,9 +262,9 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo result.symbology.modifier = '5'; // As above // ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator (FNC1 in second position), "00-99" or "A-Za-z" if (int appInd = bits.readBits(8); appInd < 100) // "00-09" - result += ZXing::ToString(appInd, 2); + result.append(ZXing::ToString(appInd, 2)); else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222)) // "A-Za-z" - result += narrow_cast(appInd - 100); + result.push_back(appInd - 100); else throw FormatError("Invalid AIM Application Indicator"); result.symbology.aiFlag = AIFlag::AIM; // see also above From 8d22f44b3dc1cc8c2f4aeebcb75243319a32931d Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 31 Mar 2025 11:31:07 +0200 Subject: [PATCH 025/104] zint: minor fixes and code improvements in WriteBarcode API In part copied from work of @gitlost. --- core/src/WriteBarcode.cpp | 24 ++++++++++++------------ core/src/libzint/stubs.c | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/src/WriteBarcode.cpp b/core/src/WriteBarcode.cpp index 8b26843959..eadc47fa54 100644 --- a/core/src/WriteBarcode.cpp +++ b/core/src/WriteBarcode.cpp @@ -181,10 +181,11 @@ struct String2Int static int ParseECLevel(int symbology, std::string_view s) { constexpr std::string_view EC_LABELS_QR[4] = {"L", "M", "Q", "H"}; - int res = 0; + + // Convert L/M/Q/H to Zint 1-4 if (Contains({BARCODE_QRCODE, BARCODE_MICROQR, BARCODE_RMQR}, symbology)) - if ((res = IndexOf(EC_LABELS_QR, s) != -1)) + if ((res = IndexOf(EC_LABELS_QR, s)) != -1) return res + 1; if (std::from_chars(s.data(), s.data() + s.size() - (s.back() == '%'), res).ec != std::errc{}) @@ -193,21 +194,20 @@ static int ParseECLevel(int symbology, std::string_view s) auto findClosestECLevel = [](const std::vector& list, int val) { int mIdx = -2, mAbs = 100; for (int i = 0; i < Size(list); ++i) - if (int abs = std::abs(val - list[i]); abs < mAbs) { + if (int abs = std::abs(val - list[i]); abs < mAbs) { mIdx = i; mAbs = abs; - } + } return mIdx + 1; }; - if (s.back()=='%'){ + // Convert percentage to Zint + if (s.back() == '%') { switch (symbology) { - case BARCODE_QRCODE: - case BARCODE_MICROQR: - case BARCODE_RMQR: - return findClosestECLevel({20, 37, 55, 65}, res); - case BARCODE_AZTEC: - return findClosestECLevel({10, 23, 26, 50}, res); + case BARCODE_QRCODE: return findClosestECLevel({20, 37, 55, 65}, res); + case BARCODE_MICROQR: return findClosestECLevel({20, 37, 55}, res); + case BARCODE_RMQR: return res <= 46 ? 2 : 4; + case BARCODE_AZTEC: return findClosestECLevel({10, 23, 36, 50}, res); case BARCODE_PDF417: // TODO: do something sensible with PDF417? default: @@ -244,7 +244,7 @@ zint_symbol* CreatorOptions::zint() const #define CHECK(ZINT_CALL) \ if (int err = (ZINT_CALL); err >= ZINT_ERROR) \ - throw std::invalid_argument(zint->errtxt); + throw std::invalid_argument(std::string(zint->errtxt) + " (retval: " + std::to_string(err) + ")"); Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions& opts) { diff --git a/core/src/libzint/stubs.c b/core/src/libzint/stubs.c index dfe8914ce1..077e252bc6 100644 --- a/core/src/libzint/stubs.c +++ b/core/src/libzint/stubs.c @@ -16,6 +16,7 @@ (void)symbol; \ (void)source; \ (void)length; \ + strcpy(symbol->errtxt, "Symbology " #NAME " not implemented in embedded libzint"); \ return ZINT_ERROR_ENCODING_PROBLEM; \ } @@ -25,6 +26,7 @@ (void)symbol; \ (void)segs; \ (void)seg_count; \ + strcpy(symbol->errtxt, "Symbology " #NAME " not implemented in embedded libzint"); \ return ZINT_ERROR_ENCODING_PROBLEM; \ } From c1a47ff359f8e8b571888376e94847b621deb650 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 31 Mar 2025 11:32:11 +0200 Subject: [PATCH 026/104] MCDecoder: minor code duplication removal --- core/src/maxicode/MCDecoder.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index cb860c69fd..4f9c6e39f8 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -276,10 +276,8 @@ DecoderResult Decode(ByteArray&& bytes, const int mode) auto country = ToString(GetCountry(bytes), 3); auto service = ToString(GetServiceClass(bytes), 3); GetMessage(bytes, 10, 84, result, sai); - 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); + result.insert(result.bytes.asString().starts_with("[)>\u001E01\u001D") ? 9 : 0, // "[)>" + RS + "01" + GS + postcode + GS + country + GS + service + GS); break; } case 4: From e60ac14094b00e9426fb7ad1f533e1b221642297 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 1 Apr 2025 00:59:37 +0200 Subject: [PATCH 027/104] zint: rebase embedded libzint to current HEAD --- core/src/libzint/2of5inter.c | 1 + core/src/libzint/codabar.c | 1 + core/src/libzint/medical.c | 1 - core/src/libzint/stubs.c | 26 +++++++++++++------------- zint | 2 +- 5 files changed, 16 insertions(+), 15 deletions(-) create mode 120000 core/src/libzint/2of5inter.c create mode 120000 core/src/libzint/codabar.c delete mode 120000 core/src/libzint/medical.c diff --git a/core/src/libzint/2of5inter.c b/core/src/libzint/2of5inter.c new file mode 120000 index 0000000000..87aea946d9 --- /dev/null +++ b/core/src/libzint/2of5inter.c @@ -0,0 +1 @@ +../../../zint/backend/2of5inter.c \ No newline at end of file diff --git a/core/src/libzint/codabar.c b/core/src/libzint/codabar.c new file mode 120000 index 0000000000..333ce63a13 --- /dev/null +++ b/core/src/libzint/codabar.c @@ -0,0 +1 @@ +../../../zint/backend/codabar.c \ No newline at end of file diff --git a/core/src/libzint/medical.c b/core/src/libzint/medical.c deleted file mode 120000 index 64507aed2c..0000000000 --- a/core/src/libzint/medical.c +++ /dev/null @@ -1 +0,0 @@ -../../../zint/backend/medical.c \ No newline at end of file diff --git a/core/src/libzint/stubs.c b/core/src/libzint/stubs.c index 077e252bc6..2a64e213f0 100644 --- a/core/src/libzint/stubs.c +++ b/core/src/libzint/stubs.c @@ -48,23 +48,24 @@ INTERNAL int emf_plot(struct zint_symbol* symbol, int rotate_angle) return ZINT_ERROR_ENCODING_PROBLEM; } -// STUB_FUNC_CHAR(pzn) // STUB_FUNC_CHAR(c25ind) // STUB_FUNC_CHAR(c25iata) -// STUB_FUNC_CHAR(c25inter) // STUB_FUNC_CHAR(c25logic) -// STUB_FUNC_CHAR(itf14) -// STUB_FUNC_CHAR(dpleit) -// STUB_FUNC_CHAR(dpident) -// STUB_FUNC_CHAR(code11) +STUB_FUNC_CHAR(itf14) +STUB_FUNC_CHAR(ean14) +STUB_FUNC_CHAR(dpleit) +STUB_FUNC_CHAR(dpident) +STUB_FUNC_CHAR(code11) STUB_FUNC_CHAR(msi_plessey) STUB_FUNC_CHAR(telepen) STUB_FUNC_CHAR(telepen_num) STUB_FUNC_CHAR(plessey) -// STUB_FUNC_CHAR(pharma) STUB_FUNC_CHAR(flat) STUB_FUNC_CHAR(fim) -// STUB_FUNC_CHAR(pharma_two) +STUB_FUNC_CHAR(code32) +STUB_FUNC_CHAR(pharma) +STUB_FUNC_CHAR(pharma_two) +STUB_FUNC_CHAR(pzn) STUB_FUNC_CHAR(postnet) STUB_FUNC_CHAR(planet) STUB_FUNC_CHAR(usps_imail) @@ -73,13 +74,12 @@ STUB_FUNC_CHAR(auspost) STUB_FUNC_CHAR(code16k) STUB_FUNC_CHAR(composite) STUB_FUNC_CHAR(kix) -// STUB_FUNC_CHAR(code32) STUB_FUNC_CHAR(daft) -// STUB_FUNC_CHAR(nve18) +STUB_FUNC_CHAR(nve18) STUB_FUNC_CHAR(koreapost) STUB_FUNC_CHAR(japanpost) STUB_FUNC_CHAR(code49) -// STUB_FUNC_CHAR(channel) +STUB_FUNC_CHAR(channel) STUB_FUNC_SEGS(codeone) STUB_FUNC_SEGS(gridmatrix) STUB_FUNC_SEGS(hanxin) @@ -88,7 +88,7 @@ STUB_FUNC_SEGS(codablockf) // STUB_FUNC_CHAR(vin) STUB_FUNC_CHAR(mailmark_2d) STUB_FUNC_CHAR(mailmark_4s) -// STUB_FUNC_CHAR(upu_s10) +STUB_FUNC_CHAR(upu_s10) STUB_FUNC_SEGS(ultra) -// STUB_FUNC_CHAR(dpd) +STUB_FUNC_CHAR(dpd) STUB_FUNC_CHAR(bc412) diff --git a/zint b/zint index 7a9fdd6cd0..2370fbfbb7 160000 --- a/zint +++ b/zint @@ -1 +1 @@ -Subproject commit 7a9fdd6cd00cd5bfd0082705d934c13ef84f25e1 +Subproject commit 2370fbfbb75dc98e63072a8bdbcbb35265008e5d From f910c5481f3b8474a64c2b33316a7105857cf12a Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 1 Apr 2025 01:32:42 +0200 Subject: [PATCH 028/104] winrt: add comment on why this can't be build with c++20 --- wrappers/winrt/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/wrappers/winrt/CMakeLists.txt b/wrappers/winrt/CMakeLists.txt index cc245d57c0..2990f1dde0 100644 --- a/wrappers/winrt/CMakeLists.txt +++ b/wrappers/winrt/CMakeLists.txt @@ -49,6 +49,7 @@ target_link_libraries (ZXingWinRT target_compile_options (ZXingWinRT PRIVATE -D_WINRT_DLL -ZW # Consume Windows Runtime Extension + # this is incompatible with c++20 (see https://learn.microsoft.com/en-us/cpp/build/reference/zw-windows-runtime-compilation?view=msvc-170) ) set_target_properties (ZXingWinRT PROPERTIES From efffdcfe9a1694bdde27564fb249a8892d4d5216 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 1 Apr 2025 22:34:20 +0200 Subject: [PATCH 029/104] MCDecoder: partly revert recent cleanup to reestablish c++17 support std::string_view::starts_with was introduced in c++20 :-/. --- core/src/maxicode/MCDecoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 4f9c6e39f8..7a6f094ee0 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -276,7 +276,7 @@ DecoderResult Decode(ByteArray&& bytes, const int mode) auto country = ToString(GetCountry(bytes), 3); auto service = ToString(GetServiceClass(bytes), 3); GetMessage(bytes, 10, 84, result, sai); - result.insert(result.bytes.asString().starts_with("[)>\u001E01\u001D") ? 9 : 0, // "[)>" + RS + "01" + GS + result.insert(result.bytes.asString().compare(0, 7, "[)>\u001E01\u001D") == 0 ? 9 : 0, // "[)>" + RS + "01" + GS postcode + GS + country + GS + service + GS); break; } From 36495ef0358c3d56d2e06e8a8ffdaac6afafcdb8 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 3 Apr 2025 01:11:46 +0200 Subject: [PATCH 030/104] DataBar: fix inconsistency / plain spec violation (missing "(01)" prefix) The GS1 DataBar ISO specification clearly states in Section 9 that our two BarcodeFormats DataBar and DataBarLimited always implicitly encode the AI "01" prefix. This has been implemented in the new DataBarLimited version but not in the old DataBar variant. Also to make the HRI of all DataBar variants (including the Expanded variant) consistent, all of them are now reported to contain GS1 data and hence be rendered as "(01)0123...." instead of "010123....". --- core/src/oned/ODDataBarLimitedReader.cpp | 2 +- core/src/oned/ODDataBarReader.cpp | 5 +++-- test/samples/rss14-1/1.txt | 2 +- test/samples/rss14-1/2.txt | 2 +- test/samples/rss14-1/3.txt | 2 +- test/samples/rss14-1/4.txt | 2 +- test/samples/rss14-1/5.txt | 2 +- test/samples/rss14-1/6.txt | 2 +- test/samples/rss14-2/0120358468019312.txt | 2 +- test/samples/rss14-2/11.txt | 2 +- test/samples/rss14-2/13.txt | 2 +- test/samples/rss14-2/14.txt | 2 +- test/samples/rss14-2/15.txt | 2 +- test/samples/rss14-2/20.txt | 2 +- test/samples/rss14-2/21.txt | 2 +- test/samples/rss14-2/22.txt | 2 +- test/samples/rss14-2/23.txt | 2 +- test/samples/rss14-2/24.txt | 2 +- test/samples/rss14-2/6.txt | 2 +- test/samples/rss14-2/7.txt | 2 +- test/samples/rss14-2/8.txt | 2 +- test/samples/rss14-2/9.txt | 2 +- test/unit/oned/ODDataBarReaderTest.cpp | 2 +- 23 files changed, 25 insertions(+), 24 deletions(-) diff --git a/core/src/oned/ODDataBarLimitedReader.cpp b/core/src/oned/ODDataBarLimitedReader.cpp index db57feb024..972368880f 100644 --- a/core/src/oned/ODDataBarLimitedReader.cpp +++ b/core/src/oned/ODDataBarLimitedReader.cpp @@ -154,7 +154,7 @@ Barcode DataBarLimitedReader::decodePattern(int rowNumber, PatternView& next, st continue; return {ConstructText(left, right), rowNumber, next.pixelsInFront(), next.pixelsTillEnd(), - BarcodeFormat::DataBarLimited, {'e', '0'}}; + BarcodeFormat::DataBarLimited, {'e', '0', 0, AIFlag::GS1}}; } // guarantee progress (see loop in ODReader.cpp) diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index ec1a9b6611..20937cb369 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -140,7 +140,8 @@ static bool ChecksumIsValid(Pair leftPair, Pair rightPair) static std::string ConstructText(Pair leftPair, Pair rightPair) { auto txt = ToString(Value(leftPair, rightPair), 13); - return txt + GTIN::ComputeCheckDigit(txt); + // see ISO/IEC 24724:2011 Section 9 + return "01" + txt + GTIN::ComputeCheckDigit(txt); } struct State : public RowReader::DecodingState @@ -193,7 +194,7 @@ Barcode DataBarReader::decodePattern(int rowNumber, PatternView& next, std::uniq 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 - Barcode res{DecoderResult(Content(ByteArray(ConstructText(leftPair, rightPair)), {'e', '0'})) + Barcode res{DecoderResult(Content(ByteArray(ConstructText(leftPair, rightPair)), {'e', '0', 0, AIFlag::GS1})) .setLineCount(EstimateLineCount(leftPair, rightPair)), {{}, EstimatePosition(leftPair, rightPair)}, BarcodeFormat::DataBar}; diff --git a/test/samples/rss14-1/1.txt b/test/samples/rss14-1/1.txt index da8b2e720b..d5ca9e842b 100644 --- a/test/samples/rss14-1/1.txt +++ b/test/samples/rss14-1/1.txt @@ -1 +1 @@ -04412345678909 \ No newline at end of file +0104412345678909 \ No newline at end of file diff --git a/test/samples/rss14-1/2.txt b/test/samples/rss14-1/2.txt index 6a694e304f..a1a054307f 100644 --- a/test/samples/rss14-1/2.txt +++ b/test/samples/rss14-1/2.txt @@ -1 +1 @@ -00821935106427 \ No newline at end of file +0100821935106427 \ No newline at end of file diff --git a/test/samples/rss14-1/3.txt b/test/samples/rss14-1/3.txt index accc36e39e..0d2a6bb971 100644 --- a/test/samples/rss14-1/3.txt +++ b/test/samples/rss14-1/3.txt @@ -1 +1 @@ -00075678164125 \ No newline at end of file +0100075678164125 \ No newline at end of file diff --git a/test/samples/rss14-1/4.txt b/test/samples/rss14-1/4.txt index b8d2ea46f4..f398bddf56 100644 --- a/test/samples/rss14-1/4.txt +++ b/test/samples/rss14-1/4.txt @@ -1 +1 @@ -20012345678909 \ No newline at end of file +0120012345678909 \ No newline at end of file diff --git a/test/samples/rss14-1/5.txt b/test/samples/rss14-1/5.txt index 50109f57af..e454fcc504 100644 --- a/test/samples/rss14-1/5.txt +++ b/test/samples/rss14-1/5.txt @@ -1 +1 @@ -00034567890125 \ No newline at end of file +0100034567890125 \ No newline at end of file diff --git a/test/samples/rss14-1/6.txt b/test/samples/rss14-1/6.txt index eabb743b0e..ab2c0890b4 100644 --- a/test/samples/rss14-1/6.txt +++ b/test/samples/rss14-1/6.txt @@ -1 +1 @@ -00012345678905 \ No newline at end of file +0100012345678905 \ No newline at end of file diff --git a/test/samples/rss14-2/0120358468019312.txt b/test/samples/rss14-2/0120358468019312.txt index 3ccb913ae2..c52ff03653 100644 --- a/test/samples/rss14-2/0120358468019312.txt +++ b/test/samples/rss14-2/0120358468019312.txt @@ -1 +1 @@ -20358468019312 \ No newline at end of file +0120358468019312 \ No newline at end of file diff --git a/test/samples/rss14-2/11.txt b/test/samples/rss14-2/11.txt index adb4ff3445..3e5352aab7 100644 --- a/test/samples/rss14-2/11.txt +++ b/test/samples/rss14-2/11.txt @@ -1 +1 @@ -02001234567893 \ No newline at end of file +0102001234567893 \ No newline at end of file diff --git a/test/samples/rss14-2/13.txt b/test/samples/rss14-2/13.txt index adb4ff3445..3e5352aab7 100644 --- a/test/samples/rss14-2/13.txt +++ b/test/samples/rss14-2/13.txt @@ -1 +1 @@ -02001234567893 \ No newline at end of file +0102001234567893 \ No newline at end of file diff --git a/test/samples/rss14-2/14.txt b/test/samples/rss14-2/14.txt index adb4ff3445..3e5352aab7 100644 --- a/test/samples/rss14-2/14.txt +++ b/test/samples/rss14-2/14.txt @@ -1 +1 @@ -02001234567893 \ No newline at end of file +0102001234567893 \ No newline at end of file diff --git a/test/samples/rss14-2/15.txt b/test/samples/rss14-2/15.txt index adb4ff3445..3e5352aab7 100644 --- a/test/samples/rss14-2/15.txt +++ b/test/samples/rss14-2/15.txt @@ -1 +1 @@ -02001234567893 \ No newline at end of file +0102001234567893 \ No newline at end of file diff --git a/test/samples/rss14-2/20.txt b/test/samples/rss14-2/20.txt index eabb743b0e..ab2c0890b4 100644 --- a/test/samples/rss14-2/20.txt +++ b/test/samples/rss14-2/20.txt @@ -1 +1 @@ -00012345678905 \ No newline at end of file +0100012345678905 \ No newline at end of file diff --git a/test/samples/rss14-2/21.txt b/test/samples/rss14-2/21.txt index eabb743b0e..ab2c0890b4 100644 --- a/test/samples/rss14-2/21.txt +++ b/test/samples/rss14-2/21.txt @@ -1 +1 @@ -00012345678905 \ No newline at end of file +0100012345678905 \ No newline at end of file diff --git a/test/samples/rss14-2/22.txt b/test/samples/rss14-2/22.txt index eabb743b0e..ab2c0890b4 100644 --- a/test/samples/rss14-2/22.txt +++ b/test/samples/rss14-2/22.txt @@ -1 +1 @@ -00012345678905 \ No newline at end of file +0100012345678905 \ No newline at end of file diff --git a/test/samples/rss14-2/23.txt b/test/samples/rss14-2/23.txt index eabb743b0e..ab2c0890b4 100644 --- a/test/samples/rss14-2/23.txt +++ b/test/samples/rss14-2/23.txt @@ -1 +1 @@ -00012345678905 \ No newline at end of file +0100012345678905 \ No newline at end of file diff --git a/test/samples/rss14-2/24.txt b/test/samples/rss14-2/24.txt index eabb743b0e..ab2c0890b4 100644 --- a/test/samples/rss14-2/24.txt +++ b/test/samples/rss14-2/24.txt @@ -1 +1 @@ -00012345678905 \ No newline at end of file +0100012345678905 \ No newline at end of file diff --git a/test/samples/rss14-2/6.txt b/test/samples/rss14-2/6.txt index adb4ff3445..3e5352aab7 100644 --- a/test/samples/rss14-2/6.txt +++ b/test/samples/rss14-2/6.txt @@ -1 +1 @@ -02001234567893 \ No newline at end of file +0102001234567893 \ No newline at end of file diff --git a/test/samples/rss14-2/7.txt b/test/samples/rss14-2/7.txt index adb4ff3445..3e5352aab7 100644 --- a/test/samples/rss14-2/7.txt +++ b/test/samples/rss14-2/7.txt @@ -1 +1 @@ -02001234567893 \ No newline at end of file +0102001234567893 \ No newline at end of file diff --git a/test/samples/rss14-2/8.txt b/test/samples/rss14-2/8.txt index adb4ff3445..3e5352aab7 100644 --- a/test/samples/rss14-2/8.txt +++ b/test/samples/rss14-2/8.txt @@ -1 +1 @@ -02001234567893 \ No newline at end of file +0102001234567893 \ No newline at end of file diff --git a/test/samples/rss14-2/9.txt b/test/samples/rss14-2/9.txt index adb4ff3445..3e5352aab7 100644 --- a/test/samples/rss14-2/9.txt +++ b/test/samples/rss14-2/9.txt @@ -1 +1 @@ -02001234567893 \ No newline at end of file +0102001234567893 \ No newline at end of file diff --git a/test/unit/oned/ODDataBarReaderTest.cpp b/test/unit/oned/ODDataBarReaderTest.cpp index 0c11dd3635..bf171e7845 100644 --- a/test/unit/oned/ODDataBarReaderTest.cpp +++ b/test/unit/oned/ODDataBarReaderTest.cpp @@ -33,6 +33,6 @@ TEST(ODDataBarReaderTest, Composite) PatternRow row = { 2, 3, 1, 2, 1, 2, 4, 1, 3, 3, 7, 1, 1, 3, 1, 2, 1, 1, 1, 4, 2, 4, 1, 1, 2, 3, 1, 1, 2, 1, 1, 2, 8, 3, 3, 2, 2, 1, 4, 1, 1, 2 }; auto result = parse(row); EXPECT_TRUE(result.isValid()); - EXPECT_EQ(result.text(), "01234567890128"); + EXPECT_EQ(result.text(), "(01)01234567890128"); } } From 8c504ecd6bd76db4ab4cd3e08eeb4dc25fd5e1ae Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 4 Apr 2025 23:06:42 +0200 Subject: [PATCH 031/104] DXFilmEdge: tune thresholds to make zint generated symbols work If the image consisted of nothing but a 1-module symbol, detection failed. This is make the WriteBarcode tests work out of the box. --- core/src/oned/ODDXFilmEdgeReader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/oned/ODDXFilmEdgeReader.cpp b/core/src/oned/ODDXFilmEdgeReader.cpp index 7b197dd576..353fb1ec7b 100644 --- a/core/src/oned/ODDXFilmEdgeReader.cpp +++ b/core/src/oned/ODDXFilmEdgeReader.cpp @@ -41,7 +41,7 @@ bool IsPattern(PatternView& view, const FixedPattern& pattern, float min bool DistIsBelowThreshold(PointI a, PointI b, PointI threshold) { - return std::abs(a.x - b.x) < threshold.x && std::abs(a.y - b.y) < threshold.y; + return std::abs(a.x - b.x) <= threshold.x && std::abs(a.y - b.y) <= threshold.y; } // DX Film Edge clock track found on 35mm films. @@ -70,7 +70,7 @@ struct DXFEState : public RowReader::DecodingState // see if we a clock that starts near {x, y} Clock* findClock(int x, int y) { - auto i = FindIf(clocks, [start = PointI{x, y}](auto& v) { return v.isCloseToStart(start.x, start.y); }); + auto i = FindIf(clocks, [start = PointI{x, y}](auto& v) { return v.rowNumber != start.y && v.isCloseToStart(start.x, start.y); }); return i != clocks.end() ? &(*i) : nullptr; } @@ -114,7 +114,7 @@ Barcode DXFilmEdgeReader::decodePattern(int rowNumber, PatternView& next, std::u auto dxState = static_cast(state.get()); // Only consider rows below the center row of the image - if (!_opts.tryRotate() && rowNumber < dxState->centerRow) + if (!_opts.tryRotate() && rowNumber < dxState->centerRow - 1) return {}; // Look for a pattern that is part of both the clock as well as the data track (ommitting the first bar) From c8e26807edd9ed995c198070b43c9f6addfe381b Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 7 Apr 2025 18:19:55 +0200 Subject: [PATCH 032/104] BarcodeFormat: new IsLinearBarcode() helper and some code cosmetic --- core/src/Barcode.cpp | 14 +++++++------- core/src/BarcodeFormat.h | 5 +++++ core/src/WriteBarcode.cpp | 2 +- core/src/oned/ODReader.cpp | 6 +++--- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/core/src/Barcode.cpp b/core/src/Barcode.cpp index 41b2137d36..82b6d01817 100644 --- a/core/src/Barcode.cpp +++ b/core/src/Barcode.cpp @@ -164,19 +164,19 @@ void Result::zint(unique_zint_symbol&& z) bool Result::operator==(const Result& o) const { - // handle case where both are MatrixCodes first - if (!BarcodeFormats(BarcodeFormat::LinearCodes).testFlags(format() | o.format())) { - if (format() != o.format() || (bytes() != o.bytes() && isValid() && o.isValid())) + if (format() != o.format()) + return false; + + // handle MatrixCodes first + if (!IsLinearBarcode(format())) { + if (bytes() != o.bytes() && isValid() && o.isValid()) return false; // check for equal position if both are valid with equal bytes or at least one is in error return IsInside(Center(o.position()), position()); } - if (format() != o.format() || bytes() != o.bytes() || error() != o.error()) - return false; - - if (orientation() != o.orientation()) + if (bytes() != o.bytes() || error() != o.error() || orientation() != o.orientation()) return false; if (lineCount() > 1 && o.lineCount() > 1) diff --git a/core/src/BarcodeFormat.h b/core/src/BarcodeFormat.h index 1b29272410..f8b7bc0e9c 100644 --- a/core/src/BarcodeFormat.h +++ b/core/src/BarcodeFormat.h @@ -53,6 +53,11 @@ enum class BarcodeFormat ZX_DECLARE_FLAGS(BarcodeFormats, BarcodeFormat) +inline constexpr bool IsLinearBarcode(BarcodeFormat format) +{ + return BarcodeFormats(BarcodeFormat::LinearCodes).testFlag(format); +} + std::string ToString(BarcodeFormat format); std::string ToString(BarcodeFormats formats); diff --git a/core/src/WriteBarcode.cpp b/core/src/WriteBarcode.cpp index eadc47fa54..22dc61b866 100644 --- a/core/src/WriteBarcode.cpp +++ b/core/src/WriteBarcode.cpp @@ -436,7 +436,7 @@ Image WriteBarcodeToImage(const Barcode& barcode, [[maybe_unused]] const WriterO auto zint = barcode.zint(); if (!zint) - return ToImage(barcode._symbol->copy(), IsLinearCode(barcode.format()), opts); + return ToImage(barcode._symbol->copy(), IsLinearBarcode(barcode.format()), opts); #if defined(ZXING_WRITERS) && defined(ZXING_USE_ZINT) auto resetOnExit = SetCommonWriterOptions(zint, opts); diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 555ae308e0..a90893b2ac 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -59,11 +59,11 @@ Reader::Reader(const ReaderOptions& opts) : ZXing::Reader(opts) _readers.emplace_back(new ITFReader(opts)); if (formats.testFlag(BarcodeFormat::Codabar)) _readers.emplace_back(new CodabarReader(opts)); - if (formats.testFlags(BarcodeFormat::DataBar)) + if (formats.testFlag(BarcodeFormat::DataBar)) _readers.emplace_back(new DataBarReader(opts)); - if (formats.testFlags(BarcodeFormat::DataBarExpanded)) + if (formats.testFlag(BarcodeFormat::DataBarExpanded)) _readers.emplace_back(new DataBarExpandedReader(opts)); - if (formats.testFlags(BarcodeFormat::DataBarLimited)) + if (formats.testFlag(BarcodeFormat::DataBarLimited)) _readers.emplace_back(new DataBarLimitedReader(opts)); if (formats.testFlag(BarcodeFormat::DXFilmEdge)) _readers.emplace_back(new DXFilmEdgeReader(opts)); From ef38ccb6faf16d5c45891a1e9cae2c82ae129448 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 7 Apr 2025 18:23:05 +0200 Subject: [PATCH 033/104] WriteBarcode: condense linear code in WriteBarcodeToUtf8 This function is meant for debugging and illustration in text terminals, so there is no need to fill the screen with multiple copies of the same line of text. --- core/src/WriteBarcode.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/WriteBarcode.cpp b/core/src/WriteBarcode.cpp index 22dc61b866..31096e44d5 100644 --- a/core/src/WriteBarcode.cpp +++ b/core/src/WriteBarcode.cpp @@ -470,6 +470,11 @@ std::string WriteBarcodeToUtf8(const Barcode& barcode, [[maybe_unused]] const Wr bool inverted = false; // TODO: take from WriterOptions for (int y = 0; y < iv.height(); y += 2) { + // for linear barcodes, only print line pairs that are distinct from the previous one + if (IsLinearBarcode(barcode.format()) && y > 1 && y < iv.height() - 1 + && memcmp(iv.data(0, y), iv.data(0, y - 2), 2 * iv.rowStride()) == 0) + continue; + for (int x = 0; x < iv.width(); ++x) { int tp = bool(*iv.data(x, y)) ^ inverted; int bt = (iv.height() == 1 && tp) || (y + 1 < iv.height() && (bool(*iv.data(x, y + 1)) ^ inverted)); From c4f5647eadde5330855861f733f5c530d6eafad9 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 7 Apr 2025 19:13:33 +0200 Subject: [PATCH 034/104] EAN/UPC: return 13-digit numbers for UPC-A/E as required by standard The old behavior was inherited from the Java code base but clearly in violation of the ISO specification. See also the discussion here: https://github.com/zxing-cpp/zxing-cpp/issues/883#issuecomment-2780644010 This also makes the add-on concatenation standards conform (no space anymore) and it "extends" the EAN-8 symbology to allow for add-ons. See above discussion. --- core/src/GTIN.cpp | 6 ++-- core/src/GTIN.h | 1 + core/src/oned/ODMultiUPCEANReader.cpp | 45 +++++++++++++-------------- test/blackbox/BlackboxTestRunner.cpp | 12 +++---- test/samples/ean13-extension-1/1.txt | 2 +- test/samples/ean13-extension-1/2.txt | 2 +- test/samples/ean13-extension-1/32.txt | 2 +- test/samples/ean13-extension-1/33.txt | 2 +- test/samples/ean13-extension-1/38.txt | 2 +- test/samples/upca-1/12.txt | 2 +- test/samples/upca-1/16.txt | 2 +- test/samples/upca-1/17.txt | 2 +- test/samples/upca-1/2.txt | 2 +- test/samples/upca-1/21.txt | 2 +- test/samples/upca-1/27.txt | 2 +- test/samples/upca-1/28.txt | 2 +- test/samples/upca-1/29.txt | 2 +- test/samples/upca-1/3.txt | 2 +- test/samples/upca-1/35.txt | 2 +- test/samples/upca-1/4.txt | 2 +- test/samples/upca-1/5.txt | 2 +- test/samples/upca-2/01.txt | 2 +- test/samples/upca-2/02.txt | 2 +- test/samples/upca-2/03.txt | 2 +- test/samples/upca-2/04.txt | 2 +- test/samples/upca-2/06.txt | 2 +- test/samples/upca-2/07.txt | 2 +- test/samples/upca-2/08.txt | 2 +- test/samples/upca-2/09.txt | 2 +- test/samples/upca-2/10.txt | 2 +- test/samples/upca-2/11.txt | 2 +- test/samples/upca-2/12.txt | 2 +- test/samples/upca-2/14.txt | 2 +- test/samples/upca-2/15.txt | 2 +- test/samples/upca-2/16.txt | 2 +- test/samples/upca-2/23.txt | 2 +- test/samples/upca-2/24.txt | 2 +- test/samples/upca-2/25.txt | 2 +- test/samples/upca-2/26.txt | 2 +- test/samples/upca-2/28.txt | 2 +- test/samples/upca-2/29.txt | 2 +- test/samples/upca-2/31.txt | 2 +- test/samples/upca-2/32.txt | 2 +- test/samples/upca-2/33.txt | 2 +- test/samples/upca-2/34.txt | 2 +- test/samples/upca-2/35.txt | 2 +- test/samples/upca-2/37.txt | 2 +- test/samples/upca-2/38.txt | 2 +- test/samples/upca-2/39.txt | 2 +- test/samples/upca-2/41.txt | 2 +- test/samples/upca-2/42.txt | 2 +- test/samples/upca-2/45.txt | 2 +- test/samples/upca-2/46.txt | 2 +- test/samples/upca-2/48.txt | 2 +- test/samples/upca-2/49.txt | 2 +- test/samples/upca-2/50.txt | 2 +- test/samples/upca-2/51.txt | 2 +- test/samples/upca-3/01.txt | 2 +- test/samples/upca-3/02.txt | 2 +- test/samples/upca-3/03.txt | 2 +- test/samples/upca-3/04.txt | 2 +- test/samples/upca-3/05.txt | 2 +- test/samples/upca-3/06.txt | 2 +- test/samples/upca-3/07.txt | 2 +- test/samples/upca-3/08.txt | 2 +- test/samples/upca-3/09.txt | 2 +- test/samples/upca-3/10.txt | 2 +- test/samples/upca-3/11.txt | 2 +- test/samples/upca-3/12.txt | 2 +- test/samples/upca-3/13.txt | 2 +- test/samples/upca-3/14.txt | 2 +- test/samples/upca-3/15.txt | 2 +- test/samples/upca-3/16.txt | 2 +- test/samples/upca-3/17.txt | 2 +- test/samples/upca-3/18.txt | 2 +- test/samples/upca-3/19.txt | 2 +- test/samples/upca-3/20.txt | 2 +- test/samples/upca-3/21.txt | 2 +- test/samples/upca-4/1.txt | 2 +- test/samples/upca-4/10.txt | 2 +- test/samples/upca-4/11.txt | 2 +- test/samples/upca-4/12.txt | 2 +- test/samples/upca-4/13.txt | 2 +- test/samples/upca-4/14.txt | 2 +- test/samples/upca-4/15.txt | 2 +- test/samples/upca-4/16.txt | 2 +- test/samples/upca-4/17.txt | 2 +- test/samples/upca-4/18.txt | 2 +- test/samples/upca-4/19.txt | 2 +- test/samples/upca-4/2.txt | 2 +- test/samples/upca-4/3.txt | 2 +- test/samples/upca-4/4.txt | 2 +- test/samples/upca-4/5.txt | 2 +- test/samples/upca-4/6.txt | 2 +- test/samples/upca-4/7.txt | 2 +- test/samples/upca-4/8.txt | 2 +- test/samples/upca-4/9.txt | 2 +- test/samples/upca-5/01.txt | 2 +- test/samples/upca-5/02.txt | 2 +- test/samples/upca-5/03.txt | 2 +- test/samples/upca-5/04.txt | 2 +- test/samples/upca-5/05.txt | 2 +- test/samples/upca-5/06.txt | 2 +- test/samples/upca-5/07.txt | 2 +- test/samples/upca-5/08.txt | 2 +- test/samples/upca-5/09.txt | 2 +- test/samples/upca-5/11.txt | 2 +- test/samples/upca-5/13.txt | 2 +- test/samples/upca-5/14.txt | 2 +- test/samples/upca-5/15.txt | 2 +- test/samples/upca-5/16.txt | 2 +- test/samples/upca-5/18.txt | 2 +- test/samples/upca-5/19.txt | 2 +- test/samples/upca-5/20.txt | 2 +- test/samples/upca-5/21.txt | 2 +- test/samples/upca-5/22.txt | 2 +- test/samples/upca-5/23.txt | 2 +- test/samples/upca-5/24.txt | 2 +- test/samples/upca-5/25.txt | 2 +- test/samples/upca-5/26.txt | 2 +- test/samples/upca-5/27.txt | 2 +- test/samples/upca-5/28.txt | 2 +- test/samples/upca-5/29.txt | 2 +- test/samples/upca-5/30.txt | 2 +- test/samples/upca-5/31.txt | 2 +- test/samples/upca-5/32.txt | 2 +- test/samples/upca-5/33.txt | 2 +- test/samples/upca-5/34.txt | 2 +- test/samples/upca-5/35.txt | 2 +- test/samples/upca-extension-1/10.txt | 2 +- test/samples/upca-extension-1/11.txt | 2 +- test/samples/upca-extension-1/18.txt | 2 +- test/samples/upca-extension-1/19.txt | 2 +- test/samples/upca-extension-1/8.txt | 2 +- test/samples/upca-extension-1/9.txt | 2 +- test/samples/upce-1/1.txt | 2 +- test/samples/upce-1/2.txt | 2 +- test/samples/upce-1/4.txt | 2 +- test/samples/upce-2/01.txt | 2 +- test/samples/upce-2/02.txt | 2 +- test/samples/upce-2/03.txt | 2 +- test/samples/upce-2/05.txt | 2 +- test/samples/upce-2/06.txt | 2 +- test/samples/upce-2/08.txt | 2 +- test/samples/upce-2/10.txt | 2 +- test/samples/upce-2/11.txt | 2 +- test/samples/upce-2/12.txt | 2 +- test/samples/upce-2/14.txt | 2 +- test/samples/upce-2/16.txt | 2 +- test/samples/upce-2/17.txt | 2 +- test/samples/upce-2/18.txt | 2 +- test/samples/upce-2/20.txt | 2 +- test/samples/upce-2/21.txt | 2 +- test/samples/upce-2/23.txt | 2 +- test/samples/upce-2/24.txt | 2 +- test/samples/upce-2/26.txt | 2 +- test/samples/upce-2/28.txt | 2 +- test/samples/upce-2/29.txt | 2 +- test/samples/upce-2/31.txt | 2 +- test/samples/upce-2/33.txt | 2 +- test/samples/upce-2/34.txt | 2 +- test/samples/upce-2/35.txt | 2 +- test/samples/upce-2/37.txt | 2 +- test/samples/upce-2/38.txt | 2 +- test/samples/upce-2/39.txt | 2 +- test/samples/upce-2/41.txt | 2 +- test/samples/upce-3/01.txt | 2 +- test/samples/upce-3/02.txt | 2 +- test/samples/upce-3/03.txt | 2 +- test/samples/upce-3/04.txt | 2 +- test/samples/upce-3/05.txt | 2 +- test/samples/upce-3/06.txt | 2 +- test/samples/upce-3/07.txt | 2 +- test/samples/upce-3/08.txt | 2 +- test/samples/upce-3/09.txt | 2 +- test/samples/upce-3/10.txt | 2 +- test/samples/upce-3/11.txt | 2 +- 177 files changed, 203 insertions(+), 207 deletions(-) diff --git a/core/src/GTIN.cpp b/core/src/GTIN.cpp index 5ee06a9de7..1c51bda2da 100644 --- a/core/src/GTIN.cpp +++ b/core/src/GTIN.cpp @@ -201,11 +201,9 @@ std::string LookupCountryIdentifier(const std::string& GTIN, const BarcodeFormat std::string EanAddOn(const Barcode& barcode) { - if (!(BarcodeFormat::EAN13 | BarcodeFormat::UPCA | BarcodeFormat::UPCE | BarcodeFormat::EAN8).testFlag(barcode.format())) + if (barcode.symbologyIdentifier() != "]E3") return {}; - auto txt = barcode.bytes().asString(); - auto pos = txt.find(' '); - return pos != std::string::npos ? std::string(txt.substr(pos + 1)) : std::string(); + return std::string(barcode.bytes().asString().substr(barcode.format() == BarcodeFormat::EAN8 ? 8 : 13)); } std::string IssueNr(const std::string& ean2AddOn) diff --git a/core/src/GTIN.h b/core/src/GTIN.h index 9148844ff3..74df5e8645 100644 --- a/core/src/GTIN.h +++ b/core/src/GTIN.h @@ -33,6 +33,7 @@ bool IsCheckDigitValid(const std::basic_string& s) return ComputeCheckDigit(s, true) == s.back(); } +//TODO: use std::string_view in 3.0 /** * Evaluate the prefix of the GTIN to estimate the country of origin. See * diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index 7dae62c5d3..71d23fda03 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -278,42 +278,39 @@ Barcode MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std:: (_opts.hasFormat(BarcodeFormat::UPCE) && UPCE(res, begin)))) return {}; - Error error; - if (!GTIN::IsCheckDigitValid(res.format == BarcodeFormat::UPCE ? UPCEANCommon::ConvertUPCEtoUPCA(res.txt) : res.txt)) - 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. - // TODO: this is questionable - if (_opts.hasFormat(BarcodeFormat::UPCA) && res.format == BarcodeFormat::EAN13 && res.txt.front() == '0') { - res.txt = res.txt.substr(1); - res.format = BarcodeFormat::UPCA; + // 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 + if (res.format == BarcodeFormat::UPCE) + // TODO: save UPCE text in JSON extension (3.0) + res.txt = "0" + UPCEANCommon::ConvertUPCEtoUPCA(res.txt); + + Error error = !GTIN::IsCheckDigitValid(res.txt) ? ChecksumError() : Error(); + + // if we explicitly excluded EAN13, don't return an EAN13 symbol + if (res.format == BarcodeFormat::EAN13 && !_opts.hasFormat(BarcodeFormat::EAN13)) { + if (res.txt.front() == '0') + res.format = BarcodeFormat::UPCA; + else + return {}; } - // if we explicitly requested UPCA but not EAN13, don't return an EAN13 symbol - if (res.format == BarcodeFormat::EAN13 && ! _opts.hasFormat(BarcodeFormat::EAN13)) - return {}; - // 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 SymbologyIdentifier symbologyIdentifier = {'E', res.format == BarcodeFormat::EAN8 ? '4' : '0'}; next = res.end; auto ext = res.end; PartialResult addOnRes; - if (_opts.eanAddOnSymbol() != EanAddOnSymbol::Ignore && ext.skipSymbol() && ext.skipSingle(static_cast(begin.sum() * 3.5)) - && (AddOn(addOnRes, ext, 5) || AddOn(addOnRes, ext, 2))) { + if (_opts.eanAddOnSymbol() != EanAddOnSymbol::Ignore && ext.skipSymbol() + && ext.skipSingle(static_cast(begin.sum() * 3.5)) && (AddOn(addOnRes, ext, 5) || AddOn(addOnRes, ext, 2))) { + res.txt += addOnRes.txt; + next = addOnRes.end; // 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 - res.txt += " " + addOnRes.txt; - next = addOnRes.end; - - if (res.format != BarcodeFormat::EAN8) // Keeping EAN-8 with add-on as "]E4" - symbologyIdentifier.modifier = '3'; // Combined packet, EAN-13, UPC-A, UPC-E, with add-on + // @gitlost and @axxel decided to extend the spec here to simply add an EAN-8 + add-on option + symbologyIdentifier.modifier = '3'; // Combined packet, EAN-13, UPC-A, UPC-E, with add-on } - + if (_opts.eanAddOnSymbol() == EanAddOnSymbol::Require && !addOnRes.isValid()) return {}; diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 45f7073166..0bc61bb00b 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -485,32 +485,32 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set runTests("upca-1", "UPC-A", 12, { { 10, 12, 0 }, { 11, 12, 180 }, - }); + }, ReaderOptions().setFormats(BarcodeFormat::UPCA)); runTests("upca-2", "UPC-A", 36, { { 17, 22, 0 }, { 17, 22, 180 }, - }); + }, ReaderOptions().setFormats(BarcodeFormat::UPCA)); runTests("upca-3", "UPC-A", 21, { { 7, 11, 0 }, { 8, 11, 180 }, - }); + }, ReaderOptions().setFormats(BarcodeFormat::UPCA)); runTests("upca-4", "UPC-A", 19, { { 8, 12, 0, 1, 0 }, { 9, 12, 0, 1, 180 }, - }); + }, ReaderOptions().setFormats(BarcodeFormat::UPCA)); runTests("upca-5", "UPC-A", 32, { { 18, 20, 0 }, { 18, 20, 180 }, - }); + }, ReaderOptions().setFormats(BarcodeFormat::UPCA)); runTests("upca-extension-1", "UPC-A", 6, { { 4, 4, 0 }, { 3, 4, 180 }, - }, ReaderOptions().setEanAddOnSymbol(EanAddOnSymbol::Require)); + }, ReaderOptions().setEanAddOnSymbol(EanAddOnSymbol::Require).setFormats(BarcodeFormat::UPCA)); runTests("upce-1", "UPC-E", 3, { { 3, 3, 0 }, diff --git a/test/samples/ean13-extension-1/1.txt b/test/samples/ean13-extension-1/1.txt index 92434c86e4..44e8017946 100644 --- a/test/samples/ean13-extension-1/1.txt +++ b/test/samples/ean13-extension-1/1.txt @@ -1 +1 @@ -9780735200449 51299 \ No newline at end of file +978073520044951299 \ No newline at end of file diff --git a/test/samples/ean13-extension-1/2.txt b/test/samples/ean13-extension-1/2.txt index 649cc2867f..fea2e84641 100644 --- a/test/samples/ean13-extension-1/2.txt +++ b/test/samples/ean13-extension-1/2.txt @@ -1 +1 @@ -9780884271789 52495 \ No newline at end of file +978088427178952495 \ No newline at end of file diff --git a/test/samples/ean13-extension-1/32.txt b/test/samples/ean13-extension-1/32.txt index 1d5d64ac73..88bae5ccf8 100644 --- a/test/samples/ean13-extension-1/32.txt +++ b/test/samples/ean13-extension-1/32.txt @@ -1 +1 @@ -9780393058673 52595 \ No newline at end of file +978039305867352595 \ No newline at end of file diff --git a/test/samples/ean13-extension-1/33.txt b/test/samples/ean13-extension-1/33.txt index f676da65cc..bf4d7ab34c 100644 --- a/test/samples/ean13-extension-1/33.txt +++ b/test/samples/ean13-extension-1/33.txt @@ -1 +1 @@ -9781558604971 90000 \ No newline at end of file +978155860497190000 \ No newline at end of file diff --git a/test/samples/ean13-extension-1/38.txt b/test/samples/ean13-extension-1/38.txt index c0b1cf9a1c..83c46070c2 100644 --- a/test/samples/ean13-extension-1/38.txt +++ b/test/samples/ean13-extension-1/38.txt @@ -1 +1 @@ -9780201752847 55999 \ No newline at end of file +978020175284755999 \ No newline at end of file diff --git a/test/samples/upca-1/12.txt b/test/samples/upca-1/12.txt index 4ea41b0647..4d4d216dc7 100644 --- a/test/samples/upca-1/12.txt +++ b/test/samples/upca-1/12.txt @@ -1 +1 @@ -781735802045 \ No newline at end of file +0781735802045 \ No newline at end of file diff --git a/test/samples/upca-1/16.txt b/test/samples/upca-1/16.txt index ec6ea1e7d0..a4c5625b88 100644 --- a/test/samples/upca-1/16.txt +++ b/test/samples/upca-1/16.txt @@ -1 +1 @@ -456314319671 \ No newline at end of file +0456314319671 \ No newline at end of file diff --git a/test/samples/upca-1/17.txt b/test/samples/upca-1/17.txt index 99d98e6d4e..8fe5efa904 100644 --- a/test/samples/upca-1/17.txt +++ b/test/samples/upca-1/17.txt @@ -1 +1 @@ -434704791429 \ No newline at end of file +0434704791429 \ No newline at end of file diff --git a/test/samples/upca-1/2.txt b/test/samples/upca-1/2.txt index 8304219133..c77da5ad5f 100644 --- a/test/samples/upca-1/2.txt +++ b/test/samples/upca-1/2.txt @@ -1 +1 @@ -036602301467 \ No newline at end of file +0036602301467 \ No newline at end of file diff --git a/test/samples/upca-1/21.txt b/test/samples/upca-1/21.txt index ac41ca891a..250276e405 100644 --- a/test/samples/upca-1/21.txt +++ b/test/samples/upca-1/21.txt @@ -1 +1 @@ -752919460009 \ No newline at end of file +0752919460009 \ No newline at end of file diff --git a/test/samples/upca-1/27.txt b/test/samples/upca-1/27.txt index 592cb74db3..f179678923 100644 --- a/test/samples/upca-1/27.txt +++ b/test/samples/upca-1/27.txt @@ -1 +1 @@ -606949762520 \ No newline at end of file +0606949762520 \ No newline at end of file diff --git a/test/samples/upca-1/28.txt b/test/samples/upca-1/28.txt index 051a382314..500284fe0e 100644 --- a/test/samples/upca-1/28.txt +++ b/test/samples/upca-1/28.txt @@ -1 +1 @@ -061869053712 \ No newline at end of file +0061869053712 \ No newline at end of file diff --git a/test/samples/upca-1/29.txt b/test/samples/upca-1/29.txt index 1335b711d4..7bf0db4c72 100644 --- a/test/samples/upca-1/29.txt +++ b/test/samples/upca-1/29.txt @@ -1 +1 @@ -619659023935 \ No newline at end of file +0619659023935 \ No newline at end of file diff --git a/test/samples/upca-1/3.txt b/test/samples/upca-1/3.txt index e53c211b19..3f0424553e 100644 --- a/test/samples/upca-1/3.txt +++ b/test/samples/upca-1/3.txt @@ -1 +1 @@ -070097025088 \ No newline at end of file +0070097025088 \ No newline at end of file diff --git a/test/samples/upca-1/35.txt b/test/samples/upca-1/35.txt index 9b3f5d3a99..c31e90076d 100644 --- a/test/samples/upca-1/35.txt +++ b/test/samples/upca-1/35.txt @@ -1 +1 @@ -045496442736 \ No newline at end of file +0045496442736 \ No newline at end of file diff --git a/test/samples/upca-1/4.txt b/test/samples/upca-1/4.txt index e53c211b19..3f0424553e 100644 --- a/test/samples/upca-1/4.txt +++ b/test/samples/upca-1/4.txt @@ -1 +1 @@ -070097025088 \ No newline at end of file +0070097025088 \ No newline at end of file diff --git a/test/samples/upca-1/5.txt b/test/samples/upca-1/5.txt index e53c211b19..3f0424553e 100644 --- a/test/samples/upca-1/5.txt +++ b/test/samples/upca-1/5.txt @@ -1 +1 @@ -070097025088 \ No newline at end of file +0070097025088 \ No newline at end of file diff --git a/test/samples/upca-2/01.txt b/test/samples/upca-2/01.txt index ad9b706644..cf0e6c9cfd 100644 --- a/test/samples/upca-2/01.txt +++ b/test/samples/upca-2/01.txt @@ -1 +1 @@ -890444000335 \ No newline at end of file +0890444000335 \ No newline at end of file diff --git a/test/samples/upca-2/02.txt b/test/samples/upca-2/02.txt index ad9b706644..cf0e6c9cfd 100644 --- a/test/samples/upca-2/02.txt +++ b/test/samples/upca-2/02.txt @@ -1 +1 @@ -890444000335 \ No newline at end of file +0890444000335 \ No newline at end of file diff --git a/test/samples/upca-2/03.txt b/test/samples/upca-2/03.txt index ad9b706644..cf0e6c9cfd 100644 --- a/test/samples/upca-2/03.txt +++ b/test/samples/upca-2/03.txt @@ -1 +1 @@ -890444000335 \ No newline at end of file +0890444000335 \ No newline at end of file diff --git a/test/samples/upca-2/04.txt b/test/samples/upca-2/04.txt index ad9b706644..cf0e6c9cfd 100644 --- a/test/samples/upca-2/04.txt +++ b/test/samples/upca-2/04.txt @@ -1 +1 @@ -890444000335 \ No newline at end of file +0890444000335 \ No newline at end of file diff --git a/test/samples/upca-2/06.txt b/test/samples/upca-2/06.txt index ad9b706644..cf0e6c9cfd 100644 --- a/test/samples/upca-2/06.txt +++ b/test/samples/upca-2/06.txt @@ -1 +1 @@ -890444000335 \ No newline at end of file +0890444000335 \ No newline at end of file diff --git a/test/samples/upca-2/07.txt b/test/samples/upca-2/07.txt index ad9b706644..cf0e6c9cfd 100644 --- a/test/samples/upca-2/07.txt +++ b/test/samples/upca-2/07.txt @@ -1 +1 @@ -890444000335 \ No newline at end of file +0890444000335 \ No newline at end of file diff --git a/test/samples/upca-2/08.txt b/test/samples/upca-2/08.txt index 00773d47e9..919c30a924 100644 --- a/test/samples/upca-2/08.txt +++ b/test/samples/upca-2/08.txt @@ -1 +1 @@ -181497000879 \ No newline at end of file +0181497000879 \ No newline at end of file diff --git a/test/samples/upca-2/09.txt b/test/samples/upca-2/09.txt index 00773d47e9..919c30a924 100644 --- a/test/samples/upca-2/09.txt +++ b/test/samples/upca-2/09.txt @@ -1 +1 @@ -181497000879 \ No newline at end of file +0181497000879 \ No newline at end of file diff --git a/test/samples/upca-2/10.txt b/test/samples/upca-2/10.txt index 00773d47e9..919c30a924 100644 --- a/test/samples/upca-2/10.txt +++ b/test/samples/upca-2/10.txt @@ -1 +1 @@ -181497000879 \ No newline at end of file +0181497000879 \ No newline at end of file diff --git a/test/samples/upca-2/11.txt b/test/samples/upca-2/11.txt index 00773d47e9..919c30a924 100644 --- a/test/samples/upca-2/11.txt +++ b/test/samples/upca-2/11.txt @@ -1 +1 @@ -181497000879 \ No newline at end of file +0181497000879 \ No newline at end of file diff --git a/test/samples/upca-2/12.txt b/test/samples/upca-2/12.txt index 00773d47e9..919c30a924 100644 --- a/test/samples/upca-2/12.txt +++ b/test/samples/upca-2/12.txt @@ -1 +1 @@ -181497000879 \ No newline at end of file +0181497000879 \ No newline at end of file diff --git a/test/samples/upca-2/14.txt b/test/samples/upca-2/14.txt index 2d0688ae31..9da2ee6c48 100644 --- a/test/samples/upca-2/14.txt +++ b/test/samples/upca-2/14.txt @@ -1 +1 @@ -051000000675 \ No newline at end of file +0051000000675 \ No newline at end of file diff --git a/test/samples/upca-2/15.txt b/test/samples/upca-2/15.txt index 2d0688ae31..9da2ee6c48 100644 --- a/test/samples/upca-2/15.txt +++ b/test/samples/upca-2/15.txt @@ -1 +1 @@ -051000000675 \ No newline at end of file +0051000000675 \ No newline at end of file diff --git a/test/samples/upca-2/16.txt b/test/samples/upca-2/16.txt index 2d0688ae31..9da2ee6c48 100644 --- a/test/samples/upca-2/16.txt +++ b/test/samples/upca-2/16.txt @@ -1 +1 @@ -051000000675 \ No newline at end of file +0051000000675 \ No newline at end of file diff --git a/test/samples/upca-2/23.txt b/test/samples/upca-2/23.txt index 2598343b9e..47d4bc5395 100644 --- a/test/samples/upca-2/23.txt +++ b/test/samples/upca-2/23.txt @@ -1 +1 @@ -752050200137 \ No newline at end of file +0752050200137 \ No newline at end of file diff --git a/test/samples/upca-2/24.txt b/test/samples/upca-2/24.txt index 2598343b9e..47d4bc5395 100644 --- a/test/samples/upca-2/24.txt +++ b/test/samples/upca-2/24.txt @@ -1 +1 @@ -752050200137 \ No newline at end of file +0752050200137 \ No newline at end of file diff --git a/test/samples/upca-2/25.txt b/test/samples/upca-2/25.txt index 2598343b9e..47d4bc5395 100644 --- a/test/samples/upca-2/25.txt +++ b/test/samples/upca-2/25.txt @@ -1 +1 @@ -752050200137 \ No newline at end of file +0752050200137 \ No newline at end of file diff --git a/test/samples/upca-2/26.txt b/test/samples/upca-2/26.txt index 2598343b9e..47d4bc5395 100644 --- a/test/samples/upca-2/26.txt +++ b/test/samples/upca-2/26.txt @@ -1 +1 @@ -752050200137 \ No newline at end of file +0752050200137 \ No newline at end of file diff --git a/test/samples/upca-2/28.txt b/test/samples/upca-2/28.txt index 2598343b9e..47d4bc5395 100644 --- a/test/samples/upca-2/28.txt +++ b/test/samples/upca-2/28.txt @@ -1 +1 @@ -752050200137 \ No newline at end of file +0752050200137 \ No newline at end of file diff --git a/test/samples/upca-2/29.txt b/test/samples/upca-2/29.txt index 2598343b9e..47d4bc5395 100644 --- a/test/samples/upca-2/29.txt +++ b/test/samples/upca-2/29.txt @@ -1 +1 @@ -752050200137 \ No newline at end of file +0752050200137 \ No newline at end of file diff --git a/test/samples/upca-2/31.txt b/test/samples/upca-2/31.txt index 8889039111..05fd1737e8 100644 --- a/test/samples/upca-2/31.txt +++ b/test/samples/upca-2/31.txt @@ -1 +1 @@ -899684001003 \ No newline at end of file +0899684001003 \ No newline at end of file diff --git a/test/samples/upca-2/32.txt b/test/samples/upca-2/32.txt index 8889039111..05fd1737e8 100644 --- a/test/samples/upca-2/32.txt +++ b/test/samples/upca-2/32.txt @@ -1 +1 @@ -899684001003 \ No newline at end of file +0899684001003 \ No newline at end of file diff --git a/test/samples/upca-2/33.txt b/test/samples/upca-2/33.txt index 8889039111..05fd1737e8 100644 --- a/test/samples/upca-2/33.txt +++ b/test/samples/upca-2/33.txt @@ -1 +1 @@ -899684001003 \ No newline at end of file +0899684001003 \ No newline at end of file diff --git a/test/samples/upca-2/34.txt b/test/samples/upca-2/34.txt index 8889039111..05fd1737e8 100644 --- a/test/samples/upca-2/34.txt +++ b/test/samples/upca-2/34.txt @@ -1 +1 @@ -899684001003 \ No newline at end of file +0899684001003 \ No newline at end of file diff --git a/test/samples/upca-2/35.txt b/test/samples/upca-2/35.txt index 8889039111..05fd1737e8 100644 --- a/test/samples/upca-2/35.txt +++ b/test/samples/upca-2/35.txt @@ -1 +1 @@ -899684001003 \ No newline at end of file +0899684001003 \ No newline at end of file diff --git a/test/samples/upca-2/37.txt b/test/samples/upca-2/37.txt index 8889039111..05fd1737e8 100644 --- a/test/samples/upca-2/37.txt +++ b/test/samples/upca-2/37.txt @@ -1 +1 @@ -899684001003 \ No newline at end of file +0899684001003 \ No newline at end of file diff --git a/test/samples/upca-2/38.txt b/test/samples/upca-2/38.txt index 43914c788c..9141f11ca5 100644 --- a/test/samples/upca-2/38.txt +++ b/test/samples/upca-2/38.txt @@ -1 +1 @@ -012546619592 \ No newline at end of file +0012546619592 \ No newline at end of file diff --git a/test/samples/upca-2/39.txt b/test/samples/upca-2/39.txt index 43914c788c..9141f11ca5 100644 --- a/test/samples/upca-2/39.txt +++ b/test/samples/upca-2/39.txt @@ -1 +1 @@ -012546619592 \ No newline at end of file +0012546619592 \ No newline at end of file diff --git a/test/samples/upca-2/41.txt b/test/samples/upca-2/41.txt index 43914c788c..9141f11ca5 100644 --- a/test/samples/upca-2/41.txt +++ b/test/samples/upca-2/41.txt @@ -1 +1 @@ -012546619592 \ No newline at end of file +0012546619592 \ No newline at end of file diff --git a/test/samples/upca-2/42.txt b/test/samples/upca-2/42.txt index 43914c788c..9141f11ca5 100644 --- a/test/samples/upca-2/42.txt +++ b/test/samples/upca-2/42.txt @@ -1 +1 @@ -012546619592 \ No newline at end of file +0012546619592 \ No newline at end of file diff --git a/test/samples/upca-2/45.txt b/test/samples/upca-2/45.txt index 2c50e36420..d68120964b 100644 --- a/test/samples/upca-2/45.txt +++ b/test/samples/upca-2/45.txt @@ -1 +1 @@ -075720003259 \ No newline at end of file +0075720003259 \ No newline at end of file diff --git a/test/samples/upca-2/46.txt b/test/samples/upca-2/46.txt index 2c50e36420..d68120964b 100644 --- a/test/samples/upca-2/46.txt +++ b/test/samples/upca-2/46.txt @@ -1 +1 @@ -075720003259 \ No newline at end of file +0075720003259 \ No newline at end of file diff --git a/test/samples/upca-2/48.txt b/test/samples/upca-2/48.txt index 2c50e36420..d68120964b 100644 --- a/test/samples/upca-2/48.txt +++ b/test/samples/upca-2/48.txt @@ -1 +1 @@ -075720003259 \ No newline at end of file +0075720003259 \ No newline at end of file diff --git a/test/samples/upca-2/49.txt b/test/samples/upca-2/49.txt index 2c50e36420..d68120964b 100644 --- a/test/samples/upca-2/49.txt +++ b/test/samples/upca-2/49.txt @@ -1 +1 @@ -075720003259 \ No newline at end of file +0075720003259 \ No newline at end of file diff --git a/test/samples/upca-2/50.txt b/test/samples/upca-2/50.txt index 2c50e36420..d68120964b 100644 --- a/test/samples/upca-2/50.txt +++ b/test/samples/upca-2/50.txt @@ -1 +1 @@ -075720003259 \ No newline at end of file +0075720003259 \ No newline at end of file diff --git a/test/samples/upca-2/51.txt b/test/samples/upca-2/51.txt index 2c50e36420..d68120964b 100644 --- a/test/samples/upca-2/51.txt +++ b/test/samples/upca-2/51.txt @@ -1 +1 @@ -075720003259 \ No newline at end of file +0075720003259 \ No newline at end of file diff --git a/test/samples/upca-3/01.txt b/test/samples/upca-3/01.txt index 6883994811..13b396d98e 100644 --- a/test/samples/upca-3/01.txt +++ b/test/samples/upca-3/01.txt @@ -1 +1 @@ -049000042566 \ No newline at end of file +0049000042566 \ No newline at end of file diff --git a/test/samples/upca-3/02.txt b/test/samples/upca-3/02.txt index 6883994811..13b396d98e 100644 --- a/test/samples/upca-3/02.txt +++ b/test/samples/upca-3/02.txt @@ -1 +1 @@ -049000042566 \ No newline at end of file +0049000042566 \ No newline at end of file diff --git a/test/samples/upca-3/03.txt b/test/samples/upca-3/03.txt index 6883994811..13b396d98e 100644 --- a/test/samples/upca-3/03.txt +++ b/test/samples/upca-3/03.txt @@ -1 +1 @@ -049000042566 \ No newline at end of file +0049000042566 \ No newline at end of file diff --git a/test/samples/upca-3/04.txt b/test/samples/upca-3/04.txt index 6883994811..13b396d98e 100644 --- a/test/samples/upca-3/04.txt +++ b/test/samples/upca-3/04.txt @@ -1 +1 @@ -049000042566 \ No newline at end of file +0049000042566 \ No newline at end of file diff --git a/test/samples/upca-3/05.txt b/test/samples/upca-3/05.txt index 6883994811..13b396d98e 100644 --- a/test/samples/upca-3/05.txt +++ b/test/samples/upca-3/05.txt @@ -1 +1 @@ -049000042566 \ No newline at end of file +0049000042566 \ No newline at end of file diff --git a/test/samples/upca-3/06.txt b/test/samples/upca-3/06.txt index 6883994811..13b396d98e 100644 --- a/test/samples/upca-3/06.txt +++ b/test/samples/upca-3/06.txt @@ -1 +1 @@ -049000042566 \ No newline at end of file +0049000042566 \ No newline at end of file diff --git a/test/samples/upca-3/07.txt b/test/samples/upca-3/07.txt index 6883994811..13b396d98e 100644 --- a/test/samples/upca-3/07.txt +++ b/test/samples/upca-3/07.txt @@ -1 +1 @@ -049000042566 \ No newline at end of file +0049000042566 \ No newline at end of file diff --git a/test/samples/upca-3/08.txt b/test/samples/upca-3/08.txt index 6883994811..13b396d98e 100644 --- a/test/samples/upca-3/08.txt +++ b/test/samples/upca-3/08.txt @@ -1 +1 @@ -049000042566 \ No newline at end of file +0049000042566 \ No newline at end of file diff --git a/test/samples/upca-3/09.txt b/test/samples/upca-3/09.txt index 6883994811..13b396d98e 100644 --- a/test/samples/upca-3/09.txt +++ b/test/samples/upca-3/09.txt @@ -1 +1 @@ -049000042566 \ No newline at end of file +0049000042566 \ No newline at end of file diff --git a/test/samples/upca-3/10.txt b/test/samples/upca-3/10.txt index 6883994811..13b396d98e 100644 --- a/test/samples/upca-3/10.txt +++ b/test/samples/upca-3/10.txt @@ -1 +1 @@ -049000042566 \ No newline at end of file +0049000042566 \ No newline at end of file diff --git a/test/samples/upca-3/11.txt b/test/samples/upca-3/11.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/11.txt +++ b/test/samples/upca-3/11.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-3/12.txt b/test/samples/upca-3/12.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/12.txt +++ b/test/samples/upca-3/12.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-3/13.txt b/test/samples/upca-3/13.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/13.txt +++ b/test/samples/upca-3/13.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-3/14.txt b/test/samples/upca-3/14.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/14.txt +++ b/test/samples/upca-3/14.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-3/15.txt b/test/samples/upca-3/15.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/15.txt +++ b/test/samples/upca-3/15.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-3/16.txt b/test/samples/upca-3/16.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/16.txt +++ b/test/samples/upca-3/16.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-3/17.txt b/test/samples/upca-3/17.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/17.txt +++ b/test/samples/upca-3/17.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-3/18.txt b/test/samples/upca-3/18.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/18.txt +++ b/test/samples/upca-3/18.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-3/19.txt b/test/samples/upca-3/19.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/19.txt +++ b/test/samples/upca-3/19.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-3/20.txt b/test/samples/upca-3/20.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/20.txt +++ b/test/samples/upca-3/20.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-3/21.txt b/test/samples/upca-3/21.txt index 389d4d6f9b..82222d49c5 100644 --- a/test/samples/upca-3/21.txt +++ b/test/samples/upca-3/21.txt @@ -1 +1 @@ -854818000116 \ No newline at end of file +0854818000116 \ No newline at end of file diff --git a/test/samples/upca-4/1.txt b/test/samples/upca-4/1.txt index 6759a5e332..eaf0ca9578 100644 --- a/test/samples/upca-4/1.txt +++ b/test/samples/upca-4/1.txt @@ -1 +1 @@ -023942431015 \ No newline at end of file +0023942431015 \ No newline at end of file diff --git a/test/samples/upca-4/10.txt b/test/samples/upca-4/10.txt index 3e76dac78d..3a324b3ced 100644 --- a/test/samples/upca-4/10.txt +++ b/test/samples/upca-4/10.txt @@ -1 +1 @@ -066721010995 \ No newline at end of file +0066721010995 \ No newline at end of file diff --git a/test/samples/upca-4/11.txt b/test/samples/upca-4/11.txt index cef4bfced1..963aa9035e 100644 --- a/test/samples/upca-4/11.txt +++ b/test/samples/upca-4/11.txt @@ -1 +1 @@ -059290522143 \ No newline at end of file +0059290522143 \ No newline at end of file diff --git a/test/samples/upca-4/12.txt b/test/samples/upca-4/12.txt index 6491be4395..cd19e215f1 100644 --- a/test/samples/upca-4/12.txt +++ b/test/samples/upca-4/12.txt @@ -1 +1 @@ -057961000228 \ No newline at end of file +0057961000228 \ No newline at end of file diff --git a/test/samples/upca-4/13.txt b/test/samples/upca-4/13.txt index cef4bfced1..963aa9035e 100644 --- a/test/samples/upca-4/13.txt +++ b/test/samples/upca-4/13.txt @@ -1 +1 @@ -059290522143 \ No newline at end of file +0059290522143 \ No newline at end of file diff --git a/test/samples/upca-4/14.txt b/test/samples/upca-4/14.txt index 14eb468600..fd14b7c7ac 100644 --- a/test/samples/upca-4/14.txt +++ b/test/samples/upca-4/14.txt @@ -1 +1 @@ -066721017185 \ No newline at end of file +0066721017185 \ No newline at end of file diff --git a/test/samples/upca-4/15.txt b/test/samples/upca-4/15.txt index 8d6fe86fb0..9929fac186 100644 --- a/test/samples/upca-4/15.txt +++ b/test/samples/upca-4/15.txt @@ -1 +1 @@ -059290571110 \ No newline at end of file +0059290571110 \ No newline at end of file diff --git a/test/samples/upca-4/16.txt b/test/samples/upca-4/16.txt index 969c6d4711..73ebc332a0 100644 --- a/test/samples/upca-4/16.txt +++ b/test/samples/upca-4/16.txt @@ -1 +1 @@ -067932000263 \ No newline at end of file +0067932000263 \ No newline at end of file diff --git a/test/samples/upca-4/17.txt b/test/samples/upca-4/17.txt index 52692d4c21..2eca9d941a 100644 --- a/test/samples/upca-4/17.txt +++ b/test/samples/upca-4/17.txt @@ -1 +1 @@ -069000061015 \ No newline at end of file +0069000061015 \ No newline at end of file diff --git a/test/samples/upca-4/18.txt b/test/samples/upca-4/18.txt index 3445d0ecf9..e63877d108 100644 --- a/test/samples/upca-4/18.txt +++ b/test/samples/upca-4/18.txt @@ -1 +1 @@ -071691155775 \ No newline at end of file +0071691155775 \ No newline at end of file diff --git a/test/samples/upca-4/19.txt b/test/samples/upca-4/19.txt index b3b9f9cbad..dfa4183ae3 100644 --- a/test/samples/upca-4/19.txt +++ b/test/samples/upca-4/19.txt @@ -1 +1 @@ -807648011401 \ No newline at end of file +0807648011401 \ No newline at end of file diff --git a/test/samples/upca-4/2.txt b/test/samples/upca-4/2.txt index 6759a5e332..eaf0ca9578 100644 --- a/test/samples/upca-4/2.txt +++ b/test/samples/upca-4/2.txt @@ -1 +1 @@ -023942431015 \ No newline at end of file +0023942431015 \ No newline at end of file diff --git a/test/samples/upca-4/3.txt b/test/samples/upca-4/3.txt index 6759a5e332..eaf0ca9578 100644 --- a/test/samples/upca-4/3.txt +++ b/test/samples/upca-4/3.txt @@ -1 +1 @@ -023942431015 \ No newline at end of file +0023942431015 \ No newline at end of file diff --git a/test/samples/upca-4/4.txt b/test/samples/upca-4/4.txt index 6759a5e332..eaf0ca9578 100644 --- a/test/samples/upca-4/4.txt +++ b/test/samples/upca-4/4.txt @@ -1 +1 @@ -023942431015 \ No newline at end of file +0023942431015 \ No newline at end of file diff --git a/test/samples/upca-4/5.txt b/test/samples/upca-4/5.txt index 6759a5e332..eaf0ca9578 100644 --- a/test/samples/upca-4/5.txt +++ b/test/samples/upca-4/5.txt @@ -1 +1 @@ -023942431015 \ No newline at end of file +0023942431015 \ No newline at end of file diff --git a/test/samples/upca-4/6.txt b/test/samples/upca-4/6.txt index 6759a5e332..eaf0ca9578 100644 --- a/test/samples/upca-4/6.txt +++ b/test/samples/upca-4/6.txt @@ -1 +1 @@ -023942431015 \ No newline at end of file +0023942431015 \ No newline at end of file diff --git a/test/samples/upca-4/7.txt b/test/samples/upca-4/7.txt index 6759a5e332..eaf0ca9578 100644 --- a/test/samples/upca-4/7.txt +++ b/test/samples/upca-4/7.txt @@ -1 +1 @@ -023942431015 \ No newline at end of file +0023942431015 \ No newline at end of file diff --git a/test/samples/upca-4/8.txt b/test/samples/upca-4/8.txt index 29cffd2f89..7ca6f67b70 100644 --- a/test/samples/upca-4/8.txt +++ b/test/samples/upca-4/8.txt @@ -1 +1 @@ -060410049235 \ No newline at end of file +0060410049235 \ No newline at end of file diff --git a/test/samples/upca-4/9.txt b/test/samples/upca-4/9.txt index 29cffd2f89..7ca6f67b70 100644 --- a/test/samples/upca-4/9.txt +++ b/test/samples/upca-4/9.txt @@ -1 +1 @@ -060410049235 \ No newline at end of file +0060410049235 \ No newline at end of file diff --git a/test/samples/upca-5/01.txt b/test/samples/upca-5/01.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/01.txt +++ b/test/samples/upca-5/01.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/02.txt b/test/samples/upca-5/02.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/02.txt +++ b/test/samples/upca-5/02.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/03.txt b/test/samples/upca-5/03.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/03.txt +++ b/test/samples/upca-5/03.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/04.txt b/test/samples/upca-5/04.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/04.txt +++ b/test/samples/upca-5/04.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/05.txt b/test/samples/upca-5/05.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/05.txt +++ b/test/samples/upca-5/05.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/06.txt b/test/samples/upca-5/06.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/06.txt +++ b/test/samples/upca-5/06.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/07.txt b/test/samples/upca-5/07.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/07.txt +++ b/test/samples/upca-5/07.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/08.txt b/test/samples/upca-5/08.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/08.txt +++ b/test/samples/upca-5/08.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/09.txt b/test/samples/upca-5/09.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/09.txt +++ b/test/samples/upca-5/09.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/11.txt b/test/samples/upca-5/11.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/11.txt +++ b/test/samples/upca-5/11.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/13.txt b/test/samples/upca-5/13.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/13.txt +++ b/test/samples/upca-5/13.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/14.txt b/test/samples/upca-5/14.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/14.txt +++ b/test/samples/upca-5/14.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/15.txt b/test/samples/upca-5/15.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/15.txt +++ b/test/samples/upca-5/15.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/16.txt b/test/samples/upca-5/16.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/16.txt +++ b/test/samples/upca-5/16.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/18.txt b/test/samples/upca-5/18.txt index 108f623013..11dbbef4c0 100644 --- a/test/samples/upca-5/18.txt +++ b/test/samples/upca-5/18.txt @@ -1 +1 @@ -312547701310 \ No newline at end of file +0312547701310 \ No newline at end of file diff --git a/test/samples/upca-5/19.txt b/test/samples/upca-5/19.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/19.txt +++ b/test/samples/upca-5/19.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/20.txt b/test/samples/upca-5/20.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/20.txt +++ b/test/samples/upca-5/20.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/21.txt b/test/samples/upca-5/21.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/21.txt +++ b/test/samples/upca-5/21.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/22.txt b/test/samples/upca-5/22.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/22.txt +++ b/test/samples/upca-5/22.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/23.txt b/test/samples/upca-5/23.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/23.txt +++ b/test/samples/upca-5/23.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/24.txt b/test/samples/upca-5/24.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/24.txt +++ b/test/samples/upca-5/24.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/25.txt b/test/samples/upca-5/25.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/25.txt +++ b/test/samples/upca-5/25.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/26.txt b/test/samples/upca-5/26.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/26.txt +++ b/test/samples/upca-5/26.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/27.txt b/test/samples/upca-5/27.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/27.txt +++ b/test/samples/upca-5/27.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/28.txt b/test/samples/upca-5/28.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/28.txt +++ b/test/samples/upca-5/28.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/29.txt b/test/samples/upca-5/29.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/29.txt +++ b/test/samples/upca-5/29.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/30.txt b/test/samples/upca-5/30.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/30.txt +++ b/test/samples/upca-5/30.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/31.txt b/test/samples/upca-5/31.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/31.txt +++ b/test/samples/upca-5/31.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/32.txt b/test/samples/upca-5/32.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/32.txt +++ b/test/samples/upca-5/32.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/33.txt b/test/samples/upca-5/33.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/33.txt +++ b/test/samples/upca-5/33.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/34.txt b/test/samples/upca-5/34.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/34.txt +++ b/test/samples/upca-5/34.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-5/35.txt b/test/samples/upca-5/35.txt index 5546cb7261..6a338b3763 100644 --- a/test/samples/upca-5/35.txt +++ b/test/samples/upca-5/35.txt @@ -1 +1 @@ -625034201058 \ No newline at end of file +0625034201058 \ No newline at end of file diff --git a/test/samples/upca-extension-1/10.txt b/test/samples/upca-extension-1/10.txt index 21a935da65..d6f98d28b1 100644 --- a/test/samples/upca-extension-1/10.txt +++ b/test/samples/upca-extension-1/10.txt @@ -1 +1 @@ -027011006951 02601 \ No newline at end of file +002701100695102601 \ No newline at end of file diff --git a/test/samples/upca-extension-1/11.txt b/test/samples/upca-extension-1/11.txt index 21a935da65..d6f98d28b1 100644 --- a/test/samples/upca-extension-1/11.txt +++ b/test/samples/upca-extension-1/11.txt @@ -1 +1 @@ -027011006951 02601 \ No newline at end of file +002701100695102601 \ No newline at end of file diff --git a/test/samples/upca-extension-1/18.txt b/test/samples/upca-extension-1/18.txt index 7e45eaa44f..8b57668bad 100644 --- a/test/samples/upca-extension-1/18.txt +++ b/test/samples/upca-extension-1/18.txt @@ -1 +1 @@ -024543136538 00 \ No newline at end of file +002454313653800 \ No newline at end of file diff --git a/test/samples/upca-extension-1/19.txt b/test/samples/upca-extension-1/19.txt index 7e45eaa44f..8b57668bad 100644 --- a/test/samples/upca-extension-1/19.txt +++ b/test/samples/upca-extension-1/19.txt @@ -1 +1 @@ -024543136538 00 \ No newline at end of file +002454313653800 \ No newline at end of file diff --git a/test/samples/upca-extension-1/8.txt b/test/samples/upca-extension-1/8.txt index 9d94cdc313..6d0c46836e 100644 --- a/test/samples/upca-extension-1/8.txt +++ b/test/samples/upca-extension-1/8.txt @@ -1 +1 @@ -071831007995 19868 \ No newline at end of file +007183100799519868 \ No newline at end of file diff --git a/test/samples/upca-extension-1/9.txt b/test/samples/upca-extension-1/9.txt index 9d94cdc313..6d0c46836e 100644 --- a/test/samples/upca-extension-1/9.txt +++ b/test/samples/upca-extension-1/9.txt @@ -1 +1 @@ -071831007995 19868 \ No newline at end of file +007183100799519868 \ No newline at end of file diff --git a/test/samples/upce-1/1.txt b/test/samples/upce-1/1.txt index ff99f0ec0c..1c785f6487 100644 --- a/test/samples/upce-1/1.txt +++ b/test/samples/upce-1/1.txt @@ -1 +1 @@ -01234565 \ No newline at end of file +0012345000065 \ No newline at end of file diff --git a/test/samples/upce-1/2.txt b/test/samples/upce-1/2.txt index f17aea9adb..90b5b6913d 100644 --- a/test/samples/upce-1/2.txt +++ b/test/samples/upce-1/2.txt @@ -1 +1 @@ -00123457 \ No newline at end of file +0001234000057 \ No newline at end of file diff --git a/test/samples/upce-1/4.txt b/test/samples/upce-1/4.txt index 8e866d267f..c57cc8b485 100644 --- a/test/samples/upce-1/4.txt +++ b/test/samples/upce-1/4.txt @@ -1 +1 @@ -01234531 \ No newline at end of file +0012300000451 \ No newline at end of file diff --git a/test/samples/upce-2/01.txt b/test/samples/upce-2/01.txt index 142011da50..5d57d12e85 100644 --- a/test/samples/upce-2/01.txt +++ b/test/samples/upce-2/01.txt @@ -1 +1 @@ -05096893 \ No newline at end of file +0050968000093 \ No newline at end of file diff --git a/test/samples/upce-2/02.txt b/test/samples/upce-2/02.txt index 142011da50..5d57d12e85 100644 --- a/test/samples/upce-2/02.txt +++ b/test/samples/upce-2/02.txt @@ -1 +1 @@ -05096893 \ No newline at end of file +0050968000093 \ No newline at end of file diff --git a/test/samples/upce-2/03.txt b/test/samples/upce-2/03.txt index 142011da50..5d57d12e85 100644 --- a/test/samples/upce-2/03.txt +++ b/test/samples/upce-2/03.txt @@ -1 +1 @@ -05096893 \ No newline at end of file +0050968000093 \ No newline at end of file diff --git a/test/samples/upce-2/05.txt b/test/samples/upce-2/05.txt index 142011da50..5d57d12e85 100644 --- a/test/samples/upce-2/05.txt +++ b/test/samples/upce-2/05.txt @@ -1 +1 @@ -05096893 \ No newline at end of file +0050968000093 \ No newline at end of file diff --git a/test/samples/upce-2/06.txt b/test/samples/upce-2/06.txt index 142011da50..5d57d12e85 100644 --- a/test/samples/upce-2/06.txt +++ b/test/samples/upce-2/06.txt @@ -1 +1 @@ -05096893 \ No newline at end of file +0050968000093 \ No newline at end of file diff --git a/test/samples/upce-2/08.txt b/test/samples/upce-2/08.txt index 5d7eb4267d..720110b3c4 100644 --- a/test/samples/upce-2/08.txt +++ b/test/samples/upce-2/08.txt @@ -1 +1 @@ -04963406 \ No newline at end of file +0049000006346 \ No newline at end of file diff --git a/test/samples/upce-2/10.txt b/test/samples/upce-2/10.txt index 5d7eb4267d..720110b3c4 100644 --- a/test/samples/upce-2/10.txt +++ b/test/samples/upce-2/10.txt @@ -1 +1 @@ -04963406 \ No newline at end of file +0049000006346 \ No newline at end of file diff --git a/test/samples/upce-2/11.txt b/test/samples/upce-2/11.txt index 5d7eb4267d..720110b3c4 100644 --- a/test/samples/upce-2/11.txt +++ b/test/samples/upce-2/11.txt @@ -1 +1 @@ -04963406 \ No newline at end of file +0049000006346 \ No newline at end of file diff --git a/test/samples/upce-2/12.txt b/test/samples/upce-2/12.txt index 5d7eb4267d..720110b3c4 100644 --- a/test/samples/upce-2/12.txt +++ b/test/samples/upce-2/12.txt @@ -1 +1 @@ -04963406 \ No newline at end of file +0049000006346 \ No newline at end of file diff --git a/test/samples/upce-2/14.txt b/test/samples/upce-2/14.txt index 5d7eb4267d..720110b3c4 100644 --- a/test/samples/upce-2/14.txt +++ b/test/samples/upce-2/14.txt @@ -1 +1 @@ -04963406 \ No newline at end of file +0049000006346 \ No newline at end of file diff --git a/test/samples/upce-2/16.txt b/test/samples/upce-2/16.txt index 1c6c6df5b1..9672a04988 100644 --- a/test/samples/upce-2/16.txt +++ b/test/samples/upce-2/16.txt @@ -1 +1 @@ -04124498 \ No newline at end of file +0041244000098 \ No newline at end of file diff --git a/test/samples/upce-2/17.txt b/test/samples/upce-2/17.txt index 1c6c6df5b1..9672a04988 100644 --- a/test/samples/upce-2/17.txt +++ b/test/samples/upce-2/17.txt @@ -1 +1 @@ -04124498 \ No newline at end of file +0041244000098 \ No newline at end of file diff --git a/test/samples/upce-2/18.txt b/test/samples/upce-2/18.txt index 1c6c6df5b1..9672a04988 100644 --- a/test/samples/upce-2/18.txt +++ b/test/samples/upce-2/18.txt @@ -1 +1 @@ -04124498 \ No newline at end of file +0041244000098 \ No newline at end of file diff --git a/test/samples/upce-2/20.txt b/test/samples/upce-2/20.txt index 1c6c6df5b1..9672a04988 100644 --- a/test/samples/upce-2/20.txt +++ b/test/samples/upce-2/20.txt @@ -1 +1 @@ -04124498 \ No newline at end of file +0041244000098 \ No newline at end of file diff --git a/test/samples/upce-2/21.txt b/test/samples/upce-2/21.txt index 1c6c6df5b1..9672a04988 100644 --- a/test/samples/upce-2/21.txt +++ b/test/samples/upce-2/21.txt @@ -1 +1 @@ -04124498 \ No newline at end of file +0041244000098 \ No newline at end of file diff --git a/test/samples/upce-2/23.txt b/test/samples/upce-2/23.txt index 1c6c6df5b1..9672a04988 100644 --- a/test/samples/upce-2/23.txt +++ b/test/samples/upce-2/23.txt @@ -1 +1 @@ -04124498 \ No newline at end of file +0041244000098 \ No newline at end of file diff --git a/test/samples/upce-2/24.txt b/test/samples/upce-2/24.txt index 1c6c6df5b1..9672a04988 100644 --- a/test/samples/upce-2/24.txt +++ b/test/samples/upce-2/24.txt @@ -1 +1 @@ -04124498 \ No newline at end of file +0041244000098 \ No newline at end of file diff --git a/test/samples/upce-2/26.txt b/test/samples/upce-2/26.txt index 1c6c6df5b1..9672a04988 100644 --- a/test/samples/upce-2/26.txt +++ b/test/samples/upce-2/26.txt @@ -1 +1 @@ -04124498 \ No newline at end of file +0041244000098 \ No newline at end of file diff --git a/test/samples/upce-2/28.txt b/test/samples/upce-2/28.txt index 1c6c6df5b1..9672a04988 100644 --- a/test/samples/upce-2/28.txt +++ b/test/samples/upce-2/28.txt @@ -1 +1 @@ -04124498 \ No newline at end of file +0041244000098 \ No newline at end of file diff --git a/test/samples/upce-2/29.txt b/test/samples/upce-2/29.txt index 1c6c6df5b1..9672a04988 100644 --- a/test/samples/upce-2/29.txt +++ b/test/samples/upce-2/29.txt @@ -1 +1 @@ -04124498 \ No newline at end of file +0041244000098 \ No newline at end of file diff --git a/test/samples/upce-2/31.txt b/test/samples/upce-2/31.txt index e9c1b632b6..a6735d6f30 100644 --- a/test/samples/upce-2/31.txt +++ b/test/samples/upce-2/31.txt @@ -1 +1 @@ -01264904 \ No newline at end of file +0012000006494 \ No newline at end of file diff --git a/test/samples/upce-2/33.txt b/test/samples/upce-2/33.txt index e9c1b632b6..a6735d6f30 100644 --- a/test/samples/upce-2/33.txt +++ b/test/samples/upce-2/33.txt @@ -1 +1 @@ -01264904 \ No newline at end of file +0012000006494 \ No newline at end of file diff --git a/test/samples/upce-2/34.txt b/test/samples/upce-2/34.txt index e9c1b632b6..a6735d6f30 100644 --- a/test/samples/upce-2/34.txt +++ b/test/samples/upce-2/34.txt @@ -1 +1 @@ -01264904 \ No newline at end of file +0012000006494 \ No newline at end of file diff --git a/test/samples/upce-2/35.txt b/test/samples/upce-2/35.txt index e9c1b632b6..a6735d6f30 100644 --- a/test/samples/upce-2/35.txt +++ b/test/samples/upce-2/35.txt @@ -1 +1 @@ -01264904 \ No newline at end of file +0012000006494 \ No newline at end of file diff --git a/test/samples/upce-2/37.txt b/test/samples/upce-2/37.txt index e9c1b632b6..a6735d6f30 100644 --- a/test/samples/upce-2/37.txt +++ b/test/samples/upce-2/37.txt @@ -1 +1 @@ -01264904 \ No newline at end of file +0012000006494 \ No newline at end of file diff --git a/test/samples/upce-2/38.txt b/test/samples/upce-2/38.txt index e9c1b632b6..a6735d6f30 100644 --- a/test/samples/upce-2/38.txt +++ b/test/samples/upce-2/38.txt @@ -1 +1 @@ -01264904 \ No newline at end of file +0012000006494 \ No newline at end of file diff --git a/test/samples/upce-2/39.txt b/test/samples/upce-2/39.txt index e9c1b632b6..a6735d6f30 100644 --- a/test/samples/upce-2/39.txt +++ b/test/samples/upce-2/39.txt @@ -1 +1 @@ -01264904 \ No newline at end of file +0012000006494 \ No newline at end of file diff --git a/test/samples/upce-2/41.txt b/test/samples/upce-2/41.txt index e9c1b632b6..a6735d6f30 100644 --- a/test/samples/upce-2/41.txt +++ b/test/samples/upce-2/41.txt @@ -1 +1 @@ -01264904 \ No newline at end of file +0012000006494 \ No newline at end of file diff --git a/test/samples/upce-3/01.txt b/test/samples/upce-3/01.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/01.txt +++ b/test/samples/upce-3/01.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file diff --git a/test/samples/upce-3/02.txt b/test/samples/upce-3/02.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/02.txt +++ b/test/samples/upce-3/02.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file diff --git a/test/samples/upce-3/03.txt b/test/samples/upce-3/03.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/03.txt +++ b/test/samples/upce-3/03.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file diff --git a/test/samples/upce-3/04.txt b/test/samples/upce-3/04.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/04.txt +++ b/test/samples/upce-3/04.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file diff --git a/test/samples/upce-3/05.txt b/test/samples/upce-3/05.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/05.txt +++ b/test/samples/upce-3/05.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file diff --git a/test/samples/upce-3/06.txt b/test/samples/upce-3/06.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/06.txt +++ b/test/samples/upce-3/06.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file diff --git a/test/samples/upce-3/07.txt b/test/samples/upce-3/07.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/07.txt +++ b/test/samples/upce-3/07.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file diff --git a/test/samples/upce-3/08.txt b/test/samples/upce-3/08.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/08.txt +++ b/test/samples/upce-3/08.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file diff --git a/test/samples/upce-3/09.txt b/test/samples/upce-3/09.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/09.txt +++ b/test/samples/upce-3/09.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file diff --git a/test/samples/upce-3/10.txt b/test/samples/upce-3/10.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/10.txt +++ b/test/samples/upce-3/10.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file diff --git a/test/samples/upce-3/11.txt b/test/samples/upce-3/11.txt index e33c450eec..3e7a7bf486 100644 --- a/test/samples/upce-3/11.txt +++ b/test/samples/upce-3/11.txt @@ -1 +1 @@ -04965802 \ No newline at end of file +0049000006582 \ No newline at end of file From d74d5bae19d35fd938f39a305c5aaac900a37eb6 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 8 Apr 2025 00:02:39 +0200 Subject: [PATCH 035/104] Aztec: add support for `Barcode::ecLevel()` --- core/src/aztec/AZDecoder.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/aztec/AZDecoder.cpp b/core/src/aztec/AZDecoder.cpp index 844be46894..519588c313 100644 --- a/core/src/aztec/AZDecoder.cpp +++ b/core/src/aztec/AZDecoder.cpp @@ -119,7 +119,7 @@ static BitArray ExtractBits(const DetectorResult& ddata) /** * @brief Performs RS error correction on an array of bits. */ -static BitArray CorrectBits(const DetectorResult& ddata, const BitArray& rawbits) +static std::pair CorrectBits(const DetectorResult& ddata, const BitArray& rawbits) { const GenericGF* gf = nullptr; int codewordSize; @@ -167,7 +167,7 @@ static BitArray CorrectBits(const DetectorResult& ddata, const BitArray& rawbits correctedBits.appendBits(dataWord, codewordSize); } - return correctedBits; + return {std::move(correctedBits), numECCodewords * 100 / numCodewords}; } /** @@ -367,8 +367,8 @@ DecoderResult Decode(const DetectorResult& detectorResult) // This is a rune - just return the rune value return DecodeRune(detectorResult); } - auto bits = CorrectBits(detectorResult, ExtractBits(detectorResult)); - return Decode(bits); + auto [bits, ecLevel] = CorrectBits(detectorResult, ExtractBits(detectorResult)); + return Decode(bits).setEcLevel(std::to_string(ecLevel) + "%"); } catch (Error e) { return e; } From f825e96550da9e12c3c6f800744915997274e7a6 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 8 Apr 2025 01:39:39 +0200 Subject: [PATCH 036/104] WriteBarcode: use new BARCODE_RAW_TEXT feature from libzint This is a significant step forward towards a complete libzint based writer backend. It removes the use of the reader API to generate the Barcode object after creating the symbol with libzint. This adds a string based `options` member to `CreatorOptions` to pass symbology specific flags and options. This is an experiment and is motivated by the discussion in #862. This changeset draws heavily from the work done by @gitlost in his diagnostics2 branch. --- core/CMakeLists.txt | 4 +- core/src/Content.cpp | 4 +- core/src/WriteBarcode.cpp | 208 ++++++++++++++++++++-- core/src/WriteBarcode.h | 12 +- core/src/ZXAlgorithms.h | 4 + example/ZXingWriter.cpp | 8 +- test/unit/CMakeLists.txt | 6 + test/unit/WriteBarcodeTest.cpp | 304 +++++++++++++++++++++++++++++++++ zint | 2 +- 9 files changed, 530 insertions(+), 22 deletions(-) create mode 100644 test/unit/WriteBarcodeTest.cpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index fb19617bb8..1022a5c854 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -103,6 +103,8 @@ set (COMMON_FILES src/CharacterSet.cpp src/Content.h src/Content.cpp + src/DecoderResult.h + src/DetectorResult.h src/ECI.h src/ECI.cpp src/Error.h @@ -156,8 +158,6 @@ if (ZXING_READERS) src/ConcentricFinder.cpp src/DecodeHints.h $<$:src/DecodeHints.cpp> # [[deprecated]] - src/DecoderResult.h - src/DetectorResult.h src/GlobalHistogramBinarizer.h src/GlobalHistogramBinarizer.cpp src/GridSampler.h diff --git a/core/src/Content.cpp b/core/src/Content.cpp index 1b93420535..ba1a7e3afd 100644 --- a/core/src/Content.cpp +++ b/core/src/Content.cpp @@ -214,13 +214,13 @@ CharacterSet Content::guessEncoding() const return TextDecoder::GuessEncoding(input.data(), input.size(), CharacterSet::ISO8859_1); #else - return CharacterSet::Unknown; + return CharacterSet::ISO8859_1; #endif } ContentType Content::type() const { -#ifdef ZXING_READERS +#if 1 //def ZXING_READERS if (empty()) return ContentType::Text; diff --git a/core/src/WriteBarcode.cpp b/core/src/WriteBarcode.cpp index 31096e44d5..c8c4bdaf05 100644 --- a/core/src/WriteBarcode.cpp +++ b/core/src/WriteBarcode.cpp @@ -1,5 +1,6 @@ /* * Copyright 2024 Axel Waggershauser +* Copyright 2025 gitlost */ // SPDX-License-Identifier: Apache-2.0 @@ -16,6 +17,9 @@ #ifdef ZXING_USE_ZINT +#include "oned/ODUPCEANCommon.h" +#include "DecoderResult.h" +#include "DetectorResult.h" #include #else @@ -29,6 +33,7 @@ namespace ZXing { struct CreatorOptions::Data { BarcodeFormat format; + std::string options; bool readerInit = false; bool forceSquareDataMatrix = false; std::string ecLevel; @@ -39,12 +44,12 @@ struct CreatorOptions::Data mutable unique_zint_symbol zint; #ifndef __cpp_aggregate_paren_init - Data(BarcodeFormat f) : format(f) {} + Data(BarcodeFormat f, std::string o) : format(f), options(std::move(o)) {} #endif }; #define ZX_PROPERTY(TYPE, NAME) \ - TYPE CreatorOptions::NAME() const noexcept { return d->NAME; } \ + const TYPE& CreatorOptions::NAME() const noexcept { return d->NAME; } \ CreatorOptions& CreatorOptions::NAME(TYPE v)& { return d->NAME = std::move(v), *this; } \ CreatorOptions&& CreatorOptions::NAME(TYPE v)&& { return d->NAME = std::move(v), std::move(*this); } @@ -52,14 +57,22 @@ struct CreatorOptions::Data ZX_PROPERTY(bool, readerInit) ZX_PROPERTY(bool, forceSquareDataMatrix) ZX_PROPERTY(std::string, ecLevel) + ZX_PROPERTY(std::string, options) #undef ZX_PROPERTY -CreatorOptions::CreatorOptions(BarcodeFormat format) : d(std::make_unique(format)) {} -CreatorOptions::~CreatorOptions() = default; -CreatorOptions::CreatorOptions(CreatorOptions&&) = default; -CreatorOptions& CreatorOptions::operator=(CreatorOptions&&) = default; +#define ZX_RO_PROPERTY(TYPE, NAME) \ + TYPE CreatorOptions::NAME() const noexcept { return Contains(std::string_view(d->options), std::string_view(#NAME)); } + ZX_RO_PROPERTY(bool, gs1); + ZX_RO_PROPERTY(bool, stacked); + +#undef ZX_PROPERTY + + CreatorOptions::CreatorOptions(BarcodeFormat format, std::string options) : d(std::make_unique(format, std::move(options))) {} + CreatorOptions::~CreatorOptions() = default; + CreatorOptions::CreatorOptions(CreatorOptions&&) = default; + CreatorOptions& CreatorOptions::operator=(CreatorOptions&&) = default; struct WriterOptions::Data { @@ -88,9 +101,10 @@ WriterOptions::~WriterOptions() = default; WriterOptions::WriterOptions(WriterOptions&&) = default; WriterOptions& WriterOptions::operator=(WriterOptions&&) = default; -static bool IsLinearCode(BarcodeFormat format) +static bool SupportsGS1(BarcodeFormat format) { - return BarcodeFormats(BarcodeFormat::LinearCodes).testFlag(format); + using enum BarcodeFormat; + return (Aztec | Code128 | DataMatrix | QRCode | RMQRCode).testFlag(format); } static std::string ToSVG(ImageView iv) @@ -136,7 +150,10 @@ static Image ToImage(BitMatrix bits, bool isLinearCode, const WriterOptions& opt #ifdef ZXING_USE_ZINT #include "ECI.h" + +#ifdef ZXING_READERS #include "ReadBarcode.h" +#endif #include #include @@ -218,6 +235,123 @@ static int ParseECLevel(int symbology, std::string_view s) return res; }; +static constexpr struct { BarcodeFormat format; SymbologyIdentifier si; } barcodeFormat2SymbologyIdentifier[] = { + {BarcodeFormat::Aztec, {'z', '0', 3}}, // '1' GS1, '2' AIM + {BarcodeFormat::Codabar, {'F', '0'}}, // if checksum processing were implemented and checksum present and stripped then modifier would be 4 + // {BarcodeFormat::CodablockF, {'O', '4'}}, // '5' GS1 + {BarcodeFormat::Code128, {'C', '0'}}, // '1' GS1, '2' AIM + // {BarcodeFormat::Code16K, {'K', '0'}}, // '1' GS1, '2' AIM, '4' D1 PAD + {BarcodeFormat::Code39, {'A', '0'}}, // '3' checksum, '4' extended, '7' checksum,extended + {BarcodeFormat::Code93, {'G', '0'}}, // no modifiers + {BarcodeFormat::DataBar, {'e', '0', 0, AIFlag::GS1}}, + {BarcodeFormat::DataBarExpanded, {'e', '0', 0, AIFlag::GS1}}, + {BarcodeFormat::DataBarLimited, {'e', '0', 0, AIFlag::GS1}}, + {BarcodeFormat::DataMatrix, {'d', '1', 3}}, // '2' GS1, '3' AIM + // {BarcodeFormat::DotCode, {'J', '0', 3}}, // '1' GS1, '2' AIM + {BarcodeFormat::DXFilmEdge, {}}, + {BarcodeFormat::EAN8, {'E', '4'}}, + {BarcodeFormat::EAN13, {'E', '0'}}, + // {BarcodeFormat::HanXin, {'h', '0', 1}}, // '2' GS1 + {BarcodeFormat::ITF, {'I', '0'}}, // '1' check digit + {BarcodeFormat::MaxiCode, {'U', '0', 2}}, // '1' mode 2 or 3 + // {BarcodeFormat::MicroPDF417, {'L', '2', char(-1)}}, + {BarcodeFormat::MicroQRCode, {'Q', '1', 1}}, + {BarcodeFormat::PDF417, {'L', '2', char(-1)}}, + {BarcodeFormat::QRCode, {'Q', '1', 1}}, // '3' GS1, '5' AIM + {BarcodeFormat::RMQRCode, {'Q', '1', 1}}, // '3' GS1, '5' AIM + {BarcodeFormat::UPCA, {'E', '0'}}, + {BarcodeFormat::UPCE, {'E', '0'}}, +}; + +static SymbologyIdentifier SymbologyIdentifierZint2ZXing(const CreatorOptions& opts, const ByteArray& ba) +{ + const BarcodeFormat format = opts.format(); + + auto i = FindIf(barcodeFormat2SymbologyIdentifier, [format](auto& v) { return v.format == format; }); + assert(i != std::end(barcodeFormat2SymbologyIdentifier)); + SymbologyIdentifier ret = i->si; + + if ((BarcodeFormat::EAN13 | BarcodeFormat::UPCA | BarcodeFormat::UPCE).testFlag(format)) { + if (Contains(ba.asString().data(), ' ')) // Have EAN-2/5 add-on? + ret.modifier = '3'; // Combined packet, EAN-13, UPC-A, UPC-E, with add-on + } else if (format == BarcodeFormat::Code39) { + if (FindIf(ba, iscntrl) != ba.end()) // Extended Code 39? + ret.modifier = static_cast(ret.modifier + 4); + } else if (opts.gs1() && SupportsGS1(format)) { + if ((BarcodeFormat::Aztec | BarcodeFormat::Code128).testFlag(format)) + ret.modifier = '1'; + else if (format == BarcodeFormat::DataMatrix) + ret.modifier = '2'; + else if ((BarcodeFormat::QRCode | BarcodeFormat::RMQRCode).testFlag(format)) + ret.modifier = '3'; + ret.aiFlag = AIFlag::GS1; + } + + return ret; +} + +static std::string ECLevelZint2ZXing(const zint_symbol* zint) +{ + constexpr char EC_LABELS_QR[4] = {'L', 'M', 'Q', 'H'}; + + const int symbology = zint->symbology; + const int option_1 = zint->option_1; + + switch (symbology) { + case BARCODE_AZTEC: + if ((option_1 >> 8) >= 0 && (option_1 >> 8) <= 99) + return std::to_string(option_1 >> 8) + "%"; + break; + case BARCODE_MAXICODE: + // Mode + if (option_1 >= 2 && option_1 <= 6) + return std::to_string(option_1); + break; + case BARCODE_PDF417: + case BARCODE_PDF417COMP: + // Convert to percentage + if (option_1 >= 0 && option_1 <= 8) { + int overhead = symbology == BARCODE_PDF417COMP ? 35 : 69; + int cols = (zint->width - overhead) / 17; + int tot_cws = zint->rows * cols; + assert(tot_cws); + return std::to_string((2 << option_1) * 100 / tot_cws) + "%"; + } + break; + // case BARCODE_MICROPDF417: + // if ((option_1 >> 8) >= 0 && (option_1 >> 8) <= 99) + // return std::to_string(option_1 >> 8) + "%"; + // break; + case BARCODE_QRCODE: + case BARCODE_MICROQR: + case BARCODE_RMQR: + // Convert to L/M/Q/H + if (option_1 >= 1 && option_1 <= 4) + return {EC_LABELS_QR[option_1 - 1]}; + break; + // case BARCODE_HANXIN: + // if (option_1 >= 1 && option_1 <= 4) + // return "L" + std::to_string(option_1); + // break; + default: + break; + } + + return {}; +} + +static std::string NormalizedOptionsString(std::string_view sv) +{ + std::string str(sv); + std::transform(str.begin(), str.end(), str.begin(), [](char c) { return (char)std::tolower(c); }); +#ifdef __cpp_lib_erase_if + std::erase_if(str, [](char c) { return Contains("\n \"", c); }); +#else + str.erase(std::remove_if(str.begin(), str.end(), [](char c) { return Contains("\n \"", c); }), str.end()); +#endif + return str; +} + zint_symbol* CreatorOptions::zint() const { auto& zint = d->zint; @@ -228,10 +362,23 @@ zint_symbol* CreatorOptions::zint() const #endif zint.reset(ZBarcode_Create()); + d->options = NormalizedOptionsString(options()); +#ifdef PRINT_DEBUG + printf("options: %s\n", options().c_str()); +#endif + auto i = FindIf(barcodeFormatZXing2Zint, [zxing = format()](auto& v) { return v.zxing == zxing; }); if (i == std::end(barcodeFormatZXing2Zint)) throw std::invalid_argument("unsupported barcode format: " + ToString(format())); - zint->symbology = i->zint; + + if (format() == BarcodeFormat::Code128 && gs1()) + zint->symbology = BARCODE_GS1_128; + else if (format() == BarcodeFormat::DataBar && stacked()) + zint->symbology = BARCODE_DBAR_OMNSTK; + else if (format() == BarcodeFormat::DataBarExpanded && stacked()) + zint->symbology = BARCODE_DBAR_EXPSTK; + else + zint->symbology = i->zint; zint->scale = 0.5f; @@ -250,8 +397,8 @@ Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions { auto zint = opts.zint(); - zint->input_mode = mode; - zint->output_options |= OUT_BUFFER_INTERMEDIATE | BARCODE_QUIET_ZONES; + zint->input_mode = mode == UNICODE_MODE && opts.gs1() && SupportsGS1(opts.format()) ? GS1_MODE | GS1PARENS_MODE : mode; + zint->output_options |= OUT_BUFFER_INTERMEDIATE | BARCODE_QUIET_ZONES | BARCODE_RAW_TEXT; if (mode == DATA_MODE && ZBarcode_Cap(zint->symbology, ZINT_CAP_ECI)) zint->eci = static_cast(ECI::Binary); @@ -262,7 +409,7 @@ Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions printf("create symbol with size: %dx%d\n", zint->width, zint->rows); #endif -#ifdef ZXING_READERS +#if 0 // use ReadBarcode to create Barcode object auto buffer = std::vector(zint->bitmap_width * zint->bitmap_height); std::transform(zint->bitmap, zint->bitmap + zint->bitmap_width * zint->bitmap_height, buffer.data(), [](unsigned char v) { return (v == '0') * 0xff; }); @@ -270,8 +417,41 @@ Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions auto res = ReadBarcode({buffer.data(), zint->bitmap_width, zint->bitmap_height, ImageFormat::Lum}, ReaderOptions().setFormats(opts.format()).setIsPure(true).setBinarizer(Binarizer::BoolCast)); #else - //TODO: replace by proper construction from encoded data from within zint - auto res = Barcode(std::string((const char*)data, size), 0, 0, 0, opts.format(), {}); + Content content; + +#ifdef ZXING_READERS + for (int i = 0; i < zint->raw_seg_count; ++i) { + const auto& raw_seg = zint->raw_segs[i]; +#ifdef PRINT_DEBUG + printf(" seg %d of %d with eci %d: %s\n", i, zint->raw_seg_count, raw_seg.eci, (char*)raw_seg.source); +#endif + if (ECI(raw_seg.eci) != ECI::ISO8859_1) + content.switchEncoding(ECI(raw_seg.eci)); + else + content.switchEncoding(CharacterSet::ISO8859_1); // set this as default to prevent guessing without setting "hasECI" + content.append({raw_seg.source, static_cast(raw_seg.length - (opts.format() == BarcodeFormat::Code93 ? 2 : 0))}); + } + if (opts.format() == BarcodeFormat::UPCE) + content.bytes = ByteArray("0" + OneD::UPCEANCommon::ConvertUPCEtoUPCA(std::string(content.bytes.asString()))); + else if (opts.format() == BarcodeFormat::UPCA) + content.bytes = ByteArray("0" + std::string(content.bytes.asString())); +#else + if (zint->text_length) { + content.switchEncoding(ECI::UTF8); + content.append({zint->text, static_cast(zint->text_length)}); + } else { + content.switchEncoding(mode == DATA_MODE ? ECI::Binary : ECI::UTF8); + content.append({static_cast(data), static_cast(size)}); + } +#endif + + content.symbology = SymbologyIdentifierZint2ZXing(opts, content.bytes); + + DecoderResult decRes(std::move(content)); + decRes.setEcLevel(ECLevelZint2ZXing(zint)); + DetectorResult detRes; + + auto res = Barcode(std::move(decRes), std::move(detRes), opts.format()); #endif auto bits = BitMatrix(zint->bitmap_width, zint->bitmap_height); diff --git a/core/src/WriteBarcode.h b/core/src/WriteBarcode.h index 6f149b2523..22f26079f3 100644 --- a/core/src/WriteBarcode.h +++ b/core/src/WriteBarcode.h @@ -26,7 +26,7 @@ class CreatorOptions friend Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions& options); public: - CreatorOptions(BarcodeFormat format); + CreatorOptions(BarcodeFormat format, std::string options = {}); ~CreatorOptions(); CreatorOptions(CreatorOptions&&); @@ -35,7 +35,7 @@ class CreatorOptions zint_symbol* zint() const; #define ZX_PROPERTY(TYPE, NAME) \ - TYPE NAME() const noexcept; \ + const TYPE& NAME() const noexcept; \ CreatorOptions& NAME(TYPE v)&; \ CreatorOptions&& NAME(TYPE v)&&; @@ -43,8 +43,16 @@ class CreatorOptions ZX_PROPERTY(bool, readerInit) ZX_PROPERTY(bool, forceSquareDataMatrix) ZX_PROPERTY(std::string, ecLevel) + ZX_PROPERTY(std::string, options) #undef ZX_PROPERTY + +#define ZX_RO_PROPERTY(TYPE, NAME) \ + TYPE NAME() const noexcept; + + ZX_RO_PROPERTY(bool, gs1); + ZX_RO_PROPERTY(bool, stacked); +#undef ZX_RO_PROPERTY }; /** diff --git a/core/src/ZXAlgorithms.h b/core/src/ZXAlgorithms.h index 66898363eb..289e53def0 100644 --- a/core/src/ZXAlgorithms.h +++ b/core/src/ZXAlgorithms.h @@ -46,6 +46,10 @@ inline bool Contains(const char* str, char c) { return strchr(str, c) != nullptr; } +inline bool Contains(std::string_view str, std::string_view substr) { + return str.find(substr) != std::string_view::npos; +} + template