comparison mupdf-source/thirdparty/zxing-cpp/core/src/Barcode.cpp @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /*
2 * Copyright 2016 Nu-book Inc.
3 * Copyright 2016 ZXing authors
4 */
5 // SPDX-License-Identifier: Apache-2.0
6
7 #include "Barcode.h"
8
9 #include "DecoderResult.h"
10 #include "DetectorResult.h"
11 #include "ZXAlgorithms.h"
12
13 #ifdef ZXING_EXPERIMENTAL_API
14 #include "BitMatrix.h"
15
16 #ifdef ZXING_USE_ZINT
17 #include <zint.h>
18 void zint_symbol_deleter::operator()(zint_symbol* p) const noexcept
19 {
20 ZBarcode_Delete(p);
21 }
22 #else
23 struct zint_symbol {};
24 void zint_symbol_deleter::operator()(zint_symbol*) const noexcept {}
25 #endif
26
27 #endif
28
29 #include <cmath>
30 #include <list>
31 #include <map>
32 #include <utility>
33
34 namespace ZXing {
35
36 Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, Error error, bool readerInit)
37 : _content({ByteArray(text)}, si),
38 _error(error),
39 _position(Line(y, xStart, xStop)),
40 _format(format),
41 _readerInit(readerInit)
42 {}
43
44 Result::Result(DecoderResult&& decodeResult, DetectorResult&& detectorResult, BarcodeFormat format)
45 : _content(std::move(decodeResult).content()),
46 _error(std::move(decodeResult).error()),
47 _position(std::move(detectorResult).position()),
48 _sai(decodeResult.structuredAppend()),
49 _format(format),
50 _lineCount(decodeResult.lineCount()),
51 _isMirrored(decodeResult.isMirrored()),
52 _readerInit(decodeResult.readerInit())
53 #ifdef ZXING_EXPERIMENTAL_API
54 , _symbol(std::make_shared<BitMatrix>(std::move(detectorResult).bits()))
55 #endif
56 {
57 if (decodeResult.versionNumber())
58 snprintf(_version, 4, "%d", decodeResult.versionNumber());
59 snprintf(_ecLevel, 4, "%s", decodeResult.ecLevel().data());
60
61 // TODO: add type opaque and code specific 'extra data'? (see DecoderResult::extra())
62 }
63
64 Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format)
65 : Result(std::move(decodeResult), {{}, std::move(position)}, format)
66 {}
67
68 bool Result::isValid() const
69 {
70 return format() != BarcodeFormat::None && !_content.bytes.empty() && !error();
71 }
72
73 const ByteArray& Result::bytes() const
74 {
75 return _content.bytes;
76 }
77
78 ByteArray Result::bytesECI() const
79 {
80 return _content.bytesECI();
81 }
82
83 std::string Result::text(TextMode mode) const
84 {
85 return _content.text(mode);
86 }
87
88 std::string Result::text() const
89 {
90 return text(_readerOpts.textMode());
91 }
92
93 std::string Result::ecLevel() const
94 {
95 return _ecLevel;
96 }
97
98 ContentType Result::contentType() const
99 {
100 return _content.type();
101 }
102
103 bool Result::hasECI() const
104 {
105 return _content.hasECI;
106 }
107
108 int Result::orientation() const
109 {
110 constexpr auto std_numbers_pi_v = 3.14159265358979323846; // TODO: c++20 <numbers>
111 return narrow_cast<int>(std::lround(_position.orientation() * 180 / std_numbers_pi_v));
112 }
113
114 std::string Result::symbologyIdentifier() const
115 {
116 return _content.symbology.toString();
117 }
118
119 int Result::sequenceSize() const
120 {
121 return _sai.count;
122 }
123
124 int Result::sequenceIndex() const
125 {
126 return _sai.index;
127 }
128
129 std::string Result::sequenceId() const
130 {
131 return _sai.id;
132 }
133
134 std::string Result::version() const
135 {
136 return _version;
137 }
138
139 Result& Result::setReaderOptions(const ReaderOptions& opts)
140 {
141 if (opts.characterSet() != CharacterSet::Unknown)
142 _content.defaultCharset = opts.characterSet();
143 _readerOpts = opts;
144 return *this;
145 }
146
147 #ifdef ZXING_EXPERIMENTAL_API
148 void Result::symbol(BitMatrix&& bits)
149 {
150 bits.flipAll();
151 _symbol = std::make_shared<BitMatrix>(std::move(bits));
152 }
153
154 ImageView Result::symbol() const
155 {
156 return {_symbol->row(0).begin(), _symbol->width(), _symbol->height(), ImageFormat::Lum};
157 }
158
159 void Result::zint(unique_zint_symbol&& z)
160 {
161 _zint = std::shared_ptr(std::move(z));
162 }
163 #endif
164
165 bool Result::operator==(const Result& o) const
166 {
167 // handle case where both are MatrixCodes first
168 if (!BarcodeFormats(BarcodeFormat::LinearCodes).testFlags(format() | o.format())) {
169 if (format() != o.format() || (bytes() != o.bytes() && isValid() && o.isValid()))
170 return false;
171
172 // check for equal position if both are valid with equal bytes or at least one is in error
173 return IsInside(Center(o.position()), position());
174 }
175
176 if (format() != o.format() || bytes() != o.bytes() || error() != o.error())
177 return false;
178
179 if (orientation() != o.orientation())
180 return false;
181
182 if (lineCount() > 1 && o.lineCount() > 1)
183 return HaveIntersectingBoundingBoxes(o.position(), position());
184
185 // the following code is only meant for this or other lineCount == 1
186 assert(lineCount() == 1 || o.lineCount() == 1);
187
188 // sl == single line, ml = multi line
189 const auto& sl = lineCount() == 1 ? *this : o;
190 const auto& ml = lineCount() == 1 ? o : *this;
191
192 // If one line is less than half the length of the other away from the
193 // latter, we consider it to belong to the same symbol.
194 // Additionally, both need to have roughly the same length (see #367).
195 auto dTop = maxAbsComponent(ml.position().topLeft() - sl.position().topLeft());
196 auto dBot = maxAbsComponent(ml.position().bottomLeft() - sl.position().topLeft());
197 auto slLength = maxAbsComponent(sl.position().topLeft() - sl.position().bottomRight());
198 bool isHorizontal = sl.position().topLeft().y == sl.position().bottomRight().y;
199 // Measure the multi line length in the same direction as the single line one (not diagonaly)
200 // to make sure overly tall symbols don't get segmented (see #769).
201 auto mlLength = isHorizontal ? std::abs(ml.position().topLeft().x - ml.position().bottomRight().x)
202 : std::abs(ml.position().topLeft().y - ml.position().bottomRight().y);
203
204 return std::min(dTop, dBot) < slLength / 2 && std::abs(slLength - mlLength) < slLength / 5;
205 }
206
207 Barcode MergeStructuredAppendSequence(const Barcodes& barcodes)
208 {
209 if (barcodes.empty())
210 return {};
211
212 std::list<Barcode> allBarcodes(barcodes.begin(), barcodes.end());
213 allBarcodes.sort([](const Barcode& r1, const Barcode& r2) { return r1.sequenceIndex() < r2.sequenceIndex(); });
214
215 Barcode res = allBarcodes.front();
216 for (auto i = std::next(allBarcodes.begin()); i != allBarcodes.end(); ++i)
217 res._content.append(i->_content);
218
219 res._position = {};
220 res._sai.index = -1;
221
222 if (allBarcodes.back().sequenceSize() != Size(allBarcodes) ||
223 !std::all_of(allBarcodes.begin(), allBarcodes.end(),
224 [&](Barcode& it) { return it.sequenceId() == allBarcodes.front().sequenceId(); }))
225 res._error = FormatError("sequenceIDs not matching during structured append sequence merging");
226
227 return res;
228 }
229
230 Barcodes MergeStructuredAppendSequences(const Barcodes& barcodes)
231 {
232 std::map<std::string, Barcodes> sas;
233 for (auto& barcode : barcodes) {
234 if (barcode.isPartOfSequence())
235 sas[barcode.sequenceId()].push_back(barcode);
236 }
237
238 Barcodes res;
239 for (auto& [id, seq] : sas) {
240 auto barcode = MergeStructuredAppendSequence(seq);
241 if (barcode.isValid())
242 res.push_back(std::move(barcode));
243 }
244
245 return res;
246 }
247
248 } // namespace ZXing