| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006 |
- /* output.c - Common routines for raster/vector
- libzint - the open source barcode library
- Copyright (C) 2020-2024 Robin Stuart <rstuart114@gmail.com>
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the project nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
- */
- /* SPDX-License-Identifier: BSD-3-Clause */
- #include <assert.h>
- #include <errno.h>
- #include <math.h>
- #ifdef _WIN32
- #include <windows.h>
- #include <direct.h>
- #else
- #include <sys/stat.h> /* mkdir(2) */
- #endif
- #include "common.h"
- #include "output.h"
- #define OUT_SSET_F (IS_NUM_F | IS_UHX_F | IS_LHX_F) /* SSET "0123456789ABCDEFabcdef" */
- /* Helper to check an individual colour option is good */
- static int out_check_colour(struct zint_symbol *symbol, const char *colour, const char *name) {
- const char *comma1, *comma2, *comma3;
- int val;
- if ((comma1 = strchr(colour, ',')) == NULL) {
- const int len = (int) strlen(colour);
- if ((len != 6) && (len != 8)) {
- return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 880, "Malformed %s RGB colour (6 or 8 characters only)",
- name);
- }
- if (not_sane(OUT_SSET_F, (unsigned char *) colour, len)) {
- return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 881,
- "Malformed %1$s RGB colour '%2$s' (hexadecimal only)", name, colour);
- }
- return 0;
- }
- /* CMYK comma-separated percentages */
- if ((comma2 = strchr(comma1 + 1, ',')) == NULL || (comma3 = strchr(comma2 + 1, ',')) == NULL
- || strchr(comma3 + 1, ',') != NULL) {
- return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 882,
- "Malformed %s CMYK colour (4 decimal numbers, comma-separated)", name);
- }
- if (comma1 - colour > 3 || comma2 - (comma1 + 1) > 3 || comma3 - (comma2 + 1) > 3 || strlen(comma3 + 1) > 3) {
- return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 883,
- "Malformed %s CMYK colour (3 digit maximum per number)", name);
- }
- if ((val = to_int((const unsigned char *) colour, (int) (comma1 - colour))) == -1 || val > 100) {
- return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 884, "Malformed %s CMYK colour C (decimal 0 to 100 only)",
- name);
- }
- if ((val = to_int((const unsigned char *) (comma1 + 1), (int) (comma2 - (comma1 + 1)))) == -1 || val > 100) {
- return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 885, "Malformed %s CMYK colour M (decimal 0 to 100 only)",
- name);
- }
- if ((val = to_int((const unsigned char *) (comma2 + 1), (int) (comma3 - (comma2 + 1)))) == -1 || val > 100) {
- return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 886, "Malformed %s CMYK colour Y (decimal 0 to 100 only)",
- name);
- }
- if ((val = to_int((const unsigned char *) (comma3 + 1), (int) strlen(comma3 + 1))) == -1 || val > 100) {
- return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 887, "Malformed %s CMYK colour K (decimal 0 to 100 only)",
- name);
- }
- return 0;
- }
- /* Check colour options are good (`symbol->fgcolour`, `symbol->bgcolour`) */
- INTERNAL int out_check_colour_options(struct zint_symbol *symbol) {
- if (out_check_colour(symbol, symbol->fgcolour, "foreground") != 0) {
- return ZINT_ERROR_INVALID_OPTION;
- }
- if (out_check_colour(symbol, symbol->bgcolour, "background") != 0) {
- return ZINT_ERROR_INVALID_OPTION;
- }
- return 0;
- }
- /* Return RGB(A) from (well-formed) colour string. Returns 0 if RGB or converted CMYK, 1 if RGBA */
- INTERNAL int out_colour_get_rgb(const char *colour, unsigned char *red, unsigned char *green, unsigned char *blue,
- unsigned char *alpha) {
- const char *comma1, *comma2, *comma3;
- int black, val;
- if ((comma1 = strchr(colour, ',')) == NULL) {
- *red = 16 * ctoi(colour[0]) + ctoi(colour[1]);
- *green = 16 * ctoi(colour[2]) + ctoi(colour[3]);
- *blue = 16 * ctoi(colour[4]) + ctoi(colour[5]);
- if (alpha) {
- *alpha = colour[6] ? 16 * ctoi(colour[6]) + ctoi(colour[7]) : 0xFF;
- return colour[6] ? 1 : 0;
- }
- return 0;
- }
- comma2 = strchr(comma1 + 1, ',');
- comma3 = strchr(comma2 + 1, ',');
- black = 100 - to_int((const unsigned char *) (comma3 + 1), (int) strlen(comma3 + 1));
- val = 100 - to_int((const unsigned char *) colour, (int) (comma1 - colour)); /* Cyan */
- *red = (int) round((0xFF * val * black) / 10000.0);
- val = 100 - to_int((const unsigned char *) (comma1 + 1), (int) (comma2 - (comma1 + 1))); /* Magenta */
- *green = (int) round((0xFF * val * black) / 10000.0);
- val = 100 - to_int((const unsigned char *) (comma2 + 1), (int) (comma3 - (comma2 + 1))); /* Yellow */
- *blue = (int) round((0xFF * val * black) / 10000.0);
- if (alpha) {
- *alpha = 0xFF;
- }
- return 0;
- }
- /* Return CMYK from (well-formed) colour string. Returns 0 if CMYK, 1 if converted RBG, 2 if converted RGBA */
- INTERNAL int out_colour_get_cmyk(const char *colour, int *cyan, int *magenta, int *yellow, int *black,
- unsigned char *rgb_alpha) {
- const char *comma1;
- unsigned char red, green, blue, alpha;
- int have_alpha, k;
- if ((comma1 = strchr(colour, ',')) != NULL) {
- const char *const comma2 = strchr(comma1 + 1, ',');
- const char *const comma3 = strchr(comma2 + 1, ',');
- *cyan = to_int((const unsigned char *) colour, (int) (comma1 - colour));
- *magenta = to_int((const unsigned char *) (comma1 + 1), (int) (comma2 - (comma1 + 1)));
- *yellow = to_int((const unsigned char *) (comma2 + 1), (int) (comma3 - (comma2 + 1)));
- *black = to_int((const unsigned char *) (comma3 + 1), (int) strlen(comma3 + 1));
- if (rgb_alpha) {
- *rgb_alpha = 0xFF;
- }
- return 0;
- }
- have_alpha = out_colour_get_rgb(colour, &red, &green, &blue, &alpha);
- k = red;
- if (green > k) {
- k = green;
- }
- if (blue > k) {
- k = blue;
- }
- if (k == 0) {
- *cyan = *magenta = *yellow = 0;
- *black = 100;
- } else {
- *cyan = (int) round((k - red) * 100.0 / k);
- *magenta = (int) round((k - green) * 100.0 / k);
- *yellow = (int) round((k - blue) * 100.0 / k);
- *black = (int) round(((0xFF - k) * 100.0) / 0xFF);
- }
- if (rgb_alpha) {
- *rgb_alpha = have_alpha ? alpha : 0xFF;
- }
- return 1 + have_alpha;
- }
- /* Convert internal colour chars "WCBMRYGK" to RGB */
- INTERNAL int out_colour_char_to_rgb(const char ch, unsigned char *red, unsigned char *green, unsigned char *blue) {
- static const char chars[] = "WCBMRYGK";
- static const unsigned char colours[8][3] = {
- { 0xff, 0xff, 0xff, }, /* White */
- { 0, 0xff, 0xff, }, /* Cyan */
- { 0, 0, 0xff, }, /* Blue */
- { 0xff, 0, 0xff, }, /* Magenta */
- { 0xff, 0, 0, }, /* Red */
- { 0xff, 0xff, 0, }, /* Yellow */
- { 0, 0xff, 0, }, /* Green */
- { 0, 0, 0, }, /* Black */
- };
- int i = posn(chars, ch);
- int ret = i != -1;
- if (i == -1) {
- i = 7; /* Black (zeroize) */
- }
- if (red) {
- *red = colours[i][0];
- }
- if (green) {
- *green = colours[i][1];
- }
- if (blue) {
- *blue = colours[i][2];
- }
- return ret;
- }
- /* Return minimum quiet zones for each symbology */
- static int out_quiet_zones(const struct zint_symbol *symbol, const int hide_text, const int comp_xoffset,
- float *left, float *right, float *top, float *bottom) {
- int done = 0;
- *left = *right = *top = *bottom = 0.0f;
- /* These always have quiet zones set (previously used whitespace_width) */
- switch (symbol->symbology) {
- case BARCODE_CODE16K:
- /* BS EN 12323:2005 Section 4.5 (c) */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *left = 10.0f;
- *right = 1.0f;
- }
- done = 1;
- break;
- case BARCODE_CODE49:
- /* ANSI/AIM BC6-2000 Section 2.4 */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *left = 10.0f;
- *right = 1.0f;
- }
- done = 1;
- break;
- case BARCODE_CODABLOCKF:
- case BARCODE_HIBC_BLOCKF:
- /* AIM ISS-X-24 Section 4.6.1 */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *left = *right = 10.0f;
- }
- done = 1;
- break;
- case BARCODE_ITF14:
- /* GS1 General Specifications 21.0.1 Section 5.3.2.2 */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *left = *right = 10.0f;
- }
- done = 1;
- break;
- case BARCODE_EANX:
- case BARCODE_EANX_CHK:
- case BARCODE_EANX_CC:
- case BARCODE_ISBNX:
- /* GS1 General Specifications 21.0.1 Section 5.2.3.4 */
- switch (ustrlen(symbol->text)) {
- case 13: /* EAN-13/ISBN */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *left = comp_xoffset >= 10 ? 1.0f : 11.0f - comp_xoffset; /* Need at least 1X for CC-A/B */
- *right = 7.0f - (comp_xoffset != 0);
- } else if (!hide_text) {
- *left = comp_xoffset >= 10 ? 1.0f : 11.0f - comp_xoffset; /* Need for outside left digit */
- }
- break;
- case 16: /* EAN-13/ISBN + 2 digit add-on */
- case 19: /* EAN-13/ISBN + 5 digit add-on */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *left = comp_xoffset >= 10 ? 1.0f : 11.0f - comp_xoffset; /* Need at least 1X for CC-A/B */
- *right = 5.0f;
- } else if (!hide_text) {
- *left = comp_xoffset >= 10 ? 1.0f : 11.0f - comp_xoffset; /* Need for outside left digit */
- }
- break;
- case 5: /* EAN-5 add-on */
- case 2: /* EAN-2 add-on */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *right = 5.0f;
- }
- break;
- case 8: /* EAN-8 */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *left = comp_xoffset >= 6 ? 1.0f : 7.0f - comp_xoffset; /* Need at least 1X for CC-A/B */
- *right = 7.0f - (comp_xoffset != 0);
- }
- break;
- default: /* EAN-8 + 2/5 digit add-on */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *left = comp_xoffset >= 6 ? 1.0f : 7.0f - comp_xoffset; /* Need at least 1X for CC-A/B */
- *right = 5.0f;
- }
- break;
- }
- done = 1;
- break;
- case BARCODE_UPCA:
- case BARCODE_UPCA_CHK:
- case BARCODE_UPCA_CC:
- /* GS1 General Specifications 21.0.1 Section 5.2.3.4 */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *left = comp_xoffset >= 8 ? 1.0f : 9.0f - comp_xoffset; /* Need at least 1X for CC-A/B */
- if (ustrlen(symbol->text) > 12) { /* UPC-A + add-on */
- *right = 5.0f;
- } else {
- *right = 9.0f - (comp_xoffset != 0);
- }
- } else if (!hide_text) {
- *left = comp_xoffset >= 8 ? 1.0f : 9.0f - comp_xoffset; /* Need for outside left digit */
- if (ustrlen(symbol->text) <= 12) { /* No add-on */
- *right = 9.0f - (comp_xoffset != 0); /* Need for outside right digit */
- }
- }
- done = 1;
- break;
- case BARCODE_UPCE:
- case BARCODE_UPCE_CHK:
- case BARCODE_UPCE_CC:
- /* GS1 General Specifications 21.0.1 Section 5.2.3.4 */
- if (!(symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- *left = comp_xoffset >= 8 ? 1.0f : 9.0f - comp_xoffset;
- if (ustrlen(symbol->text) > 8) { /* UPC-E + add-on */
- *right = 5.0f;
- } else {
- *right = 7.0f - (comp_xoffset != 0);
- }
- } else if (!hide_text) {
- *left = comp_xoffset >= 8 ? 1.0f : 9.0f - comp_xoffset; /* Need for outside left digit */
- if (ustrlen(symbol->text) <= 8) { /* No add-on */
- *right = 7.0f - (comp_xoffset != 0); /* Need for outside right digit */
- }
- }
- done = 1;
- break;
- }
- if (done) {
- return done;
- }
- /* Only do others if flag set */
- if (!(symbol->output_options & BARCODE_QUIET_ZONES) || (symbol->output_options & BARCODE_NO_QUIET_ZONES)) {
- return 0;
- }
- switch (symbol->symbology) {
- case BARCODE_CODE11:
- /* No known standard. Following ITF-14, set to 10X */
- *left = *right = 10.0f;
- done = 1;
- break;
-
- case BARCODE_DXFILMEDGE:
- /* No known standard. Add a little horizontal space to make the detection easier. Tested with Zxing-CPP */
- *left = *right = 1.8f;
- done = 1;
- break;
- case BARCODE_C25INTER:
- /* ISO/IEC 16390:2007 Section 4.4 10X */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_C25STANDARD:
- case BARCODE_C25IATA:
- case BARCODE_C25LOGIC:
- case BARCODE_C25IND:
- /* No known standards. Following C25INTER, set to 10X */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_CODE39:
- case BARCODE_EXCODE39:
- case BARCODE_LOGMARS:
- case BARCODE_PZN:
- case BARCODE_VIN:
- case BARCODE_HIBC_39:
- case BARCODE_CODE32:
- /* ISO/IEC 16388:2007 Section 4.4 (d) */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_GS1_128: /* GS1-128 */
- case BARCODE_EAN14:
- /* GS1 General Specifications 21.0.1 Section 5.4.4.2 */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_GS1_128_CC:
- /* 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) */
- {
- int comp_roffset = 0; /* Right offset of linear */
- int min_qz; /* Minimum quiet zone - 1X for CC-A/B, 2X for CC-C */
- int x;
- for (x = symbol->width - 1; x >= 0 && !module_is_set(symbol, symbol->rows - 1, x); x--) {
- comp_roffset++;
- }
- /* Determine if CC-C by counting initial start pattern */
- for (x = 0; x < 8 && module_is_set(symbol, 0, x); x++);
- min_qz = x == 8 ? 2 : 1;
- *left = comp_xoffset >= 10 - min_qz ? min_qz : 10.0f - comp_xoffset;
- *right = comp_roffset >= 10 - min_qz ? min_qz : 10.0f - comp_roffset;
- }
- done = 1;
- break;
- case BARCODE_CODABAR:
- /* BS EN 798:1995 Section 4.4.1 (d) */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_CODE128:
- case BARCODE_CODE128AB:
- case BARCODE_HIBC_128:
- case BARCODE_NVE18:
- /* ISO/IEC 15417:2007 4.4.2 */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_DPLEIT:
- case BARCODE_DPIDENT:
- /* Using C25INTER values TODO: Find doc */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_CODE93:
- /* ANSI/AIM BC5-1995 Section 2.4 */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_FLAT:
- /* TODO: Find doc (application defined according to TEC-IT) */
- break;
- case BARCODE_DBAR_OMN: /* GS1 Databar Omnidirectional */
- case BARCODE_DBAR_LTD: /* GS1 Databar Limited */
- case BARCODE_DBAR_EXP: /* GS1 Databar Expanded */
- case BARCODE_DBAR_STK: /* GS1 DataBar Stacked */
- case BARCODE_DBAR_OMNSTK: /* GS1 DataBar Stacked Omnidirectional */
- case BARCODE_DBAR_EXPSTK: /* GS1 Databar Expanded Stacked */
- /* GS1 General Specifications 21.0.1 Section 5.5.1.1 - Quiet Zones: None required */
- done = 1;
- break;
- /* GS1 General Specifications 21.0.1 Sections 5.11.2.1 (CC-A) & 5.11.2.2 (CC-B) require 1X either side
- but this may be supplied by the positioning of the linear component */
- case BARCODE_DBAR_OMN_CC:
- case BARCODE_DBAR_LTD_CC:
- /* Always have at least 1X to right of CC-A/B */
- if (comp_xoffset > 1) { /* Exclude DBAR_LTD_CC with CC-A which always has 1X to left */
- *left = 1.0f;
- }
- done = 1;
- break;
- case BARCODE_DBAR_STK_CC:
- case BARCODE_DBAR_OMNSTK_CC:
- /* Always have at least 1X to left of CC-A/B */
- *right = 1.0f;
- done = 1;
- break;
- case BARCODE_DBAR_EXP_CC:
- case BARCODE_DBAR_EXPSTK_CC:
- /* Always have at least 1X to left and right of CC-A/B */
- done = 1;
- break;
- case BARCODE_TELEPEN:
- case BARCODE_TELEPEN_NUM:
- /* Appears to be ~10X from diagram in Telepen Barcode Symbology information and History */
- /* TODO: Find better doc */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_POSTNET:
- case BARCODE_PLANET:
- /* USPS DMM 300 2006 (2011) 5.7 Barcode in Address Block
- left/right 0.125" / 0.025" (X max) = 5, top/bottom 0.04" / 0.025" (X max) = 1.6 */
- *left = *right = 5.0f;
- *top = *bottom = 1.6f;
- done = 1;
- break;
- case BARCODE_CEPNET:
- /* CEPNet e Código Bidimensional Datamatrix 2D (26/05/2021) 3.8 Localização */
- *left = *right = 10.0f;
- *top = *bottom = 1.6f; /* As POSTNET (1.016mm == 0.025") */
- done = 1;
- break;
- case BARCODE_MSI_PLESSEY:
- /* TODO Find doc (TEC-IT says 12X so use that for the moment) */
- *left = *right = 12.0f;
- done = 1;
- break;
- case BARCODE_FIM:
- /* USPS DMM 300 2006 (2011) 708.9.3 (top/bottom zero)
- right 0.125" (min) / 0.03925" (X max) ~ 3.18, left 1.25" - 0.66725" (max width of barcode)
- - 0.375 (max right) = 0.20775" / 0.03925" (X max) ~ 5.29 */
- *right = 3.18471336f; /* 0.125 / 0.03925 */
- *left = 5.29299355f; /* 0.20775 / 0.03925 */
- done = 1;
- break;
- case BARCODE_PHARMA:
- case BARCODE_PHARMA_TWO:
- /* Laetus Pharmacode Guide 2.2 from 6mm depending on context, 6mm / 1mm (Pharma Two X) = 6 */
- *left = *right = 6.0f;
- done = 1;
- break;
- case BARCODE_PDF417:
- case BARCODE_PDF417COMP:
- case BARCODE_HIBC_PDF:
- /* ISO/IEC 15438:2015 Section 5.8.3 */
- *left = *right = *top = *bottom = 2.0f;
- done = 1;
- break;
- case BARCODE_MICROPDF417:
- case BARCODE_HIBC_MICPDF:
- /* ISO/IEC 24728:2006 Section 5.8.3 */
- *left = *right = *top = *bottom = 1.0f;
- done = 1;
- break;
- case BARCODE_MAXICODE:
- /* ISO/IEC 16023:2000 Section 4.11.5 */
- *left = *right = *top = *bottom = 1.0f;
- done = 1;
- break;
- case BARCODE_QRCODE:
- case BARCODE_UPNQR:
- case BARCODE_HIBC_QR:
- /* ISO/IEC 18004:2015 Section 9.1 */
- *left = *right = *top = *bottom = 4.0f;
- done = 1;
- break;
- case BARCODE_DPD:
- /* DPD Parcel Label Specification Version 2.4.1 Section 4.6.1.2, 5mm / 0.4mm (X max) = 12.5 */
- *left = *right = 12.5f;
- done = 1;
- break;
- case BARCODE_MICROQR:
- /* ISO/IEC 18004:2015 Section 9.1 */
- *left = *right = *top = *bottom = 2.0f;
- done = 1;
- break;
- case BARCODE_RMQR:
- /* ISO/IEC JTC1/SC31N000 Section 6.3.10 */
- *left = *right = *top = *bottom = 2.0f;
- done = 1;
- break;
- case BARCODE_AUSPOST:
- case BARCODE_AUSREPLY:
- case BARCODE_AUSROUTE:
- case BARCODE_AUSREDIRECT:
- /* Customer Barcode Technical Specifications (2012) left/right 6mm / 0.6mm = 10,
- top/bottom 2mm / 0.6mm ~ 3.33 (X max) */
- *left = *right = 10.0f;
- *top = *bottom = 3.33333325f; /* 2.0 / 0.6 */
- done = 1;
- break;
- case BARCODE_RM4SCC:
- /* Royal Mail Know How User's Manual Appendix C: using CBC, same as MAILMARK_4S, 2mm all round,
- use X max (25.4mm / 39) i.e. 20 bars per 25.4mm */
- *left = *right = *top = *bottom = 3.07086611f; /* (2.0 * 39.0) / 25.4 */
- done = 1;
- break;
- case BARCODE_DATAMATRIX:
- case BARCODE_HIBC_DM:
- /* ISO/IEC 16022:2006 Section 7.1 */
- *left = *right = *top = *bottom = 1.0f;
- done = 1;
- break;
- case BARCODE_JAPANPOST:
- /* Japan Post Zip/Barcode Manual p.13 2mm all round, X 0.6mm, 2mm / 0.6mm ~ 3.33 */
- *left = *right = *top = *bottom = 3.33333325f; /* 2.0 / 0.6 */
- done = 1;
- break;
- case BARCODE_KOREAPOST:
- /* TODO Find doc (TEC-IT uses 10X but says not exactly specified - do the same for the moment) */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_USPS_IMAIL:
- /* USPS-B-3200 (2015) Section 2.3.2 left/right 0.125", top/bottom 0.026", use X max (1 / 39)
- i.e. 20 bars per inch */
- *left = *right = 4.875f; /* 0.125 * 39.0 */
- *top = *bottom = 1.01400006f; /* 0.026 * 39.0 */
- done = 1;
- break;
- case BARCODE_PLESSEY:
- /* TODO Find doc (see MSI_PLESSEY) */
- *left = *right = 12.0f;
- done = 1;
- break;
- case BARCODE_KIX:
- /* Handleiding KIX code brochure - same as RM4SCC/MAILMARK_4S */
- *left = *right = *top = *bottom = 3.07086611f; /* (2.0 * 39.0) / 25.4 */
- done = 1;
- break;
- case BARCODE_AZTEC:
- case BARCODE_HIBC_AZTEC:
- case BARCODE_AZRUNE:
- /* ISO/IEC 24778:2008 Section 4.1 (c) & Annex A.1 (Rune) - no quiet zone required */
- done = 1;
- break;
- case BARCODE_DAFT:
- /* Generic so unlikely to be defined */
- done = 1;
- break;
- case BARCODE_DOTCODE:
- /* ISS DotCode Rev. 4.0 Section 4.1 (3) (c) */
- *left = *right = *top = *bottom = 3.0f;
- done = 1;
- break;
- case BARCODE_HANXIN:
- /* ISO/IEC DIS 20830:2019 Section 4.2.8 (also Section 6.2) */
- *left = *right = *top = *bottom = 3.0f;
- done = 1;
- break;
- case BARCODE_MAILMARK_4S:
- /* Royal Mail Mailmark Barcode Definition Document Section 3.5.2, 2mm all round, use X max (25.4mm / 39)
- i.e. 20 bars per 25.4mm */
- *left = *right = *top = *bottom = 3.07086611f; /* (2.0 * 39.0) / 25.4 */
- done = 1;
- break;
- case BARCODE_UPU_S10:
- /* Universal Postal Union S10 Section 8 */
- *left = *right = 10.0f;
- done = 1;
- break;
- case BARCODE_MAILMARK_2D:
- /* Royal Mail Mailmark Barcode Definition Document, Section 2.4 */
- *left = *right = *top = *bottom = 4.0f;
- done = 1;
- break;
- case BARCODE_CHANNEL:
- /* ANSI/AIM BC12-1998 Section 4.4 (c) */
- *left = 1.0f;
- *right = 2.0f;
- done = 1;
- break;
- case BARCODE_CODEONE:
- /* USS Code One AIM 1994 Section 2.2.4 No quiet zone required for Versions A to H */
- if (symbol->option_2 == 9 || symbol->option_2 == 10) { /* Section 2.3.2 Versions S & T */
- *left = *right = *bottom = 1.0f;
- }
- done = 1;
- break;
- case BARCODE_GRIDMATRIX:
- /* AIMD014 (v 1.63) Section 7.1 */
- *left = *right = *top = *bottom = 6.0f;
- done = 1;
- break;
- case BARCODE_ULTRA:
- /* AIMD/TSC15032-43 (v 0.99c) Section 9.2 */
- *left = *right = *top = *bottom = 1.0f;
- done = 1;
- break;
- case BARCODE_BC412:
- /* SEMI T1-95 Table 4 */
- *left = *right = 10.0f;
- done = 1;
- break;
- }
- return done; /* For self-checking */
- }
- #ifdef ZINT_TEST /* Wrapper for direct testing */
- INTERNAL int out_quiet_zones_test(const struct zint_symbol *symbol, const int hide_text, const int comp_xoffset,
- float *left, float *right, float *top, float *bottom) {
- return out_quiet_zones(symbol, hide_text, comp_xoffset, left, right, top, bottom);
- }
- #endif
- /* Set left (x), top (y), right and bottom offsets for whitespace, also right quiet zone */
- INTERNAL void out_set_whitespace_offsets(const struct zint_symbol *symbol, const int hide_text,
- const int comp_xoffset, float *p_xoffset, float *p_yoffset, float *p_roffset, float *p_boffset,
- float *p_qz_right, const float scaler, int *p_xoffset_si, int *p_yoffset_si, int *p_roffset_si,
- int *p_boffset_si, int *p_qz_right_si) {
- float qz_left, qz_right, qz_top, qz_bottom;
- out_quiet_zones(symbol, hide_text, comp_xoffset, &qz_left, &qz_right, &qz_top, &qz_bottom);
- *p_xoffset = symbol->whitespace_width + qz_left;
- *p_roffset = symbol->whitespace_width + qz_right;
- if (symbol->output_options & BARCODE_BOX) {
- *p_xoffset += symbol->border_width;
- *p_roffset += symbol->border_width;
- }
- *p_yoffset = symbol->whitespace_height + qz_top;
- *p_boffset = symbol->whitespace_height + qz_bottom;
- if (symbol->output_options & (BARCODE_BOX | BARCODE_BIND | BARCODE_BIND_TOP)) {
- *p_yoffset += symbol->border_width;
- if (!(symbol->output_options & BARCODE_BIND_TOP)) { /* Trumps BARCODE_BOX & BARCODE_BIND */
- *p_boffset += symbol->border_width;
- }
- }
- if (p_qz_right) {
- *p_qz_right = qz_right;
- }
- if (scaler) {
- if (p_xoffset_si) {
- *p_xoffset_si = (int) (*p_xoffset * scaler);
- }
- if (p_yoffset_si) {
- *p_yoffset_si = (int) (*p_yoffset * scaler);
- }
- if (p_roffset_si) {
- *p_roffset_si = (int) (*p_roffset * scaler);
- }
- if (p_boffset_si) {
- *p_boffset_si = (int) (*p_boffset * scaler);
- }
- if (p_qz_right_si) {
- *p_qz_right_si = (int) (qz_right * scaler);
- }
- }
- }
- /* Set composite offset and main width excluding add-on (for start of add-on calc) and add-on text, returning
- EAN/UPC type */
- INTERNAL int out_process_upcean(const struct zint_symbol *symbol, const int comp_xoffset, int *p_main_width,
- unsigned char addon[6], int *p_addon_len, int *p_addon_gap) {
- int main_width; /* Width of main linear symbol, excluding add-on */
- int upceanflag; /* EAN/UPC type flag */
- int i, j, latch;
- const int text_length = (int) ustrlen(symbol->text);
- latch = 0;
- j = 0;
- /* Isolate add-on text */
- for (i = 6; i < text_length && j < 5; i++) {
- if (latch == 1) {
- /* Use dummy space-filled add-on if no hrt */
- addon[j] = symbol->show_hrt ? symbol->text[i] : ' ';
- j++;
- } else if (symbol->text[i] == '+') {
- latch = 1;
- }
- }
- addon[j] = '\0';
- if (latch) {
- *p_addon_len = (int) ustrlen(addon);
- if (symbol->symbology == BARCODE_UPCA || symbol->symbology == BARCODE_UPCA_CHK
- || symbol->symbology == BARCODE_UPCA_CC) {
- *p_addon_gap = symbol->option_2 >= 9 && symbol->option_2 <= 12 ? symbol->option_2 : 9;
- } else {
- *p_addon_gap = symbol->option_2 >= 7 && symbol->option_2 <= 12 ? symbol->option_2 : 7;
- }
- }
- upceanflag = 0;
- main_width = symbol->width;
- if ((symbol->symbology == BARCODE_EANX) || (symbol->symbology == BARCODE_EANX_CHK)
- || (symbol->symbology == BARCODE_EANX_CC) || (symbol->symbology == BARCODE_ISBNX)) {
- switch (text_length) {
- case 13: /* EAN-13 */
- case 16: /* EAN-13 + EAN-2 */
- case 19: /* EAN-13 + EAN-5 */
- main_width = 95 + comp_xoffset; /* EAN-13 main symbol 95 modules wide */
- upceanflag = 13;
- break;
- case 2:
- /* EAN-2 can't have add-on or be composite */
- upceanflag = 2;
- break;
- case 5:
- /* EAN-5 can't have add-on or be composite */
- upceanflag = 5;
- break;
- default:
- main_width = 68 + comp_xoffset; /* EAN-8 main symbol 68 modules wide */
- upceanflag = 8;
- break;
- }
- } else if ((symbol->symbology == BARCODE_UPCA) || (symbol->symbology == BARCODE_UPCA_CHK)
- || (symbol->symbology == BARCODE_UPCA_CC)) {
- main_width = 95 + comp_xoffset; /* UPC-A main symbol 95 modules wide */
- upceanflag = 12;
- } else if ((symbol->symbology == BARCODE_UPCE) || (symbol->symbology == BARCODE_UPCE_CHK)
- || (symbol->symbology == BARCODE_UPCE_CC)) {
- main_width = 51 + comp_xoffset; /* UPC-E main symbol 51 modules wide */
- upceanflag = 6;
- }
- *p_main_width = main_width;
- return upceanflag;
- }
- /* Calculate large bar height i.e. linear bars with zero row height that respond to the symbol height.
- If scaler `si` non-zero (raster), then large_bar_height if non-zero or else row heights will be rounded
- to nearest pixel and symbol height adjusted */
- INTERNAL float out_large_bar_height(struct zint_symbol *symbol, const int si, int *row_heights_si,
- int *symbol_height_si) {
- float fixed_height = 0.0f;
- int zero_count = 0;
- int round_rows = 0;
- int i;
- float large_bar_height = 0.0f; /* Not used if zero_count zero */
- if (si) {
- for (i = 0; i < symbol->rows; i++) {
- if (symbol->row_height[i]) {
- fixed_height += symbol->row_height[i];
- if (!round_rows && !isfintf(symbol->row_height[i] * si)) {
- round_rows = 1;
- }
- } else {
- zero_count++;
- }
- }
- if (zero_count) {
- large_bar_height = stripf((symbol->height - fixed_height) / zero_count);
- assert(large_bar_height >= 0.5f); /* Min row height as set by `set_height()` */
- if (!isfintf(large_bar_height * si)) {
- large_bar_height = stripf(roundf(large_bar_height * si) / si);
- }
- symbol->height = stripf(large_bar_height * zero_count + fixed_height);
- /* Note should never happen that have both zero_count and round_rows */
- } else {
- if (round_rows) {
- float total_height = 0.0f;
- for (i = 0; i < symbol->rows; i++) {
- if (!isfintf(symbol->row_height[i] * si)) {
- symbol->row_height[i] = roundf(symbol->row_height[i] * si) / si;
- }
- total_height += symbol->row_height[i];
- }
- symbol->height = stripf(total_height);
- }
- }
- if (row_heights_si) {
- assert(symbol_height_si);
- *symbol_height_si = 0;
- for (i = 0; i < symbol->rows; i++) {
- if (symbol->row_height[i]) {
- row_heights_si[i] = (int) roundf(symbol->row_height[i] * si);
- } else {
- row_heights_si[i] = (int) roundf(large_bar_height * si);
- }
- *symbol_height_si += row_heights_si[i];
- }
- }
- } else {
- for (i = 0; i < symbol->rows; i++) {
- if (symbol->row_height[i]) {
- fixed_height += symbol->row_height[i];
- } else {
- zero_count++;
- }
- }
- if (zero_count) {
- large_bar_height = stripf((symbol->height - fixed_height) / zero_count);
- assert(large_bar_height >= 0.5f); /* Min row height as set by `set_height()` */
- symbol->height = stripf(large_bar_height * zero_count + fixed_height);
- }
- }
- return large_bar_height;
- }
- #ifdef _WIN32
- /* Convert UTF-8 to Windows wide chars. Ticket #288, props Marcel */
- #define utf8_to_wide(u, w, r) \
- { \
- int lenW; /* Includes NUL terminator */ \
- if ((lenW = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u, -1, NULL, 0)) == 0) return r; \
- w = (wchar_t *) z_alloca(sizeof(wchar_t) * lenW); \
- if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u, -1, w, lenW) == 0) return r; \
- }
- /* Do `fopen()` on Windows, assuming `filename` is UTF-8 encoded. Ticket #288, props Marcel */
- INTERNAL FILE *out_win_fopen(const char *filename, const char *mode) {
- wchar_t *filenameW, *modeW;
- utf8_to_wide(filename, filenameW, NULL);
- utf8_to_wide(mode, modeW, NULL);
- return _wfopen(filenameW, modeW);
- }
- #endif
- /* Make a directory; already existing dir okay */
- /* Adapted from https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950 and
- https://nachtimwald.com/2019/07/10/recursive-create-directory-in-c-revisited/ */
- static int out_maybe_mkdir(const char *path) {
- #ifdef _WIN32
- DWORD dwAttrib;
- wchar_t *pathW;
- /* Assumes `path` is UTF-8 encoded */
- utf8_to_wide(path, pathW, 0);
- /* Try to make the directory */
- if (CreateDirectoryW(pathW, NULL) != 0) { /* Non-zero on success */
- return 0;
- }
- /* If it fails for any reason but already exists, fail */
- if (GetLastError() != ERROR_ALREADY_EXISTS) {
- return -1;
- }
- /* Check if the existing path is a directory */
- if ((dwAttrib = GetFileAttributesW(pathW)) == (DWORD) -1 || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) {
- return -1;
- }
- #else
- struct stat st;
- /* Try to make the directory */
- if (mkdir(path, 0777) == 0) {
- return 0;
- }
- /* If it fails for any reason but already exists, fail */
- if (errno != EEXIST) {
- return -1;
- }
- /* Check if the existing path is a directory */
- if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode)) {
- return -1;
- }
- #endif
- return 0;
- }
- /* Create output file, creating sub-directories if necessary. Returns `fopen()` FILE pointer */
- INTERNAL FILE *out_fopen(const char filename[256], const char *mode) {
- FILE *outfile;
- #ifdef _WIN32
- if (!(outfile = out_win_fopen(filename, mode))) {
- #else
- if (!(outfile = fopen(filename, mode))) {
- #endif
- char dirname[256];
- char *d;
- #ifdef _WIN32
- char *dirend = strrchr(filename, '\\');
- if (!dirend) {
- dirend = strrchr(filename, '/');
- }
- #else
- char *dirend = strrchr(filename, '/');
- #endif
- if (!dirend) {
- return outfile;
- }
- /* Adapted from https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950 */
- /* Remove filename, leaving directories */
- memcpy(dirname, filename, dirend - filename);
- dirname[dirend - filename] = '/';
- dirname[dirend - filename + 1] = '\0';
- #ifdef _WIN32
- for (d = dirname; *d; d++) { /* Convert to Unix separators */
- if (*d == '\\') {
- *d = '/';
- }
- }
- #endif
- for (d = dirname + 1; *d; d++) { /* Ignore slash at start if any */
- if (*d == '/' && *(d - 1) != '/') { /* Ignore double-slashes */
- *d = '\0'; /* Temporarily truncate */
- if (out_maybe_mkdir(dirname) != 0) {
- return NULL;
- }
- *d = '/'; /* Restore */
- }
- }
- #ifdef _WIN32
- outfile = out_win_fopen(filename, mode);
- #else
- outfile = fopen(filename, mode);
- #endif
- }
- return outfile;
- }
- /* vim: set ts=4 sw=4 et : */
|