Mercurial > hgrepos > Python2 > PyMuPDF
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/fitz/barcode.cpp Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,502 @@ +// Copyright (C) 2004-2025 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" + +#if FZ_ENABLE_BARCODE + +#ifndef HAVE_ZXINGCPP +#error "FZ_ENABLE_BARCODE set without HAVE_ZXINGCPP!" +#endif + +#include "ReadBarcode.h" + +#ifdef ZXING_EXPERIMENTAL_API +#include "WriteBarcode.h" +#else +#include "BitMatrix.h" +#include "MultiFormatWriter.h" +#endif + +using namespace ZXing; + +extern "C" +{ + +static const char *fz_barcode_type_strings[FZ_BARCODE__LIMIT] = +{ + "none", + "aztec", + "codabar", + "code39", + "code93", + "code128", + "databar", + "databarexpanded", + "datamatrix", + "ean8", + "ean13", + "itf", + "maxicode", + "pdf417", + "qrcode", + "upca", + "upce", + "microqrcode", + "rmqrcode", + "dxfilmedge", + "databarlimited" +}; + +BarcodeFormat formats[FZ_BARCODE__LIMIT] = +{ + BarcodeFormat::None, + BarcodeFormat::Aztec, + BarcodeFormat::Codabar, + BarcodeFormat::Code39, + BarcodeFormat::Code93, + BarcodeFormat::Code128, + BarcodeFormat::DataBar, + BarcodeFormat::DataBarExpanded, + BarcodeFormat::DataMatrix, + BarcodeFormat::EAN8, + BarcodeFormat::EAN13, + BarcodeFormat::ITF, + BarcodeFormat::MaxiCode, + BarcodeFormat::PDF417, + BarcodeFormat::QRCode, + BarcodeFormat::UPCA, + BarcodeFormat::UPCE, + BarcodeFormat::MicroQRCode, +#ifdef ZXING_EXPERIMENTAL_API + BarcodeFormat::RMQRCode, + BarcodeFormat::DXFilmEdge, + BarcodeFormat::DataBarLimited, +#endif +}; + +fz_barcode_type +fz_barcode_type_from_string(const char *str) +{ + int n = nelem(fz_barcode_type_strings); + int i; + + if (str == NULL) + return FZ_BARCODE_NONE; + + for (i = 1; i < n; i++) + { + if (!fz_strcasecmp(str, fz_barcode_type_strings[i])) + return (fz_barcode_type)i; + } + return FZ_BARCODE_NONE; +} + +const char * +fz_string_from_barcode_type(fz_barcode_type type) +{ + if (type < 0 || type >= FZ_BARCODE__LIMIT) + return "unknown"; + return fz_barcode_type_strings[type]; +} + +static fz_barcode_type +format_to_fz(BarcodeFormat format) +{ + int n = nelem(formats); + int i; + + for (i = 1; i < n; i++) + { + if (format == formats[i]) + return (fz_barcode_type)i; + } + return FZ_BARCODE_NONE; +} + +#ifdef ZXING_EXPERIMENTAL_API + +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) +{ + fz_pixmap *pix; + int x, y, w, h, ps, rs; + uint8_t *tmp_copy = NULL; + + if (type <= FZ_BARCODE_NONE || type >= FZ_BARCODE__LIMIT) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported barcode type"); + if (ec_level < 0 || ec_level >= 8) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported barcode error correction level"); + + /* The following has been carefully constructed to ensure + * that the mixture of C++ and fz error handling works OK. + * The try here doesn't call anything that might fz_throw. + * When the try exits, all the C++ objects should be + * destructed, leaving us just with a malloced temporary + * copy of the bitmap data. */ + { + char exception_text[256] = ""; + try + { + char ec_str[16]; + BarcodeFormat format = formats[type]; + sprintf(ec_str, "%d", ec_level); + CreatorOptions cOpts = CreatorOptions(format).ecLevel(ec_str); + Barcode barcode = CreateBarcodeFromText(value, cOpts); + WriterOptions wOpts = WriterOptions().withQuietZones(!!quiet).withHRT(!!hrt).rotate(0); + if (size) + wOpts.sizeHint(size); + else + wOpts.scale(2); /* Smallest size that gives text */ + Image bitmap = WriteBarcodeToImage(barcode, wOpts); + const uint8_t *src = bitmap.data(); + if (src != NULL) + { + h = bitmap.height(); + w = bitmap.width(); + tmp_copy = (uint8_t *)malloc(w * h); + } + if (tmp_copy != NULL) + { + uint8_t *dst = tmp_copy; + ps = bitmap.pixStride(); + rs = bitmap.rowStride(); + + for (y = 0; y < h; y++) + { + const uint8_t *s = src; + src += rs; + for (x = 0; x < w; x++) + { + *dst++ = *s; + s += ps; + } + } + } + } + catch (std::exception & e) + { + /* If C++ throws an exception to here, we can trust + * that it will have freed everything in the above + * block. That just leaves tmp_copy outstanding. */ + free(tmp_copy); + fz_strlcpy(exception_text, e.what(), sizeof(exception_text)); + } + if (exception_text[0]) + fz_throw(ctx, FZ_ERROR_LIBRARY, "Barcode exception: %s", exception_text); + } + + if (tmp_copy == NULL) + fz_throw(ctx, FZ_ERROR_LIBRARY, "Barcode generation failed"); + + fz_try(ctx) + { + pix = fz_new_pixmap(ctx, fz_device_gray(ctx), w, h, NULL, 0); + pix->xres = 72; + pix->yres = 72; + memcpy(pix->samples, tmp_copy, w*h); + } + fz_always(ctx) + free(tmp_copy); + fz_catch(ctx) + fz_rethrow(ctx); + + return pix; +} + +#else + +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) +{ + fz_pixmap *pix; + int x, y, w, h; + uint8_t *tmp_copy = NULL; + + if (type <= FZ_BARCODE_NONE || type >= FZ_BARCODE__LIMIT) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported barcode type"); + if (ec_level < 0 || ec_level >= 8) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unsupported barcode error correction level"); + + /* The following has been carefully constructed to ensure + * that the mixture of C++ and fz error handling works OK. + * The try here doesn't call anything that might fz_throw. + * When the try exits, all the C++ objects should be + * destructed, leaving us just with a malloced temporary + * copy of the bitmap data. */ + { + char exception_text[256] = ""; + try + { + auto format = formats[type]; + auto writer = MultiFormatWriter(format); + BitMatrix matrix; + writer.setEncoding(CharacterSet::UTF8); + writer.setMargin(quiet ? 10 : 0); + writer.setEccLevel(ec_level); + matrix = writer.encode(value, size, size ? size : 50); + auto bitmap = ToMatrix<uint8_t>(matrix); + + // TODO: human readable text (not available with non-experimental API) + + const uint8_t *src = bitmap.data(); + if (src != NULL) + { + h = bitmap.height(); + w = bitmap.width(); + tmp_copy = (uint8_t *)malloc(w * h); + } + if (tmp_copy != NULL) + { + uint8_t *dst = tmp_copy; + for (y = 0; y < h; y++) + { + const uint8_t *s = src; + src += w; + for (x = 0; x < w; x++) + { + *dst++ = *s; + s ++; + } + } + } + } + catch (std::exception & e) + { + /* If C++ throws an exception to here, we can trust + * that it will have freed everything in the above + * block. That just leaves tmp_copy outstanding. */ + free(tmp_copy); + fz_strlcpy(exception_text, e.what(), sizeof(exception_text)); + } + if (exception_text[0]) + fz_throw(ctx, FZ_ERROR_LIBRARY, "Barcode exception: %s", exception_text); + } + + if (tmp_copy == NULL) + fz_throw(ctx, FZ_ERROR_LIBRARY, "Barcode generation failed"); + + fz_try(ctx) + { + pix = fz_new_pixmap(ctx, fz_device_gray(ctx), w, h, NULL, 0); + pix->xres = 36; + pix->yres = 36; + memcpy(pix->samples, tmp_copy, w*h); + } + fz_always(ctx) + free(tmp_copy); + fz_catch(ctx) + fz_rethrow(ctx); + + return pix; +} + +#endif + +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) +{ + fz_pixmap *pix = fz_new_barcode_pixmap(ctx, type, value, size, ec_level, quiet, hrt); + fz_image *image; + + fz_try(ctx) + image = fz_new_image_from_pixmap(ctx, pix, NULL); + fz_always(ctx) + fz_drop_pixmap(ctx, pix); + fz_catch(ctx) + fz_rethrow(ctx); + + return image; +} + +char *fz_decode_barcode_from_page(fz_context *ctx, fz_barcode_type *type, fz_page *page, fz_rect subarea, int rotate) +{ + fz_rect rect; + fz_irect bbox; + fz_pixmap *pix; + fz_device *dev = NULL; + char *str; + + fz_var(dev); + + rect = fz_bound_page(ctx, page); + rect = fz_intersect_rect(rect, subarea); + bbox = fz_round_rect(rect); + + pix = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox, NULL, 0); + fz_clear_pixmap_with_value(ctx, pix, 0xFF); + + fz_try(ctx) + { + dev = fz_new_draw_device(ctx, fz_identity, pix); + fz_run_page(ctx, page, dev, fz_identity, NULL); + fz_close_device(ctx, dev); + + str = fz_decode_barcode_from_pixmap(ctx, type, pix, rotate); + } + fz_always(ctx) + { + fz_drop_device(ctx, dev); + fz_drop_pixmap(ctx, pix); + } + fz_catch(ctx) + fz_rethrow(ctx); + + return str; +} + + +char *fz_decode_barcode_from_pixmap(fz_context *ctx, fz_barcode_type *type, fz_pixmap *pix, int rotate) +{ + ImageFormat format; + char *ret = NULL; + char *tmp_copy = NULL; + + if (pix == NULL) + return NULL; + + if (pix->n == 1) + format = ImageFormat::Lum; + else if (pix->n == 3) + format = ImageFormat::RGB; + else + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Barcodes can be greyscale or RGB only"); + + /* The following has been carefully constructed to ensure + * that the mixture of C++ and fz error handling works OK. + * The try here doesn't call anything that might fz_throw. + * When the try exits, all the C++ objects should be + * destructed, leaving us just with a malloced temporary + * copy of the results. */ + { + char exception_text[256] = ""; + try + { + ImageView image{pix->samples, pix->w, pix->h, format}; + auto barcode = ReadBarcode(image.rotated(rotate)); + std::string str; + + if (barcode.isValid()) + str = barcode.text(TextMode::Escaped); + else if (barcode.error()) + str = ToString(barcode.error()); + else + str = "Unknown " + ToString(barcode.format()); + if (type) + *type = format_to_fz(barcode.format()); + + tmp_copy = strdup(str.c_str()); + } + catch (std::exception & e) + { + /* If C++ throws an exception to here, we can trust + * that it will have freed everything in the above + * block. That just leaves tmp_copy outstanding. */ + free(tmp_copy); + fz_strlcpy(exception_text, e.what(), sizeof(exception_text)); + } + if (exception_text[0]) + fz_throw(ctx, FZ_ERROR_LIBRARY, "Barcode exception: %s", exception_text); + } + + fz_try(ctx) + if (tmp_copy) + ret = fz_strdup(ctx, tmp_copy); + fz_always(ctx) + free(tmp_copy); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +char *fz_decode_barcode_from_display_list(fz_context *ctx, fz_barcode_type *type, fz_display_list *list, fz_rect subarea, int rotate) +{ + fz_rect rect; + fz_irect bbox; + fz_pixmap *pix; + char *str; + + rect = fz_bound_display_list(ctx, list); + rect = fz_intersect_rect(rect, subarea); + bbox = fz_round_rect(rect); + + pix = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox, NULL, 0); + fz_clear_pixmap_with_value(ctx, pix, 0xFF); + + fz_try(ctx) + { + fz_fill_pixmap_from_display_list(ctx, list, fz_identity, pix); + str = fz_decode_barcode_from_pixmap(ctx, type, pix, rotate); + } + fz_always(ctx) + { + fz_drop_pixmap(ctx, pix); + } + fz_catch(ctx) + fz_rethrow(ctx); + + return str; +} + +} + +#else + +extern "C" +{ + +char *fz_decode_barcode_from_display_list(fz_context *ctx, fz_barcode_type *type, fz_display_list *list, fz_rect subarea, int rotate) +{ + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Barcode functionality not included"); +} + +char *fz_decode_barcode_from_pixmap(fz_context *ctx, fz_barcode_type *type, fz_pixmap *pix, int rotate) +{ + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Barcode functionality not included"); +} + +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) +{ + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Barcode functionality not included"); +} + +char *fz_decode_barcode_from_page(fz_context *ctx, fz_barcode_type *type, fz_page *page, fz_rect subarea, int rotate) +{ + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Barcode functionality not included"); +} + +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) +{ + fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "Barcode functionality not included"); +} + +fz_barcode_type fz_barcode_type_from_string(const char *str) +{ + return FZ_BARCODE_NONE; +} + +const char *fz_string_from_barcode_type(fz_barcode_type type) +{ + return "unknown"; +} + +} + +#endif /* FZ_ENABLE_BARCODE */
