Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/zint/backend/png.c @ 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 /* png.c - Handles output to PNG file */ | |
| 2 /* | |
| 3 libzint - the open source barcode library | |
| 4 Copyright (C) 2009-2024 Robin Stuart <rstuart114@gmail.com> | |
| 5 | |
| 6 Redistribution and use in source and binary forms, with or without | |
| 7 modification, are permitted provided that the following conditions | |
| 8 are met: | |
| 9 | |
| 10 1. Redistributions of source code must retain the above copyright | |
| 11 notice, this list of conditions and the following disclaimer. | |
| 12 2. Redistributions in binary form must reproduce the above copyright | |
| 13 notice, this list of conditions and the following disclaimer in the | |
| 14 documentation and/or other materials provided with the distribution. | |
| 15 3. Neither the name of the project nor the names of its contributors | |
| 16 may be used to endorse or promote products derived from this software | |
| 17 without specific prior written permission. | |
| 18 | |
| 19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
| 20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE | |
| 23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 25 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 26 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 27 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 28 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 29 SUCH DAMAGE. | |
| 30 */ | |
| 31 /* SPDX-License-Identifier: BSD-3-Clause */ | |
| 32 | |
| 33 #ifndef ZINT_NO_PNG | |
| 34 | |
| 35 #include <errno.h> | |
| 36 #include <math.h> | |
| 37 #include <stdio.h> | |
| 38 #include <png.h> | |
| 39 #include <zlib.h> | |
| 40 #include <setjmp.h> | |
| 41 #include "common.h" | |
| 42 #include "filemem.h" | |
| 43 #include "output.h" | |
| 44 | |
| 45 /* Note using "wpng_" prefix not "png_" (except for `png_pixel_plot()`) to avoid clashing with libpng */ | |
| 46 | |
| 47 /* Note if change this need to change "backend/tests/test_png.c" definition also */ | |
| 48 struct wpng_error_type { | |
| 49 struct zint_symbol *symbol; | |
| 50 jmp_buf jmpbuf; | |
| 51 }; | |
| 52 | |
| 53 static void wpng_error_handler(png_structp png_ptr, png_const_charp msg) { | |
| 54 struct wpng_error_type *wpng_error_ptr; | |
| 55 | |
| 56 wpng_error_ptr = (struct wpng_error_type *) png_get_error_ptr(png_ptr); | |
| 57 if (wpng_error_ptr == NULL) { | |
| 58 /* we are completely hosed now */ | |
| 59 fprintf(stderr, "Error 636: libpng error: %s\n", msg ? msg : "<NULL>"); | |
| 60 fprintf(stderr, "Error 637: jmpbuf not recoverable, terminating\n"); | |
| 61 fflush(stderr); | |
| 62 return; /* libpng will call abort() */ | |
| 63 } | |
| 64 errtxtf(0, wpng_error_ptr->symbol, 635, "libpng error: %s", msg ? msg : "<NULL>"); | |
| 65 longjmp(wpng_error_ptr->jmpbuf, 1); | |
| 66 } | |
| 67 | |
| 68 #ifdef ZINT_TEST /* Wrapper for direct testing */ | |
| 69 INTERNAL void wpng_error_handler_test(png_structp png_ptr, png_const_charp msg) { | |
| 70 wpng_error_handler(png_ptr, msg); | |
| 71 } | |
| 72 #endif | |
| 73 | |
| 74 /* libpng write callback */ | |
| 75 static void wpng_write(png_structp png_ptr, png_bytep ptr, size_t size) { | |
| 76 struct filemem *fmp = (struct filemem *) png_get_io_ptr(png_ptr); | |
| 77 (void) fm_write(ptr, 1, size, fmp); | |
| 78 } | |
| 79 | |
| 80 /* libpng flush callback */ | |
| 81 static void wpng_flush(png_structp png_ptr) { | |
| 82 struct filemem *fmp = (struct filemem *) png_get_io_ptr(png_ptr); | |
| 83 (void) fm_flush(fmp); | |
| 84 } | |
| 85 | |
| 86 /* Guestimate best compression strategy */ | |
| 87 static int wpng_guess_compression_strategy(struct zint_symbol *symbol, const unsigned char *pixelbuf) { | |
| 88 (void)pixelbuf; | |
| 89 | |
| 90 /* TODO: Do properly */ | |
| 91 | |
| 92 /* It seems the best choice for typical barcode pngs is one of Z_DEFAULT_STRATEGY and Z_FILTERED */ | |
| 93 | |
| 94 /* Some guesses */ | |
| 95 if (symbol->symbology == BARCODE_MAXICODE) { | |
| 96 return Z_DEFAULT_STRATEGY; | |
| 97 } | |
| 98 if (symbol->symbology == BARCODE_AZTEC && symbol->bitmap_width <= 30) { | |
| 99 return Z_DEFAULT_STRATEGY; | |
| 100 } | |
| 101 | |
| 102 /* Z_FILTERED seems to work better for slightly more barcodes+data so default to that */ | |
| 103 return Z_FILTERED; | |
| 104 } | |
| 105 | |
| 106 INTERNAL int png_pixel_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf) { | |
| 107 struct wpng_error_type wpng_error; | |
| 108 struct filemem fm; | |
| 109 struct filemem *const fmp = &fm; | |
| 110 png_structp png_ptr; | |
| 111 png_infop info_ptr; | |
| 112 int i; | |
| 113 int row, column; | |
| 114 png_color bg, fg; | |
| 115 unsigned char bg_alpha, fg_alpha; | |
| 116 unsigned char map[128]; | |
| 117 png_color palette[32]; | |
| 118 int num_palette; | |
| 119 unsigned char trans_alpha[32]; | |
| 120 int num_trans; /* Note initialize below to avoid gcc -Wclobbered warning due to `longjmp()` */ | |
| 121 int bit_depth; | |
| 122 int compression_strategy; | |
| 123 const unsigned char *pb; | |
| 124 unsigned char *outdata = (unsigned char *) z_alloca(symbol->bitmap_width); | |
| 125 | |
| 126 wpng_error.symbol = symbol; | |
| 127 | |
| 128 (void) out_colour_get_rgb(symbol->fgcolour, &fg.red, &fg.green, &fg.blue, &fg_alpha); | |
| 129 (void) out_colour_get_rgb(symbol->bgcolour, &bg.red, &bg.green, &bg.blue, &bg_alpha); | |
| 130 | |
| 131 num_trans = 0; | |
| 132 if (symbol->symbology == BARCODE_ULTRA) { | |
| 133 static const unsigned char ultra_chars[8] = { 'W', 'C', 'B', 'M', 'R', 'Y', 'G', 'K' }; | |
| 134 for (i = 0; i < 8; i++) { | |
| 135 map[ultra_chars[i]] = i; | |
| 136 out_colour_char_to_rgb(ultra_chars[i], &palette[i].red, &palette[i].green, &palette[i].blue); | |
| 137 if (fg_alpha != 0xff) { | |
| 138 trans_alpha[i] = fg_alpha; | |
| 139 } | |
| 140 } | |
| 141 num_palette = 8; | |
| 142 if (fg_alpha != 0xff) { | |
| 143 num_trans = 8; | |
| 144 } | |
| 145 | |
| 146 /* For Ultracode, have foreground only if have bind/box */ | |
| 147 if (symbol->border_width > 0 && (symbol->output_options & (BARCODE_BIND | BARCODE_BOX | BARCODE_BIND_TOP))) { | |
| 148 /* Check whether can re-use black */ | |
| 149 if (fg.red == 0 && fg.green == 0 && fg.blue == 0) { | |
| 150 map['1'] = 7; /* Re-use black */ | |
| 151 } else { | |
| 152 map['1'] = num_palette; | |
| 153 palette[num_palette++] = fg; | |
| 154 if (fg_alpha != 0xff) { | |
| 155 trans_alpha[num_trans++] = fg_alpha; | |
| 156 } | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 /* For Ultracode, have background only if have whitespace/quiet zones */ | |
| 161 if (symbol->whitespace_width > 0 || symbol->whitespace_height > 0 | |
| 162 || ((symbol->output_options & BARCODE_QUIET_ZONES) | |
| 163 && !(symbol->output_options & BARCODE_NO_QUIET_ZONES))) { | |
| 164 /* Check whether can re-use white */ | |
| 165 if (bg.red == 0xff && bg.green == 0xff && bg.blue == 0xff && bg_alpha == fg_alpha) { | |
| 166 map['0'] = 0; /* Re-use white */ | |
| 167 } else { | |
| 168 if (bg_alpha == 0xff || fg_alpha != 0xff) { | |
| 169 /* No alpha or have foreground alpha - add to end */ | |
| 170 map['0'] = num_palette; | |
| 171 palette[num_palette++] = bg; | |
| 172 } else { | |
| 173 /* Alpha and no foreground alpha - add to front & move white to end */ | |
| 174 png_color white = palette[0]; /* Take copy */ | |
| 175 map['0'] = 0; | |
| 176 palette[0] = bg; | |
| 177 map['W'] = num_palette; | |
| 178 palette[num_palette++] = white; | |
| 179 } | |
| 180 if (bg_alpha != 0xff) { | |
| 181 trans_alpha[num_trans++] = bg_alpha; | |
| 182 } | |
| 183 } | |
| 184 } | |
| 185 } else { | |
| 186 int bg_idx = 0, fg_idx = 1; | |
| 187 /* Do alphas first so can swop indexes if background not alpha */ | |
| 188 if (bg_alpha != 0xff) { | |
| 189 trans_alpha[num_trans++] = bg_alpha; | |
| 190 } | |
| 191 if (fg_alpha != 0xff) { | |
| 192 trans_alpha[num_trans++] = fg_alpha; | |
| 193 if (num_trans == 1) { | |
| 194 /* Only foreground has alpha so swop indexes - saves a byte! */ | |
| 195 bg_idx = 1; | |
| 196 fg_idx = 0; | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 map['0'] = bg_idx; | |
| 201 palette[bg_idx] = bg; | |
| 202 map['1'] = fg_idx; | |
| 203 palette[fg_idx] = fg; | |
| 204 num_palette = 2; | |
| 205 } | |
| 206 | |
| 207 if (num_palette <= 2) { | |
| 208 bit_depth = 1; | |
| 209 } else { | |
| 210 bit_depth = 4; | |
| 211 } | |
| 212 | |
| 213 /* Open output file in binary mode */ | |
| 214 if (!fm_open(fmp, symbol, "wb")) { | |
| 215 return errtxtf(ZINT_ERROR_FILE_ACCESS, symbol, 632, "Could not open PNG output file (%1$d: %2$s)", fmp->err, | |
| 216 strerror(fmp->err)); | |
| 217 } | |
| 218 | |
| 219 /* Set up error handling routine as proc() above */ | |
| 220 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, &wpng_error, wpng_error_handler, NULL); | |
| 221 if (!png_ptr) { | |
| 222 (void) fm_close(fmp, symbol); | |
| 223 return errtxt(ZINT_ERROR_MEMORY, symbol, 633, "Insufficient memory for PNG write structure buffer"); | |
| 224 } | |
| 225 | |
| 226 info_ptr = png_create_info_struct(png_ptr); | |
| 227 if (!info_ptr) { | |
| 228 png_destroy_write_struct(&png_ptr, NULL); | |
| 229 (void) fm_close(fmp, symbol); | |
| 230 return errtxt(ZINT_ERROR_MEMORY, symbol, 634, "Insufficient memory for PNG info structure buffer"); | |
| 231 } | |
| 232 | |
| 233 /* catch jumping here */ | |
| 234 if (setjmp(wpng_error.jmpbuf)) { | |
| 235 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 236 (void) fm_close(fmp, symbol); | |
| 237 return ZINT_ERROR_MEMORY; | |
| 238 } | |
| 239 | |
| 240 /* Set our output functions */ | |
| 241 png_set_write_fn(png_ptr, fmp, wpng_write, wpng_flush); | |
| 242 | |
| 243 /* set compression */ | |
| 244 png_set_compression_level(png_ptr, 9); | |
| 245 | |
| 246 /* Compression strategy can make a difference */ | |
| 247 compression_strategy = wpng_guess_compression_strategy(symbol, pixelbuf); | |
| 248 if (compression_strategy != Z_DEFAULT_STRATEGY) { | |
| 249 png_set_compression_strategy(png_ptr, compression_strategy); | |
| 250 } | |
| 251 | |
| 252 if (symbol->dpmm) { | |
| 253 int resolution = (int) roundf(stripf(symbol->dpmm * 1000.0f)); /* pixels per metre */ | |
| 254 png_set_pHYs(png_ptr, info_ptr, resolution, resolution, PNG_RESOLUTION_METER); | |
| 255 } | |
| 256 | |
| 257 /* set Header block */ | |
| 258 png_set_IHDR(png_ptr, info_ptr, symbol->bitmap_width, symbol->bitmap_height, | |
| 259 bit_depth, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, | |
| 260 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | |
| 261 | |
| 262 png_set_PLTE(png_ptr, info_ptr, palette, num_palette); | |
| 263 if (num_trans) { | |
| 264 png_set_tRNS(png_ptr, info_ptr, trans_alpha, num_trans, NULL); | |
| 265 } | |
| 266 | |
| 267 /* write all chunks up to (but not including) first IDAT */ | |
| 268 png_write_info(png_ptr, info_ptr); | |
| 269 | |
| 270 /* Pixel Plotting */ | |
| 271 pb = pixelbuf; | |
| 272 if (bit_depth == 1) { | |
| 273 for (row = 0; row < symbol->bitmap_height; row++) { | |
| 274 if (row && memcmp(pb, pb - symbol->bitmap_width, symbol->bitmap_width) == 0) { | |
| 275 pb += symbol->bitmap_width; | |
| 276 } else { | |
| 277 unsigned char *image_data = outdata; | |
| 278 for (column = 0; column < symbol->bitmap_width; column += 8, image_data++) { | |
| 279 unsigned char byte = 0; | |
| 280 for (i = 0; i < 8 && column + i < symbol->bitmap_width; i++, pb++) { | |
| 281 byte |= map[*pb] << (7 - i); | |
| 282 } | |
| 283 *image_data = byte; | |
| 284 } | |
| 285 } | |
| 286 /* write row contents to file */ | |
| 287 png_write_row(png_ptr, outdata); | |
| 288 } | |
| 289 } else { /* Bit depth 4 */ | |
| 290 for (row = 0; row < symbol->bitmap_height; row++) { | |
| 291 if (row && memcmp(pb, pb - symbol->bitmap_width, symbol->bitmap_width) == 0) { | |
| 292 pb += symbol->bitmap_width; | |
| 293 } else { | |
| 294 unsigned char *image_data = outdata; | |
| 295 for (column = 0; column < symbol->bitmap_width; column += 2, image_data++) { | |
| 296 unsigned char byte = map[*pb++] << 4; | |
| 297 if (column + 1 < symbol->bitmap_width) { | |
| 298 byte |= map[*pb++]; | |
| 299 } | |
| 300 *image_data = byte; | |
| 301 } | |
| 302 } | |
| 303 /* write row contents to file */ | |
| 304 png_write_row(png_ptr, outdata); | |
| 305 } | |
| 306 } | |
| 307 | |
| 308 /* End the file */ | |
| 309 png_write_end(png_ptr, NULL); | |
| 310 | |
| 311 /* make sure we have disengaged */ | |
| 312 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 313 | |
| 314 if (fm_error(fmp)) { | |
| 315 errtxtf(0, symbol, 638, "Incomplete write of PNG output (%1$d: %2$s)", fmp->err, strerror(fmp->err)); | |
| 316 (void) fm_close(fmp, symbol); | |
| 317 return ZINT_ERROR_FILE_WRITE; | |
| 318 } | |
| 319 | |
| 320 if (!fm_close(fmp, symbol)) { | |
| 321 return errtxtf(ZINT_ERROR_FILE_WRITE, symbol, 960, "Failure on closing PNG output file (%1$d: %2$s)", | |
| 322 fmp->err, strerror(fmp->err)); | |
| 323 } | |
| 324 | |
| 325 return 0; | |
| 326 } | |
| 327 /* vim: set ts=4 sw=4 et : */ | |
| 328 #else | |
| 329 #if defined(__clang__) | |
| 330 /* Suppresses clang-tidy-18 "clang-diagnostic-empty-translation-unit" */ | |
| 331 typedef int wpng_make_clang_tidy_compilers_happy; | |
| 332 #endif | |
| 333 #endif /* ZINT_NO_PNG */ |
