Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/zint/backend/output.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 /* output.c - Common routines for raster/vector | |
| 2 | |
| 3 libzint - the open source barcode library | |
| 4 Copyright (C) 2020-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 <errno.h> | |
| 35 #include <math.h> | |
| 36 #ifdef _WIN32 | |
| 37 #include <windows.h> | |
| 38 #include <direct.h> | |
| 39 #else | |
| 40 #include <sys/stat.h> /* mkdir(2) */ | |
| 41 #endif | |
| 42 #include "common.h" | |
| 43 #include "output.h" | |
| 44 | |
| 45 #define OUT_SSET_F (IS_NUM_F | IS_UHX_F | IS_LHX_F) /* SSET "0123456789ABCDEFabcdef" */ | |
| 46 | |
| 47 /* Helper to check an individual colour option is good */ | |
| 48 static int out_check_colour(struct zint_symbol *symbol, const char *colour, const char *name) { | |
| 49 const char *comma1, *comma2, *comma3; | |
| 50 int val; | |
| 51 | |
| 52 if ((comma1 = strchr(colour, ',')) == NULL) { | |
| 53 const int len = (int) strlen(colour); | |
| 54 if ((len != 6) && (len != 8)) { | |
| 55 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 880, "Malformed %s RGB colour (6 or 8 characters only)", | |
| 56 name); | |
| 57 } | |
| 58 if (not_sane(OUT_SSET_F, (unsigned char *) colour, len)) { | |
| 59 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 881, | |
| 60 "Malformed %1$s RGB colour '%2$s' (hexadecimal only)", name, colour); | |
| 61 } | |
| 62 | |
| 63 return 0; | |
| 64 } | |
| 65 | |
| 66 /* CMYK comma-separated percentages */ | |
| 67 if ((comma2 = strchr(comma1 + 1, ',')) == NULL || (comma3 = strchr(comma2 + 1, ',')) == NULL | |
| 68 || strchr(comma3 + 1, ',') != NULL) { | |
| 69 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 882, | |
| 70 "Malformed %s CMYK colour (4 decimal numbers, comma-separated)", name); | |
| 71 } | |
| 72 if (comma1 - colour > 3 || comma2 - (comma1 + 1) > 3 || comma3 - (comma2 + 1) > 3 || strlen(comma3 + 1) > 3) { | |
| 73 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 883, | |
| 74 "Malformed %s CMYK colour (3 digit maximum per number)", name); | |
| 75 } | |
| 76 | |
| 77 if ((val = to_int((const unsigned char *) colour, (int) (comma1 - colour))) == -1 || val > 100) { | |
| 78 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 884, "Malformed %s CMYK colour C (decimal 0 to 100 only)", | |
| 79 name); | |
| 80 } | |
| 81 if ((val = to_int((const unsigned char *) (comma1 + 1), (int) (comma2 - (comma1 + 1)))) == -1 || val > 100) { | |
| 82 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 885, "Malformed %s CMYK colour M (decimal 0 to 100 only)", | |
| 83 name); | |
| 84 } | |
| 85 if ((val = to_int((const unsigned char *) (comma2 + 1), (int) (comma3 - (comma2 + 1)))) == -1 || val > 100) { | |
| 86 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 886, "Malformed %s CMYK colour Y (decimal 0 to 100 only)", | |
| 87 name); | |
| 88 } | |
| 89 if ((val = to_int((const unsigned char *) (comma3 + 1), (int) strlen(comma3 + 1))) == -1 || val > 100) { | |
| 90 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 887, "Malformed %s CMYK colour K (decimal 0 to 100 only)", | |
| 91 name); | |
| 92 } | |
| 93 | |
| 94 return 0; | |
| 95 } | |
| 96 | |
| 97 /* Check colour options are good (`symbol->fgcolour`, `symbol->bgcolour`) */ | |
| 98 INTERNAL int out_check_colour_options(struct zint_symbol *symbol) { | |
| 99 | |
| 100 if (out_check_colour(symbol, symbol->fgcolour, "foreground") != 0) { | |
| 101 return ZINT_ERROR_INVALID_OPTION; | |
| 102 } | |
| 103 if (out_check_colour(symbol, symbol->bgcolour, "background") != 0) { | |
| 104 return ZINT_ERROR_INVALID_OPTION; | |
| 105 } | |
| 106 | |
| 107 return 0; | |
| 108 } | |
| 109 | |
| 110 /* Return RGB(A) from (well-formed) colour string. Returns 0 if RGB or converted CMYK, 1 if RGBA */ | |
| 111 INTERNAL int out_colour_get_rgb(const char *colour, unsigned char *red, unsigned char *green, unsigned char *blue, | |
| 112 unsigned char *alpha) { | |
| 113 const char *comma1, *comma2, *comma3; | |
| 114 int black, val; | |
| 115 | |
| 116 if ((comma1 = strchr(colour, ',')) == NULL) { | |
| 117 *red = 16 * ctoi(colour[0]) + ctoi(colour[1]); | |
| 118 *green = 16 * ctoi(colour[2]) + ctoi(colour[3]); | |
| 119 *blue = 16 * ctoi(colour[4]) + ctoi(colour[5]); | |
| 120 if (alpha) { | |
| 121 *alpha = colour[6] ? 16 * ctoi(colour[6]) + ctoi(colour[7]) : 0xFF; | |
| 122 return colour[6] ? 1 : 0; | |
| 123 } | |
| 124 return 0; | |
| 125 } | |
| 126 comma2 = strchr(comma1 + 1, ','); | |
| 127 comma3 = strchr(comma2 + 1, ','); | |
| 128 | |
| 129 black = 100 - to_int((const unsigned char *) (comma3 + 1), (int) strlen(comma3 + 1)); | |
| 130 | |
| 131 val = 100 - to_int((const unsigned char *) colour, (int) (comma1 - colour)); /* Cyan */ | |
| 132 *red = (int) round((0xFF * val * black) / 10000.0); | |
| 133 | |
| 134 val = 100 - to_int((const unsigned char *) (comma1 + 1), (int) (comma2 - (comma1 + 1))); /* Magenta */ | |
| 135 *green = (int) round((0xFF * val * black) / 10000.0); | |
| 136 | |
| 137 val = 100 - to_int((const unsigned char *) (comma2 + 1), (int) (comma3 - (comma2 + 1))); /* Yellow */ | |
| 138 *blue = (int) round((0xFF * val * black) / 10000.0); | |
| 139 | |
| 140 if (alpha) { | |
| 141 *alpha = 0xFF; | |
| 142 } | |
| 143 | |
| 144 return 0; | |
| 145 } | |
| 146 | |
| 147 /* Return CMYK from (well-formed) colour string. Returns 0 if CMYK, 1 if converted RBG, 2 if converted RGBA */ | |
| 148 INTERNAL int out_colour_get_cmyk(const char *colour, int *cyan, int *magenta, int *yellow, int *black, | |
| 149 unsigned char *rgb_alpha) { | |
| 150 const char *comma1; | |
| 151 unsigned char red, green, blue, alpha; | |
| 152 int have_alpha, k; | |
| 153 | |
| 154 if ((comma1 = strchr(colour, ',')) != NULL) { | |
| 155 const char *const comma2 = strchr(comma1 + 1, ','); | |
| 156 const char *const comma3 = strchr(comma2 + 1, ','); | |
| 157 *cyan = to_int((const unsigned char *) colour, (int) (comma1 - colour)); | |
| 158 *magenta = to_int((const unsigned char *) (comma1 + 1), (int) (comma2 - (comma1 + 1))); | |
| 159 *yellow = to_int((const unsigned char *) (comma2 + 1), (int) (comma3 - (comma2 + 1))); | |
| 160 *black = to_int((const unsigned char *) (comma3 + 1), (int) strlen(comma3 + 1)); | |
| 161 if (rgb_alpha) { | |
| 162 *rgb_alpha = 0xFF; | |
| 163 } | |
| 164 return 0; | |
| 165 } | |
| 166 have_alpha = out_colour_get_rgb(colour, &red, &green, &blue, &alpha); | |
| 167 | |
| 168 k = red; | |
| 169 if (green > k) { | |
| 170 k = green; | |
| 171 } | |
| 172 if (blue > k) { | |
| 173 k = blue; | |
| 174 } | |
| 175 if (k == 0) { | |
| 176 *cyan = *magenta = *yellow = 0; | |
| 177 *black = 100; | |
| 178 } else { | |
| 179 *cyan = (int) round((k - red) * 100.0 / k); | |
| 180 *magenta = (int) round((k - green) * 100.0 / k); | |
| 181 *yellow = (int) round((k - blue) * 100.0 / k); | |
| 182 *black = (int) round(((0xFF - k) * 100.0) / 0xFF); | |
| 183 } | |
| 184 | |
| 185 if (rgb_alpha) { | |
| 186 *rgb_alpha = have_alpha ? alpha : 0xFF; | |
| 187 } | |
| 188 | |
| 189 return 1 + have_alpha; | |
| 190 } | |
| 191 | |
| 192 /* Convert internal colour chars "WCBMRYGK" to RGB */ | |
| 193 INTERNAL int out_colour_char_to_rgb(const char ch, unsigned char *red, unsigned char *green, unsigned char *blue) { | |
| 194 static const char chars[] = "WCBMRYGK"; | |
| 195 static const unsigned char colours[8][3] = { | |
| 196 { 0xff, 0xff, 0xff, }, /* White */ | |
| 197 { 0, 0xff, 0xff, }, /* Cyan */ | |
| 198 { 0, 0, 0xff, }, /* Blue */ | |
| 199 { 0xff, 0, 0xff, }, /* Magenta */ | |
| 200 { 0xff, 0, 0, }, /* Red */ | |
| 201 { 0xff, 0xff, 0, }, /* Yellow */ | |
| 202 { 0, 0xff, 0, }, /* Green */ | |
| 203 { 0, 0, 0, }, /* Black */ | |
| 204 }; | |
| 205 int i = posn(chars, ch); | |
| 206 int ret = i != -1; | |
| 207 | |
| 208 if (i == -1) { | |
| 209 i = 7; /* Black (zeroize) */ | |
| 210 } | |
| 211 if (red) { | |
| 212 *red = colours[i][0]; | |
| 213 } | |
| 214 if (green) { | |
| 215 *green = colours[i][1]; | |
| 216 } | |
| 217 if (blue) { | |
| 218 *blue = colours[i][2]; | |
| 219 } | |
| 220 | |
| 221 return ret; | |
| 222 } | |
| 223 | |
| 224 /* Return minimum quiet zones for each symbology */ | |
| 225 static int out_quiet_zones(const struct zint_symbol *symbol, const int hide_text, const int comp_xoffset, | |
| 226 float *left, float *right, float *top, float *bottom) { | |
| 227 int done = 0; | |
| 228 | |
| 229 *left = *right = *top = *bottom = 0.0f; | |
| 230 | |
| 231 /* These always have quiet zones set (previously used whitespace_width) */ | |
| 232 switch (symbol->symbology) { | |
| 233 case BARCODE_CODE16K: | |
| 234 /* BS EN 12323:2005 Section 4.5 (c) */ | |
| 235 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 236 *left = 10.0f; | |
| 237 *right = 1.0f; | |
| 238 } | |
| 239 done = 1; | |
| 240 break; | |
| 241 case BARCODE_CODE49: | |
| 242 /* ANSI/AIM BC6-2000 Section 2.4 */ | |
| 243 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 244 *left = 10.0f; | |
| 245 *right = 1.0f; | |
| 246 } | |
| 247 done = 1; | |
| 248 break; | |
| 249 case BARCODE_CODABLOCKF: | |
| 250 case BARCODE_HIBC_BLOCKF: | |
| 251 /* AIM ISS-X-24 Section 4.6.1 */ | |
| 252 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 253 *left = *right = 10.0f; | |
| 254 } | |
| 255 done = 1; | |
| 256 break; | |
| 257 case BARCODE_ITF14: | |
| 258 /* GS1 General Specifications 21.0.1 Section 5.3.2.2 */ | |
| 259 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 260 *left = *right = 10.0f; | |
| 261 } | |
| 262 done = 1; | |
| 263 break; | |
| 264 case BARCODE_EANX: | |
| 265 case BARCODE_EANX_CHK: | |
| 266 case BARCODE_EANX_CC: | |
| 267 case BARCODE_ISBNX: | |
| 268 /* GS1 General Specifications 21.0.1 Section 5.2.3.4 */ | |
| 269 switch (ustrlen(symbol->text)) { | |
| 270 case 13: /* EAN-13/ISBN */ | |
| 271 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 272 *left = comp_xoffset >= 10 ? 1.0f : 11.0f - comp_xoffset; /* Need at least 1X for CC-A/B */ | |
| 273 *right = 7.0f - (comp_xoffset != 0); | |
| 274 } else if (!hide_text) { | |
| 275 *left = comp_xoffset >= 10 ? 1.0f : 11.0f - comp_xoffset; /* Need for outside left digit */ | |
| 276 } | |
| 277 break; | |
| 278 case 16: /* EAN-13/ISBN + 2 digit add-on */ | |
| 279 case 19: /* EAN-13/ISBN + 5 digit add-on */ | |
| 280 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 281 *left = comp_xoffset >= 10 ? 1.0f : 11.0f - comp_xoffset; /* Need at least 1X for CC-A/B */ | |
| 282 *right = 5.0f; | |
| 283 } else if (!hide_text) { | |
| 284 *left = comp_xoffset >= 10 ? 1.0f : 11.0f - comp_xoffset; /* Need for outside left digit */ | |
| 285 } | |
| 286 break; | |
| 287 case 5: /* EAN-5 add-on */ | |
| 288 case 2: /* EAN-2 add-on */ | |
| 289 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 290 *right = 5.0f; | |
| 291 } | |
| 292 break; | |
| 293 case 8: /* EAN-8 */ | |
| 294 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 295 *left = comp_xoffset >= 6 ? 1.0f : 7.0f - comp_xoffset; /* Need at least 1X for CC-A/B */ | |
| 296 *right = 7.0f - (comp_xoffset != 0); | |
| 297 } | |
| 298 break; | |
| 299 default: /* EAN-8 + 2/5 digit add-on */ | |
| 300 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 301 *left = comp_xoffset >= 6 ? 1.0f : 7.0f - comp_xoffset; /* Need at least 1X for CC-A/B */ | |
| 302 *right = 5.0f; | |
| 303 } | |
| 304 break; | |
| 305 } | |
| 306 done = 1; | |
| 307 break; | |
| 308 case BARCODE_UPCA: | |
| 309 case BARCODE_UPCA_CHK: | |
| 310 case BARCODE_UPCA_CC: | |
| 311 /* GS1 General Specifications 21.0.1 Section 5.2.3.4 */ | |
| 312 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 313 *left = comp_xoffset >= 8 ? 1.0f : 9.0f - comp_xoffset; /* Need at least 1X for CC-A/B */ | |
| 314 if (ustrlen(symbol->text) > 12) { /* UPC-A + add-on */ | |
| 315 *right = 5.0f; | |
| 316 } else { | |
| 317 *right = 9.0f - (comp_xoffset != 0); | |
| 318 } | |
| 319 } else if (!hide_text) { | |
| 320 *left = comp_xoffset >= 8 ? 1.0f : 9.0f - comp_xoffset; /* Need for outside left digit */ | |
| 321 if (ustrlen(symbol->text) <= 12) { /* No add-on */ | |
| 322 *right = 9.0f - (comp_xoffset != 0); /* Need for outside right digit */ | |
| 323 } | |
| 324 } | |
| 325 done = 1; | |
| 326 break; | |
| 327 case BARCODE_UPCE: | |
| 328 case BARCODE_UPCE_CHK: | |
| 329 case BARCODE_UPCE_CC: | |
| 330 /* GS1 General Specifications 21.0.1 Section 5.2.3.4 */ | |
| 331 if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 332 *left = comp_xoffset >= 8 ? 1.0f : 9.0f - comp_xoffset; | |
| 333 if (ustrlen(symbol->text) > 8) { /* UPC-E + add-on */ | |
| 334 *right = 5.0f; | |
| 335 } else { | |
| 336 *right = 7.0f - (comp_xoffset != 0); | |
| 337 } | |
| 338 } else if (!hide_text) { | |
| 339 *left = comp_xoffset >= 8 ? 1.0f : 9.0f - comp_xoffset; /* Need for outside left digit */ | |
| 340 if (ustrlen(symbol->text) <= 8) { /* No add-on */ | |
| 341 *right = 7.0f - (comp_xoffset != 0); /* Need for outside right digit */ | |
| 342 } | |
| 343 } | |
| 344 done = 1; | |
| 345 break; | |
| 346 } | |
| 347 | |
| 348 if (done) { | |
| 349 return done; | |
| 350 } | |
| 351 | |
| 352 /* Only do others if flag set */ | |
| 353 if (!(symbol->output_options & BARCODE_QUIET_ZONES) || (symbol->output_options & BARCODE_NO_QUIET_ZONES)) { | |
| 354 return 0; | |
| 355 } | |
| 356 | |
| 357 switch (symbol->symbology) { | |
| 358 case BARCODE_CODE11: | |
| 359 /* No known standard. Following ITF-14, set to 10X */ | |
| 360 *left = *right = 10.0f; | |
| 361 done = 1; | |
| 362 break; | |
| 363 | |
| 364 case BARCODE_DXFILMEDGE: | |
| 365 /* No known standard. Add a little horizontal space to make the detection easier. Tested with Zxing-CPP */ | |
| 366 *left = *right = 1.8f; | |
| 367 done = 1; | |
| 368 break; | |
| 369 | |
| 370 case BARCODE_C25INTER: | |
| 371 /* ISO/IEC 16390:2007 Section 4.4 10X */ | |
| 372 *left = *right = 10.0f; | |
| 373 done = 1; | |
| 374 break; | |
| 375 case BARCODE_C25STANDARD: | |
| 376 case BARCODE_C25IATA: | |
| 377 case BARCODE_C25LOGIC: | |
| 378 case BARCODE_C25IND: | |
| 379 /* No known standards. Following C25INTER, set to 10X */ | |
| 380 *left = *right = 10.0f; | |
| 381 done = 1; | |
| 382 break; | |
| 383 | |
| 384 case BARCODE_CODE39: | |
| 385 case BARCODE_EXCODE39: | |
| 386 case BARCODE_LOGMARS: | |
| 387 case BARCODE_PZN: | |
| 388 case BARCODE_VIN: | |
| 389 case BARCODE_HIBC_39: | |
| 390 case BARCODE_CODE32: | |
| 391 /* ISO/IEC 16388:2007 Section 4.4 (d) */ | |
| 392 *left = *right = 10.0f; | |
| 393 done = 1; | |
| 394 break; | |
| 395 case BARCODE_GS1_128: /* GS1-128 */ | |
| 396 case BARCODE_EAN14: | |
| 397 /* GS1 General Specifications 21.0.1 Section 5.4.4.2 */ | |
| 398 *left = *right = 10.0f; | |
| 399 done = 1; | |
| 400 break; | |
| 401 case BARCODE_GS1_128_CC: | |
| 402 /* GS1 General Specifications 21.0.1 Sections 5.11.2.1 (CC-A), 5.11.2.2 (CC-B) & 5.11.2.3 (CC-C) */ | |
| 403 { | |
| 404 int comp_roffset = 0; /* Right offset of linear */ | |
| 405 int min_qz; /* Minimum quiet zone - 1X for CC-A/B, 2X for CC-C */ | |
| 406 int x; | |
| 407 for (x = symbol->width - 1; x >= 0 && !module_is_set(symbol, symbol->rows - 1, x); x--) { | |
| 408 comp_roffset++; | |
| 409 } | |
| 410 /* Determine if CC-C by counting initial start pattern */ | |
| 411 for (x = 0; x < 8 && module_is_set(symbol, 0, x); x++); | |
| 412 min_qz = x == 8 ? 2 : 1; | |
| 413 *left = comp_xoffset >= 10 - min_qz ? min_qz : 10.0f - comp_xoffset; | |
| 414 *right = comp_roffset >= 10 - min_qz ? min_qz : 10.0f - comp_roffset; | |
| 415 } | |
| 416 done = 1; | |
| 417 break; | |
| 418 case BARCODE_CODABAR: | |
| 419 /* BS EN 798:1995 Section 4.4.1 (d) */ | |
| 420 *left = *right = 10.0f; | |
| 421 done = 1; | |
| 422 break; | |
| 423 case BARCODE_CODE128: | |
| 424 case BARCODE_CODE128AB: | |
| 425 case BARCODE_HIBC_128: | |
| 426 case BARCODE_NVE18: | |
| 427 /* ISO/IEC 15417:2007 4.4.2 */ | |
| 428 *left = *right = 10.0f; | |
| 429 done = 1; | |
| 430 break; | |
| 431 case BARCODE_DPLEIT: | |
| 432 case BARCODE_DPIDENT: | |
| 433 /* Using C25INTER values TODO: Find doc */ | |
| 434 *left = *right = 10.0f; | |
| 435 done = 1; | |
| 436 break; | |
| 437 | |
| 438 case BARCODE_CODE93: | |
| 439 /* ANSI/AIM BC5-1995 Section 2.4 */ | |
| 440 *left = *right = 10.0f; | |
| 441 done = 1; | |
| 442 break; | |
| 443 | |
| 444 case BARCODE_FLAT: | |
| 445 /* TODO: Find doc (application defined according to TEC-IT) */ | |
| 446 break; | |
| 447 | |
| 448 case BARCODE_DBAR_OMN: /* GS1 Databar Omnidirectional */ | |
| 449 case BARCODE_DBAR_LTD: /* GS1 Databar Limited */ | |
| 450 case BARCODE_DBAR_EXP: /* GS1 Databar Expanded */ | |
| 451 case BARCODE_DBAR_STK: /* GS1 DataBar Stacked */ | |
| 452 case BARCODE_DBAR_OMNSTK: /* GS1 DataBar Stacked Omnidirectional */ | |
| 453 case BARCODE_DBAR_EXPSTK: /* GS1 Databar Expanded Stacked */ | |
| 454 /* GS1 General Specifications 21.0.1 Section 5.5.1.1 - Quiet Zones: None required */ | |
| 455 done = 1; | |
| 456 break; | |
| 457 | |
| 458 /* GS1 General Specifications 21.0.1 Sections 5.11.2.1 (CC-A) & 5.11.2.2 (CC-B) require 1X either side | |
| 459 but this may be supplied by the positioning of the linear component */ | |
| 460 case BARCODE_DBAR_OMN_CC: | |
| 461 case BARCODE_DBAR_LTD_CC: | |
| 462 /* Always have at least 1X to right of CC-A/B */ | |
| 463 if (comp_xoffset > 1) { /* Exclude DBAR_LTD_CC with CC-A which always has 1X to left */ | |
| 464 *left = 1.0f; | |
| 465 } | |
| 466 done = 1; | |
| 467 break; | |
| 468 case BARCODE_DBAR_STK_CC: | |
| 469 case BARCODE_DBAR_OMNSTK_CC: | |
| 470 /* Always have at least 1X to left of CC-A/B */ | |
| 471 *right = 1.0f; | |
| 472 done = 1; | |
| 473 break; | |
| 474 case BARCODE_DBAR_EXP_CC: | |
| 475 case BARCODE_DBAR_EXPSTK_CC: | |
| 476 /* Always have at least 1X to left and right of CC-A/B */ | |
| 477 done = 1; | |
| 478 break; | |
| 479 | |
| 480 case BARCODE_TELEPEN: | |
| 481 case BARCODE_TELEPEN_NUM: | |
| 482 /* Appears to be ~10X from diagram in Telepen Barcode Symbology information and History */ | |
| 483 /* TODO: Find better doc */ | |
| 484 *left = *right = 10.0f; | |
| 485 done = 1; | |
| 486 break; | |
| 487 | |
| 488 case BARCODE_POSTNET: | |
| 489 case BARCODE_PLANET: | |
| 490 /* USPS DMM 300 2006 (2011) 5.7 Barcode in Address Block | |
| 491 left/right 0.125" / 0.025" (X max) = 5, top/bottom 0.04" / 0.025" (X max) = 1.6 */ | |
| 492 *left = *right = 5.0f; | |
| 493 *top = *bottom = 1.6f; | |
| 494 done = 1; | |
| 495 break; | |
| 496 case BARCODE_CEPNET: | |
| 497 /* CEPNet e Código Bidimensional Datamatrix 2D (26/05/2021) 3.8 Localização */ | |
| 498 *left = *right = 10.0f; | |
| 499 *top = *bottom = 1.6f; /* As POSTNET (1.016mm == 0.025") */ | |
| 500 done = 1; | |
| 501 break; | |
| 502 | |
| 503 case BARCODE_MSI_PLESSEY: | |
| 504 /* TODO Find doc (TEC-IT says 12X so use that for the moment) */ | |
| 505 *left = *right = 12.0f; | |
| 506 done = 1; | |
| 507 break; | |
| 508 | |
| 509 case BARCODE_FIM: | |
| 510 /* USPS DMM 300 2006 (2011) 708.9.3 (top/bottom zero) | |
| 511 right 0.125" (min) / 0.03925" (X max) ~ 3.18, left 1.25" - 0.66725" (max width of barcode) | |
| 512 - 0.375 (max right) = 0.20775" / 0.03925" (X max) ~ 5.29 */ | |
| 513 *right = 3.18471336f; /* 0.125 / 0.03925 */ | |
| 514 *left = 5.29299355f; /* 0.20775 / 0.03925 */ | |
| 515 done = 1; | |
| 516 break; | |
| 517 case BARCODE_PHARMA: | |
| 518 case BARCODE_PHARMA_TWO: | |
| 519 /* Laetus Pharmacode Guide 2.2 from 6mm depending on context, 6mm / 1mm (Pharma Two X) = 6 */ | |
| 520 *left = *right = 6.0f; | |
| 521 done = 1; | |
| 522 break; | |
| 523 case BARCODE_PDF417: | |
| 524 case BARCODE_PDF417COMP: | |
| 525 case BARCODE_HIBC_PDF: | |
| 526 /* ISO/IEC 15438:2015 Section 5.8.3 */ | |
| 527 *left = *right = *top = *bottom = 2.0f; | |
| 528 done = 1; | |
| 529 break; | |
| 530 case BARCODE_MICROPDF417: | |
| 531 case BARCODE_HIBC_MICPDF: | |
| 532 /* ISO/IEC 24728:2006 Section 5.8.3 */ | |
| 533 *left = *right = *top = *bottom = 1.0f; | |
| 534 done = 1; | |
| 535 break; | |
| 536 case BARCODE_MAXICODE: | |
| 537 /* ISO/IEC 16023:2000 Section 4.11.5 */ | |
| 538 *left = *right = *top = *bottom = 1.0f; | |
| 539 done = 1; | |
| 540 break; | |
| 541 case BARCODE_QRCODE: | |
| 542 case BARCODE_UPNQR: | |
| 543 case BARCODE_HIBC_QR: | |
| 544 /* ISO/IEC 18004:2015 Section 9.1 */ | |
| 545 *left = *right = *top = *bottom = 4.0f; | |
| 546 done = 1; | |
| 547 break; | |
| 548 case BARCODE_DPD: | |
| 549 /* DPD Parcel Label Specification Version 2.4.1 Section 4.6.1.2, 5mm / 0.4mm (X max) = 12.5 */ | |
| 550 *left = *right = 12.5f; | |
| 551 done = 1; | |
| 552 break; | |
| 553 case BARCODE_MICROQR: | |
| 554 /* ISO/IEC 18004:2015 Section 9.1 */ | |
| 555 *left = *right = *top = *bottom = 2.0f; | |
| 556 done = 1; | |
| 557 break; | |
| 558 case BARCODE_RMQR: | |
| 559 /* ISO/IEC JTC1/SC31N000 Section 6.3.10 */ | |
| 560 *left = *right = *top = *bottom = 2.0f; | |
| 561 done = 1; | |
| 562 break; | |
| 563 case BARCODE_AUSPOST: | |
| 564 case BARCODE_AUSREPLY: | |
| 565 case BARCODE_AUSROUTE: | |
| 566 case BARCODE_AUSREDIRECT: | |
| 567 /* Customer Barcode Technical Specifications (2012) left/right 6mm / 0.6mm = 10, | |
| 568 top/bottom 2mm / 0.6mm ~ 3.33 (X max) */ | |
| 569 *left = *right = 10.0f; | |
| 570 *top = *bottom = 3.33333325f; /* 2.0 / 0.6 */ | |
| 571 done = 1; | |
| 572 break; | |
| 573 case BARCODE_RM4SCC: | |
| 574 /* Royal Mail Know How User's Manual Appendix C: using CBC, same as MAILMARK_4S, 2mm all round, | |
| 575 use X max (25.4mm / 39) i.e. 20 bars per 25.4mm */ | |
| 576 *left = *right = *top = *bottom = 3.07086611f; /* (2.0 * 39.0) / 25.4 */ | |
| 577 done = 1; | |
| 578 break; | |
| 579 case BARCODE_DATAMATRIX: | |
| 580 case BARCODE_HIBC_DM: | |
| 581 /* ISO/IEC 16022:2006 Section 7.1 */ | |
| 582 *left = *right = *top = *bottom = 1.0f; | |
| 583 done = 1; | |
| 584 break; | |
| 585 case BARCODE_JAPANPOST: | |
| 586 /* Japan Post Zip/Barcode Manual p.13 2mm all round, X 0.6mm, 2mm / 0.6mm ~ 3.33 */ | |
| 587 *left = *right = *top = *bottom = 3.33333325f; /* 2.0 / 0.6 */ | |
| 588 done = 1; | |
| 589 break; | |
| 590 | |
| 591 case BARCODE_KOREAPOST: | |
| 592 /* TODO Find doc (TEC-IT uses 10X but says not exactly specified - do the same for the moment) */ | |
| 593 *left = *right = 10.0f; | |
| 594 done = 1; | |
| 595 break; | |
| 596 | |
| 597 case BARCODE_USPS_IMAIL: | |
| 598 /* USPS-B-3200 (2015) Section 2.3.2 left/right 0.125", top/bottom 0.026", use X max (1 / 39) | |
| 599 i.e. 20 bars per inch */ | |
| 600 *left = *right = 4.875f; /* 0.125 * 39.0 */ | |
| 601 *top = *bottom = 1.01400006f; /* 0.026 * 39.0 */ | |
| 602 done = 1; | |
| 603 break; | |
| 604 | |
| 605 case BARCODE_PLESSEY: | |
| 606 /* TODO Find doc (see MSI_PLESSEY) */ | |
| 607 *left = *right = 12.0f; | |
| 608 done = 1; | |
| 609 break; | |
| 610 | |
| 611 case BARCODE_KIX: | |
| 612 /* Handleiding KIX code brochure - same as RM4SCC/MAILMARK_4S */ | |
| 613 *left = *right = *top = *bottom = 3.07086611f; /* (2.0 * 39.0) / 25.4 */ | |
| 614 done = 1; | |
| 615 break; | |
| 616 case BARCODE_AZTEC: | |
| 617 case BARCODE_HIBC_AZTEC: | |
| 618 case BARCODE_AZRUNE: | |
| 619 /* ISO/IEC 24778:2008 Section 4.1 (c) & Annex A.1 (Rune) - no quiet zone required */ | |
| 620 done = 1; | |
| 621 break; | |
| 622 case BARCODE_DAFT: | |
| 623 /* Generic so unlikely to be defined */ | |
| 624 done = 1; | |
| 625 break; | |
| 626 case BARCODE_DOTCODE: | |
| 627 /* ISS DotCode Rev. 4.0 Section 4.1 (3) (c) */ | |
| 628 *left = *right = *top = *bottom = 3.0f; | |
| 629 done = 1; | |
| 630 break; | |
| 631 case BARCODE_HANXIN: | |
| 632 /* ISO/IEC DIS 20830:2019 Section 4.2.8 (also Section 6.2) */ | |
| 633 *left = *right = *top = *bottom = 3.0f; | |
| 634 done = 1; | |
| 635 break; | |
| 636 case BARCODE_MAILMARK_4S: | |
| 637 /* Royal Mail Mailmark Barcode Definition Document Section 3.5.2, 2mm all round, use X max (25.4mm / 39) | |
| 638 i.e. 20 bars per 25.4mm */ | |
| 639 *left = *right = *top = *bottom = 3.07086611f; /* (2.0 * 39.0) / 25.4 */ | |
| 640 done = 1; | |
| 641 break; | |
| 642 case BARCODE_UPU_S10: | |
| 643 /* Universal Postal Union S10 Section 8 */ | |
| 644 *left = *right = 10.0f; | |
| 645 done = 1; | |
| 646 break; | |
| 647 case BARCODE_MAILMARK_2D: | |
| 648 /* Royal Mail Mailmark Barcode Definition Document, Section 2.4 */ | |
| 649 *left = *right = *top = *bottom = 4.0f; | |
| 650 done = 1; | |
| 651 break; | |
| 652 case BARCODE_CHANNEL: | |
| 653 /* ANSI/AIM BC12-1998 Section 4.4 (c) */ | |
| 654 *left = 1.0f; | |
| 655 *right = 2.0f; | |
| 656 done = 1; | |
| 657 break; | |
| 658 | |
| 659 case BARCODE_CODEONE: | |
| 660 /* USS Code One AIM 1994 Section 2.2.4 No quiet zone required for Versions A to H */ | |
| 661 if (symbol->option_2 == 9 || symbol->option_2 == 10) { /* Section 2.3.2 Versions S & T */ | |
| 662 *left = *right = *bottom = 1.0f; | |
| 663 } | |
| 664 done = 1; | |
| 665 break; | |
| 666 | |
| 667 case BARCODE_GRIDMATRIX: | |
| 668 /* AIMD014 (v 1.63) Section 7.1 */ | |
| 669 *left = *right = *top = *bottom = 6.0f; | |
| 670 done = 1; | |
| 671 break; | |
| 672 case BARCODE_ULTRA: | |
| 673 /* AIMD/TSC15032-43 (v 0.99c) Section 9.2 */ | |
| 674 *left = *right = *top = *bottom = 1.0f; | |
| 675 done = 1; | |
| 676 break; | |
| 677 | |
| 678 case BARCODE_BC412: | |
| 679 /* SEMI T1-95 Table 4 */ | |
| 680 *left = *right = 10.0f; | |
| 681 done = 1; | |
| 682 break; | |
| 683 } | |
| 684 | |
| 685 return done; /* For self-checking */ | |
| 686 } | |
| 687 | |
| 688 #ifdef ZINT_TEST /* Wrapper for direct testing */ | |
| 689 INTERNAL int out_quiet_zones_test(const struct zint_symbol *symbol, const int hide_text, const int comp_xoffset, | |
| 690 float *left, float *right, float *top, float *bottom) { | |
| 691 return out_quiet_zones(symbol, hide_text, comp_xoffset, left, right, top, bottom); | |
| 692 } | |
| 693 #endif | |
| 694 | |
| 695 /* Set left (x), top (y), right and bottom offsets for whitespace, also right quiet zone */ | |
| 696 INTERNAL void out_set_whitespace_offsets(const struct zint_symbol *symbol, const int hide_text, | |
| 697 const int comp_xoffset, float *p_xoffset, float *p_yoffset, float *p_roffset, float *p_boffset, | |
| 698 float *p_qz_right, const float scaler, int *p_xoffset_si, int *p_yoffset_si, int *p_roffset_si, | |
| 699 int *p_boffset_si, int *p_qz_right_si) { | |
| 700 float qz_left, qz_right, qz_top, qz_bottom; | |
| 701 | |
| 702 out_quiet_zones(symbol, hide_text, comp_xoffset, &qz_left, &qz_right, &qz_top, &qz_bottom); | |
| 703 | |
| 704 *p_xoffset = symbol->whitespace_width + qz_left; | |
| 705 *p_roffset = symbol->whitespace_width + qz_right; | |
| 706 if (symbol->output_options & BARCODE_BOX) { | |
| 707 *p_xoffset += symbol->border_width; | |
| 708 *p_roffset += symbol->border_width; | |
| 709 } | |
| 710 | |
| 711 *p_yoffset = symbol->whitespace_height + qz_top; | |
| 712 *p_boffset = symbol->whitespace_height + qz_bottom; | |
| 713 if (symbol->output_options & (BARCODE_BOX | BARCODE_BIND | BARCODE_BIND_TOP)) { | |
| 714 *p_yoffset += symbol->border_width; | |
| 715 if (!(symbol->output_options & BARCODE_BIND_TOP)) { /* Trumps BARCODE_BOX & BARCODE_BIND */ | |
| 716 *p_boffset += symbol->border_width; | |
| 717 } | |
| 718 } | |
| 719 | |
| 720 if (p_qz_right) { | |
| 721 *p_qz_right = qz_right; | |
| 722 } | |
| 723 | |
| 724 if (scaler) { | |
| 725 if (p_xoffset_si) { | |
| 726 *p_xoffset_si = (int) (*p_xoffset * scaler); | |
| 727 } | |
| 728 if (p_yoffset_si) { | |
| 729 *p_yoffset_si = (int) (*p_yoffset * scaler); | |
| 730 } | |
| 731 if (p_roffset_si) { | |
| 732 *p_roffset_si = (int) (*p_roffset * scaler); | |
| 733 } | |
| 734 if (p_boffset_si) { | |
| 735 *p_boffset_si = (int) (*p_boffset * scaler); | |
| 736 } | |
| 737 if (p_qz_right_si) { | |
| 738 *p_qz_right_si = (int) (qz_right * scaler); | |
| 739 } | |
| 740 } | |
| 741 } | |
| 742 | |
| 743 /* Set composite offset and main width excluding add-on (for start of add-on calc) and add-on text, returning | |
| 744 EAN/UPC type */ | |
| 745 INTERNAL int out_process_upcean(const struct zint_symbol *symbol, const int comp_xoffset, int *p_main_width, | |
| 746 unsigned char addon[6], int *p_addon_len, int *p_addon_gap) { | |
| 747 int main_width; /* Width of main linear symbol, excluding add-on */ | |
| 748 int upceanflag; /* EAN/UPC type flag */ | |
| 749 int i, j, latch; | |
| 750 const int text_length = (int) ustrlen(symbol->text); | |
| 751 | |
| 752 latch = 0; | |
| 753 j = 0; | |
| 754 /* Isolate add-on text */ | |
| 755 for (i = 6; i < text_length && j < 5; i++) { | |
| 756 if (latch == 1) { | |
| 757 /* Use dummy space-filled add-on if no hrt */ | |
| 758 addon[j] = symbol->show_hrt ? symbol->text[i] : ' '; | |
| 759 j++; | |
| 760 } else if (symbol->text[i] == '+') { | |
| 761 latch = 1; | |
| 762 } | |
| 763 } | |
| 764 addon[j] = '\0'; | |
| 765 if (latch) { | |
| 766 *p_addon_len = (int) ustrlen(addon); | |
| 767 if (symbol->symbology == BARCODE_UPCA || symbol->symbology == BARCODE_UPCA_CHK | |
| 768 || symbol->symbology == BARCODE_UPCA_CC) { | |
| 769 *p_addon_gap = symbol->option_2 >= 9 && symbol->option_2 <= 12 ? symbol->option_2 : 9; | |
| 770 } else { | |
| 771 *p_addon_gap = symbol->option_2 >= 7 && symbol->option_2 <= 12 ? symbol->option_2 : 7; | |
| 772 } | |
| 773 } | |
| 774 | |
| 775 upceanflag = 0; | |
| 776 main_width = symbol->width; | |
| 777 if ((symbol->symbology == BARCODE_EANX) || (symbol->symbology == BARCODE_EANX_CHK) | |
| 778 || (symbol->symbology == BARCODE_EANX_CC) || (symbol->symbology == BARCODE_ISBNX)) { | |
| 779 switch (text_length) { | |
| 780 case 13: /* EAN-13 */ | |
| 781 case 16: /* EAN-13 + EAN-2 */ | |
| 782 case 19: /* EAN-13 + EAN-5 */ | |
| 783 main_width = 95 + comp_xoffset; /* EAN-13 main symbol 95 modules wide */ | |
| 784 upceanflag = 13; | |
| 785 break; | |
| 786 case 2: | |
| 787 /* EAN-2 can't have add-on or be composite */ | |
| 788 upceanflag = 2; | |
| 789 break; | |
| 790 case 5: | |
| 791 /* EAN-5 can't have add-on or be composite */ | |
| 792 upceanflag = 5; | |
| 793 break; | |
| 794 default: | |
| 795 main_width = 68 + comp_xoffset; /* EAN-8 main symbol 68 modules wide */ | |
| 796 upceanflag = 8; | |
| 797 break; | |
| 798 } | |
| 799 } else if ((symbol->symbology == BARCODE_UPCA) || (symbol->symbology == BARCODE_UPCA_CHK) | |
| 800 || (symbol->symbology == BARCODE_UPCA_CC)) { | |
| 801 main_width = 95 + comp_xoffset; /* UPC-A main symbol 95 modules wide */ | |
| 802 upceanflag = 12; | |
| 803 } else if ((symbol->symbology == BARCODE_UPCE) || (symbol->symbology == BARCODE_UPCE_CHK) | |
| 804 || (symbol->symbology == BARCODE_UPCE_CC)) { | |
| 805 main_width = 51 + comp_xoffset; /* UPC-E main symbol 51 modules wide */ | |
| 806 upceanflag = 6; | |
| 807 } | |
| 808 | |
| 809 *p_main_width = main_width; | |
| 810 | |
| 811 return upceanflag; | |
| 812 } | |
| 813 | |
| 814 /* Calculate large bar height i.e. linear bars with zero row height that respond to the symbol height. | |
| 815 If scaler `si` non-zero (raster), then large_bar_height if non-zero or else row heights will be rounded | |
| 816 to nearest pixel and symbol height adjusted */ | |
| 817 INTERNAL float out_large_bar_height(struct zint_symbol *symbol, const int si, int *row_heights_si, | |
| 818 int *symbol_height_si) { | |
| 819 float fixed_height = 0.0f; | |
| 820 int zero_count = 0; | |
| 821 int round_rows = 0; | |
| 822 int i; | |
| 823 float large_bar_height = 0.0f; /* Not used if zero_count zero */ | |
| 824 | |
| 825 if (si) { | |
| 826 for (i = 0; i < symbol->rows; i++) { | |
| 827 if (symbol->row_height[i]) { | |
| 828 fixed_height += symbol->row_height[i]; | |
| 829 if (!round_rows && !isfintf(symbol->row_height[i] * si)) { | |
| 830 round_rows = 1; | |
| 831 } | |
| 832 } else { | |
| 833 zero_count++; | |
| 834 } | |
| 835 } | |
| 836 | |
| 837 if (zero_count) { | |
| 838 large_bar_height = stripf((symbol->height - fixed_height) / zero_count); | |
| 839 assert(large_bar_height >= 0.5f); /* Min row height as set by `set_height()` */ | |
| 840 if (!isfintf(large_bar_height * si)) { | |
| 841 large_bar_height = stripf(roundf(large_bar_height * si) / si); | |
| 842 } | |
| 843 symbol->height = stripf(large_bar_height * zero_count + fixed_height); | |
| 844 /* Note should never happen that have both zero_count and round_rows */ | |
| 845 } else { | |
| 846 if (round_rows) { | |
| 847 float total_height = 0.0f; | |
| 848 for (i = 0; i < symbol->rows; i++) { | |
| 849 if (!isfintf(symbol->row_height[i] * si)) { | |
| 850 symbol->row_height[i] = roundf(symbol->row_height[i] * si) / si; | |
| 851 } | |
| 852 total_height += symbol->row_height[i]; | |
| 853 } | |
| 854 symbol->height = stripf(total_height); | |
| 855 } | |
| 856 } | |
| 857 | |
| 858 if (row_heights_si) { | |
| 859 assert(symbol_height_si); | |
| 860 *symbol_height_si = 0; | |
| 861 for (i = 0; i < symbol->rows; i++) { | |
| 862 if (symbol->row_height[i]) { | |
| 863 row_heights_si[i] = (int) roundf(symbol->row_height[i] * si); | |
| 864 } else { | |
| 865 row_heights_si[i] = (int) roundf(large_bar_height * si); | |
| 866 } | |
| 867 *symbol_height_si += row_heights_si[i]; | |
| 868 } | |
| 869 } | |
| 870 } else { | |
| 871 for (i = 0; i < symbol->rows; i++) { | |
| 872 if (symbol->row_height[i]) { | |
| 873 fixed_height += symbol->row_height[i]; | |
| 874 } else { | |
| 875 zero_count++; | |
| 876 } | |
| 877 } | |
| 878 if (zero_count) { | |
| 879 large_bar_height = stripf((symbol->height - fixed_height) / zero_count); | |
| 880 assert(large_bar_height >= 0.5f); /* Min row height as set by `set_height()` */ | |
| 881 symbol->height = stripf(large_bar_height * zero_count + fixed_height); | |
| 882 } | |
| 883 } | |
| 884 | |
| 885 return large_bar_height; | |
| 886 } | |
| 887 | |
| 888 #ifdef _WIN32 | |
| 889 /* Convert UTF-8 to Windows wide chars. Ticket #288, props Marcel */ | |
| 890 #define utf8_to_wide(u, w, r) \ | |
| 891 { \ | |
| 892 int lenW; /* Includes NUL terminator */ \ | |
| 893 if ((lenW = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u, -1, NULL, 0)) == 0) return r; \ | |
| 894 w = (wchar_t *) z_alloca(sizeof(wchar_t) * lenW); \ | |
| 895 if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u, -1, w, lenW) == 0) return r; \ | |
| 896 } | |
| 897 | |
| 898 /* Do `fopen()` on Windows, assuming `filename` is UTF-8 encoded. Ticket #288, props Marcel */ | |
| 899 INTERNAL FILE *out_win_fopen(const char *filename, const char *mode) { | |
| 900 wchar_t *filenameW, *modeW; | |
| 901 | |
| 902 utf8_to_wide(filename, filenameW, NULL); | |
| 903 utf8_to_wide(mode, modeW, NULL); | |
| 904 | |
| 905 return _wfopen(filenameW, modeW); | |
| 906 } | |
| 907 #endif | |
| 908 | |
| 909 /* Make a directory; already existing dir okay */ | |
| 910 /* Adapted from https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950 and | |
| 911 https://nachtimwald.com/2019/07/10/recursive-create-directory-in-c-revisited/ */ | |
| 912 static int out_maybe_mkdir(const char *path) { | |
| 913 #ifdef _WIN32 | |
| 914 DWORD dwAttrib; | |
| 915 wchar_t *pathW; | |
| 916 | |
| 917 /* Assumes `path` is UTF-8 encoded */ | |
| 918 utf8_to_wide(path, pathW, 0); | |
| 919 | |
| 920 /* Try to make the directory */ | |
| 921 if (CreateDirectoryW(pathW, NULL) != 0) { /* Non-zero on success */ | |
| 922 return 0; | |
| 923 } | |
| 924 /* If it fails for any reason but already exists, fail */ | |
| 925 if (GetLastError() != ERROR_ALREADY_EXISTS) { | |
| 926 return -1; | |
| 927 } | |
| 928 /* Check if the existing path is a directory */ | |
| 929 if ((dwAttrib = GetFileAttributesW(pathW)) == (DWORD) -1 || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 930 return -1; | |
| 931 } | |
| 932 #else | |
| 933 struct stat st; | |
| 934 | |
| 935 /* Try to make the directory */ | |
| 936 if (mkdir(path, 0777) == 0) { | |
| 937 return 0; | |
| 938 } | |
| 939 /* If it fails for any reason but already exists, fail */ | |
| 940 if (errno != EEXIST) { | |
| 941 return -1; | |
| 942 } | |
| 943 /* Check if the existing path is a directory */ | |
| 944 if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode)) { | |
| 945 return -1; | |
| 946 } | |
| 947 #endif | |
| 948 | |
| 949 return 0; | |
| 950 } | |
| 951 | |
| 952 /* Create output file, creating sub-directories if necessary. Returns `fopen()` FILE pointer */ | |
| 953 INTERNAL FILE *out_fopen(const char filename[256], const char *mode) { | |
| 954 FILE *outfile; | |
| 955 | |
| 956 #ifdef _WIN32 | |
| 957 if (!(outfile = out_win_fopen(filename, mode))) { | |
| 958 #else | |
| 959 if (!(outfile = fopen(filename, mode))) { | |
| 960 #endif | |
| 961 char dirname[256]; | |
| 962 char *d; | |
| 963 #ifdef _WIN32 | |
| 964 char *dirend = strrchr(filename, '\\'); | |
| 965 if (!dirend) { | |
| 966 dirend = strrchr(filename, '/'); | |
| 967 } | |
| 968 #else | |
| 969 char *dirend = strrchr(filename, '/'); | |
| 970 #endif | |
| 971 if (!dirend) { | |
| 972 return outfile; | |
| 973 } | |
| 974 | |
| 975 /* Adapted from https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950 */ | |
| 976 /* Remove filename, leaving directories */ | |
| 977 memcpy(dirname, filename, dirend - filename); | |
| 978 dirname[dirend - filename] = '/'; | |
| 979 dirname[dirend - filename + 1] = '\0'; | |
| 980 #ifdef _WIN32 | |
| 981 for (d = dirname; *d; d++) { /* Convert to Unix separators */ | |
| 982 if (*d == '\\') { | |
| 983 *d = '/'; | |
| 984 } | |
| 985 } | |
| 986 #endif | |
| 987 for (d = dirname + 1; *d; d++) { /* Ignore slash at start if any */ | |
| 988 if (*d == '/' && *(d - 1) != '/') { /* Ignore double-slashes */ | |
| 989 *d = '\0'; /* Temporarily truncate */ | |
| 990 if (out_maybe_mkdir(dirname) != 0) { | |
| 991 return NULL; | |
| 992 } | |
| 993 *d = '/'; /* Restore */ | |
| 994 } | |
| 995 } | |
| 996 #ifdef _WIN32 | |
| 997 outfile = out_win_fopen(filename, mode); | |
| 998 #else | |
| 999 outfile = fopen(filename, mode); | |
| 1000 #endif | |
| 1001 } | |
| 1002 | |
| 1003 return outfile; | |
| 1004 } | |
| 1005 | |
| 1006 /* vim: set ts=4 sw=4 et : */ |
