1
1
/*
2
2
* Copyright 2024 Axel Waggershauser
3
+ * Copyright 2025 gitlost
3
4
*/
4
5
// SPDX-License-Identifier: Apache-2.0
5
6
16
17
17
18
#ifdef ZXING_USE_ZINT
18
19
20
+ #include " oned/ODUPCEANCommon.h"
21
+ #include " DecoderResult.h"
22
+ #include " DetectorResult.h"
19
23
#include < zint.h>
20
24
21
25
#else
@@ -29,6 +33,7 @@ namespace ZXing {
29
33
struct CreatorOptions ::Data
30
34
{
31
35
BarcodeFormat format;
36
+ std::string options;
32
37
bool readerInit = false ;
33
38
bool forceSquareDataMatrix = false ;
34
39
std::string ecLevel;
@@ -39,27 +44,35 @@ struct CreatorOptions::Data
39
44
mutable unique_zint_symbol zint;
40
45
41
46
#ifndef __cpp_aggregate_paren_init
42
- Data (BarcodeFormat f) : format(f) {}
47
+ Data (BarcodeFormat f, std::string o ) : format(f), options(std::move(o) ) {}
43
48
#endif
44
49
};
45
50
46
51
#define ZX_PROPERTY (TYPE, NAME ) \
47
- TYPE CreatorOptions::NAME () const noexcept { return d->NAME ; } \
52
+ const TYPE& CreatorOptions::NAME () const noexcept { return d->NAME ; } \
48
53
CreatorOptions& CreatorOptions::NAME (TYPE v)& { return d->NAME = std::move (v), *this ; } \
49
54
CreatorOptions&& CreatorOptions::NAME(TYPE v)&& { return d->NAME = std::move (v), std::move (*this ); }
50
55
51
56
ZX_PROPERTY (BarcodeFormat, format)
52
57
ZX_PROPERTY (bool , readerInit)
53
58
ZX_PROPERTY (bool , forceSquareDataMatrix)
54
59
ZX_PROPERTY (std::string, ecLevel)
60
+ ZX_PROPERTY (std::string, options)
55
61
56
62
#undef ZX_PROPERTY
57
63
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 ;
64
+ #define ZX_RO_PROPERTY (TYPE, NAME ) \
65
+ TYPE CreatorOptions::NAME () const noexcept { return Contains (std::string_view (d->options ), std::string_view (#NAME)); }
62
66
67
+ ZX_RO_PROPERTY (bool , gs1);
68
+ ZX_RO_PROPERTY (bool , stacked);
69
+
70
+ #undef ZX_PROPERTY
71
+
72
+ CreatorOptions::CreatorOptions (BarcodeFormat format, std::string options) : d(std::make_unique<Data>(format, std::move(options))) {}
73
+ CreatorOptions::~CreatorOptions () = default ;
74
+ CreatorOptions::CreatorOptions (CreatorOptions&&) = default ;
75
+ CreatorOptions& CreatorOptions::operator =(CreatorOptions&&) = default ;
63
76
64
77
struct WriterOptions ::Data
65
78
{
@@ -88,9 +101,10 @@ WriterOptions::~WriterOptions() = default;
88
101
WriterOptions::WriterOptions (WriterOptions&&) = default ;
89
102
WriterOptions& WriterOptions::operator =(WriterOptions&&) = default ;
90
103
91
- static bool IsLinearCode (BarcodeFormat format)
104
+ static bool SupportsGS1 (BarcodeFormat format)
92
105
{
93
- return BarcodeFormats (BarcodeFormat::LinearCodes).testFlag (format);
106
+ using enum BarcodeFormat;
107
+ return (Aztec | Code128 | DataMatrix | QRCode | RMQRCode).testFlag (format);
94
108
}
95
109
96
110
static std::string ToSVG (ImageView iv)
@@ -136,7 +150,10 @@ static Image ToImage(BitMatrix bits, bool isLinearCode, const WriterOptions& opt
136
150
137
151
#ifdef ZXING_USE_ZINT
138
152
#include " ECI.h"
153
+
154
+ #ifdef ZXING_READERS
139
155
#include " ReadBarcode.h"
156
+ #endif
140
157
141
158
#include < charconv>
142
159
#include < zint.h>
@@ -218,6 +235,123 @@ static int ParseECLevel(int symbology, std::string_view s)
218
235
return res;
219
236
};
220
237
238
+ static constexpr struct { BarcodeFormat format; SymbologyIdentifier si; } barcodeFormat2SymbologyIdentifier[] = {
239
+ {BarcodeFormat::Aztec, {' z' , ' 0' , 3 }}, // '1' GS1, '2' AIM
240
+ {BarcodeFormat::Codabar, {' F' , ' 0' }}, // if checksum processing were implemented and checksum present and stripped then modifier would be 4
241
+ // {BarcodeFormat::CodablockF, {'O', '4'}}, // '5' GS1
242
+ {BarcodeFormat::Code128, {' C' , ' 0' }}, // '1' GS1, '2' AIM
243
+ // {BarcodeFormat::Code16K, {'K', '0'}}, // '1' GS1, '2' AIM, '4' D1 PAD
244
+ {BarcodeFormat::Code39, {' A' , ' 0' }}, // '3' checksum, '4' extended, '7' checksum,extended
245
+ {BarcodeFormat::Code93, {' G' , ' 0' }}, // no modifiers
246
+ {BarcodeFormat::DataBar, {' e' , ' 0' , 0 , AIFlag::GS1}},
247
+ {BarcodeFormat::DataBarExpanded, {' e' , ' 0' , 0 , AIFlag::GS1}},
248
+ {BarcodeFormat::DataBarLimited, {' e' , ' 0' , 0 , AIFlag::GS1}},
249
+ {BarcodeFormat::DataMatrix, {' d' , ' 1' , 3 }}, // '2' GS1, '3' AIM
250
+ // {BarcodeFormat::DotCode, {'J', '0', 3}}, // '1' GS1, '2' AIM
251
+ {BarcodeFormat::DXFilmEdge, {}},
252
+ {BarcodeFormat::EAN8, {' E' , ' 4' }},
253
+ {BarcodeFormat::EAN13, {' E' , ' 0' }},
254
+ // {BarcodeFormat::HanXin, {'h', '0', 1}}, // '2' GS1
255
+ {BarcodeFormat::ITF, {' I' , ' 0' }}, // '1' check digit
256
+ {BarcodeFormat::MaxiCode, {' U' , ' 0' , 2 }}, // '1' mode 2 or 3
257
+ // {BarcodeFormat::MicroPDF417, {'L', '2', char(-1)}},
258
+ {BarcodeFormat::MicroQRCode, {' Q' , ' 1' , 1 }},
259
+ {BarcodeFormat::PDF417, {' L' , ' 2' , char (-1 )}},
260
+ {BarcodeFormat::QRCode, {' Q' , ' 1' , 1 }}, // '3' GS1, '5' AIM
261
+ {BarcodeFormat::RMQRCode, {' Q' , ' 1' , 1 }}, // '3' GS1, '5' AIM
262
+ {BarcodeFormat::UPCA, {' E' , ' 0' }},
263
+ {BarcodeFormat::UPCE, {' E' , ' 0' }},
264
+ };
265
+
266
+ static SymbologyIdentifier SymbologyIdentifierZint2ZXing (const CreatorOptions& opts, const ByteArray& ba)
267
+ {
268
+ const BarcodeFormat format = opts.format ();
269
+
270
+ auto i = FindIf (barcodeFormat2SymbologyIdentifier, [format](auto & v) { return v.format == format; });
271
+ assert (i != std::end (barcodeFormat2SymbologyIdentifier));
272
+ SymbologyIdentifier ret = i->si ;
273
+
274
+ if ((BarcodeFormat::EAN13 | BarcodeFormat::UPCA | BarcodeFormat::UPCE).testFlag (format)) {
275
+ if (Contains (ba.asString ().data (), ' ' )) // Have EAN-2/5 add-on?
276
+ ret.modifier = ' 3' ; // Combined packet, EAN-13, UPC-A, UPC-E, with add-on
277
+ } else if (format == BarcodeFormat::Code39) {
278
+ if (FindIf (ba, iscntrl) != ba.end ()) // Extended Code 39?
279
+ ret.modifier = static_cast <char >(ret.modifier + 4 );
280
+ } else if (opts.gs1 () && SupportsGS1 (format)) {
281
+ if ((BarcodeFormat::Aztec | BarcodeFormat::Code128).testFlag (format))
282
+ ret.modifier = ' 1' ;
283
+ else if (format == BarcodeFormat::DataMatrix)
284
+ ret.modifier = ' 2' ;
285
+ else if ((BarcodeFormat::QRCode | BarcodeFormat::RMQRCode).testFlag (format))
286
+ ret.modifier = ' 3' ;
287
+ ret.aiFlag = AIFlag::GS1;
288
+ }
289
+
290
+ return ret;
291
+ }
292
+
293
+ static std::string ECLevelZint2ZXing (const zint_symbol* zint)
294
+ {
295
+ constexpr char EC_LABELS_QR[4 ] = {' L' , ' M' , ' Q' , ' H' };
296
+
297
+ const int symbology = zint->symbology ;
298
+ const int option_1 = zint->option_1 ;
299
+
300
+ switch (symbology) {
301
+ case BARCODE_AZTEC:
302
+ if ((option_1 >> 8 ) >= 0 && (option_1 >> 8 ) <= 99 )
303
+ return std::to_string (option_1 >> 8 ) + " %" ;
304
+ break ;
305
+ case BARCODE_MAXICODE:
306
+ // Mode
307
+ if (option_1 >= 2 && option_1 <= 6 )
308
+ return std::to_string (option_1);
309
+ break ;
310
+ case BARCODE_PDF417:
311
+ case BARCODE_PDF417COMP:
312
+ // Convert to percentage
313
+ if (option_1 >= 0 && option_1 <= 8 ) {
314
+ int overhead = symbology == BARCODE_PDF417COMP ? 35 : 69 ;
315
+ int cols = (zint->width - overhead) / 17 ;
316
+ int tot_cws = zint->rows * cols;
317
+ assert (tot_cws);
318
+ return std::to_string ((2 << option_1) * 100 / tot_cws) + " %" ;
319
+ }
320
+ break ;
321
+ // case BARCODE_MICROPDF417:
322
+ // if ((option_1 >> 8) >= 0 && (option_1 >> 8) <= 99)
323
+ // return std::to_string(option_1 >> 8) + "%";
324
+ // break;
325
+ case BARCODE_QRCODE:
326
+ case BARCODE_MICROQR:
327
+ case BARCODE_RMQR:
328
+ // Convert to L/M/Q/H
329
+ if (option_1 >= 1 && option_1 <= 4 )
330
+ return {EC_LABELS_QR[option_1 - 1 ]};
331
+ break ;
332
+ // case BARCODE_HANXIN:
333
+ // if (option_1 >= 1 && option_1 <= 4)
334
+ // return "L" + std::to_string(option_1);
335
+ // break;
336
+ default :
337
+ break ;
338
+ }
339
+
340
+ return {};
341
+ }
342
+
343
+ static std::string NormalizedOptionsString (std::string_view sv)
344
+ {
345
+ std::string str (sv);
346
+ std::transform (str.begin (), str.end (), str.begin (), [](char c) { return (char )std::tolower (c); });
347
+ #ifdef __cpp_lib_erase_if
348
+ std::erase_if (str, [](char c) { return Contains (" \n \" " , c); });
349
+ #else
350
+ str.erase (std::remove_if (str.begin (), str.end (), [](char c) { return Contains (" \n \" " , c); }), str.end ());
351
+ #endif
352
+ return str;
353
+ }
354
+
221
355
zint_symbol* CreatorOptions::zint () const
222
356
{
223
357
auto & zint = d->zint ;
@@ -228,10 +362,23 @@ zint_symbol* CreatorOptions::zint() const
228
362
#endif
229
363
zint.reset (ZBarcode_Create ());
230
364
365
+ d->options = NormalizedOptionsString (options ());
366
+ #ifdef PRINT_DEBUG
367
+ printf (" options: %s\n " , options ().c_str ());
368
+ #endif
369
+
231
370
auto i = FindIf (barcodeFormatZXing2Zint, [zxing = format ()](auto & v) { return v.zxing == zxing; });
232
371
if (i == std::end (barcodeFormatZXing2Zint))
233
372
throw std::invalid_argument (" unsupported barcode format: " + ToString (format ()));
234
- zint->symbology = i->zint ;
373
+
374
+ if (format () == BarcodeFormat::Code128 && gs1 ())
375
+ zint->symbology = BARCODE_GS1_128;
376
+ else if (format () == BarcodeFormat::DataBar && stacked ())
377
+ zint->symbology = BARCODE_DBAR_OMNSTK;
378
+ else if (format () == BarcodeFormat::DataBarExpanded && stacked ())
379
+ zint->symbology = BARCODE_DBAR_EXPSTK;
380
+ else
381
+ zint->symbology = i->zint ;
235
382
236
383
zint->scale = 0 .5f ;
237
384
@@ -250,8 +397,8 @@ Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions
250
397
{
251
398
auto zint = opts.zint ();
252
399
253
- zint->input_mode = mode;
254
- zint->output_options |= OUT_BUFFER_INTERMEDIATE | BARCODE_QUIET_ZONES;
400
+ zint->input_mode = mode == UNICODE_MODE && opts. gs1 () && SupportsGS1 (opts. format ()) ? GS1_MODE | GS1PARENS_MODE : mode ;
401
+ zint->output_options |= OUT_BUFFER_INTERMEDIATE | BARCODE_QUIET_ZONES | BARCODE_RAW_TEXT ;
255
402
256
403
if (mode == DATA_MODE && ZBarcode_Cap (zint->symbology , ZINT_CAP_ECI))
257
404
zint->eci = static_cast <int >(ECI::Binary);
@@ -262,16 +409,49 @@ Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions
262
409
printf (" create symbol with size: %dx%d\n " , zint->width , zint->rows );
263
410
#endif
264
411
265
- #ifdef ZXING_READERS
412
+ #if 0 // use ReadBarcode to create Barcode object
266
413
auto buffer = std::vector<uint8_t>(zint->bitmap_width * zint->bitmap_height);
267
414
std::transform(zint->bitmap, zint->bitmap + zint->bitmap_width * zint->bitmap_height, buffer.data(),
268
415
[](unsigned char v) { return (v == '0') * 0xff; });
269
416
270
417
auto res = ReadBarcode({buffer.data(), zint->bitmap_width, zint->bitmap_height, ImageFormat::Lum},
271
418
ReaderOptions().setFormats(opts.format()).setIsPure(true).setBinarizer(Binarizer::BoolCast));
272
419
#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 (), {});
420
+ Content content;
421
+
422
+ #ifdef ZXING_READERS
423
+ for (int i = 0 ; i < zint->raw_seg_count ; ++i) {
424
+ const auto & raw_seg = zint->raw_segs [i];
425
+ #ifdef PRINT_DEBUG
426
+ printf (" seg %d of %d with eci %d: %s\n " , i, zint->raw_seg_count , raw_seg.eci , (char *)raw_seg.source );
427
+ #endif
428
+ if (ECI (raw_seg.eci ) != ECI::ISO8859_1)
429
+ content.switchEncoding (ECI (raw_seg.eci ));
430
+ else
431
+ content.switchEncoding (CharacterSet::ISO8859_1); // set this as default to prevent guessing without setting "hasECI"
432
+ content.append ({raw_seg.source , static_cast <size_t >(raw_seg.length - (opts.format () == BarcodeFormat::Code93 ? 2 : 0 ))});
433
+ }
434
+ if (opts.format () == BarcodeFormat::UPCE)
435
+ content.bytes = ByteArray (" 0" + OneD::UPCEANCommon::ConvertUPCEtoUPCA (std::string (content.bytes .asString ())));
436
+ else if (opts.format () == BarcodeFormat::UPCA)
437
+ content.bytes = ByteArray (" 0" + std::string (content.bytes .asString ()));
438
+ #else
439
+ if (zint->text_length ) {
440
+ content.switchEncoding (ECI::UTF8);
441
+ content.append ({zint->text , static_cast <size_t >(zint->text_length )});
442
+ } else {
443
+ content.switchEncoding (mode == DATA_MODE ? ECI::Binary : ECI::UTF8);
444
+ content.append ({static_cast <const uint8_t *>(data), static_cast <size_t >(size)});
445
+ }
446
+ #endif
447
+
448
+ content.symbology = SymbologyIdentifierZint2ZXing (opts, content.bytes );
449
+
450
+ DecoderResult decRes (std::move (content));
451
+ decRes.setEcLevel (ECLevelZint2ZXing (zint));
452
+ DetectorResult detRes;
453
+
454
+ auto res = Barcode (std::move (decRes), std::move (detRes), opts.format ());
275
455
#endif
276
456
277
457
auto bits = BitMatrix (zint->bitmap_width , zint->bitmap_height );
0 commit comments