comparison mupdf-source/source/fitz/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 // Copyright (C) 2004-2025 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24
25 #if FZ_ENABLE_BARCODE
26
27 #ifndef HAVE_ZXINGCPP
28 #error "FZ_ENABLE_BARCODE set without HAVE_ZXINGCPP!"
29 #endif
30
31 #include "ReadBarcode.h"
32
33 #ifdef ZXING_EXPERIMENTAL_API
34 #include "WriteBarcode.h"
35 #else
36 #include "BitMatrix.h"
37 #include "MultiFormatWriter.h"
38 #endif
39
40 using namespace ZXing;
41
42 extern "C"
43 {
44
45 static const char *fz_barcode_type_strings[FZ_BARCODE__LIMIT] =
46 {
47 "none",
48 "aztec",
49 "codabar",
50 "code39",
51 "code93",
52 "code128",
53 "databar",
54 "databarexpanded",
55 "datamatrix",
56 "ean8",
57 "ean13",
58 "itf",
59 "maxicode",
60 "pdf417",
61 "qrcode",
62 "upca",
63 "upce",
64 "microqrcode",
65 "rmqrcode",
66 "dxfilmedge",
67 "databarlimited"
68 };
69
70 BarcodeFormat formats[FZ_BARCODE__LIMIT] =
71 {
72 BarcodeFormat::None,
73 BarcodeFormat::Aztec,
74 BarcodeFormat::Codabar,
75 BarcodeFormat::Code39,
76 BarcodeFormat::Code93,
77 BarcodeFormat::Code128,
78 BarcodeFormat::DataBar,
79 BarcodeFormat::DataBarExpanded,
80 BarcodeFormat::DataMatrix,
81 BarcodeFormat::EAN8,
82 BarcodeFormat::EAN13,
83 BarcodeFormat::ITF,
84 BarcodeFormat::MaxiCode,
85 BarcodeFormat::PDF417,
86 BarcodeFormat::QRCode,
87 BarcodeFormat::UPCA,
88 BarcodeFormat::UPCE,
89 BarcodeFormat::MicroQRCode,
90 #ifdef ZXING_EXPERIMENTAL_API
91 BarcodeFormat::RMQRCode,
92 BarcodeFormat::DXFilmEdge,
93 BarcodeFormat::DataBarLimited,
94 #endif
95 };
96
97 fz_barcode_type
98 fz_barcode_type_from_string(const char *str)
99 {
100 int n = nelem(fz_barcode_type_strings);
101 int i;
102
103 if (str == NULL)
104 return FZ_BARCODE_NONE;
105
106 for (i = 1; i < n; i++)
107 {
108 if (!fz_strcasecmp(str, fz_barcode_type_strings[i]))
109 return (fz_barcode_type)i;
110 }
111 return FZ_BARCODE_NONE;
112 }
113
114 const char *
115 fz_string_from_barcode_type(fz_barcode_type type)
116 {
117 if (type < 0 || type >= FZ_BARCODE__LIMIT)
118 return "unknown";
119 return fz_barcode_type_strings[type];
120 }
121
122 static fz_barcode_type
123 format_to_fz(BarcodeFormat format)
124 {
125 int n = nelem(formats);
126 int i;
127
128 for (i = 1; i < n; i++)
129 {
130 if (format == formats[i])
131 return (fz_barcode_type)i;
132 }
133 return FZ_BARCODE_NONE;
134 }
135
136 #ifdef ZXING_EXPERIMENTAL_API
137
138 fz_pixmap *fz_new_barcode_pixmap(fz_context *ctx, fz_barcode_type type, const char *value, int size, int ec_level, int quiet, int hrt)
139 {
140 fz_pixmap *pix;
141 int x, y, w, h, ps, rs;
142 uint8_t *tmp_copy = NULL;
143
144 if (type <= FZ_BARCODE_NONE || type >= FZ_BARCODE__LIMIT)
145 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported barcode type");
146 if (ec_level < 0 || ec_level >= 8)
147 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported barcode error correction level");
148
149 /* The following has been carefully constructed to ensure
150 * that the mixture of C++ and fz error handling works OK.
151 * The try here doesn't call anything that might fz_throw.
152 * When the try exits, all the C++ objects should be
153 * destructed, leaving us just with a malloced temporary
154 * copy of the bitmap data. */
155 {
156 char exception_text[256] = "";
157 try
158 {
159 char ec_str[16];
160 BarcodeFormat format = formats[type];
161 sprintf(ec_str, "%d", ec_level);
162 CreatorOptions cOpts = CreatorOptions(format).ecLevel(ec_str);
163 Barcode barcode = CreateBarcodeFromText(value, cOpts);
164 WriterOptions wOpts = WriterOptions().withQuietZones(!!quiet).withHRT(!!hrt).rotate(0);
165 if (size)
166 wOpts.sizeHint(size);
167 else
168 wOpts.scale(2); /* Smallest size that gives text */
169 Image bitmap = WriteBarcodeToImage(barcode, wOpts);
170 const uint8_t *src = bitmap.data();
171 if (src != NULL)
172 {
173 h = bitmap.height();
174 w = bitmap.width();
175 tmp_copy = (uint8_t *)malloc(w * h);
176 }
177 if (tmp_copy != NULL)
178 {
179 uint8_t *dst = tmp_copy;
180 ps = bitmap.pixStride();
181 rs = bitmap.rowStride();
182
183 for (y = 0; y < h; y++)
184 {
185 const uint8_t *s = src;
186 src += rs;
187 for (x = 0; x < w; x++)
188 {
189 *dst++ = *s;
190 s += ps;
191 }
192 }
193 }
194 }
195 catch (std::exception & e)
196 {
197 /* If C++ throws an exception to here, we can trust
198 * that it will have freed everything in the above
199 * block. That just leaves tmp_copy outstanding. */
200 free(tmp_copy);
201 fz_strlcpy(exception_text, e.what(), sizeof(exception_text));
202 }
203 if (exception_text[0])
204 fz_throw(ctx, FZ_ERROR_LIBRARY, "Barcode exception: %s", exception_text);
205 }
206
207 if (tmp_copy == NULL)
208 fz_throw(ctx, FZ_ERROR_LIBRARY, "Barcode generation failed");
209
210 fz_try(ctx)
211 {
212 pix = fz_new_pixmap(ctx, fz_device_gray(ctx), w, h, NULL, 0);
213 pix->xres = 72;
214 pix->yres = 72;
215 memcpy(pix->samples, tmp_copy, w*h);
216 }
217 fz_always(ctx)
218 free(tmp_copy);
219 fz_catch(ctx)
220 fz_rethrow(ctx);
221
222 return pix;
223 }
224
225 #else
226
227 fz_pixmap *fz_new_barcode_pixmap(fz_context *ctx, fz_barcode_type type, const char *value, int size, int ec_level, int quiet, int hrt)
228 {
229 fz_pixmap *pix;
230 int x, y, w, h;
231 uint8_t *tmp_copy = NULL;
232
233 if (type <= FZ_BARCODE_NONE || type >= FZ_BARCODE__LIMIT)
234 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported barcode type");
235 if (ec_level < 0 || ec_level >= 8)
236 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported barcode error correction level");
237
238 /* The following has been carefully constructed to ensure
239 * that the mixture of C++ and fz error handling works OK.
240 * The try here doesn't call anything that might fz_throw.
241 * When the try exits, all the C++ objects should be
242 * destructed, leaving us just with a malloced temporary
243 * copy of the bitmap data. */
244 {
245 char exception_text[256] = "";
246 try
247 {
248 auto format = formats[type];
249 auto writer = MultiFormatWriter(format);
250 BitMatrix matrix;
251 writer.setEncoding(CharacterSet::UTF8);
252 writer.setMargin(quiet ? 10 : 0);
253 writer.setEccLevel(ec_level);
254 matrix = writer.encode(value, size, size ? size : 50);
255 auto bitmap = ToMatrix<uint8_t>(matrix);
256
257 // TODO: human readable text (not available with non-experimental API)
258
259 const uint8_t *src = bitmap.data();
260 if (src != NULL)
261 {
262 h = bitmap.height();
263 w = bitmap.width();
264 tmp_copy = (uint8_t *)malloc(w * h);
265 }
266 if (tmp_copy != NULL)
267 {
268 uint8_t *dst = tmp_copy;
269 for (y = 0; y < h; y++)
270 {
271 const uint8_t *s = src;
272 src += w;
273 for (x = 0; x < w; x++)
274 {
275 *dst++ = *s;
276 s ++;
277 }
278 }
279 }
280 }
281 catch (std::exception & e)
282 {
283 /* If C++ throws an exception to here, we can trust
284 * that it will have freed everything in the above
285 * block. That just leaves tmp_copy outstanding. */
286 free(tmp_copy);
287 fz_strlcpy(exception_text, e.what(), sizeof(exception_text));
288 }
289 if (exception_text[0])
290 fz_throw(ctx, FZ_ERROR_LIBRARY, "Barcode exception: %s", exception_text);
291 }
292
293 if (tmp_copy == NULL)
294 fz_throw(ctx, FZ_ERROR_LIBRARY, "Barcode generation failed");
295
296 fz_try(ctx)
297 {
298 pix = fz_new_pixmap(ctx, fz_device_gray(ctx), w, h, NULL, 0);
299 pix->xres = 36;
300 pix->yres = 36;
301 memcpy(pix->samples, tmp_copy, w*h);
302 }
303 fz_always(ctx)
304 free(tmp_copy);
305 fz_catch(ctx)
306 fz_rethrow(ctx);
307
308 return pix;
309 }
310
311 #endif
312
313 fz_image *fz_new_barcode_image(fz_context *ctx, fz_barcode_type type, const char *value, int size, int ec_level, int quiet, int hrt)
314 {
315 fz_pixmap *pix = fz_new_barcode_pixmap(ctx, type, value, size, ec_level, quiet, hrt);
316 fz_image *image;
317
318 fz_try(ctx)
319 image = fz_new_image_from_pixmap(ctx, pix, NULL);
320 fz_always(ctx)
321 fz_drop_pixmap(ctx, pix);
322 fz_catch(ctx)
323 fz_rethrow(ctx);
324
325 return image;
326 }
327
328 char *fz_decode_barcode_from_page(fz_context *ctx, fz_barcode_type *type, fz_page *page, fz_rect subarea, int rotate)
329 {
330 fz_rect rect;
331 fz_irect bbox;
332 fz_pixmap *pix;
333 fz_device *dev = NULL;
334 char *str;
335
336 fz_var(dev);
337
338 rect = fz_bound_page(ctx, page);
339 rect = fz_intersect_rect(rect, subarea);
340 bbox = fz_round_rect(rect);
341
342 pix = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox, NULL, 0);
343 fz_clear_pixmap_with_value(ctx, pix, 0xFF);
344
345 fz_try(ctx)
346 {
347 dev = fz_new_draw_device(ctx, fz_identity, pix);
348 fz_run_page(ctx, page, dev, fz_identity, NULL);
349 fz_close_device(ctx, dev);
350
351 str = fz_decode_barcode_from_pixmap(ctx, type, pix, rotate);
352 }
353 fz_always(ctx)
354 {
355 fz_drop_device(ctx, dev);
356 fz_drop_pixmap(ctx, pix);
357 }
358 fz_catch(ctx)
359 fz_rethrow(ctx);
360
361 return str;
362 }
363
364
365 char *fz_decode_barcode_from_pixmap(fz_context *ctx, fz_barcode_type *type, fz_pixmap *pix, int rotate)
366 {
367 ImageFormat format;
368 char *ret = NULL;
369 char *tmp_copy = NULL;
370
371 if (pix == NULL)
372 return NULL;
373
374 if (pix->n == 1)
375 format = ImageFormat::Lum;
376 else if (pix->n == 3)
377 format = ImageFormat::RGB;
378 else
379 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Barcodes can be greyscale or RGB only");
380
381 /* The following has been carefully constructed to ensure
382 * that the mixture of C++ and fz error handling works OK.
383 * The try here doesn't call anything that might fz_throw.
384 * When the try exits, all the C++ objects should be
385 * destructed, leaving us just with a malloced temporary
386 * copy of the results. */
387 {
388 char exception_text[256] = "";
389 try
390 {
391 ImageView image{pix->samples, pix->w, pix->h, format};
392 auto barcode = ReadBarcode(image.rotated(rotate));
393 std::string str;
394
395 if (barcode.isValid())
396 str = barcode.text(TextMode::Escaped);
397 else if (barcode.error())
398 str = ToString(barcode.error());
399 else
400 str = "Unknown " + ToString(barcode.format());
401 if (type)
402 *type = format_to_fz(barcode.format());
403
404 tmp_copy = strdup(str.c_str());
405 }
406 catch (std::exception & e)
407 {
408 /* If C++ throws an exception to here, we can trust
409 * that it will have freed everything in the above
410 * block. That just leaves tmp_copy outstanding. */
411 free(tmp_copy);
412 fz_strlcpy(exception_text, e.what(), sizeof(exception_text));
413 }
414 if (exception_text[0])
415 fz_throw(ctx, FZ_ERROR_LIBRARY, "Barcode exception: %s", exception_text);
416 }
417
418 fz_try(ctx)
419 if (tmp_copy)
420 ret = fz_strdup(ctx, tmp_copy);
421 fz_always(ctx)
422 free(tmp_copy);
423 fz_catch(ctx)
424 fz_rethrow(ctx);
425
426 return ret;
427 }
428
429 char *fz_decode_barcode_from_display_list(fz_context *ctx, fz_barcode_type *type, fz_display_list *list, fz_rect subarea, int rotate)
430 {
431 fz_rect rect;
432 fz_irect bbox;
433 fz_pixmap *pix;
434 char *str;
435
436 rect = fz_bound_display_list(ctx, list);
437 rect = fz_intersect_rect(rect, subarea);
438 bbox = fz_round_rect(rect);
439
440 pix = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox, NULL, 0);
441 fz_clear_pixmap_with_value(ctx, pix, 0xFF);
442
443 fz_try(ctx)
444 {
445 fz_fill_pixmap_from_display_list(ctx, list, fz_identity, pix);
446 str = fz_decode_barcode_from_pixmap(ctx, type, pix, rotate);
447 }
448 fz_always(ctx)
449 {
450 fz_drop_pixmap(ctx, pix);
451 }
452 fz_catch(ctx)
453 fz_rethrow(ctx);
454
455 return str;
456 }
457
458 }
459
460 #else
461
462 extern "C"
463 {
464
465 char *fz_decode_barcode_from_display_list(fz_context *ctx, fz_barcode_type *type, fz_display_list *list, fz_rect subarea, int rotate)
466 {
467 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Barcode functionality not included");
468 }
469
470 char *fz_decode_barcode_from_pixmap(fz_context *ctx, fz_barcode_type *type, fz_pixmap *pix, int rotate)
471 {
472 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Barcode functionality not included");
473 }
474
475 fz_image *fz_new_barcode_image(fz_context *ctx, fz_barcode_type type, const char *value, int size, int ec_level, int quiet, int hrt)
476 {
477 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Barcode functionality not included");
478 }
479
480 char *fz_decode_barcode_from_page(fz_context *ctx, fz_barcode_type *type, fz_page *page, fz_rect subarea, int rotate)
481 {
482 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Barcode functionality not included");
483 }
484
485 fz_pixmap *fz_new_barcode_pixmap(fz_context *ctx, fz_barcode_type type, const char *value, int size, int ec_level, int quiet, int hrt)
486 {
487 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Barcode functionality not included");
488 }
489
490 fz_barcode_type fz_barcode_type_from_string(const char *str)
491 {
492 return FZ_BARCODE_NONE;
493 }
494
495 const char *fz_string_from_barcode_type(fz_barcode_type type)
496 {
497 return "unknown";
498 }
499
500 }
501
502 #endif /* FZ_ENABLE_BARCODE */