| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- // 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 */
|