Skip to content

Commit 0ee68df

Browse files
committed
zint: add libzint backend for experimental new WriteBarcode API
Set cmake option `ZXING_USE_ZINT` to `ON` to enable this. libzint needs to be from current HEAD (post 2.12 release) to include support for zint_symbol->memfile. Use `cmake -DBUILD_DEPENDENCIES=GITHUB` to force download directly from github.
1 parent 3a79c38 commit 0ee68df

File tree

5 files changed

+258
-0
lines changed

5 files changed

+258
-0
lines changed

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ option (BUILD_UNIT_TESTS "Build the unit tests (don't enable for production buil
1010
option (BUILD_PYTHON_MODULE "Build the python module" OFF)
1111
option (BUILD_C_API "Build the C-API" OFF)
1212
option (BUILD_EXPERIMENTAL_API "Build with experimental API" OFF)
13+
option (ZXING_USE_ZINT "Use libzint for barcode creation/generation" OFF)
1314
set(BUILD_DEPENDENCIES "AUTO" CACHE STRING "Fetch from github or use locally installed (AUTO/GITHUB/LOCAL)")
1415

1516
if (WIN32)
@@ -47,6 +48,11 @@ if (BUILD_UNIT_TESTS AND (NOT BUILD_WRITERS OR NOT BUILD_READERS))
4748
set (BUILD_READERS ON)
4849
endif()
4950

51+
if (ZXING_USE_ZINT)
52+
set (BUILD_EXPERIMENTAL_API ON)
53+
add_definitions (-DZXING_USE_ZINT)
54+
endif()
55+
5056
if (BUILD_EXPERIMENTAL_API)
5157
set (CMAKE_CXX_STANDARD 20)
5258
add_definitions (-DZXING_BUILD_EXPERIMENTAL_API)

core/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,12 @@ target_compile_features(ZXing PUBLIC cxx_std_17)
479479

480480
target_link_libraries (ZXing PRIVATE Threads::Threads)
481481

482+
if (ZXING_USE_ZINT)
483+
include(../zxing.cmake)
484+
zxing_add_package(zint zint https://github.com/zint/zint.git 11b3c18aed29cf9eb2a31debd66c8fe8a1d77604)
485+
target_link_libraries (ZXing PRIVATE zint)
486+
endif()
487+
482488
add_library(ZXing::ZXing ALIAS ZXing)
483489
# add the old alias as well, to keep old clients compiling
484490
# note: this only affects client code that includes ZXing via sub_directory.

core/src/Barcode.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
#include "DetectorResult.h"
1111
#include "ZXAlgorithms.h"
1212

13+
#ifdef ZXING_USE_ZINT
14+
#include <zint.h>
15+
#endif
16+
1317
#include <cmath>
1418
#include <list>
1519
#include <map>
@@ -128,6 +132,13 @@ Result& Result::setReaderOptions(const ReaderOptions& opts)
128132
return *this;
129133
}
130134

135+
#ifdef ZXING_USE_ZINT
136+
void Result::zint(std::unique_ptr<zint_symbol>&& z)
137+
{
138+
_zint = std::shared_ptr(std::move(z));
139+
}
140+
#endif
141+
131142
bool Result::operator==(const Result& o) const
132143
{
133144
// handle case where both are MatrixCodes first

core/src/Barcode.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#ifdef ZXING_BUILD_EXPERIMENTAL_API
1919
#include "BitMatrix.h"
2020
#include <memory>
21+
extern "C" struct zint_symbol;
2122
#endif
2223

2324
#include <string>
@@ -167,6 +168,10 @@ class Result
167168
void symbol(BitMatrix&& bits) { _symbol = std::make_shared<BitMatrix>(std::move(bits)); }
168169
const BitMatrix& symbol() const { return *_symbol; }
169170
#endif
171+
#ifdef ZXING_USE_ZINT
172+
void zint(std::unique_ptr<zint_symbol>&& z);
173+
zint_symbol* zint() const { return _zint.get(); }
174+
#endif
170175

171176
bool operator==(const Result& o) const;
172177

@@ -186,6 +191,9 @@ class Result
186191
#ifdef ZXING_BUILD_EXPERIMENTAL_API
187192
std::shared_ptr<BitMatrix> _symbol;
188193
#endif
194+
#ifdef ZXING_USE_ZINT
195+
std::shared_ptr<zint_symbol> _zint;
196+
#endif
189197
};
190198

191199
/**

core/src/WriteBarcode.cpp

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,18 @@
77

88
#include "WriteBarcode.h"
99

10+
#ifdef ZXING_USE_ZINT
11+
#include <zint.h>
12+
13+
template <>
14+
struct std::default_delete<zint_symbol>
15+
{
16+
void operator()(zint_symbol* p) const noexcept { ZBarcode_Delete(p); }
17+
};
18+
19+
#else
1020
struct zint_symbol {};
21+
#endif
1122

1223
namespace ZXing {
1324

@@ -71,6 +82,220 @@ WriterOptions& WriterOptions::operator=(WriterOptions&&) = default;
7182

7283
} // namespace ZXing
7384

85+
#ifdef ZXING_USE_ZINT
86+
#include "ECI.h"
87+
#include "ReadBarcode.h"
88+
89+
#include <zint.h>
90+
#include <charconv>
91+
92+
namespace ZXing {
93+
94+
struct BarcodeFormatZXing2Zint
95+
{
96+
BarcodeFormat zxing;
97+
int zint;
98+
};
99+
100+
static constexpr BarcodeFormatZXing2Zint barcodeFormatZXing2Zint[] = {
101+
{BarcodeFormat::Aztec, BARCODE_AZTEC},
102+
{BarcodeFormat::Codabar, BARCODE_CODABAR},
103+
{BarcodeFormat::Code39, BARCODE_CODE39},
104+
{BarcodeFormat::Code93, BARCODE_CODE93},
105+
{BarcodeFormat::Code128, BARCODE_CODE128},
106+
{BarcodeFormat::DataBar, BARCODE_DBAR_OMN},
107+
{BarcodeFormat::DataBarExpanded, BARCODE_DBAR_EXP},
108+
{BarcodeFormat::DataMatrix, BARCODE_DATAMATRIX},
109+
{BarcodeFormat::DXFilmEdge, -1},
110+
{BarcodeFormat::EAN8, BARCODE_EANX},
111+
{BarcodeFormat::EAN13, BARCODE_EANX},
112+
{BarcodeFormat::ITF, BARCODE_ITF14},
113+
{BarcodeFormat::MaxiCode, BARCODE_MAXICODE},
114+
{BarcodeFormat::MicroQRCode, BARCODE_MICROQR},
115+
{BarcodeFormat::PDF417, BARCODE_PDF417},
116+
{BarcodeFormat::QRCode, BARCODE_QRCODE},
117+
{BarcodeFormat::RMQRCode, BARCODE_RMQR},
118+
{BarcodeFormat::UPCA, BARCODE_UPCA},
119+
{BarcodeFormat::UPCE, BARCODE_UPCE},
120+
};
121+
122+
struct String2Int
123+
{
124+
const char* str;
125+
int val;
126+
};
127+
128+
static int ParseECLevel(int symbology, std::string_view s)
129+
{
130+
constexpr std::string_view EC_LABELS_QR[4] = {"L", "M", "Q", "H"};
131+
132+
int res = 0;
133+
if (Contains({BARCODE_QRCODE, BARCODE_MICROQR, BARCODE_RMQR}, symbology))
134+
if ((res = IndexOf(EC_LABELS_QR, s) != -1))
135+
return res + 1;
136+
137+
if (std::from_chars(s.data(), s.data() + s.size() - (s.back() == '%'), res).ec != std::errc{})
138+
throw std::invalid_argument("Invalid ecLevel: '" + std::string(s) + "'");
139+
140+
auto findClosestECLevel = [](const std::vector<int>& list, int val) {
141+
int mIdx, mAbs = 100;
142+
for (int i = 0; i < Size(list); ++i)
143+
if (int abs = std::abs(val - list[i]); abs < mAbs) {
144+
mIdx = i;
145+
mAbs = abs;
146+
}
147+
return mIdx + 1;
148+
};
149+
150+
if (s.back()=='%'){
151+
switch (symbology) {
152+
case BARCODE_QRCODE:
153+
case BARCODE_MICROQR:
154+
case BARCODE_RMQR:
155+
return findClosestECLevel({20, 37, 55, 65}, res);
156+
case BARCODE_AZTEC:
157+
return findClosestECLevel({10, 23, 26, 50}, res);
158+
case BARCODE_PDF417:
159+
// TODO: do something sensible with PDF417?
160+
default:
161+
return -1;
162+
}
163+
}
164+
165+
return res;
166+
};
167+
168+
zint_symbol* CreatorOptions::zint() const
169+
{
170+
auto& zint = d->zint;
171+
172+
if (!zint) {
173+
printf("zint version: %d, sizeof(zint_symbol): %ld\n", ZBarcode_Version(), sizeof(zint_symbol));
174+
175+
zint.reset(ZBarcode_Create());
176+
177+
auto i = FindIf(barcodeFormatZXing2Zint, [zxing = format()](auto& v) { return v.zxing == zxing; });
178+
if (i == std::end(barcodeFormatZXing2Zint))
179+
throw std::invalid_argument("unsupported barcode format: " + ToString(format()));
180+
zint->symbology = i->zint;
181+
182+
zint->scale = 0.5f;
183+
184+
if (!ecLevel().empty())
185+
zint->option_1 = ParseECLevel(zint->symbology, ecLevel());
186+
}
187+
188+
return zint.get();
189+
}
190+
191+
Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions& opts)
192+
{
193+
auto zint = opts.zint();
194+
195+
zint->input_mode = mode;
196+
zint->output_options |= OUT_BUFFER_INTERMEDIATE | BARCODE_QUIET_ZONES;
197+
198+
if (mode == DATA_MODE && ZBarcode_Cap(zint->symbology, ZINT_CAP_ECI))
199+
zint->eci = static_cast<int>(ECI::Binary);
200+
201+
int err = ZBarcode_Encode_and_Buffer(zint, (uint8_t*)data, size, 0);
202+
203+
if (err || *zint->errtxt)
204+
printf("Error %d: %s\n", err, zint->errtxt);
205+
206+
printf("symbol size: %dx%d\n", zint->width, zint->rows);
207+
208+
auto buffer = std::vector<uint8_t>(zint->bitmap_width * zint->bitmap_height);
209+
std::transform(zint->bitmap, zint->bitmap + zint->bitmap_width * zint->bitmap_height, buffer.data(),
210+
[](unsigned char v) { return (v == '0') * 0xff; });
211+
auto bits = BitMatrix(zint->bitmap_width, zint->bitmap_height);
212+
std::transform(zint->bitmap, zint->bitmap + zint->bitmap_width * zint->bitmap_height, bits.row(0).begin(),
213+
[](unsigned char v) { return (v == '0') * BitMatrix::SET_V; });
214+
215+
auto res = ReadBarcode({buffer.data(), zint->bitmap_width, zint->bitmap_height, ImageFormat::Lum},
216+
ReaderOptions().setFormats(opts.format()).setIsPure(true).setBinarizer(Binarizer::BoolCast));
217+
res.symbol(std::move(bits));
218+
res.zint(std::move(opts.d->zint));
219+
220+
return res;
221+
}
222+
223+
Barcode CreateBarcodeFromText(std::string_view contents, const CreatorOptions& opts)
224+
{
225+
return CreateBarcode(contents.data(), contents.size(), UNICODE_MODE, opts);
226+
}
227+
228+
Barcode CreateBarcodeFromText(std::u8string_view contents, const CreatorOptions& opts)
229+
{
230+
return CreateBarcode(contents.data(), contents.size(), UNICODE_MODE, opts);
231+
}
232+
233+
Barcode CreateBarcodeFromBytes(const void* data, int size, const CreatorOptions& opts)
234+
{
235+
return CreateBarcode(data, size, DATA_MODE, opts);
236+
}
237+
238+
// Writer ========================================================================
239+
240+
static void SetCommonWriterOptions(zint_symbol* zint, const WriterOptions& opts)
241+
{
242+
zint->show_hrt = opts.withHRT();
243+
244+
zint->output_options &= ~OUT_BUFFER_INTERMEDIATE;
245+
zint->output_options |= opts.withQuietZones() ? BARCODE_QUIET_ZONES: BARCODE_NO_QUIET_ZONES;
246+
247+
if (opts.scale())
248+
zint->scale = opts.scale();
249+
else if (opts.sizeHint()) {
250+
int size = std::max(zint->width, zint->rows);
251+
zint->scale = std::max(1, int(float(opts.sizeHint()) / size)) / 2.f;
252+
}
253+
}
254+
255+
std::string WriteBarcodeToSVG(const Barcode& barcode, const WriterOptions& opts)
256+
{
257+
auto zs = barcode.zint();
258+
259+
if (!zs)
260+
return ToSVG(barcode.symbol());
261+
262+
SetCommonWriterOptions(zs, opts);
263+
264+
zs->output_options |= BARCODE_MEMORY_FILE;// | EMBED_VECTOR_FONT;
265+
strcpy(zs->outfile, "null.svg");
266+
267+
int err = ZBarcode_Print(zs, opts.rotate());
268+
if (err || *zs->errtxt)
269+
printf("Error %d: %s\n", err, zs->errtxt);
270+
271+
return std::string(reinterpret_cast<const char*>(zs->memfile), zs->memfile_size);
272+
}
273+
274+
Image WriteBarcodeToImage(const Barcode& barcode, const WriterOptions& opts)
275+
{
276+
auto zs = barcode.zint();
277+
278+
SetCommonWriterOptions(zs, opts);
279+
280+
int err = ZBarcode_Buffer(zs, opts.rotate());
281+
if (err || *zs->errtxt)
282+
printf("Error %d: %s\n", err, zs->errtxt);
283+
284+
printf("symbol size: %dx%d\n", zs->bitmap_width, zs->bitmap_height);
285+
auto iv = Image(zs->bitmap_width, zs->bitmap_height);
286+
auto* src = zs->bitmap;
287+
auto* dst = const_cast<uint8_t*>(iv.data());
288+
for(int y = 0; y < iv.height(); ++y)
289+
for(int x = 0, w = iv.width(); x < w; ++x, src += 3)
290+
*dst++ = RGBToLum(src[0], src[1], src[2]);
291+
292+
return iv;
293+
}
294+
295+
} // ZXing
296+
297+
298+
#else
74299

75300
#include "BitMatrix.h"
76301
#include "MultiFormatWriter.h"
@@ -139,4 +364,6 @@ Image WriteBarcodeToImage(const Barcode& barcode, [[maybe_unused]] const WriterO
139364

140365
} // namespace ZXing
141366

367+
#endif
368+
142369
#endif // ZXING_BUILD_EXPERIMENTAL_API

0 commit comments

Comments
 (0)
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