barcode.cpp 12 KB

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