Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/zint/backend/raster.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 /* raster.c - Handles output to raster files */ | |
| 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 #include <assert.h> | |
| 34 #include <math.h> | |
| 35 | |
| 36 #ifdef _MSC_VER | |
| 37 #include <fcntl.h> | |
| 38 #include <io.h> | |
| 39 #endif /* _MSC_VER */ | |
| 40 | |
| 41 #include "common.h" | |
| 42 #include "output.h" | |
| 43 #include "zfiletypes.h" | |
| 44 | |
| 45 #include "raster_font.h" /* Font for human readable text */ | |
| 46 | |
| 47 #define DEFAULT_INK '1' /* Black */ | |
| 48 #define DEFAULT_PAPER '0' /* White */ | |
| 49 | |
| 50 /* Flags for `draw_string()`/`draw_letter()` */ | |
| 51 #define ZFONT_HALIGN_CENTRE 0 | |
| 52 #define ZFONT_HALIGN_LEFT 1 | |
| 53 #define ZFONT_HALIGN_RIGHT 2 | |
| 54 #define ZFONT_UPCEAN_TEXT 4 /* Helper flag to indicate dealing with EAN/UPC */ | |
| 55 | |
| 56 #ifndef ZINT_NO_PNG | |
| 57 INTERNAL int png_pixel_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf); | |
| 58 #endif /* ZINT_NO_PNG */ | |
| 59 INTERNAL int bmp_pixel_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf); | |
| 60 INTERNAL int pcx_pixel_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf); | |
| 61 INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf); | |
| 62 INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf); | |
| 63 | |
| 64 static const char ultra_colour[] = "0CBMRYGKW"; | |
| 65 | |
| 66 /* Wrapper to pre-check `size` on `malloc()` isn't too big (`prev_size` given if doing 2nd `malloc()` in a row) */ | |
| 67 static void *raster_malloc(size_t size, size_t prev_size) { | |
| 68 /* Check for large image `malloc`s, which produce very large files most systems can't handle anyway */ | |
| 69 /* Also `malloc()` on Linux will (usually) succeed regardless of request, and then get untrappably killed on | |
| 70 access by OOM killer if too much, so this is a crude mitigation */ | |
| 71 if (size + prev_size < size /*Overflow check*/ || size + prev_size > 0x40000000 /*1GB*/) { | |
| 72 return NULL; | |
| 73 } | |
| 74 return malloc(size); | |
| 75 } | |
| 76 | |
| 77 static int buffer_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf) { | |
| 78 /* Place pixelbuffer into symbol */ | |
| 79 unsigned char alpha[2]; | |
| 80 unsigned char map[91][3] = { | |
| 81 {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, /* 0x00-0F */ | |
| 82 {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, /* 0x10-1F */ | |
| 83 {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, /* 0x20-2F */ | |
| 84 {0} /*bg*/, {0} /*fg*/, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, /* 0-9 */ | |
| 85 {0}, {0}, {0}, {0}, {0}, {0}, {0}, /* :;<=>?@ */ | |
| 86 {0}, { 0, 0, 0xff } /*Blue*/, { 0, 0xff, 0xff } /*Cyan*/, {0}, {0}, {0}, { 0, 0xff, 0 } /*Green*/, /* A-G */ | |
| 87 {0}, {0}, {0}, { 0, 0, 0 } /*blacK*/, {0}, { 0xff, 0, 0xff } /*Magenta*/, {0}, /* H-N */ | |
| 88 {0}, {0}, {0}, { 0xff, 0, 0 } /*Red*/, {0}, {0}, {0}, {0}, /* O-V */ | |
| 89 { 0xff, 0xff, 0xff } /*White*/, {0}, { 0xff, 0xff, 0 } /*Yellow*/, {0} /* W-Z */ | |
| 90 }; | |
| 91 int row; | |
| 92 int plot_alpha = 0; | |
| 93 const size_t bm_bitmap_width = (size_t) symbol->bitmap_width * 3; | |
| 94 const size_t bm_bitmap_size = bm_bitmap_width * symbol->bitmap_height; | |
| 95 | |
| 96 if (out_colour_get_rgb(symbol->fgcolour, &map[DEFAULT_INK][0], &map[DEFAULT_INK][1], &map[DEFAULT_INK][2], | |
| 97 &alpha[0])) { | |
| 98 plot_alpha = 1; | |
| 99 } | |
| 100 if (out_colour_get_rgb(symbol->bgcolour, &map[DEFAULT_PAPER][0], &map[DEFAULT_PAPER][1], &map[DEFAULT_PAPER][2], | |
| 101 &alpha[1])) { | |
| 102 plot_alpha = 1; | |
| 103 } | |
| 104 | |
| 105 /* Free any previous bitmap */ | |
| 106 if (symbol->bitmap != NULL) { | |
| 107 free(symbol->bitmap); | |
| 108 symbol->bitmap = NULL; | |
| 109 } | |
| 110 if (symbol->alphamap != NULL) { | |
| 111 free(symbol->alphamap); | |
| 112 symbol->alphamap = NULL; | |
| 113 } | |
| 114 | |
| 115 if (!(symbol->bitmap = (unsigned char *) raster_malloc(bm_bitmap_size, 0 /*prev_size*/))) { | |
| 116 return errtxt(ZINT_ERROR_MEMORY, symbol, 661, "Insufficient memory for bitmap buffer"); | |
| 117 } | |
| 118 | |
| 119 if (plot_alpha) { | |
| 120 const size_t alpha_size = (size_t) symbol->bitmap_width * symbol->bitmap_height; | |
| 121 if (!(symbol->alphamap = (unsigned char *) raster_malloc(alpha_size, bm_bitmap_size))) { | |
| 122 return errtxt(ZINT_ERROR_MEMORY, symbol, 662, "Insufficient memory for alphamap buffer"); | |
| 123 } | |
| 124 for (row = 0; row < symbol->bitmap_height; row++) { | |
| 125 size_t p = (size_t) symbol->bitmap_width * row; | |
| 126 const unsigned char *pb = pixelbuf + p; | |
| 127 unsigned char *bitmap = symbol->bitmap + p * 3; | |
| 128 if (row && memcmp(pb, pb - symbol->bitmap_width, symbol->bitmap_width) == 0) { | |
| 129 memcpy(bitmap, bitmap - bm_bitmap_width, bm_bitmap_width); | |
| 130 memcpy(symbol->alphamap + p, symbol->alphamap + p - symbol->bitmap_width, symbol->bitmap_width); | |
| 131 } else { | |
| 132 const size_t pe = p + symbol->bitmap_width; | |
| 133 for (; p < pe; p++, bitmap += 3) { | |
| 134 memcpy(bitmap, map[pixelbuf[p]], 3); | |
| 135 symbol->alphamap[p] = alpha[pixelbuf[p] == DEFAULT_PAPER]; | |
| 136 } | |
| 137 } | |
| 138 } | |
| 139 } else { | |
| 140 for (row = 0; row < symbol->bitmap_height; row++) { | |
| 141 const size_t r = (size_t) symbol->bitmap_width * row; | |
| 142 const unsigned char *pb = pixelbuf + r; | |
| 143 unsigned char *bitmap = symbol->bitmap + r * 3; | |
| 144 if (row && memcmp(pb, pb - symbol->bitmap_width, symbol->bitmap_width) == 0) { | |
| 145 memcpy(bitmap, bitmap - bm_bitmap_width, bm_bitmap_width); | |
| 146 } else { | |
| 147 const unsigned char *const pbe = pb + symbol->bitmap_width; | |
| 148 for (; pb < pbe; pb++, bitmap += 3) { | |
| 149 memcpy(bitmap, map[*pb], 3); | |
| 150 } | |
| 151 } | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 return 0; | |
| 156 } | |
| 157 | |
| 158 static int save_raster_image_to_file(struct zint_symbol *symbol, const int image_height, const int image_width, | |
| 159 unsigned char *pixelbuf, int rotate_angle, const int file_type) { | |
| 160 int error_number; | |
| 161 int row, column; | |
| 162 | |
| 163 unsigned char *rotated_pixbuf = pixelbuf; | |
| 164 | |
| 165 /* Suppress clang-analyzer-core.UndefinedBinaryOperatorResult warning */ | |
| 166 assert(rotate_angle == 0 || rotate_angle == 90 || rotate_angle == 180 || rotate_angle == 270); | |
| 167 switch (rotate_angle) { | |
| 168 case 0: | |
| 169 case 180: | |
| 170 symbol->bitmap_width = image_width; | |
| 171 symbol->bitmap_height = image_height; | |
| 172 break; | |
| 173 case 90: | |
| 174 case 270: | |
| 175 symbol->bitmap_width = image_height; | |
| 176 symbol->bitmap_height = image_width; | |
| 177 break; | |
| 178 } | |
| 179 | |
| 180 if (rotate_angle) { | |
| 181 if (!(rotated_pixbuf = (unsigned char *) raster_malloc((size_t) image_width * image_height, | |
| 182 0 /*prev_size*/))) { | |
| 183 return errtxt(ZINT_ERROR_MEMORY, symbol, 650, "Insufficient memory for pixel buffer"); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 /* Rotate image before plotting */ | |
| 188 switch (rotate_angle) { | |
| 189 case 0: /* Plot the right way up */ | |
| 190 /* Nothing to do */ | |
| 191 break; | |
| 192 case 90: /* Plot 90 degrees clockwise */ | |
| 193 for (row = 0; row < image_width; row++) { | |
| 194 const size_t h_offset = (size_t) image_height * row; | |
| 195 for (column = 0; column < image_height; column++) { | |
| 196 const size_t w_offset = (size_t) image_width * (image_height - column - 1); | |
| 197 rotated_pixbuf[h_offset + column] = *(pixelbuf + w_offset + row); | |
| 198 } | |
| 199 } | |
| 200 break; | |
| 201 case 180: /* Plot upside down */ | |
| 202 for (row = 0; row < image_height; row++) { | |
| 203 const size_t w_offset = (size_t) image_width * row; | |
| 204 const size_t wh_offset = (size_t) image_width * (image_height - row - 1); | |
| 205 for (column = 0; column < image_width; column++) { | |
| 206 rotated_pixbuf[w_offset + column] = *(pixelbuf + wh_offset + (image_width - column - 1)); | |
| 207 } | |
| 208 } | |
| 209 break; | |
| 210 case 270: /* Plot 90 degrees anti-clockwise */ | |
| 211 for (row = 0; row < image_width; row++) { | |
| 212 const size_t h_offset = (size_t) image_height * row; | |
| 213 for (column = 0; column < image_height; column++) { | |
| 214 const size_t w_offset = (size_t) image_width * column; | |
| 215 rotated_pixbuf[h_offset + column] = *(pixelbuf + w_offset + (image_width - row - 1)); | |
| 216 } | |
| 217 } | |
| 218 break; | |
| 219 } | |
| 220 | |
| 221 switch (file_type) { | |
| 222 case OUT_BUFFER: | |
| 223 if (symbol->output_options & OUT_BUFFER_INTERMEDIATE) { | |
| 224 if (symbol->bitmap != NULL) { | |
| 225 free(symbol->bitmap); | |
| 226 symbol->bitmap = NULL; | |
| 227 } | |
| 228 if (symbol->alphamap != NULL) { | |
| 229 free(symbol->alphamap); | |
| 230 symbol->alphamap = NULL; | |
| 231 } | |
| 232 symbol->bitmap = rotated_pixbuf; | |
| 233 rotate_angle = 0; /* Suppress freeing buffer if rotated */ | |
| 234 error_number = 0; | |
| 235 } else { | |
| 236 error_number = buffer_plot(symbol, rotated_pixbuf); | |
| 237 } | |
| 238 break; | |
| 239 case OUT_PNG_FILE: | |
| 240 #ifndef ZINT_NO_PNG | |
| 241 error_number = png_pixel_plot(symbol, rotated_pixbuf); | |
| 242 #else | |
| 243 error_number = ZINT_ERROR_INVALID_OPTION; | |
| 244 #endif | |
| 245 break; | |
| 246 #if defined(__GNUC__) && !defined(__clang__) && defined(NDEBUG) && defined(ZINT_NO_PNG) | |
| 247 /* Suppress gcc warning ‘<unknown>’ may be used uninitialized - only when Release and ZINT_NO_PNG */ | |
| 248 #pragma GCC diagnostic push | |
| 249 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | |
| 250 #endif | |
| 251 case OUT_PCX_FILE: | |
| 252 error_number = pcx_pixel_plot(symbol, rotated_pixbuf); | |
| 253 break; | |
| 254 case OUT_GIF_FILE: | |
| 255 error_number = gif_pixel_plot(symbol, rotated_pixbuf); | |
| 256 break; | |
| 257 case OUT_TIF_FILE: | |
| 258 error_number = tif_pixel_plot(symbol, rotated_pixbuf); | |
| 259 break; | |
| 260 default: | |
| 261 error_number = bmp_pixel_plot(symbol, rotated_pixbuf); | |
| 262 break; | |
| 263 #if defined(__GNUC__) && !defined(__clang__) && defined(NDEBUG) && defined(ZINT_NO_PNG) | |
| 264 #pragma GCC diagnostic pop | |
| 265 #endif | |
| 266 } | |
| 267 | |
| 268 if (rotate_angle) { | |
| 269 free(rotated_pixbuf); | |
| 270 } | |
| 271 return error_number; | |
| 272 } | |
| 273 | |
| 274 /* Helper to check point within bounds before setting */ | |
| 275 static void draw_pt(unsigned char *buf, const int buf_width, const int buf_height, | |
| 276 const int x, const int y, const int fill) { | |
| 277 if (x >= 0 && x < buf_width && y >= 0 && y < buf_height) { | |
| 278 buf[y * buf_width + x] = fill; | |
| 279 } | |
| 280 } | |
| 281 | |
| 282 /* Draw the first line of a bar, to be completed by `copy_bar_line()`; more performant than multiple `draw_bar()`s */ | |
| 283 static void draw_bar_line(unsigned char *pixelbuf, const int xpos, const int xlen, const int ypos, | |
| 284 const int image_width, const int fill) { | |
| 285 unsigned char *pb = pixelbuf + ((size_t) image_width * ypos) + xpos; | |
| 286 | |
| 287 memset(pb, fill, xlen); | |
| 288 } | |
| 289 | |
| 290 /* Fill out a bar code row by copying the first line (called after multiple `draw_bar_line()`s) */ | |
| 291 static void copy_bar_line(unsigned char *pixelbuf, const int xpos, const int xlen, const int ypos, const int ylen, | |
| 292 const int image_width, const int image_height) { | |
| 293 int y; | |
| 294 const int ye = ypos + ylen > image_height ? image_height : ypos + ylen; /* Defensive, should never happen */ | |
| 295 unsigned char *pb = pixelbuf + ((size_t) image_width * ypos) + xpos; | |
| 296 | |
| 297 assert(ypos + ylen <= image_height); /* Trigger assert if "should never happen" happens */ | |
| 298 | |
| 299 for (y = ypos + 1; y < ye; y++) { | |
| 300 memcpy(pixelbuf + ((size_t) image_width * y) + xpos, pb, xlen); | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 /* Draw a rectangle */ | |
| 305 static void draw_bar(unsigned char *pixelbuf, const int xpos, const int xlen, const int ypos, const int ylen, | |
| 306 const int image_width, const int image_height, const int fill) { | |
| 307 int y; | |
| 308 const int ye = ypos + ylen > image_height ? image_height : ypos + ylen; /* Defensive, should never happen */ | |
| 309 unsigned char *pb = pixelbuf + ((size_t) image_width * ypos) + xpos; | |
| 310 | |
| 311 assert(ypos + ylen <= image_height); /* Trigger assert if "should never happen" happens */ | |
| 312 | |
| 313 for (y = ypos; y < ye; y++, pb += image_width) { | |
| 314 memset(pb, fill, xlen); | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 /* Put a letter into a position */ | |
| 319 static void draw_letter(unsigned char *pixelbuf, const unsigned char letter, int xposn, const int yposn, | |
| 320 const int textflags, const int image_width, const int image_height, const int si) { | |
| 321 int glyph_no; | |
| 322 int x, y; | |
| 323 int max_x, max_y; | |
| 324 const raster_font_item *font_table; | |
| 325 int bold = 0; | |
| 326 unsigned glyph_mask; | |
| 327 int font_y; | |
| 328 int half_si; | |
| 329 int odd_si; | |
| 330 unsigned char *linePtr; | |
| 331 int x_start = 0; | |
| 332 | |
| 333 if (letter < 33) { | |
| 334 return; | |
| 335 } | |
| 336 | |
| 337 if ((letter >= 127) && (letter < 161)) { | |
| 338 return; | |
| 339 } | |
| 340 | |
| 341 if (yposn < 0) { /* Allow xposn < 0, dealt with below */ | |
| 342 return; | |
| 343 } | |
| 344 | |
| 345 half_si = si / 2; | |
| 346 odd_si = si & 1; | |
| 347 | |
| 348 if (letter > 127) { | |
| 349 glyph_no = letter - 67; /* 161 - (127 - 33) */ | |
| 350 } else { | |
| 351 glyph_no = letter - 33; | |
| 352 } | |
| 353 | |
| 354 if (textflags & ZFONT_UPCEAN_TEXT) { /* Needs to be before SMALL_TEXT check */ | |
| 355 /* No bold for UPCEAN */ | |
| 356 if (textflags & SMALL_TEXT) { | |
| 357 font_table = upcean_small_font; | |
| 358 max_x = UPCEAN_SMALL_FONT_WIDTH; | |
| 359 max_y = UPCEAN_SMALL_FONT_HEIGHT; | |
| 360 } else { | |
| 361 font_table = upcean_font; | |
| 362 max_x = UPCEAN_FONT_WIDTH; | |
| 363 max_y = UPCEAN_FONT_HEIGHT; | |
| 364 } | |
| 365 glyph_no = letter - '0'; | |
| 366 } else if (textflags & SMALL_TEXT) { /* small font 5x9 */ | |
| 367 /* No bold for small */ | |
| 368 max_x = SMALL_FONT_WIDTH; | |
| 369 max_y = SMALL_FONT_HEIGHT; | |
| 370 font_table = small_font; | |
| 371 } else if (textflags & BOLD_TEXT) { /* bold font -> regular font + 1 */ | |
| 372 max_x = NORMAL_FONT_WIDTH + 1; | |
| 373 max_y = NORMAL_FONT_HEIGHT; | |
| 374 font_table = ascii_font; | |
| 375 bold = 1; | |
| 376 } else { /* regular font 7x14 */ | |
| 377 max_x = NORMAL_FONT_WIDTH; | |
| 378 max_y = NORMAL_FONT_HEIGHT; | |
| 379 font_table = ascii_font; | |
| 380 } | |
| 381 glyph_mask = ((unsigned) 1) << (max_x - 1); | |
| 382 font_y = glyph_no * max_y; | |
| 383 | |
| 384 if (xposn < 0) { | |
| 385 x_start = -xposn; | |
| 386 xposn = 0; | |
| 387 } | |
| 388 | |
| 389 if (yposn + max_y > image_height) { | |
| 390 max_y = image_height - yposn; | |
| 391 } | |
| 392 | |
| 393 linePtr = pixelbuf + ((size_t) yposn * image_width) + xposn; | |
| 394 for (y = 0; y < max_y; y++) { | |
| 395 int x_si, y_si; | |
| 396 unsigned char *pixelPtr = linePtr; /* Avoid warning */ | |
| 397 for (y_si = 0; y_si < half_si; y_si++) { | |
| 398 int extra_dot = 0; | |
| 399 unsigned char *const maxPtr = linePtr + image_width - xposn; | |
| 400 pixelPtr = linePtr; | |
| 401 for (x = x_start; x < max_x && pixelPtr < maxPtr; x++) { | |
| 402 unsigned set = font_table[font_y + y] & (glyph_mask >> x); | |
| 403 for (x_si = 0; x_si < half_si && pixelPtr < maxPtr; x_si++) { | |
| 404 if (set) { | |
| 405 *pixelPtr = DEFAULT_INK; | |
| 406 extra_dot = bold; | |
| 407 } else if (extra_dot) { | |
| 408 *pixelPtr = DEFAULT_INK; | |
| 409 extra_dot = 0; | |
| 410 } | |
| 411 pixelPtr++; | |
| 412 } | |
| 413 if (pixelPtr < maxPtr && odd_si && (x & 1)) { | |
| 414 if (set) { | |
| 415 *pixelPtr = DEFAULT_INK; | |
| 416 } | |
| 417 pixelPtr++; | |
| 418 } | |
| 419 } | |
| 420 if (pixelPtr < maxPtr && extra_dot) { | |
| 421 *pixelPtr++ = DEFAULT_INK; | |
| 422 } | |
| 423 linePtr += image_width; | |
| 424 } | |
| 425 if (odd_si && (y & 1)) { | |
| 426 memcpy(linePtr, linePtr - image_width, pixelPtr - (linePtr - image_width)); | |
| 427 linePtr += image_width; | |
| 428 } | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 /* Plot a string into the pixel buffer */ | |
| 433 static void draw_string(unsigned char *pixelbuf, const unsigned char input_string[], int length, const int xposn, | |
| 434 const int yposn, const int textflags, const int image_width, const int image_height, const int si) { | |
| 435 int i, string_left_hand, letter_width, letter_gap; | |
| 436 const int half_si = si / 2, odd_si = si & 1; | |
| 437 int x_incr; | |
| 438 | |
| 439 if (textflags & ZFONT_UPCEAN_TEXT) { /* Needs to be before SMALL_TEXT check */ | |
| 440 /* No bold for UPCEAN */ | |
| 441 letter_width = textflags & SMALL_TEXT ? UPCEAN_SMALL_FONT_WIDTH : UPCEAN_FONT_WIDTH; | |
| 442 letter_gap = 4; | |
| 443 } else if (textflags & SMALL_TEXT) { /* small font 5x9 */ | |
| 444 /* No bold for small */ | |
| 445 letter_width = SMALL_FONT_WIDTH; | |
| 446 letter_gap = 0; | |
| 447 } else if (textflags & BOLD_TEXT) { /* bold font -> width of the regular font + 1 extra dot + 1 extra space */ | |
| 448 letter_width = NORMAL_FONT_WIDTH + 1; | |
| 449 letter_gap = 1; | |
| 450 } else { /* regular font 7x15 */ | |
| 451 letter_width = NORMAL_FONT_WIDTH; | |
| 452 letter_gap = 0; | |
| 453 } | |
| 454 letter_width += letter_gap; | |
| 455 | |
| 456 if (length == -1) { | |
| 457 length = (int) ustrlen(input_string); | |
| 458 } | |
| 459 | |
| 460 if (textflags & ZFONT_HALIGN_LEFT) { | |
| 461 string_left_hand = xposn; | |
| 462 } else if (textflags & ZFONT_HALIGN_RIGHT) { | |
| 463 string_left_hand = xposn - ((letter_width * length - letter_gap) * half_si); | |
| 464 } else { | |
| 465 string_left_hand = xposn - (int) roundf(((letter_width * length - letter_gap) * half_si) / 2.0f); | |
| 466 } | |
| 467 if (odd_si) { | |
| 468 string_left_hand -= (letter_width * length - letter_gap) / 4; | |
| 469 } | |
| 470 for (i = 0; i < length; i++) { | |
| 471 x_incr = i * letter_width * half_si; | |
| 472 if (odd_si) { | |
| 473 x_incr += i * letter_width / 2; | |
| 474 } | |
| 475 draw_letter(pixelbuf, input_string[i], string_left_hand + x_incr, yposn, textflags, image_width, image_height, | |
| 476 si); | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 /* Draw disc using x² + y² <= r² */ | |
| 481 static void draw_circle(unsigned char *pixelbuf, const int image_width, const int image_height, | |
| 482 const int x0, const int y0, const float radius, const char fill) { | |
| 483 int x, y; | |
| 484 const int radius_i = (int) radius; | |
| 485 const int radius_squared = radius_i * radius_i; | |
| 486 | |
| 487 for (y = -radius_i; y <= radius_i; y++) { | |
| 488 const int y_squared = y * y; | |
| 489 for (x = -radius_i; x <= radius_i; x++) { | |
| 490 if ((x * x) + y_squared <= radius_squared) { | |
| 491 draw_pt(pixelbuf, image_width, image_height, x0 + x, y0 + y, fill); | |
| 492 } | |
| 493 } | |
| 494 } | |
| 495 } | |
| 496 | |
| 497 /* Helper for `draw_mp_circle()` to draw horizontal filler lines within disc */ | |
| 498 static void draw_mp_circle_lines(unsigned char *pixelbuf, const int image_width, const int image_height, | |
| 499 const int x0, const int y0, const int x, const int y, const int fill) { | |
| 500 int i; | |
| 501 for (i = x0 - x; i <= x0 + x; i++) { | |
| 502 draw_pt(pixelbuf, image_width, image_height, i, y0 + y, fill); /* (-x, y) to (x, y) */ | |
| 503 draw_pt(pixelbuf, image_width, image_height, i, y0 - y, fill); /* (-x, -y) to (x, -y) */ | |
| 504 } | |
| 505 for (i = x0 - y; i <= x0 + y; i++) { | |
| 506 draw_pt(pixelbuf, image_width, image_height, i, y0 + x, fill); /* (-y, x) to (y, x) */ | |
| 507 draw_pt(pixelbuf, image_width, image_height, i, y0 - x, fill); /* (-y, -x) to (y, -x) */ | |
| 508 } | |
| 509 } | |
| 510 | |
| 511 /* Draw disc using Midpoint Circle Algorithm. Using this for MaxiCode rather than `draw_circle()` because it gives a | |
| 512 * flatter circumference with no single pixel peaks, similar to Figures J3 and J6 in ISO/IEC 16023:2000. | |
| 513 * Taken from https://rosettacode.org/wiki/Bitmap/Midpoint_circle_algorithm#C | |
| 514 * "Content is available under GNU Free Documentation License 1.2 unless otherwise noted." | |
| 515 * https://www.gnu.org/licenses/old-licenses/fdl-1.2.html */ | |
| 516 static void draw_mp_circle(unsigned char *pixelbuf, const int image_width, const int image_height, | |
| 517 const int x0, const int y0, const int r, const int fill) { | |
| 518 /* Using top RHS octant from (0, r) going clockwise, so fast direction is x (i.e. always incremented) */ | |
| 519 int f = 1 - r; | |
| 520 int ddF_x = 0; | |
| 521 int ddF_y = -2 * r; | |
| 522 int x = 0; | |
| 523 int y = r; | |
| 524 | |
| 525 draw_mp_circle_lines(pixelbuf, image_width, image_height, x0, y0, x, y, fill); | |
| 526 | |
| 527 while (x < y) { | |
| 528 if (f >= 0) { | |
| 529 y--; | |
| 530 ddF_y += 2; | |
| 531 f += ddF_y; | |
| 532 } | |
| 533 x++; | |
| 534 ddF_x += 2; | |
| 535 f += ddF_x + 1; | |
| 536 draw_mp_circle_lines(pixelbuf, image_width, image_height, x0, y0, x, y, fill); | |
| 537 } | |
| 538 } | |
| 539 | |
| 540 /* Draw central bullseye finder in Maxicode symbols */ | |
| 541 static void draw_bullseye(unsigned char *pixelbuf, const int image_width, const int image_height, | |
| 542 const int hex_width, const int hex_height, const int hx_start, const int hx_end, | |
| 543 const int hex_image_height, const int xoffset_si, const int yoffset_si) { | |
| 544 | |
| 545 /* ISO/IEC 16023:2000 4.11.4 and 4.2.1.1 */ | |
| 546 | |
| 547 /* 14W right from leftmost centre = 14.5X */ | |
| 548 const int x = (int) (14.5f * hex_width - hx_start + xoffset_si); | |
| 549 /* 16Y above bottom-most centre = halfway */ | |
| 550 const int y = (int) ceilf(hex_image_height / 2 + yoffset_si); | |
| 551 | |
| 552 const int r1 = (int) ceilf(hex_height / 2.0f); /* Inner diameter is hex_height (V) */ | |
| 553 /* Total finder diameter is 9X, so radial increment for 5 radii r2 to r6 is ((9X - r1) / 5) / 2 */ | |
| 554 int r_incr = ((hex_width * 9 - r1) / 5) / 2; | |
| 555 /* Fudge increment to lessen overlapping of finder with top/bottom of hexagons due to rounding errors */ | |
| 556 if (r_incr > hx_end) { | |
| 557 r_incr -= hx_end; | |
| 558 } | |
| 559 | |
| 560 draw_mp_circle(pixelbuf, image_width, image_height, x, y, r1 + r_incr * 5, DEFAULT_INK); | |
| 561 draw_mp_circle(pixelbuf, image_width, image_height, x, y, r1 + r_incr * 4, DEFAULT_PAPER); | |
| 562 draw_mp_circle(pixelbuf, image_width, image_height, x, y, r1 + r_incr * 3, DEFAULT_INK); | |
| 563 draw_mp_circle(pixelbuf, image_width, image_height, x, y, r1 + r_incr * 2, DEFAULT_PAPER); | |
| 564 draw_mp_circle(pixelbuf, image_width, image_height, x, y, r1 + r_incr, DEFAULT_INK); | |
| 565 draw_mp_circle(pixelbuf, image_width, image_height, x, y, r1, DEFAULT_PAPER); | |
| 566 } | |
| 567 | |
| 568 /* Put a hexagon into the pixel buffer */ | |
| 569 static void draw_hexagon(unsigned char *pixelbuf, const int image_width, const int image_height, | |
| 570 const unsigned char *scaled_hexagon, const int hex_width, const int hex_height, | |
| 571 const int xposn, const int yposn) { | |
| 572 int i, j; | |
| 573 | |
| 574 for (i = 0; i < hex_height; i++) { | |
| 575 for (j = 0; j < hex_width; j++) { | |
| 576 if (scaled_hexagon[(i * hex_width) + j] == DEFAULT_INK) { | |
| 577 draw_pt(pixelbuf, image_width, image_height, xposn + j, yposn + i, DEFAULT_INK); | |
| 578 } | |
| 579 } | |
| 580 } | |
| 581 } | |
| 582 | |
| 583 /* Bresenham's line algorithm https://en.wikipedia.org/wiki/Bresenham's_line_algorithm | |
| 584 * Creative Commons Attribution-ShareAlike License | |
| 585 * https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License */ | |
| 586 static void plot_hexline(unsigned char *scaled_hexagon, const int hex_width, const int hex_height, | |
| 587 int start_x, int start_y, const int end_x, const int end_y) { | |
| 588 const int dx = abs(end_x - start_x); | |
| 589 const int sx = start_x < end_x ? 1 : -1; | |
| 590 const int dy = -abs(end_y - start_y); | |
| 591 const int sy = start_y < end_y ? 1 : -1; | |
| 592 int e_xy = dx + dy; | |
| 593 | |
| 594 for (;;) { | |
| 595 int e2; | |
| 596 draw_pt(scaled_hexagon, hex_width, hex_height, start_x, start_y, DEFAULT_INK); | |
| 597 if (start_x == end_x && start_y == end_y) { | |
| 598 break; | |
| 599 } | |
| 600 e2 = 2 * e_xy; | |
| 601 if (e2 >= dy) { /* e_xy+e_x > 0 */ | |
| 602 e_xy += dy; | |
| 603 start_x += sx; | |
| 604 } | |
| 605 if (e2 <= dx) { /* e_xy+e_y < 0 */ | |
| 606 e_xy += dx; | |
| 607 start_y += sy; | |
| 608 } | |
| 609 } | |
| 610 } | |
| 611 | |
| 612 /* Create a hexagon shape and fill it */ | |
| 613 static void plot_hexagon(unsigned char *scaled_hexagon, const int hex_width, const int hex_height, | |
| 614 const int hx_start, const int hy_start, const int hx_end, const int hy_end) { | |
| 615 int line, i; | |
| 616 int not_top; | |
| 617 | |
| 618 const int hx_width = hex_width - hx_start - hx_end; | |
| 619 const int hy_height = hex_height - hx_start - hx_end; | |
| 620 | |
| 621 const int hx_width_odd = hx_width & 1; | |
| 622 const int hy_height_odd = hy_height & 1; | |
| 623 | |
| 624 const int hx_radius = hx_width / 2; | |
| 625 const int hy_radius = hy_height / 2; | |
| 626 | |
| 627 /* To ensure symmetry, draw top left quadrant first, then copy/flip to other quadrants */ | |
| 628 | |
| 629 int start_y = hy_start + (hy_radius + 1) / 2; | |
| 630 int end_x = hx_start + hx_radius; | |
| 631 if (hx_radius > 2 && (hx_radius < 10 || !hx_width_odd)) { | |
| 632 /* Line drawing matches examples in ISO/IEC 16023:2000 Annexe J if point just to the left of end midpoint */ | |
| 633 end_x--; | |
| 634 } | |
| 635 | |
| 636 /* Plot line of top left quadrant */ | |
| 637 plot_hexline(scaled_hexagon, hex_width, hex_height, hx_start, start_y, end_x, hy_start); | |
| 638 | |
| 639 /* Fill to right */ | |
| 640 not_top = 0; | |
| 641 for (line = hy_start; line < hy_start + hy_radius + hy_height_odd; line++) { | |
| 642 int first = -1; | |
| 643 for (i = hx_start; i < hx_start + hx_radius + hx_width_odd; i++) { | |
| 644 if (first != -1) { | |
| 645 scaled_hexagon[(hex_width * line) + i] = DEFAULT_INK; | |
| 646 not_top = 1; | |
| 647 } else if (scaled_hexagon[(hex_width * line) + i] == DEFAULT_INK) { | |
| 648 first = i + 1; | |
| 649 } | |
| 650 } | |
| 651 if (not_top && first == -1) { /* Fill empty lines at bottom */ | |
| 652 for (i = hx_start; i < hx_start + hx_radius + hx_width_odd; i++) { | |
| 653 scaled_hexagon[(hex_width * line) + i] = DEFAULT_INK; | |
| 654 } | |
| 655 } | |
| 656 } | |
| 657 | |
| 658 /* Copy left quadrant to right, flipping horizontally */ | |
| 659 for (line = hy_start; line < hy_start + hy_radius + hy_height_odd; line++) { | |
| 660 for (i = hx_start; i < hx_start + hx_radius + hx_width_odd; i++) { | |
| 661 if (scaled_hexagon[(hex_width * line) + i] == DEFAULT_INK) { | |
| 662 scaled_hexagon[(hex_width * line) + hex_width - hx_end - (i - hx_start + 1)] = DEFAULT_INK; | |
| 663 } | |
| 664 } | |
| 665 } | |
| 666 | |
| 667 /* Copy top to bottom, flipping vertically */ | |
| 668 for (line = hy_start; line < hy_start + hy_radius + hy_height_odd; line++) { | |
| 669 for (i = hx_start; i < hex_width; i++) { | |
| 670 if (scaled_hexagon[(hex_width * line) + i] == DEFAULT_INK) { | |
| 671 scaled_hexagon[(hex_width * (hex_height - hy_end - (line - hy_start + 1))) + i] = DEFAULT_INK; | |
| 672 } | |
| 673 } | |
| 674 } | |
| 675 } | |
| 676 | |
| 677 /* Draw binding or box */ | |
| 678 static void draw_bind_box(const struct zint_symbol *symbol, unsigned char *pixelbuf, | |
| 679 const int xoffset_si, const int yoffset_si, const int symbol_height_si, const int dot_overspill_si, | |
| 680 const int upceanflag, const int textoffset_si, const int image_width, const int image_height, | |
| 681 const int si) { | |
| 682 if (symbol->border_width > 0 && (symbol->output_options & (BARCODE_BOX | BARCODE_BIND | BARCODE_BIND_TOP))) { | |
| 683 const int no_extend = symbol->symbology == BARCODE_CODABLOCKF || symbol->symbology == BARCODE_HIBC_BLOCKF | |
| 684 || symbol->symbology == BARCODE_DPD; | |
| 685 const int horz_outside = is_fixed_ratio(symbol->symbology); | |
| 686 const int bwidth_si = symbol->border_width * si; | |
| 687 int ybind_top = yoffset_si - bwidth_si; | |
| 688 int ybind_bot = yoffset_si + symbol_height_si + dot_overspill_si; | |
| 689 if (horz_outside) { | |
| 690 ybind_top = 0; | |
| 691 ybind_bot = image_height - bwidth_si; | |
| 692 } else if (upceanflag == 2 || upceanflag == 5) { | |
| 693 ybind_top += textoffset_si; | |
| 694 ybind_bot += textoffset_si; | |
| 695 } | |
| 696 /* Horizontal boundary bars */ | |
| 697 if ((symbol->output_options & BARCODE_BOX) || !no_extend) { | |
| 698 /* Box or not CodaBlockF/DPD */ | |
| 699 draw_bar(pixelbuf, 0, image_width, ybind_top, bwidth_si, image_width, image_height, DEFAULT_INK); | |
| 700 if (!(symbol->output_options & BARCODE_BIND_TOP)) { /* Trumps BARCODE_BOX & BARCODE_BIND */ | |
| 701 draw_bar(pixelbuf, 0, image_width, ybind_bot, bwidth_si, image_width, image_height, DEFAULT_INK); | |
| 702 } | |
| 703 } else { | |
| 704 /* CodaBlockF/DPD bind - does not extend over horizontal whitespace */ | |
| 705 const int width_si = symbol->width * si; | |
| 706 draw_bar(pixelbuf, xoffset_si, width_si, ybind_top, bwidth_si, image_width, image_height, DEFAULT_INK); | |
| 707 if (!(symbol->output_options & BARCODE_BIND_TOP)) { /* Trumps BARCODE_BOX & BARCODE_BIND */ | |
| 708 draw_bar(pixelbuf, xoffset_si, width_si, ybind_bot, bwidth_si, image_width, image_height, | |
| 709 DEFAULT_INK); | |
| 710 } | |
| 711 } | |
| 712 if (symbol->output_options & BARCODE_BOX) { | |
| 713 /* Vertical side bars */ | |
| 714 const int xbox_right = image_width - bwidth_si; | |
| 715 int box_top = yoffset_si; | |
| 716 int box_height = symbol_height_si + dot_overspill_si; | |
| 717 if (horz_outside) { | |
| 718 box_top = bwidth_si; | |
| 719 box_height = image_height - bwidth_si * 2; | |
| 720 } else if (upceanflag == 2 || upceanflag == 5) { | |
| 721 box_top += textoffset_si; | |
| 722 } | |
| 723 draw_bar(pixelbuf, 0, bwidth_si, box_top, box_height, image_width, image_height, DEFAULT_INK); | |
| 724 draw_bar(pixelbuf, xbox_right, bwidth_si, box_top, box_height, image_width, image_height, DEFAULT_INK); | |
| 725 } | |
| 726 } | |
| 727 } | |
| 728 | |
| 729 /* Plot a MaxiCode symbol with hexagons and bullseye */ | |
| 730 static int plot_raster_maxicode(struct zint_symbol *symbol, const int rotate_angle, const int file_type) { | |
| 731 int row, column; | |
| 732 int image_height, image_width; | |
| 733 size_t image_size; | |
| 734 unsigned char *pixelbuf; | |
| 735 int error_number; | |
| 736 float xoffset, yoffset, roffset, boffset; | |
| 737 float scaler = symbol->scale; | |
| 738 unsigned char *scaled_hexagon; | |
| 739 int hex_width, hex_height; | |
| 740 size_t hex_size; | |
| 741 int hx_start, hy_start, hx_end, hy_end; | |
| 742 int hex_image_width, hex_image_height; | |
| 743 int yposn_offset; | |
| 744 int xoffset_si, yoffset_si, roffset_si, boffset_si; | |
| 745 | |
| 746 const float two_div_sqrt3 = 1.1547f; /* 2 / √3 */ | |
| 747 const float sqrt3_div_two = 0.866f; /* √3 / 2 == 1.5 / √3 */ | |
| 748 | |
| 749 if (scaler < 0.2f) { | |
| 750 scaler = 0.2f; | |
| 751 } | |
| 752 scaler *= 10.0f; | |
| 753 | |
| 754 out_set_whitespace_offsets(symbol, 0 /*hide_text*/, 0 /*comp_xoffset*/, &xoffset, &yoffset, &roffset, &boffset, | |
| 755 NULL /*qz_right*/, scaler, &xoffset_si, &yoffset_si, &roffset_si, &boffset_si, NULL /*qz_right_si*/); | |
| 756 | |
| 757 hex_width = (int) roundf(scaler); /* Short diameter, X in ISO/IEC 16023:2000 Figure 8 (same as W) */ | |
| 758 hex_height = (int) roundf(scaler * two_div_sqrt3); /* Long diameter, V in Figure 8 */ | |
| 759 | |
| 760 /* Allow for whitespace around each hexagon (see ISO/IEC 16023:2000 Annexe J.4) | |
| 761 TODO: replace following kludge with proper calc of whitespace as per J.4 Steps 8 to 11 */ | |
| 762 hx_start = (int) (scaler < 3.5f ? 0.0f : ceilf(hex_width * 0.05f)); | |
| 763 hy_start = (int) ceilf(hex_height * 0.05f); | |
| 764 hx_end = (int) roundf((hex_width - hx_start) * 0.05f); | |
| 765 hy_end = (int) roundf((hex_height - hy_start) * 0.05f); | |
| 766 | |
| 767 /* The hexagons will be drawn within box (hex_width - hx_start - hx_end) x (hex_height - hy_start - hy_end) | |
| 768 and plotted starting at (-hx_start, -hy_start) */ | |
| 769 | |
| 770 hex_image_width = 30 * hex_width - hx_start - hx_end; | |
| 771 /* `yposn_offset` is vertical distance between rows, Y in Figure 8 */ | |
| 772 /* TODO: replace following kludge with proper calc of hex_width/hex_height/yposn_offset as per J.4 Steps 1 to 7 */ | |
| 773 yposn_offset = (int) (scaler > 10.0f ? (sqrt3_div_two * hex_width) : roundf(sqrt3_div_two * hex_width)); | |
| 774 /* 32 rows drawn yposn_offset apart + final hexagon */ | |
| 775 hex_image_height = 32 * yposn_offset + hex_height - hy_start - hy_end; | |
| 776 | |
| 777 image_width = (int) ceilf(hex_image_width + xoffset_si + roffset_si); | |
| 778 image_height = (int) ceilf(hex_image_height + yoffset_si + boffset_si); | |
| 779 assert(image_width && image_height); | |
| 780 image_size = (size_t) image_width * image_height; | |
| 781 | |
| 782 if (!(pixelbuf = (unsigned char *) raster_malloc(image_size, 0 /*prev_size*/))) { | |
| 783 return errtxt(ZINT_ERROR_MEMORY, symbol, 655, "Insufficient memory for pixel buffer"); | |
| 784 } | |
| 785 memset(pixelbuf, DEFAULT_PAPER, image_size); | |
| 786 | |
| 787 hex_size = (size_t) hex_width * hex_height; | |
| 788 if (!(scaled_hexagon = (unsigned char *) raster_malloc(hex_size, image_size))) { | |
| 789 free(pixelbuf); | |
| 790 return errtxt(ZINT_ERROR_MEMORY, symbol, 656, "Insufficient memory for pixel buffer"); | |
| 791 } | |
| 792 memset(scaled_hexagon, DEFAULT_PAPER, hex_size); | |
| 793 | |
| 794 plot_hexagon(scaled_hexagon, hex_width, hex_height, hx_start, hy_start, hx_end, hy_end); | |
| 795 | |
| 796 for (row = 0; row < symbol->rows; row++) { | |
| 797 const int odd_row = row & 1; /* Odd (reduced) row, even (full) row */ | |
| 798 const int yposn = row * yposn_offset + yoffset_si - hy_start; | |
| 799 const int xposn_offset = (odd_row ? hex_width / 2 : 0) + xoffset_si - hx_start; | |
| 800 for (column = 0; column < symbol->width - odd_row; column++) { | |
| 801 const int xposn = column * hex_width + xposn_offset; | |
| 802 if (module_is_set(symbol, row, column)) { | |
| 803 draw_hexagon(pixelbuf, image_width, image_height, scaled_hexagon, hex_width, hex_height, xposn, | |
| 804 yposn); | |
| 805 } | |
| 806 } | |
| 807 } | |
| 808 | |
| 809 draw_bullseye(pixelbuf, image_width, image_height, hex_width, hex_height, hx_start, hx_end, hex_image_height, | |
| 810 xoffset_si, yoffset_si); | |
| 811 | |
| 812 draw_bind_box(symbol, pixelbuf, xoffset_si, yoffset_si, hex_image_height, 0 /*dot_overspill_si*/, | |
| 813 0 /*upceanflag*/, 0 /*textoffset_si*/, image_width, image_height, (int) scaler); | |
| 814 | |
| 815 error_number = save_raster_image_to_file(symbol, image_height, image_width, pixelbuf, rotate_angle, file_type); | |
| 816 free(scaled_hexagon); | |
| 817 if (rotate_angle || file_type != OUT_BUFFER || !(symbol->output_options & OUT_BUFFER_INTERMEDIATE)) { | |
| 818 free(pixelbuf); | |
| 819 } | |
| 820 if (error_number == 0) { | |
| 821 /* Check whether size is compliant */ | |
| 822 const float min_ratio = 0.92993629f; /* 24.82 / 26.69 */ | |
| 823 const float max_ratio = 1.177984f; /* 27.93 / 23.71 */ | |
| 824 const float size_ratio = (float) hex_image_width / hex_image_height; | |
| 825 if (size_ratio < min_ratio || size_ratio > max_ratio) { | |
| 826 return errtxt(ZINT_WARN_NONCOMPLIANT, symbol, 663, "Size not within the minimum/maximum ranges"); | |
| 827 } | |
| 828 } | |
| 829 return error_number; | |
| 830 } | |
| 831 | |
| 832 static int plot_raster_dotty(struct zint_symbol *symbol, const int rotate_angle, const int file_type) { | |
| 833 float scaler = 2 * symbol->scale; | |
| 834 unsigned char *scaled_pixelbuf; | |
| 835 int r, i; | |
| 836 int scale_width, scale_height; | |
| 837 size_t scale_size; | |
| 838 int error_number = 0; | |
| 839 float xoffset, yoffset, roffset, boffset; | |
| 840 float dot_offset_s; | |
| 841 float dot_radius_s; | |
| 842 int dot_radius_si; | |
| 843 int dot_overspill_si; | |
| 844 int xoffset_si, yoffset_si, roffset_si, boffset_si; | |
| 845 int symbol_height_si; | |
| 846 | |
| 847 if (scaler < 2.0f) { | |
| 848 scaler = 2.0f; | |
| 849 } | |
| 850 symbol_height_si = (int) ceilf(symbol->height * scaler); | |
| 851 dot_radius_s = (symbol->dot_size * scaler) / 2.0f; | |
| 852 dot_radius_si = (int) dot_radius_s; | |
| 853 | |
| 854 out_set_whitespace_offsets(symbol, 0 /*hide_text*/, 0 /*comp_xoffset*/, &xoffset, &yoffset, &roffset, &boffset, | |
| 855 NULL /*qz_right*/, scaler, &xoffset_si, &yoffset_si, &roffset_si, &boffset_si, NULL /*qz_right_si*/); | |
| 856 | |
| 857 /* TODO: Revisit this overspill stuff, it's hacky */ | |
| 858 if (symbol->dot_size < 1.0f) { | |
| 859 dot_overspill_si = 0; | |
| 860 /* Offset (1 - dot_size) / 2 + dot_radius == (1 - dot_size + dot_size) / 2 == 1 / 2 */ | |
| 861 dot_offset_s = scaler / 2.0f; | |
| 862 } else { /* Allow for exceeding 1X */ | |
| 863 dot_overspill_si = (int) ceilf((symbol->dot_size - 1.0f) * scaler); | |
| 864 dot_offset_s = dot_radius_s; | |
| 865 } | |
| 866 if (dot_overspill_si == 0) { | |
| 867 dot_overspill_si = 1; | |
| 868 } | |
| 869 | |
| 870 scale_width = (int) (symbol->width * scaler + xoffset_si + roffset_si + dot_overspill_si); | |
| 871 scale_height = (int) (symbol_height_si + yoffset_si + boffset_si + dot_overspill_si); | |
| 872 scale_size = (size_t) scale_width * scale_height; | |
| 873 | |
| 874 /* Apply scale options by creating pixel buffer */ | |
| 875 if (!(scaled_pixelbuf = (unsigned char *) raster_malloc(scale_size, 0 /*prev_size*/))) { | |
| 876 return errtxt(ZINT_ERROR_MEMORY, symbol, 657, "Insufficient memory for pixel buffer"); | |
| 877 } | |
| 878 memset(scaled_pixelbuf, DEFAULT_PAPER, scale_size); | |
| 879 | |
| 880 /* Plot the body of the symbol to the pixel buffer */ | |
| 881 for (r = 0; r < symbol->rows; r++) { | |
| 882 int row_si = (int) (r * scaler + yoffset_si + dot_offset_s); | |
| 883 for (i = 0; i < symbol->width; i++) { | |
| 884 if (module_is_set(symbol, r, i)) { | |
| 885 draw_circle(scaled_pixelbuf, scale_width, scale_height, | |
| 886 (int) (i * scaler + xoffset_si + dot_offset_s), | |
| 887 row_si, dot_radius_si, DEFAULT_INK); | |
| 888 } | |
| 889 } | |
| 890 } | |
| 891 | |
| 892 draw_bind_box(symbol, scaled_pixelbuf, xoffset_si, yoffset_si, symbol_height_si, dot_overspill_si, | |
| 893 0 /*upceanflag*/, 0 /*textoffset_si*/, scale_width, scale_height, (int) scaler); | |
| 894 | |
| 895 error_number = save_raster_image_to_file(symbol, scale_height, scale_width, scaled_pixelbuf, rotate_angle, | |
| 896 file_type); | |
| 897 if (rotate_angle || file_type != OUT_BUFFER || !(symbol->output_options & OUT_BUFFER_INTERMEDIATE)) { | |
| 898 free(scaled_pixelbuf); | |
| 899 } | |
| 900 | |
| 901 return error_number; | |
| 902 } | |
| 903 | |
| 904 /* Convert UTF-8 to ISO/IEC 8859-1 for `draw_string()` human readable text */ | |
| 905 static void to_iso8859_1(const unsigned char source[], unsigned char preprocessed[]) { | |
| 906 int j, i, input_length; | |
| 907 | |
| 908 input_length = (int) ustrlen(source); | |
| 909 | |
| 910 j = 0; | |
| 911 i = 0; | |
| 912 while (i < input_length) { | |
| 913 switch (source[i]) { | |
| 914 case 0xC2: | |
| 915 /* UTF-8 C2xxh */ | |
| 916 /* Character range: C280h (latin: 80h) to C2BFh (latin: BFh) */ | |
| 917 assert(i + 1 < input_length); | |
| 918 i++; | |
| 919 preprocessed[j] = source[i]; | |
| 920 j++; | |
| 921 break; | |
| 922 case 0xC3: | |
| 923 /* UTF-8 C3xx */ | |
| 924 /* Character range: C380h (latin: C0h) to C3BFh (latin: FFh) */ | |
| 925 assert(i + 1 < input_length); | |
| 926 i++; | |
| 927 preprocessed[j] = source[i] + 64; | |
| 928 j++; | |
| 929 break; | |
| 930 default: | |
| 931 /* Process ASCII (< 80h), all other unicode points are ignored */ | |
| 932 if (source[i] < 128) { | |
| 933 preprocessed[j] = source[i]; | |
| 934 j++; | |
| 935 } | |
| 936 break; | |
| 937 } | |
| 938 i++; | |
| 939 } | |
| 940 preprocessed[j] = '\0'; | |
| 941 } | |
| 942 | |
| 943 static int plot_raster_default(struct zint_symbol *symbol, const int rotate_angle, const int file_type) { | |
| 944 int error_number; | |
| 945 int main_width; | |
| 946 int comp_xoffset = 0; | |
| 947 unsigned char addon[6]; | |
| 948 int addon_len = 0; | |
| 949 int addon_gap = 0; | |
| 950 float addon_text_yposn = 0.0f; | |
| 951 float xoffset, yoffset, roffset, boffset; | |
| 952 float textoffset; | |
| 953 int upceanflag = 0; | |
| 954 int addon_latch = 0; | |
| 955 int hide_text; | |
| 956 int i, r; | |
| 957 int block_width = 0; | |
| 958 int font_height; /* Font pixel size (so whole integers) */ | |
| 959 float guard_descent; | |
| 960 const int upcean_guard_whitespace = !(symbol->output_options & BARCODE_NO_QUIET_ZONES) | |
| 961 && (symbol->output_options & EANUPC_GUARD_WHITESPACE); | |
| 962 const int is_codablockf = symbol->symbology == BARCODE_CODABLOCKF || symbol->symbology == BARCODE_HIBC_BLOCKF; | |
| 963 | |
| 964 int textflags = 0; | |
| 965 int xoffset_si, yoffset_si, roffset_si, boffset_si, qz_right_si; | |
| 966 int xoffset_comp_si; | |
| 967 int row_heights_si[200]; | |
| 968 int symbol_height_si; | |
| 969 int image_width, image_height; | |
| 970 size_t image_size; | |
| 971 unsigned char *pixelbuf; | |
| 972 float scaler = symbol->scale; | |
| 973 int si; | |
| 974 int half_int_scaling; | |
| 975 int yposn_si; | |
| 976 | |
| 977 /* Ignore scaling < 0.5 for raster as would drop modules */ | |
| 978 if (scaler < 0.5f) { | |
| 979 scaler = 0.5f; | |
| 980 } | |
| 981 /* If half-integer scaling, then set integer scaler `si` to avoid scaling at end */ | |
| 982 half_int_scaling = isfintf(scaler * 2.0f); | |
| 983 if (half_int_scaling) { | |
| 984 si = (int) (scaler * 2.0f); | |
| 985 } else { | |
| 986 si = 2; | |
| 987 } | |
| 988 | |
| 989 (void) out_large_bar_height(symbol, si /*(scale and round)*/, row_heights_si, &symbol_height_si); | |
| 990 | |
| 991 main_width = symbol->width; | |
| 992 | |
| 993 if (is_composite(symbol->symbology)) { | |
| 994 while (!module_is_set(symbol, symbol->rows - 1, comp_xoffset)) { | |
| 995 comp_xoffset++; | |
| 996 } | |
| 997 } | |
| 998 if (is_upcean(symbol->symbology)) { | |
| 999 upceanflag = out_process_upcean(symbol, comp_xoffset, &main_width, addon, &addon_len, &addon_gap); | |
| 1000 } | |
| 1001 | |
| 1002 hide_text = ((!symbol->show_hrt) || (ustrlen(symbol->text) == 0) || scaler < 1.0f); | |
| 1003 | |
| 1004 out_set_whitespace_offsets(symbol, hide_text, comp_xoffset, &xoffset, &yoffset, &roffset, &boffset, | |
| 1005 NULL /*qz_right*/, si, &xoffset_si, &yoffset_si, &roffset_si, &boffset_si, &qz_right_si); | |
| 1006 | |
| 1007 xoffset_comp_si = xoffset_si + comp_xoffset * si; | |
| 1008 | |
| 1009 image_width = symbol->width * si + xoffset_si + roffset_si; | |
| 1010 | |
| 1011 /* Note font sizes halved as in pixels */ | |
| 1012 if (upceanflag) { | |
| 1013 textflags = ZFONT_UPCEAN_TEXT | (symbol->output_options & SMALL_TEXT); /* Bold not available for EAN/UPC */ | |
| 1014 font_height = (UPCEAN_FONT_HEIGHT + 1) / 2; | |
| 1015 /* Height of guard bar descent (none for EAN-2 and EAN-5) */ | |
| 1016 guard_descent = upceanflag >= 6 ? symbol->guard_descent : 0.0f; | |
| 1017 } else { | |
| 1018 textflags = symbol->output_options & (SMALL_TEXT | BOLD_TEXT); | |
| 1019 font_height = textflags & SMALL_TEXT ? (SMALL_FONT_HEIGHT + 1) / 2 : (NORMAL_FONT_HEIGHT + 1) / 2; | |
| 1020 guard_descent = 0.0f; | |
| 1021 } | |
| 1022 | |
| 1023 if (hide_text) { | |
| 1024 textoffset = guard_descent; | |
| 1025 } else { | |
| 1026 if (upceanflag) { | |
| 1027 textoffset = font_height + symbol->text_gap; | |
| 1028 if (textoffset < guard_descent) { | |
| 1029 textoffset = guard_descent; | |
| 1030 } | |
| 1031 } else { | |
| 1032 textoffset = font_height + symbol->text_gap; | |
| 1033 } | |
| 1034 } | |
| 1035 | |
| 1036 image_height = symbol_height_si + (int) ceilf(textoffset * si) + yoffset_si + boffset_si; | |
| 1037 assert(image_width && image_height); | |
| 1038 image_size = (size_t) image_width * image_height; | |
| 1039 | |
| 1040 if (!(pixelbuf = (unsigned char *) raster_malloc(image_size, 0 /*prev_size*/))) { | |
| 1041 return errtxt(ZINT_ERROR_MEMORY, symbol, 658, "Insufficient memory for pixel buffer"); | |
| 1042 } | |
| 1043 memset(pixelbuf, DEFAULT_PAPER, image_size); | |
| 1044 | |
| 1045 yposn_si = yoffset_si; | |
| 1046 | |
| 1047 /* Plot the body of the symbol to the pixel buffer */ | |
| 1048 if (symbol->symbology == BARCODE_ULTRA) { | |
| 1049 for (r = 0; r < symbol->rows; r++) { | |
| 1050 const int row_height_si = row_heights_si[r]; | |
| 1051 | |
| 1052 for (i = 0; i < symbol->width; i += block_width) { | |
| 1053 const int fill = module_colour_is_set(symbol, r, i); | |
| 1054 for (block_width = 1; (i + block_width < symbol->width) | |
| 1055 && module_colour_is_set(symbol, r, i + block_width) == fill; block_width++); | |
| 1056 if (fill) { | |
| 1057 /* a colour block */ | |
| 1058 draw_bar_line(pixelbuf, i * si + xoffset_si, block_width * si, yposn_si, image_width, | |
| 1059 ultra_colour[fill]); | |
| 1060 } | |
| 1061 } | |
| 1062 copy_bar_line(pixelbuf, xoffset_si, image_width - xoffset_si - roffset_si, yposn_si, row_height_si, | |
| 1063 image_width, image_height); | |
| 1064 yposn_si += row_height_si; | |
| 1065 } | |
| 1066 | |
| 1067 } else if (upceanflag >= 6) { /* UPC-E, EAN-8, UPC-A, EAN-13 */ | |
| 1068 for (r = 0; r < symbol->rows; r++) { | |
| 1069 int row_height_si = row_heights_si[r]; | |
| 1070 | |
| 1071 for (i = 0; i < symbol->width; i += block_width) { | |
| 1072 const int fill = module_is_set(symbol, r, i); | |
| 1073 for (block_width = 1; (i + block_width < symbol->width) | |
| 1074 && module_is_set(symbol, r, i + block_width) == fill; block_width++); | |
| 1075 if ((r == (symbol->rows - 1)) && (i > main_width) && (addon_latch == 0)) { | |
| 1076 int addon_row_height_si; | |
| 1077 const int addon_row_adj_si = (int) ceilf((font_height + symbol->text_gap) * si); | |
| 1078 copy_bar_line(pixelbuf, xoffset_si, main_width * si, yposn_si, row_height_si, image_width, | |
| 1079 image_height); | |
| 1080 addon_text_yposn = yposn_si; | |
| 1081 yposn_si += addon_row_adj_si; | |
| 1082 addon_row_height_si = row_height_si - addon_row_adj_si; | |
| 1083 /* Following ISO/IEC 15420:2009 Figure 5 — UPC-A bar code symbol with 2-digit add-on (contrary to | |
| 1084 GS1 General Specs v24.0 Figure 5.2.6.6-5) descends for all including UPC-A/E */ | |
| 1085 addon_row_height_si += guard_descent * si; | |
| 1086 if (addon_row_height_si == 0) { | |
| 1087 addon_row_height_si = 1; | |
| 1088 } | |
| 1089 row_height_si = addon_row_height_si; | |
| 1090 addon_latch = 1; | |
| 1091 } | |
| 1092 if (fill) { | |
| 1093 /* a bar */ | |
| 1094 draw_bar_line(pixelbuf, i * si + xoffset_si, block_width * si, yposn_si, image_width, | |
| 1095 DEFAULT_INK); | |
| 1096 } | |
| 1097 } | |
| 1098 if (addon_latch) { | |
| 1099 copy_bar_line(pixelbuf, xoffset_si + main_width * si, | |
| 1100 image_width - main_width * si - xoffset_si - roffset_si, yposn_si, row_height_si, | |
| 1101 image_width, image_height); | |
| 1102 } else { | |
| 1103 copy_bar_line(pixelbuf, xoffset_si, image_width - xoffset_si - roffset_si, yposn_si, row_height_si, | |
| 1104 image_width, image_height); | |
| 1105 } | |
| 1106 yposn_si += row_height_si; | |
| 1107 } | |
| 1108 | |
| 1109 } else { | |
| 1110 if (upceanflag && !hide_text) { /* EAN-2, EAN-5 (standalone add-ons) */ | |
| 1111 yposn_si += (int) ceilf((font_height + symbol->text_gap) * si); | |
| 1112 } | |
| 1113 for (r = 0; r < symbol->rows; r++) { | |
| 1114 int row_height_si = row_heights_si[r]; | |
| 1115 if (upceanflag && !hide_text) { /* EAN-2, EAN-5 (standalone add-ons) */ | |
| 1116 row_height_si += textoffset * si - (yposn_si - yoffset_si); | |
| 1117 } | |
| 1118 | |
| 1119 for (i = 0; i < symbol->width; i += block_width) { | |
| 1120 const int fill = module_is_set(symbol, r, i); | |
| 1121 for (block_width = 1; (i + block_width < symbol->width) | |
| 1122 && module_is_set(symbol, r, i + block_width) == fill; block_width++); | |
| 1123 if (fill) { | |
| 1124 /* a bar */ | |
| 1125 draw_bar_line(pixelbuf, i * si + xoffset_si, block_width * si, yposn_si, image_width, | |
| 1126 DEFAULT_INK); | |
| 1127 } | |
| 1128 } | |
| 1129 copy_bar_line(pixelbuf, xoffset_si, image_width - xoffset_si - roffset_si, yposn_si, row_height_si, | |
| 1130 image_width, image_height); | |
| 1131 yposn_si += row_height_si; | |
| 1132 } | |
| 1133 } | |
| 1134 | |
| 1135 if (guard_descent && upceanflag >= 6) { /* UPC-E, EAN-8, UPC-A, EAN-13 */ | |
| 1136 /* Guard bar extension */ | |
| 1137 const int guard_yoffset_si = yoffset_si + symbol_height_si; | |
| 1138 const int guard_descent_si = guard_descent * si; | |
| 1139 | |
| 1140 if (upceanflag == 6) { /* UPC-E */ | |
| 1141 draw_bar_line(pixelbuf, 0 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1142 draw_bar_line(pixelbuf, 2 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1143 draw_bar_line(pixelbuf, 46 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1144 draw_bar_line(pixelbuf, 48 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1145 draw_bar_line(pixelbuf, 50 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1146 | |
| 1147 } else if (upceanflag == 8) { /* EAN-8 */ | |
| 1148 draw_bar_line(pixelbuf, 0 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1149 draw_bar_line(pixelbuf, 2 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1150 draw_bar_line(pixelbuf, 32 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1151 draw_bar_line(pixelbuf, 34 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1152 draw_bar_line(pixelbuf, 64 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1153 draw_bar_line(pixelbuf, 66 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1154 | |
| 1155 } else if (upceanflag == 12) { /* UPC-A */ | |
| 1156 for (i = 0 + comp_xoffset; i < 11 + comp_xoffset; i += block_width) { | |
| 1157 const int fill = module_is_set(symbol, symbol->rows - 1, i); | |
| 1158 for (block_width = 1; (i + block_width < symbol->width) | |
| 1159 && module_is_set(symbol, symbol->rows - 1, i + block_width) == fill; | |
| 1160 block_width++); | |
| 1161 if (fill) { | |
| 1162 draw_bar_line(pixelbuf, i * si + xoffset_si, block_width * si, guard_yoffset_si, image_width, | |
| 1163 DEFAULT_INK); | |
| 1164 } | |
| 1165 } | |
| 1166 draw_bar_line(pixelbuf, 46 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1167 draw_bar_line(pixelbuf, 48 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1168 for (i = 85 + comp_xoffset; i < 96 + comp_xoffset; i += block_width) { | |
| 1169 const int fill = module_is_set(symbol, symbol->rows - 1, i); | |
| 1170 for (block_width = 1; (i + block_width < symbol->width) | |
| 1171 && module_is_set(symbol, symbol->rows - 1, i + block_width) == fill; | |
| 1172 block_width++); | |
| 1173 if (fill) { | |
| 1174 draw_bar_line(pixelbuf, i * si + xoffset_si, block_width * si, guard_yoffset_si, image_width, | |
| 1175 DEFAULT_INK); | |
| 1176 } | |
| 1177 } | |
| 1178 | |
| 1179 } else { /* EAN-13 */ | |
| 1180 draw_bar_line(pixelbuf, 0 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1181 draw_bar_line(pixelbuf, 2 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1182 draw_bar_line(pixelbuf, 46 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1183 draw_bar_line(pixelbuf, 48 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1184 draw_bar_line(pixelbuf, 92 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1185 draw_bar_line(pixelbuf, 94 * si + xoffset_comp_si, 1 * si, guard_yoffset_si, image_width, DEFAULT_INK); | |
| 1186 } | |
| 1187 copy_bar_line(pixelbuf, xoffset_comp_si, image_width - xoffset_comp_si - roffset_si, guard_yoffset_si, | |
| 1188 guard_descent_si, image_width, image_height); | |
| 1189 } | |
| 1190 | |
| 1191 /* Add the text */ | |
| 1192 | |
| 1193 if (!hide_text) { | |
| 1194 | |
| 1195 if (upceanflag >= 6) { /* UPC-E, EAN-8, UPC-A, EAN-13 */ | |
| 1196 | |
| 1197 /* Note font sizes halved as in pixels */ | |
| 1198 const int upcea_height_adj = ((UPCEAN_FONT_HEIGHT - UPCEAN_SMALL_FONT_HEIGHT) * si + 1) / 2; | |
| 1199 | |
| 1200 int text_yposn = yoffset_si + symbol_height_si + (int) (symbol->text_gap * si); | |
| 1201 if (symbol->border_width > 0 && (symbol->output_options & (BARCODE_BOX | BARCODE_BIND)) | |
| 1202 && !(symbol->output_options & BARCODE_BIND_TOP)) { /* Trumps BARCODE_BOX & BARCODE_BIND */ | |
| 1203 text_yposn += symbol->border_width * si; | |
| 1204 } | |
| 1205 | |
| 1206 if (upceanflag == 6) { /* UPC-E */ | |
| 1207 int text_xposn = -5 * si + xoffset_comp_si; | |
| 1208 draw_string(pixelbuf, symbol->text, 1, text_xposn, text_yposn + upcea_height_adj, | |
| 1209 textflags | SMALL_TEXT | ZFONT_HALIGN_RIGHT, image_width, image_height, si); | |
| 1210 text_xposn = 24 * si + xoffset_comp_si; | |
| 1211 draw_string(pixelbuf, symbol->text + 1, 6, text_xposn, text_yposn, textflags, image_width, | |
| 1212 image_height, si); | |
| 1213 /* TODO: GS1 General Specs v24.0 5.2.5 Human readable interpretation says 3X but this could cause | |
| 1214 digit's righthand to touch any add-on, now that they descend, so use 2X, until clarified */ | |
| 1215 text_xposn = (51 + 2) * si + xoffset_comp_si; | |
| 1216 draw_string(pixelbuf, symbol->text + 7, 1, text_xposn, text_yposn + upcea_height_adj, | |
| 1217 textflags | SMALL_TEXT | ZFONT_HALIGN_LEFT, image_width, image_height, si); | |
| 1218 if (addon_len) { | |
| 1219 text_xposn = ((addon_len == 2 ? 61 : 75) + addon_gap) * si + xoffset_comp_si; | |
| 1220 draw_string(pixelbuf, addon, addon_len, text_xposn, addon_text_yposn, textflags, | |
| 1221 image_width, image_height, si); | |
| 1222 if (upcean_guard_whitespace) { | |
| 1223 text_xposn = symbol->width * si + qz_right_si + xoffset_si; | |
| 1224 draw_string(pixelbuf, (const unsigned char *) ">", 1, text_xposn, addon_text_yposn, | |
| 1225 textflags | ZFONT_HALIGN_RIGHT, image_width, image_height, si); | |
| 1226 } | |
| 1227 } | |
| 1228 | |
| 1229 } else if (upceanflag == 8) { /* EAN-8 */ | |
| 1230 int text_xposn; | |
| 1231 if (upcean_guard_whitespace) { | |
| 1232 text_xposn = -7 * si + xoffset_comp_si; | |
| 1233 draw_string(pixelbuf, (const unsigned char *) "<", 1, text_xposn, text_yposn, | |
| 1234 textflags | ZFONT_HALIGN_LEFT, image_width, image_height, si); | |
| 1235 } | |
| 1236 text_xposn = 17 * si + xoffset_comp_si; | |
| 1237 draw_string(pixelbuf, symbol->text, 4, text_xposn, text_yposn, textflags, image_width, image_height, | |
| 1238 si); | |
| 1239 text_xposn = 50 * si + xoffset_comp_si; | |
| 1240 draw_string(pixelbuf, symbol->text + 4, 4, text_xposn, text_yposn, textflags, image_width, | |
| 1241 image_height, si); | |
| 1242 if (addon_len) { | |
| 1243 text_xposn = ((addon_len == 2 ? 77 : 91) + addon_gap) * si + xoffset_comp_si; | |
| 1244 draw_string(pixelbuf, addon, addon_len, text_xposn, addon_text_yposn, textflags, | |
| 1245 image_width, image_height, si); | |
| 1246 if (upcean_guard_whitespace) { | |
| 1247 text_xposn = symbol->width * si + qz_right_si + xoffset_si; | |
| 1248 draw_string(pixelbuf, (const unsigned char *) ">", 1, text_xposn, addon_text_yposn, | |
| 1249 textflags | ZFONT_HALIGN_RIGHT, image_width, image_height, si); | |
| 1250 } | |
| 1251 } else if (upcean_guard_whitespace) { | |
| 1252 text_xposn = symbol->width * si + qz_right_si + xoffset_si; | |
| 1253 draw_string(pixelbuf, (const unsigned char *) ">", 1, text_xposn, text_yposn, | |
| 1254 textflags | ZFONT_HALIGN_RIGHT, image_width, image_height, si); | |
| 1255 } | |
| 1256 | |
| 1257 } else if (upceanflag == 12) { /* UPC-A */ | |
| 1258 int text_xposn = -5 * si + xoffset_comp_si; | |
| 1259 draw_string(pixelbuf, symbol->text, 1, text_xposn, text_yposn + upcea_height_adj, | |
| 1260 textflags | SMALL_TEXT | ZFONT_HALIGN_RIGHT, image_width, image_height, si); | |
| 1261 text_xposn = 28 * si + xoffset_comp_si; | |
| 1262 draw_string(pixelbuf, symbol->text + 1, 5, text_xposn, text_yposn, textflags, image_width, | |
| 1263 image_height, si); | |
| 1264 text_xposn = 67 * si + xoffset_comp_si; | |
| 1265 draw_string(pixelbuf, symbol->text + 6, 5, text_xposn, text_yposn, textflags, image_width, | |
| 1266 image_height, si); | |
| 1267 /* TODO: GS1 General Specs v24.0 5.2.5 Human readable interpretation says 5X but this could cause | |
| 1268 digit's righthand to touch any add-on, now that they descend, so use 4X, until clarified */ | |
| 1269 text_xposn = (95 + 4) * si + xoffset_comp_si; | |
| 1270 draw_string(pixelbuf, symbol->text + 11, 1, text_xposn, text_yposn + upcea_height_adj, | |
| 1271 textflags | SMALL_TEXT | ZFONT_HALIGN_LEFT, image_width, image_height, si); | |
| 1272 if (addon_len) { | |
| 1273 text_xposn = ((addon_len == 2 ? 105 : 119) + addon_gap) * si + xoffset_comp_si; | |
| 1274 draw_string(pixelbuf, addon, addon_len, text_xposn, addon_text_yposn, textflags, | |
| 1275 image_width, image_height, si); | |
| 1276 if (upcean_guard_whitespace) { | |
| 1277 text_xposn = symbol->width * si + qz_right_si + xoffset_si; | |
| 1278 draw_string(pixelbuf, (const unsigned char *) ">", 1, text_xposn, addon_text_yposn, | |
| 1279 textflags | ZFONT_HALIGN_RIGHT, image_width, image_height, si); | |
| 1280 } | |
| 1281 } | |
| 1282 | |
| 1283 } else { /* EAN-13 */ | |
| 1284 int text_xposn = -5 * si + xoffset_comp_si; | |
| 1285 draw_string(pixelbuf, symbol->text, 1, text_xposn, text_yposn, textflags | ZFONT_HALIGN_RIGHT, | |
| 1286 image_width, image_height, si); | |
| 1287 text_xposn = 24 * si + xoffset_comp_si; | |
| 1288 draw_string(pixelbuf, symbol->text + 1, 6, text_xposn, text_yposn, textflags, image_width, | |
| 1289 image_height, si); | |
| 1290 text_xposn = 71 * si + xoffset_comp_si; | |
| 1291 draw_string(pixelbuf, symbol->text + 7, 6, text_xposn, text_yposn, textflags, image_width, | |
| 1292 image_height, si); | |
| 1293 if (addon_len) { | |
| 1294 text_xposn = ((addon_len == 2 ? 105 : 119) + addon_gap) * si + xoffset_comp_si; | |
| 1295 draw_string(pixelbuf, addon, addon_len, text_xposn, addon_text_yposn, textflags, | |
| 1296 image_width, image_height, si); | |
| 1297 if (upcean_guard_whitespace) { | |
| 1298 text_xposn = symbol->width * si + qz_right_si + xoffset_si; | |
| 1299 draw_string(pixelbuf, (const unsigned char *) ">", 1, text_xposn, addon_text_yposn, | |
| 1300 textflags | ZFONT_HALIGN_RIGHT, image_width, image_height, si); | |
| 1301 } | |
| 1302 } else if (upcean_guard_whitespace) { | |
| 1303 text_xposn = symbol->width * si + qz_right_si + xoffset_si; | |
| 1304 draw_string(pixelbuf, (const unsigned char *) ">", 1, text_xposn, text_yposn, | |
| 1305 textflags | ZFONT_HALIGN_RIGHT, image_width, image_height, si); | |
| 1306 } | |
| 1307 } | |
| 1308 } else if (upceanflag) { /* EAN-2, EAN-5 (standalone add-ons) */ | |
| 1309 int text_xposn = (int) ((main_width / 2.0f) * si) + xoffset_si; | |
| 1310 int text_yposn = yoffset_si; | |
| 1311 if (symbol->border_width > 0 | |
| 1312 && (symbol->output_options & (BARCODE_BOX | BARCODE_BIND | BARCODE_BIND_TOP))) { | |
| 1313 text_yposn -= symbol->border_width * si; | |
| 1314 } | |
| 1315 /* Put the human readable text at the top */ | |
| 1316 draw_string(pixelbuf, symbol->text, -1, text_xposn, text_yposn, textflags, image_width, image_height, si); | |
| 1317 if (upcean_guard_whitespace) { | |
| 1318 text_xposn = symbol->width * si + qz_right_si + xoffset_comp_si; | |
| 1319 draw_string(pixelbuf, (const unsigned char *) ">", 1, text_xposn, text_yposn, | |
| 1320 textflags | ZFONT_HALIGN_RIGHT, image_width, image_height, si); | |
| 1321 } | |
| 1322 } else { | |
| 1323 /* Suppress clang-analyzer-core.CallAndMessage warning */ | |
| 1324 unsigned char local_text[sizeof(symbol->text)] = {0}; | |
| 1325 int text_xposn = (int) ((main_width / 2.0f) * si) + xoffset_si; | |
| 1326 int text_yposn = yoffset_si + symbol_height_si + (int) (symbol->text_gap * si); | |
| 1327 if (symbol->border_width > 0 && (symbol->output_options & (BARCODE_BOX | BARCODE_BIND)) | |
| 1328 && !(symbol->output_options & BARCODE_BIND_TOP)) { /* Trumps BARCODE_BOX & BARCODE_BIND */ | |
| 1329 text_yposn += symbol->border_width * si; | |
| 1330 } | |
| 1331 to_iso8859_1(symbol->text, local_text); | |
| 1332 /* Put the human readable text at the bottom */ | |
| 1333 draw_string(pixelbuf, local_text, -1, text_xposn, text_yposn, textflags, image_width, image_height, si); | |
| 1334 } | |
| 1335 } | |
| 1336 | |
| 1337 /* Separator binding for stacked barcodes */ | |
| 1338 if ((symbol->output_options & BARCODE_BIND) && (symbol->rows > 1) && is_stackable(symbol->symbology)) { | |
| 1339 int sep_xoffset_si = xoffset_si; | |
| 1340 int sep_width_si = symbol->width * si; | |
| 1341 int sep_height_si, sep_yoffset_si; | |
| 1342 float sep_height = 1.0f; | |
| 1343 if (symbol->option_3 > 0 && symbol->option_3 <= 4) { | |
| 1344 sep_height = symbol->option_3; | |
| 1345 } | |
| 1346 sep_height_si = (int) (sep_height * si); | |
| 1347 sep_yoffset_si = yoffset_si + row_heights_si[0] - sep_height_si / 2; | |
| 1348 if (is_codablockf) { | |
| 1349 /* Avoid 11-module start and 13-module stop chars */ | |
| 1350 sep_xoffset_si += 11 * si; | |
| 1351 sep_width_si -= (11 + 13) * si; | |
| 1352 } | |
| 1353 for (r = 1; r < symbol->rows; r++) { | |
| 1354 draw_bar(pixelbuf, sep_xoffset_si, sep_width_si, sep_yoffset_si, sep_height_si, image_width, image_height, | |
| 1355 DEFAULT_INK); | |
| 1356 sep_yoffset_si += row_heights_si[r]; | |
| 1357 } | |
| 1358 } | |
| 1359 | |
| 1360 draw_bind_box(symbol, pixelbuf, xoffset_si, yoffset_si, symbol_height_si, 0 /*dot_overspill_si*/, | |
| 1361 upceanflag, (int) (textoffset * si), image_width, image_height, si); | |
| 1362 | |
| 1363 if (!half_int_scaling) { | |
| 1364 size_t prev_image_row; | |
| 1365 unsigned char *scaled_pixelbuf; | |
| 1366 const int scale_width = (int) stripf(image_width * scaler); | |
| 1367 const int scale_height = (int) stripf(image_height * scaler); | |
| 1368 | |
| 1369 /* Apply scale options by creating another pixel buffer */ | |
| 1370 if (!(scaled_pixelbuf = (unsigned char *) raster_malloc((size_t) scale_width * scale_height, image_size))) { | |
| 1371 free(pixelbuf); | |
| 1372 return errtxt(ZINT_ERROR_MEMORY, symbol, 659, "Insufficient memory for scaled pixel buffer"); | |
| 1373 } | |
| 1374 memset(scaled_pixelbuf, DEFAULT_PAPER, (size_t) scale_width * scale_height); | |
| 1375 | |
| 1376 /* Interpolate */ | |
| 1377 for (r = 0; r < scale_height; r++) { | |
| 1378 size_t scaled_row = (size_t) scale_width * r; | |
| 1379 size_t image_row = (size_t) stripf(r / scaler) * image_width; | |
| 1380 if (r && (image_row == prev_image_row | |
| 1381 || memcmp(pixelbuf + image_row, pixelbuf + prev_image_row, image_width) == 0)) { | |
| 1382 memcpy(scaled_pixelbuf + scaled_row, scaled_pixelbuf + scaled_row - scale_width, scale_width); | |
| 1383 } else { | |
| 1384 for (i = 0; i < scale_width; i++) { | |
| 1385 *(scaled_pixelbuf + scaled_row + i) = *(pixelbuf + image_row + (int) stripf(i / scaler)); | |
| 1386 } | |
| 1387 } | |
| 1388 prev_image_row = image_row; | |
| 1389 } | |
| 1390 | |
| 1391 error_number = save_raster_image_to_file(symbol, scale_height, scale_width, scaled_pixelbuf, rotate_angle, | |
| 1392 file_type); | |
| 1393 if (rotate_angle || file_type != OUT_BUFFER || !(symbol->output_options & OUT_BUFFER_INTERMEDIATE)) { | |
| 1394 free(scaled_pixelbuf); | |
| 1395 } | |
| 1396 free(pixelbuf); | |
| 1397 } else { | |
| 1398 error_number = save_raster_image_to_file(symbol, image_height, image_width, pixelbuf, rotate_angle, | |
| 1399 file_type); | |
| 1400 if (rotate_angle || file_type != OUT_BUFFER || !(symbol->output_options & OUT_BUFFER_INTERMEDIATE)) { | |
| 1401 free(pixelbuf); | |
| 1402 } | |
| 1403 } | |
| 1404 return error_number; | |
| 1405 } | |
| 1406 | |
| 1407 INTERNAL int plot_raster(struct zint_symbol *symbol, int rotate_angle, int file_type) { | |
| 1408 int error; | |
| 1409 | |
| 1410 #ifdef ZINT_NO_PNG | |
| 1411 if (file_type == OUT_PNG_FILE) { | |
| 1412 return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 660, "PNG format disabled at compile time"); | |
| 1413 } | |
| 1414 #endif /* ZINT_NO_PNG */ | |
| 1415 | |
| 1416 error = out_check_colour_options(symbol); | |
| 1417 if (error != 0) { | |
| 1418 return error; | |
| 1419 } | |
| 1420 if (symbol->rows <= 0) { | |
| 1421 return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 664, "No rows"); | |
| 1422 } | |
| 1423 | |
| 1424 if (symbol->symbology == BARCODE_MAXICODE) { | |
| 1425 error = plot_raster_maxicode(symbol, rotate_angle, file_type); | |
| 1426 } else if (symbol->output_options & BARCODE_DOTTY_MODE) { | |
| 1427 error = plot_raster_dotty(symbol, rotate_angle, file_type); | |
| 1428 } else { | |
| 1429 error = plot_raster_default(symbol, rotate_angle, file_type); | |
| 1430 } | |
| 1431 | |
| 1432 return error; | |
| 1433 } | |
| 1434 | |
| 1435 /* vim: set ts=4 sw=4 et : */ |
