diff --git a/README.md b/README.md index 1054cb2fae..826388d960 100644 --- a/README.md +++ b/README.md @@ -40,12 +40,12 @@ Thanks a lot for your contribution! | EAN-8 | Code 128 | rMQR Code | | EAN-13 | Codabar | Aztec | | DataBar | DataBar Expanded | DataMatrix | -| | ITF | PDF417 | -| | | MaxiCode (partial) | +| | DX Film Edge | PDF417 | +| | ITF | MaxiCode (partial) | [Note:] * DataBar used to be called RSS. - * DataBar, 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. * Building with C++20 (see [CMakeLists.txt](https://github.com/zxing-cpp/zxing-cpp/blob/d4b0f502775857f257d13efd25fb840ece1bca3e/CMakeLists.txt#L45)) changes the behaviour of the library: it then supports multi-symbol and position independent detection for DataMatrix. This comes at a noticable performace cost. C++20 is enabled by default for the Android, iOS, Python and WASM wrappers. ## Getting Started diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index f18067e46e..9036c65d99 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -289,6 +289,8 @@ if (BUILD_READERS) src/oned/ODDataBarExpandedBitDecoder.cpp src/oned/ODDataBarExpandedReader.h src/oned/ODDataBarExpandedReader.cpp + src/oned/ODDXFilmEdgeReader.h + src/oned/ODDXFilmEdgeReader.cpp src/oned/ODITFReader.h src/oned/ODITFReader.cpp src/oned/ODMultiUPCEANReader.h diff --git a/core/src/BarcodeFormat.cpp b/core/src/BarcodeFormat.cpp index 01f4d3860e..b43917764e 100644 --- a/core/src/BarcodeFormat.cpp +++ b/core/src/BarcodeFormat.cpp @@ -32,6 +32,7 @@ static BarcodeFormatName NAMES[] = { {BarcodeFormat::DataBar, "DataBar"}, {BarcodeFormat::DataBarExpanded, "DataBarExpanded"}, {BarcodeFormat::DataMatrix, "DataMatrix"}, + {BarcodeFormat::DXFilmEdge, "DXFilmEdge"}, {BarcodeFormat::EAN8, "EAN-8"}, {BarcodeFormat::EAN13, "EAN-13"}, {BarcodeFormat::ITF, "ITF"}, diff --git a/core/src/BarcodeFormat.h b/core/src/BarcodeFormat.h index 72542ed363..f2d56edad7 100644 --- a/core/src/BarcodeFormat.h +++ b/core/src/BarcodeFormat.h @@ -1,4 +1,4 @@ -/* +/* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors */ @@ -40,12 +40,13 @@ enum class BarcodeFormat UPCE = (1 << 15), ///< UPC-E MicroQRCode = (1 << 16), ///< Micro QR Code RMQRCode = (1 << 17), ///< Rectangular Micro QR Code + DXFilmEdge = (1 << 18), ///< DX Film Edge Barcode - LinearCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE, + LinearCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | DXFilmEdge | UPCA | UPCE, MatrixCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode | MicroQRCode | RMQRCode, Any = LinearCodes | MatrixCodes, - _max = RMQRCode, ///> implementation detail, don't use + _max = DXFilmEdge, ///> implementation detail, don't use }; ZX_DECLARE_FLAGS(BarcodeFormats, BarcodeFormat) diff --git a/core/src/oned/ODDXFilmEdgeReader.cpp b/core/src/oned/ODDXFilmEdgeReader.cpp new file mode 100644 index 0000000000..f7647bd05d --- /dev/null +++ b/core/src/oned/ODDXFilmEdgeReader.cpp @@ -0,0 +1,380 @@ +/* + * Copyright 2023 Antoine Mérino + */ +// SPDX-License-Identifier: Apache-2.0 + +#include "DecodeHints.h" +#include "GTIN.h" +#include "ODDXFilmEdgeReader.h" +#include "Result.h" +#include "ZXAlgorithms.h" + +#include +#include +namespace ZXing::OneD { + +// Detection is made from center to bottom. +// We ensure the clock signal is decoded before the data signal to avoid false positives. +// They are two version of a DX Edge codes : without half-frame information and with half-frame information. +// The clock signal is longer if the DX code contains the half-frame information (more recent version) +constexpr int CLOCK_PATTERN_LENGTH_HF = 31; +constexpr int CLOCK_PATTERN_LENGTH_NO_HF = 23; +constexpr int DATA_START_PATTERN_SIZE = 5; +constexpr auto CLOCK_PATTERN_COMMON = FixedPattern<15, 20> {5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; +constexpr auto CLOCK_PATTERN_HF = + FixedPattern<25, CLOCK_PATTERN_LENGTH_HF>{5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3}; +constexpr auto CLOCK_PATTERN_NO_HF = FixedPattern<17, CLOCK_PATTERN_LENGTH_NO_HF>{5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3}; +constexpr auto DATA_START_PATTERN_ = FixedPattern<5, 5> {1, 1, 1, 1, 1}; +constexpr auto DATA_STOP_PATTERN_ = FixedPattern<3, 3> {1, 1, 1}; + +// Signal data length, without the start and stop patterns +constexpr int DATA_LENGTH_HF = 23; +constexpr int DATA_LENGTH_NO_HF = 15; + + +/** + * @brief Parse a part of a vector of bits (boolean) to a decimal number. + * Eg: {1, 1, 0} -> 6. + * @param begin begin of the vector's part to be parsed + * @param end end of the vector's part to be parsed + * @return The decimal value of the parsed part + */ +int toDecimal(const std::vector::iterator begin, const std::vector::iterator end) +{ + int retval = 0; + auto i = std::distance(begin, end) - 1; + for (std::vector::iterator it = begin; it != end; it++, i--) { + retval += (*it ? (1 << i) : 0); + } + return retval; +} + +// DX Film Edge Clock signal found on 35mm films. +struct Clock { + int rowNumber = 0; + bool containsHFNumber = false; // Clock signal (thus data signal) with half-frame number (longer version) + int xStart = 0; // Beginning of the clock signal on the X-axis, in pixels + int xStop = 0; // End of the clock signal on the X-axis, in pixels + int pixelTolerance = 0; // Pixel tolerance will be set depending of the length of the clock signal (in pixels) + + bool operator<(const int x) const + { + return xStart < x; + } + + bool operator < (const Clock& other) const + { + return xStart < other.xStart; + } + + /* + * @brief Check if this clocks start at about the same x position as another. + * We assume two clock are the same when they start in about the same X position, + * even if they are different clocks (stop at different position or different type). + * Only the more recent clock is kept. + */ + bool xStartInRange(const Clock& other) const + { + auto tolerance = std::max(pixelTolerance, other.pixelTolerance); + return (xStart - tolerance) <= other.xStart && (xStart + tolerance) >= other.xStart; + } + + bool xStartInRange(const int x) const + { + return (xStart - pixelTolerance) <= x && (xStart + pixelTolerance) >= x; + } + + bool xStopInRange(const int x) const + { + return (xStop - pixelTolerance) <= x && (xStop + pixelTolerance) >= x; + } + + /* + * @brief Check the clock's row number is next to the row we want to compare. + * Since we update the clock row number with the latest found signal's row number, + * the signal may be either: + * - below the clock, but not too far + * - slightly above the clock + * @param otherRownumber the other row to check if it's in range or not + * @param totalRows the image total row number (~image height) + */ + bool rowInRange(const int otherRowNumber, const int totalRows) const + { + const auto acceptedRowRange = totalRows / 5 + 1; // Below the clock, not too far + const auto rowMarginTolerance = totalRows / 20 + 1; // If above the clock, it should be really close + auto result = ((otherRowNumber >= rowNumber && otherRowNumber - rowNumber <= acceptedRowRange) + || (rowNumber <= otherRowNumber && otherRowNumber - rowNumber <= rowMarginTolerance)); + return result; + } + +}; + +class ClockSet : public std::set> { +public: + + /* + * @brief Return the clock which starts at the closest X position. + */ + const ClockSet::iterator closestElement(const int x) + { + const auto it = lower_bound(x); + if (it == begin()) + return it; + + const auto prev_it = std::prev(it); + return (it == end() || x - prev_it->xStart <= it->xStart - x) ? prev_it : it; + } + + /** + * @brief Add a new clock to the set. + * If the new clock is close enough to an existing clock in the set, + * the old clock is removed. + */ + void update(const Clock& newClock) + { + auto closestClock = closestElement(newClock.xStart); + if (closestClock != end() && newClock.xStartInRange(*closestClock)) { + erase(closestClock); + insert(newClock); + } else { + insert(newClock); + } + } +}; + + +/* +* @brief To avoid many false positives, +* the clock signal must be found to attempt to decode a data signal. +* We ensure the data signal starts below a clock. +* We accept a tolerance margin, +* ie. the signal may start a few pixels before or after the clock on the X-axis. +*/ +struct DXFEState : public RowReader::DecodingState { + ClockSet allClocks; + int totalRows = 0; +}; + +/** + * @brief Try to find a DX Film Edge clock in the given row. + * @param rowNumber the row number + * @param end end of the vector's part to be parsed + * @return The decimal value of the parsed part + */ +std::optional findClock(int rowNumber, PatternView& view) +{ + // Minimum "allowed "white" zone to the left and the right sides of the clock signal. + constexpr float minClockNoHFQuietZone = 2; + // On HF versions, the decimal number uses to be really close to the clock + constexpr float minClockHFQuietZone = 0.5; + + // Adjust the pixel shift tolerance between the data signal and the clock signal. + // 1 means the signal can be shifted up to one bar to the left or the right. + constexpr float pixelToleranceRatio = 0.5; + + // Before detecting any clock, + // try to detect the common pattern between all types of clocks. + // This avoid doing two detections at each interations instead of one, + // when they is no DX Edge code to detect. + auto commonClockPattern = + FindLeftGuard(view, CLOCK_PATTERN_COMMON.size(), CLOCK_PATTERN_COMMON, std::min(minClockNoHFQuietZone, minClockHFQuietZone)); + if (commonClockPattern.isValid()) { + bool foundClock = false; + bool containsHFNumber = false; + auto clock_pattern = FindLeftGuard(view, CLOCK_PATTERN_HF.size(), CLOCK_PATTERN_HF, minClockHFQuietZone); + if (clock_pattern.isValid()) { + foundClock = true; + containsHFNumber = true; + } else { + clock_pattern = FindLeftGuard(view, CLOCK_PATTERN_NO_HF.size(), CLOCK_PATTERN_NO_HF, minClockNoHFQuietZone); + if (clock_pattern.isValid()) + foundClock = true; + } + if (foundClock) { + Clock clock; + clock.rowNumber = rowNumber; + clock.containsHFNumber = containsHFNumber; + clock.xStart = clock_pattern.pixelsInFront(); + clock.xStop = clock_pattern.pixelsTillEnd(); + clock.pixelTolerance = (clock_pattern.pixelsTillEnd() - clock_pattern.pixelsInFront()) + / (containsHFNumber ? CLOCK_PATTERN_LENGTH_HF : CLOCK_PATTERN_LENGTH_NO_HF) * pixelToleranceRatio; + return clock; + } + } + return std::nullopt; +} + + +Result DXFilmEdgeReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const +{ + + // Retrieve the decoding state to check if a clock signal has already been found before. + // We check also if it contains the half-frame number, since it affects the signal structure. + if (!state) { + state.reset(new DXFEState); + // We need the total row number to adjust clock & signal detection sensitivity + static_cast(state.get())->totalRows = 2 * rowNumber; + } + auto& allClocks = static_cast(state.get())->allClocks; + auto& totalRows = static_cast(state.get())->totalRows; + // Minimum "allowed "white" zone to the left and the right sides of the data signal. + // We allow a smaller quiet zone, ie improve detection at risk of getting false positives, + // because the risk is greatly reduced when we check we found the clock before the signal. + constexpr float minDataQuietZone = 0.2; + + // We should find at least one clock before attempting to decode the data signal. + auto clock = findClock(rowNumber, next); + + if (clock) + allClocks.update(clock.value()); + + if (allClocks.empty()) + return {}; + + // Now that we found at least a clock, attempt to decode the data signal. + // Start by finding the data start pattern. + next = FindLeftGuard(next, DATA_START_PATTERN_.size(), DATA_START_PATTERN_, minDataQuietZone); + if (!next.isValid()) + return {}; + + auto xStart = next.pixelsInFront(); + + // The found data signal must be below the clock signal, otherwise we abort the decoding (potential false positive) + auto closestClock = allClocks.closestElement(xStart); + if (!closestClock->xStartInRange(xStart)) + return {}; + + // Avoid decoding a signal found at the top or too far from the clock + // (might happen when stacking two films one of top of the other, or other false positive situations) + if (!closestClock->rowInRange(rowNumber, totalRows)) + return {}; + + // Compute the length of a bar + // It may be greater than 1 depending on what have been found in the raw signal + auto perBarRawWidth = *next.data(); + + // Skip the data start pattern (black, white, black, white, black) + // The first signal bar is always white: this is the + // separation between the start pattern and the product number) + next.shift(DATA_START_PATTERN_SIZE); + + if (!next.isValid()) + return {}; + + std::vector dataSignalBits; + + // They are two possible data signal lengths (with or without half-frame information) + dataSignalBits.reserve(closestClock->containsHFNumber ? DATA_LENGTH_HF : DATA_LENGTH_NO_HF); + + // Populate a vector of booleans to represent the bits. true = black, false = white. + // We start the parsing just after the data start signal. + // The first bit is always a white bar (we include the separator just after the start pattern) + // Eg: {3, 1, 2} -> {0, 0, 0, 1, 0, 0} + int signalLength = 0; + bool currentBarIsBlack = false; // the current bar is white + while (signalLength < (closestClock->containsHFNumber ? DATA_LENGTH_HF : DATA_LENGTH_NO_HF)) { + if (!next.isValid()) + return {}; + + // Zero means we can't conclude on black or white bar. Abort the decoding. + if (*next.data() == 0) + return {}; + + // Adjust the current bar according to the computed ratio above. + // When the raw result is not exact (between two bars), + // we round the bar size to the nearest integer. + auto currentBarWidth = + *next.data() / perBarRawWidth + (*next.data() % perBarRawWidth >= (perBarRawWidth / 2) ? 1 : 0); + + signalLength += currentBarWidth; + + // Extend the bit array according to the current bar length. + // Eg: one white bars -> {0}, three black bars -> {1, 1, 1} + while (currentBarWidth > 0 + && (int)dataSignalBits.size() < (closestClock->containsHFNumber ? DATA_LENGTH_HF : DATA_LENGTH_NO_HF)) { + dataSignalBits.push_back(currentBarIsBlack); + --currentBarWidth; + } + + // Iterate to the next data signal bar (the color is inverted) + currentBarIsBlack = !currentBarIsBlack; + next.shift(1); + } + + // Check the signal length + if (signalLength != (closestClock->containsHFNumber ? DATA_LENGTH_HF : DATA_LENGTH_NO_HF)) + return {}; + + // Check there is the Stop pattern at the end of the data signal + next = next.subView(0, 3); + if (!IsRightGuard(next, DATA_STOP_PATTERN_, minDataQuietZone)) + return {}; + + // Check the data signal has been fully parsed + if (closestClock->containsHFNumber && dataSignalBits.size() < DATA_LENGTH_HF) + return {}; + if (!closestClock->containsHFNumber && dataSignalBits.size() < DATA_LENGTH_NO_HF) + return {}; + + // The following bits are always white (=false), they are separators. + if (dataSignalBits.at(0) || dataSignalBits.at(8)) + return {}; + if (closestClock->containsHFNumber && (dataSignalBits.at(20) || dataSignalBits.at(22))) + return {}; + if (!closestClock->containsHFNumber && (dataSignalBits.at(8) || dataSignalBits.at(14))) + return {}; + + // Check the parity bit + auto signalSum = std::accumulate(dataSignalBits.begin(), dataSignalBits.end()-2, 0); + auto parityBit = *(dataSignalBits.end() - 2); + if (signalSum % 2 != (int)parityBit) + return {}; + + // Compute the DX 1 number (product number) + auto productNumber = toDecimal(dataSignalBits.begin() + 1, dataSignalBits.begin() + 8); + if (!productNumber) + return {}; + + // Compute the DX 2 number (generation number) + auto generationNumber = toDecimal(dataSignalBits.begin() + 9, dataSignalBits.begin() + 13); + + // Generate the textual representation. + // Eg: 115-10/11A means: DX1 = 115, DX2 = 10, Frame number = 11A + std::string txt; + txt.reserve(10); + txt = std::to_string(productNumber) + "-" + std::to_string(generationNumber); + if (closestClock->containsHFNumber) { + auto halfFrameNumber = toDecimal(dataSignalBits.begin() + 13, dataSignalBits.begin() + 20); + txt += "/" + std::to_string(halfFrameNumber / 2); + if (halfFrameNumber % 2) + txt += "A"; + } + + Error error; + + // TODO is it required? + // AFAIK The DX Edge barcode doesn't follow any symbology identifier. + SymbologyIdentifier symbologyIdentifier = {'I', '0'}; // No check character validation ? + + auto xStop = next.pixelsTillEnd(); + + // The found data signal must be below the clock signal, otherwise we abort the decoding (potential false positive) + if (!closestClock->xStopInRange(xStop)) + return {}; + + + // Update the clock X coordinates with the latest corresponding data signal + // This may improve signal detection for next row iterations + if (closestClock->xStop != xStop || closestClock->xStart != xStart) { + Clock clock(*closestClock); + clock.xStart = xStart; + clock.xStop = xStop; + clock.rowNumber = rowNumber; + allClocks.erase(closestClock); + allClocks.insert(clock); + } + closestClock = allClocks.closestElement(xStart); + return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::DXFilmEdge, symbologyIdentifier, error); +} + +} // namespace ZXing::OneD diff --git a/core/src/oned/ODDXFilmEdgeReader.h b/core/src/oned/ODDXFilmEdgeReader.h new file mode 100644 index 0000000000..357e5e4517 --- /dev/null +++ b/core/src/oned/ODDXFilmEdgeReader.h @@ -0,0 +1,26 @@ +/* + * Copyright 2023 Antoine Mérino + */ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "ODRowReader.h" + +namespace ZXing::OneD { + +/** + *

Implements decoding of the DX Edge Film code format, a type or barcode found on 35mm films.

+ * + *

See https://en.wikipedia.org/wiki/DX_encoding

+ */ + +class DXFilmEdgeReader : public RowReader +{ +public: + using RowReader::RowReader; + + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; +}; + +} // namespace ZXing::OneD diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 07f6a62533..0caf3805d4 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -15,6 +15,7 @@ #include "ODCode93Reader.h" #include "ODDataBarExpandedReader.h" #include "ODDataBarReader.h" +#include "ODDXFilmEdgeReader.h" #include "ODITFReader.h" #include "ODMultiUPCEANReader.h" #include "Result.h" @@ -61,6 +62,8 @@ Reader::Reader(const ReaderOptions& opts) : ZXing::Reader(opts) _readers.emplace_back(new DataBarReader(opts)); if (formats.testFlags(BarcodeFormat::DataBarExpanded)) _readers.emplace_back(new DataBarExpandedReader(opts)); + if (formats.testFlag(BarcodeFormat::DXFilmEdge)) + _readers.emplace_back(new DXFilmEdgeReader(opts)); } Reader::~Reader() = default; diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 7dd5234333..1fecb431e9 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -333,6 +333,13 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set { auto startTime = std::chrono::steady_clock::now(); + runTests("dxfilmedge-1", "DXFilmEdge", 3, { + { 1, 2, 0 }, + { 1, 2, 0 }, + { 1, 2, 0 }, + }); + + // clang-format off runTests("aztec-1", "Aztec", 27, { { 26, 27, 0 }, diff --git a/test/samples/dxfilmedge-1/1.jpg b/test/samples/dxfilmedge-1/1.jpg new file mode 100644 index 0000000000..9f34211716 Binary files /dev/null and b/test/samples/dxfilmedge-1/1.jpg differ diff --git a/test/samples/dxfilmedge-1/1.txt b/test/samples/dxfilmedge-1/1.txt new file mode 100644 index 0000000000..49f698075c --- /dev/null +++ b/test/samples/dxfilmedge-1/1.txt @@ -0,0 +1 @@ +36-2/21 \ No newline at end of file diff --git a/test/samples/dxfilmedge-1/2.png b/test/samples/dxfilmedge-1/2.png new file mode 100644 index 0000000000..665f080a37 Binary files /dev/null and b/test/samples/dxfilmedge-1/2.png differ diff --git a/test/samples/dxfilmedge-1/2.txt b/test/samples/dxfilmedge-1/2.txt new file mode 100644 index 0000000000..a2b143407c --- /dev/null +++ b/test/samples/dxfilmedge-1/2.txt @@ -0,0 +1 @@ +80-11/23 \ No newline at end of file diff --git a/test/samples/dxfilmedge-1/3.jpg b/test/samples/dxfilmedge-1/3.jpg new file mode 100644 index 0000000000..ce106b7804 Binary files /dev/null and b/test/samples/dxfilmedge-1/3.jpg differ diff --git a/test/samples/dxfilmedge-1/3.txt b/test/samples/dxfilmedge-1/3.txt new file mode 100644 index 0000000000..2c0e0b1b49 --- /dev/null +++ b/test/samples/dxfilmedge-1/3.txt @@ -0,0 +1 @@ +10-3 \ No newline at end of file diff --git a/wrappers/android/zxingcpp/src/main/cpp/ZXingCpp.cpp b/wrappers/android/zxingcpp/src/main/cpp/ZXingCpp.cpp index e8154aaa3d..36545e262a 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/ZXingCpp.cpp +++ b/wrappers/android/zxingcpp/src/main/cpp/ZXingCpp.cpp @@ -44,6 +44,7 @@ static const char* JavaBarcodeFormatName(BarcodeFormat format) case BarcodeFormat::MicroQRCode: return "MICRO_QR_CODE"; case BarcodeFormat::RMQRCode: return "RMQR_CODE"; case BarcodeFormat::DataBar: return "DATA_BAR"; + case BarcodeFormat::DXFilmEdge: return "DX_FILM_EDGE"; case BarcodeFormat::DataBarExpanded: return "DATA_BAR_EXPANDED"; case BarcodeFormat::UPCA: return "UPC_A"; case BarcodeFormat::UPCE: return "UPC_E"; diff --git a/wrappers/android/zxingcpp/src/main/java/zxingcpp/BarcodeReader.kt b/wrappers/android/zxingcpp/src/main/java/zxingcpp/BarcodeReader.kt index 2d330baa7b..b4f7542290 100644 --- a/wrappers/android/zxingcpp/src/main/java/zxingcpp/BarcodeReader.kt +++ b/wrappers/android/zxingcpp/src/main/java/zxingcpp/BarcodeReader.kt @@ -40,7 +40,7 @@ public class BarcodeReader(public var options: Options = Options()) { // Note that this has to be kept synchronized with native (C++/JNI) side. public enum class Format { NONE, AZTEC, CODABAR, CODE_39, CODE_93, CODE_128, DATA_BAR, DATA_BAR_EXPANDED, - DATA_MATRIX, EAN_8, EAN_13, ITF, MAXICODE, PDF_417, QR_CODE, MICRO_QR_CODE, RMQR_CODE, UPC_A, UPC_E + DATA_MATRIX, DX_FILM_EDGE, EAN_8, EAN_13, ITF, MAXICODE, PDF_417, QR_CODE, MICRO_QR_CODE, RMQR_CODE, UPC_A, UPC_E } public enum class ContentType { diff --git a/wrappers/c/zxing-c.h b/wrappers/c/zxing-c.h index 636dd433b4..58a370dda7 100644 --- a/wrappers/c/zxing-c.h +++ b/wrappers/c/zxing-c.h @@ -77,11 +77,12 @@ typedef enum zxing_BarcodeFormat_UPCE = (1 << 15), zxing_BarcodeFormat_MicroQRCode = (1 << 16), zxing_BarcodeFormat_RMQRCode = (1 << 17), + zxing_BarcodeFormat_DXFilmEdge = (1 << 18), zxing_BarcodeFormat_LinearCodes = zxing_BarcodeFormat_Codabar | zxing_BarcodeFormat_Code39 | zxing_BarcodeFormat_Code93 | zxing_BarcodeFormat_Code128 | zxing_BarcodeFormat_EAN8 | zxing_BarcodeFormat_EAN13 - | zxing_BarcodeFormat_ITF | zxing_BarcodeFormat_DataBar | zxing_BarcodeFormat_DataBarExpanded - | zxing_BarcodeFormat_UPCA | zxing_BarcodeFormat_UPCE, + | zxing_BarcodeFormat_ITF | zxing_BarcodeFormat_DataBar | zxing_BarcodeFormat_DataBarExpanded | zxing_BarcodeFormat_UPCA | zxing_BarcodeFormat_UPCE + | zxing_BarcodeFormat_DXFilmEdge, zxing_BarcodeFormat_MatrixCodes = zxing_BarcodeFormat_Aztec | zxing_BarcodeFormat_DataMatrix | zxing_BarcodeFormat_MaxiCode | zxing_BarcodeFormat_PDF417 | zxing_BarcodeFormat_QRCode | zxing_BarcodeFormat_MicroQRCode | zxing_BarcodeFormat_RMQRCode, diff --git a/wrappers/ios/Sources/Wrapper/ZXIFormat.h b/wrappers/ios/Sources/Wrapper/ZXIFormat.h index 0ba9bd8f6d..78156102b7 100644 --- a/wrappers/ios/Sources/Wrapper/ZXIFormat.h +++ b/wrappers/ios/Sources/Wrapper/ZXIFormat.h @@ -8,7 +8,7 @@ typedef NS_ENUM(NSInteger, ZXIFormat) { NONE, AZTEC, CODABAR, CODE_39, CODE_93, CODE_128, DATA_BAR, DATA_BAR_EXPANDED, - DATA_MATRIX, EAN_8, EAN_13, ITF, MAXICODE, PDF_417, QR_CODE, MICRO_QR_CODE, RMQR_CODE, UPC_A, UPC_E, + DATA_MATRIX, DX_FILM_EDGE, EAN_8, EAN_13, ITF, MAXICODE, PDF_417, QR_CODE, MICRO_QR_CODE, RMQR_CODE, UPC_A, UPC_E, LINEAR_CODES, MATRIX_CODES, ANY }; diff --git a/wrappers/ios/Sources/Wrapper/ZXIFormatHelper.mm b/wrappers/ios/Sources/Wrapper/ZXIFormatHelper.mm index 487933f4e4..ef6bf9160b 100644 --- a/wrappers/ios/Sources/Wrapper/ZXIFormatHelper.mm +++ b/wrappers/ios/Sources/Wrapper/ZXIFormatHelper.mm @@ -34,6 +34,8 @@ return ZXing::BarcodeFormat::DataBarExpanded; case ZXIFormat::DATA_BAR: return ZXing::BarcodeFormat::DataBar; + case ZXIFormat::DX_FILM_EDGE: + return ZXing::BarcodeFormat::DXFilmEdge; case ZXIFormat::CODE_128: return ZXing::BarcodeFormat::Code128; case ZXIFormat::CODE_93: @@ -71,6 +73,8 @@ ZXIFormat ZXIFormatFromBarcodeFormat(ZXing::BarcodeFormat format) { return ZXIFormat::CODE_128; case ZXing::BarcodeFormat::DataBar: return ZXIFormat::DATA_BAR; + case ZXing::BarcodeFormat::DXFilmEdge: + return ZXIFormat::DX_FILM_EDGE; case ZXing::BarcodeFormat::DataBarExpanded: return ZXIFormat::DATA_BAR_EXPANDED; case ZXing::BarcodeFormat::DataMatrix: diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index a83e1b36ea..b6dd88078b 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -181,6 +181,7 @@ PYBIND11_MODULE(zxingcpp, m) .value("Code93", BarcodeFormat::Code93) .value("Code128", BarcodeFormat::Code128) .value("DataMatrix", BarcodeFormat::DataMatrix) + .value("DXFilmEdge", BarcodeFormat::DXFilmEdge) .value("EAN8", BarcodeFormat::EAN8) .value("EAN13", BarcodeFormat::EAN13) .value("ITF", BarcodeFormat::ITF) diff --git a/wrappers/winrt/BarcodeReader.cpp b/wrappers/winrt/BarcodeReader.cpp index b3186024c1..300ed69ac3 100644 --- a/wrappers/winrt/BarcodeReader.cpp +++ b/wrappers/winrt/BarcodeReader.cpp @@ -78,6 +78,8 @@ BarcodeFormat BarcodeReader::ConvertRuntimeToNative(BarcodeType type) return BarcodeFormat::Code93; case BarcodeType::DATA_MATRIX: return BarcodeFormat::DataMatrix; + case BarcodeType::DX_FILM_EDGE: + return BarcodeFormat::DXFilmEdge; case BarcodeType::EAN_13: return BarcodeFormat::EAN13; case BarcodeType::EAN_8: diff --git a/wrappers/winrt/BarcodeReader.h b/wrappers/winrt/BarcodeReader.h index 16e8352130..f70be9f73a 100644 --- a/wrappers/winrt/BarcodeReader.h +++ b/wrappers/winrt/BarcodeReader.h @@ -19,6 +19,7 @@ public enum class BarcodeType : int { CODE_93, CODE_128, DATA_MATRIX, + DX_FILM_EDGE, EAN_8, EAN_13, ITF, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy