Mercurial > hgrepos > Python2 > PyMuPDF
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 */ |
