comparison mupdf-source/thirdparty/zxing-cpp/core/src/WriteBarcode.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 2024 Axel Waggershauser
3 */
4 // SPDX-License-Identifier: Apache-2.0
5
6 #ifdef ZXING_EXPERIMENTAL_API
7
8 #include "WriteBarcode.h"
9 #include "BitMatrix.h"
10
11 #if !defined(ZXING_READERS) && !defined(ZXING_WRITERS)
12 #include "Version.h"
13 #endif
14
15 #include <sstream>
16
17 #ifdef ZXING_USE_ZINT
18
19 #include <zint.h>
20
21 #else
22
23 struct zint_symbol {};
24
25 #endif // ZXING_USE_ZINT
26
27 namespace ZXing {
28
29 struct CreatorOptions::Data
30 {
31 BarcodeFormat format;
32 bool readerInit = false;
33 bool forceSquareDataMatrix = false;
34 std::string ecLevel;
35
36 // symbol size (qrcode, datamatrix, etc), map from I, 'WxH'
37 // structured_append (idx, cnt, ID)
38
39 mutable unique_zint_symbol zint;
40
41 #ifndef __cpp_aggregate_paren_init
42 Data(BarcodeFormat f) : format(f) {}
43 #endif
44 };
45
46 #define ZX_PROPERTY(TYPE, NAME) \
47 TYPE CreatorOptions::NAME() const noexcept { return d->NAME; } \
48 CreatorOptions& CreatorOptions::NAME(TYPE v)& { return d->NAME = std::move(v), *this; } \
49 CreatorOptions&& CreatorOptions::NAME(TYPE v)&& { return d->NAME = std::move(v), std::move(*this); }
50
51 ZX_PROPERTY(BarcodeFormat, format)
52 ZX_PROPERTY(bool, readerInit)
53 ZX_PROPERTY(bool, forceSquareDataMatrix)
54 ZX_PROPERTY(std::string, ecLevel)
55
56 #undef ZX_PROPERTY
57
58 CreatorOptions::CreatorOptions(BarcodeFormat format) : d(std::make_unique<Data>(format)) {}
59 CreatorOptions::~CreatorOptions() = default;
60 CreatorOptions::CreatorOptions(CreatorOptions&&) = default;
61 CreatorOptions& CreatorOptions::operator=(CreatorOptions&&) = default;
62
63
64 struct WriterOptions::Data
65 {
66 int scale = 0;
67 int sizeHint = 0;
68 int rotate = 0;
69 bool withHRT = false;
70 bool withQuietZones = true;
71 };
72
73 #define ZX_PROPERTY(TYPE, NAME) \
74 TYPE WriterOptions::NAME() const noexcept { return d->NAME; } \
75 WriterOptions& WriterOptions::NAME(TYPE v)& { return d->NAME = std::move(v), *this; } \
76 WriterOptions&& WriterOptions::NAME(TYPE v)&& { return d->NAME = std::move(v), std::move(*this); }
77
78 ZX_PROPERTY(int, scale)
79 ZX_PROPERTY(int, sizeHint)
80 ZX_PROPERTY(int, rotate)
81 ZX_PROPERTY(bool, withHRT)
82 ZX_PROPERTY(bool, withQuietZones)
83
84 #undef ZX_PROPERTY
85
86 WriterOptions::WriterOptions() : d(std::make_unique<Data>()) {}
87 WriterOptions::~WriterOptions() = default;
88 WriterOptions::WriterOptions(WriterOptions&&) = default;
89 WriterOptions& WriterOptions::operator=(WriterOptions&&) = default;
90
91 static bool IsLinearCode(BarcodeFormat format)
92 {
93 return BarcodeFormats(BarcodeFormat::LinearCodes).testFlag(format);
94 }
95
96 static std::string ToSVG(ImageView iv)
97 {
98 if (!iv.data())
99 return {};
100
101 // see https://stackoverflow.com/questions/10789059/create-qr-code-in-vector-image/60638350#60638350
102
103 std::ostringstream res;
104
105 res << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
106 << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 " << iv.width() << " " << iv.height()
107 << "\" stroke=\"none\">\n"
108 << "<path d=\"";
109
110 for (int y = 0; y < iv.height(); ++y)
111 for (int x = 0; x < iv.width(); ++x)
112 if (*iv.data(x, y) == 0)
113 res << "M" << x << "," << y << "h1v1h-1z";
114
115 res << "\"/>\n</svg>";
116
117 return res.str();
118 }
119
120 static Image ToImage(BitMatrix bits, bool isLinearCode, const WriterOptions& opts)
121 {
122 bits.flipAll();
123 auto symbol = Inflate(std::move(bits), opts.sizeHint(),
124 isLinearCode ? std::clamp(opts.sizeHint() / 2, 50, 300) : opts.sizeHint(),
125 opts.withQuietZones() ? 10 : 0);
126 auto bitmap = ToMatrix<uint8_t>(symbol);
127 auto iv = Image(symbol.width(), symbol.height());
128 std::memcpy(const_cast<uint8_t*>(iv.data()), bitmap.data(), iv.width() * iv.height());
129 return iv;
130 }
131
132 } // namespace ZXing
133
134
135 #ifdef ZXING_WRITERS
136
137 #ifdef ZXING_USE_ZINT
138 #include "ECI.h"
139 #include "ReadBarcode.h"
140
141 #include <charconv>
142 #include <zint.h>
143
144 namespace ZXing {
145
146 struct BarcodeFormatZXing2Zint
147 {
148 BarcodeFormat zxing;
149 int zint;
150 };
151
152 static constexpr BarcodeFormatZXing2Zint barcodeFormatZXing2Zint[] = {
153 {BarcodeFormat::Aztec, BARCODE_AZTEC},
154 {BarcodeFormat::Codabar, BARCODE_CODABAR},
155 {BarcodeFormat::Code39, BARCODE_CODE39},
156 {BarcodeFormat::Code93, BARCODE_CODE93},
157 {BarcodeFormat::Code128, BARCODE_CODE128},
158 {BarcodeFormat::DataBar, BARCODE_DBAR_OMN},
159 {BarcodeFormat::DataBarExpanded, BARCODE_DBAR_EXP},
160 {BarcodeFormat::DataBarLimited, BARCODE_DBAR_LTD},
161 {BarcodeFormat::DataMatrix, BARCODE_DATAMATRIX},
162 {BarcodeFormat::DXFilmEdge, BARCODE_DXFILMEDGE},
163 {BarcodeFormat::EAN8, BARCODE_EANX},
164 {BarcodeFormat::EAN13, BARCODE_EANX},
165 {BarcodeFormat::ITF, BARCODE_C25INTER},
166 {BarcodeFormat::MaxiCode, BARCODE_MAXICODE},
167 {BarcodeFormat::MicroQRCode, BARCODE_MICROQR},
168 {BarcodeFormat::PDF417, BARCODE_PDF417},
169 {BarcodeFormat::QRCode, BARCODE_QRCODE},
170 {BarcodeFormat::RMQRCode, BARCODE_RMQR},
171 {BarcodeFormat::UPCA, BARCODE_UPCA},
172 {BarcodeFormat::UPCE, BARCODE_UPCE},
173 };
174
175 struct String2Int
176 {
177 const char* str;
178 int val;
179 };
180
181 static int ParseECLevel(int symbology, std::string_view s)
182 {
183 constexpr std::string_view EC_LABELS_QR[4] = {"L", "M", "Q", "H"};
184
185 int res = 0;
186 if (Contains({BARCODE_QRCODE, BARCODE_MICROQR, BARCODE_RMQR}, symbology))
187 if ((res = IndexOf(EC_LABELS_QR, s) != -1))
188 return res + 1;
189
190 if (std::from_chars(s.data(), s.data() + s.size() - (s.back() == '%'), res).ec != std::errc{})
191 throw std::invalid_argument("Invalid ecLevel: '" + std::string(s) + "'");
192
193 auto findClosestECLevel = [](const std::vector<int>& list, int val) {
194 int mIdx = -2, mAbs = 100;
195 for (int i = 0; i < Size(list); ++i)
196 if (int abs = std::abs(val - list[i]); abs < mAbs) {
197 mIdx = i;
198 mAbs = abs;
199 }
200 return mIdx + 1;
201 };
202
203 if (s.back()=='%'){
204 switch (symbology) {
205 case BARCODE_QRCODE:
206 case BARCODE_MICROQR:
207 case BARCODE_RMQR:
208 return findClosestECLevel({20, 37, 55, 65}, res);
209 case BARCODE_AZTEC:
210 return findClosestECLevel({10, 23, 26, 50}, res);
211 case BARCODE_PDF417:
212 // TODO: do something sensible with PDF417?
213 default:
214 return -1;
215 }
216 }
217
218 return res;
219 };
220
221 zint_symbol* CreatorOptions::zint() const
222 {
223 auto& zint = d->zint;
224
225 if (!zint) {
226 #ifdef PRINT_DEBUG
227 printf("zint version: %d, sizeof(zint_symbol): %ld\n", ZBarcode_Version(), sizeof(zint_symbol));
228 #endif
229 zint.reset(ZBarcode_Create());
230
231 auto i = FindIf(barcodeFormatZXing2Zint, [zxing = format()](auto& v) { return v.zxing == zxing; });
232 if (i == std::end(barcodeFormatZXing2Zint))
233 throw std::invalid_argument("unsupported barcode format: " + ToString(format()));
234 zint->symbology = i->zint;
235
236 zint->scale = 0.5f;
237
238 if (!ecLevel().empty())
239 zint->option_1 = ParseECLevel(zint->symbology, ecLevel());
240 }
241
242 return zint.get();
243 }
244
245 #define CHECK(ZINT_CALL) \
246 if (int err = (ZINT_CALL); err >= ZINT_ERROR) \
247 throw std::invalid_argument(zint->errtxt);
248
249 Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions& opts)
250 {
251 auto zint = opts.zint();
252
253 zint->input_mode = mode;
254 zint->output_options |= OUT_BUFFER_INTERMEDIATE | BARCODE_QUIET_ZONES;
255
256 if (mode == DATA_MODE && ZBarcode_Cap(zint->symbology, ZINT_CAP_ECI))
257 zint->eci = static_cast<int>(ECI::Binary);
258
259 CHECK(ZBarcode_Encode_and_Buffer(zint, (uint8_t*)data, size, 0));
260
261 #ifdef PRINT_DEBUG
262 printf("create symbol with size: %dx%d\n", zint->width, zint->rows);
263 #endif
264
265 #ifdef ZXING_READERS
266 auto buffer = std::vector<uint8_t>(zint->bitmap_width * zint->bitmap_height);
267 std::transform(zint->bitmap, zint->bitmap + zint->bitmap_width * zint->bitmap_height, buffer.data(),
268 [](unsigned char v) { return (v == '0') * 0xff; });
269
270 auto res = ReadBarcode({buffer.data(), zint->bitmap_width, zint->bitmap_height, ImageFormat::Lum},
271 ReaderOptions().setFormats(opts.format()).setIsPure(true).setBinarizer(Binarizer::BoolCast));
272 #else
273 //TODO: replace by proper construction from encoded data from within zint
274 auto res = Barcode(std::string((const char*)data, size), 0, 0, 0, opts.format(), {});
275 #endif
276
277 auto bits = BitMatrix(zint->bitmap_width, zint->bitmap_height);
278 std::transform(zint->bitmap, zint->bitmap + zint->bitmap_width * zint->bitmap_height, bits.row(0).begin(),
279 [](unsigned char v) { return (v == '1') * BitMatrix::SET_V; });
280 res.symbol(std::move(bits));
281 res.zint(std::move(opts.d->zint));
282
283 return res;
284 }
285
286 Barcode CreateBarcodeFromText(std::string_view contents, const CreatorOptions& opts)
287 {
288 return CreateBarcode(contents.data(), contents.size(), UNICODE_MODE, opts);
289 }
290
291 #if __cplusplus > 201703L
292 Barcode CreateBarcodeFromText(std::u8string_view contents, const CreatorOptions& opts)
293 {
294 return CreateBarcode(contents.data(), contents.size(), UNICODE_MODE, opts);
295 }
296 #endif
297
298 Barcode CreateBarcodeFromBytes(const void* data, int size, const CreatorOptions& opts)
299 {
300 return CreateBarcode(data, size, DATA_MODE, opts);
301 }
302
303 // Writer ========================================================================
304
305 struct SetCommonWriterOptions
306 {
307 zint_symbol* zint;
308
309 SetCommonWriterOptions(zint_symbol* zint, const WriterOptions& opts) : zint(zint)
310 {
311 zint->show_hrt = opts.withHRT();
312
313 zint->output_options &= ~OUT_BUFFER_INTERMEDIATE;
314 zint->output_options |= opts.withQuietZones() ? BARCODE_QUIET_ZONES : BARCODE_NO_QUIET_ZONES;
315
316 if (opts.scale())
317 zint->scale = opts.scale() / 2.f;
318 else if (opts.sizeHint()) {
319 int size = std::max(zint->width, zint->rows);
320 zint->scale = std::max(1, int(float(opts.sizeHint()) / size)) / 2.f;
321 }
322 }
323
324 // reset the defaults such that consecutive write calls don't influence each other
325 ~SetCommonWriterOptions() { zint->scale = 0.5f; }
326 };
327
328 } // ZXing
329
330 #else // ZXING_USE_ZINT
331
332 #include "MultiFormatWriter.h"
333 #include "ReadBarcode.h"
334
335 namespace ZXing {
336
337 zint_symbol* CreatorOptions::zint() const { return nullptr; }
338
339 static Barcode CreateBarcode(BitMatrix&& bits, const CreatorOptions& opts)
340 {
341 auto img = ToMatrix<uint8_t>(bits);
342
343 auto res = ReadBarcode({img.data(), img.width(), img.height(), ImageFormat::Lum},
344 ReaderOptions().setFormats(opts.format()).setIsPure(true).setBinarizer(Binarizer::BoolCast));
345 res.symbol(std::move(bits));
346 return res;
347 }
348
349 Barcode CreateBarcodeFromText(std::string_view contents, const CreatorOptions& opts)
350 {
351 auto writer = MultiFormatWriter(opts.format()).setMargin(0);
352 if (!opts.ecLevel().empty())
353 writer.setEccLevel(std::stoi(opts.ecLevel()));
354 writer.setEncoding(CharacterSet::UTF8); // write UTF8 (ECI value 26) for maximum compatibility
355
356 return CreateBarcode(writer.encode(std::string(contents), 0, IsLinearCode(opts.format()) ? 50 : 0), opts);
357 }
358
359 #if __cplusplus > 201703L
360 Barcode CreateBarcodeFromText(std::u8string_view contents, const CreatorOptions& opts)
361 {
362 return CreateBarcodeFromText({reinterpret_cast<const char*>(contents.data()), contents.size()}, opts);
363 }
364 #endif
365
366 Barcode CreateBarcodeFromBytes(const void* data, int size, const CreatorOptions& opts)
367 {
368 std::wstring bytes;
369 for (uint8_t c : std::basic_string_view<uint8_t>((uint8_t*)data, size))
370 bytes.push_back(c);
371
372 auto writer = MultiFormatWriter(opts.format()).setMargin(0);
373 if (!opts.ecLevel().empty())
374 writer.setEccLevel(std::stoi(opts.ecLevel()));
375 writer.setEncoding(CharacterSet::BINARY);
376
377 return CreateBarcode(writer.encode(bytes, 0, IsLinearCode(opts.format()) ? 50 : 0), opts);
378 }
379
380 } // namespace ZXing
381
382 #endif // ZXING_USE_ZINT
383
384 #else // ZXING_WRITERS
385
386 namespace ZXing {
387
388 zint_symbol* CreatorOptions::zint() const { return nullptr; }
389
390 Barcode CreateBarcodeFromText(std::string_view, const CreatorOptions&)
391 {
392 throw std::runtime_error("This build of zxing-cpp does not support creating barcodes.");
393 }
394
395 #if __cplusplus > 201703L
396 Barcode CreateBarcodeFromText(std::u8string_view, const CreatorOptions&)
397 {
398 throw std::runtime_error("This build of zxing-cpp does not support creating barcodes.");
399 }
400 #endif
401
402 Barcode CreateBarcodeFromBytes(const void*, int, const CreatorOptions&)
403 {
404 throw std::runtime_error("This build of zxing-cpp does not support creating barcodes.");
405 }
406
407 } // namespace ZXing
408
409 #endif // ZXING_WRITERS
410
411 namespace ZXing {
412
413 std::string WriteBarcodeToSVG(const Barcode& barcode, [[maybe_unused]] const WriterOptions& opts)
414 {
415 auto zint = barcode.zint();
416
417 if (!zint)
418 return ToSVG(barcode.symbol());
419
420 #if defined(ZXING_WRITERS) && defined(ZXING_USE_ZINT)
421 auto resetOnExit = SetCommonWriterOptions(zint, opts);
422
423 zint->output_options |= BARCODE_MEMORY_FILE;// | EMBED_VECTOR_FONT;
424 strcpy(zint->outfile, "null.svg");
425
426 CHECK(ZBarcode_Print(zint, opts.rotate()));
427
428 return std::string(reinterpret_cast<const char*>(zint->memfile), zint->memfile_size);
429 #else
430 return {}; // unreachable code
431 #endif
432 }
433
434 Image WriteBarcodeToImage(const Barcode& barcode, [[maybe_unused]] const WriterOptions& opts)
435 {
436 auto zint = barcode.zint();
437
438 if (!zint)
439 return ToImage(barcode._symbol->copy(), IsLinearCode(barcode.format()), opts);
440
441 #if defined(ZXING_WRITERS) && defined(ZXING_USE_ZINT)
442 auto resetOnExit = SetCommonWriterOptions(zint, opts);
443
444 CHECK(ZBarcode_Buffer(zint, opts.rotate()));
445
446 #ifdef PRINT_DEBUG
447 printf("write symbol with size: %dx%d\n", zint->bitmap_width, zint->bitmap_height);
448 #endif
449 auto iv = Image(zint->bitmap_width, zint->bitmap_height);
450 auto* src = zint->bitmap;
451 auto* dst = const_cast<uint8_t*>(iv.data());
452 for(int y = 0; y < iv.height(); ++y)
453 for(int x = 0, w = iv.width(); x < w; ++x, src += 3)
454 *dst++ = RGBToLum(src[0], src[1], src[2]);
455
456 return iv;
457 #else
458 return {}; // unreachable code
459 #endif
460 }
461
462 std::string WriteBarcodeToUtf8(const Barcode& barcode, [[maybe_unused]] const WriterOptions& options)
463 {
464 auto iv = barcode.symbol();
465 if (!iv.data())
466 return {};
467
468 constexpr auto map = std::array{" ", "▀", "▄", "█"};
469 std::ostringstream res;
470 bool inverted = false; // TODO: take from WriterOptions
471
472 for (int y = 0; y < iv.height(); y += 2) {
473 for (int x = 0; x < iv.width(); ++x) {
474 int tp = bool(*iv.data(x, y)) ^ inverted;
475 int bt = (iv.height() == 1 && tp) || (y + 1 < iv.height() && (bool(*iv.data(x, y + 1)) ^ inverted));
476 res << map[tp | (bt << 1)];
477 }
478 res << '\n';
479 }
480
481 return res.str();
482 }
483
484 } // namespace ZXing
485
486 #endif // ZXING_EXPERIMENTAL_API