Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/zint/backend/code128.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 /* code128.c - Handles Code 128 and derivatives */ | |
| 2 /* | |
| 3 libzint - the open source barcode library | |
| 4 Copyright (C) 2008-2024 Robin Stuart <rstuart114@gmail.com> | |
| 5 Bugfixes thanks to Christian Sakowski and BogDan Vatra | |
| 6 | |
| 7 Redistribution and use in source and binary forms, with or without | |
| 8 modification, are permitted provided that the following conditions | |
| 9 are met: | |
| 10 | |
| 11 1. Redistributions of source code must retain the above copyright | |
| 12 notice, this list of conditions and the following disclaimer. | |
| 13 2. Redistributions in binary form must reproduce the above copyright | |
| 14 notice, this list of conditions and the following disclaimer in the | |
| 15 documentation and/or other materials provided with the distribution. | |
| 16 3. Neither the name of the project nor the names of its contributors | |
| 17 may be used to endorse or promote products derived from this software | |
| 18 without specific prior written permission. | |
| 19 | |
| 20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
| 21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE | |
| 24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 26 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 29 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 30 SUCH DAMAGE. | |
| 31 */ | |
| 32 /* SPDX-License-Identifier: BSD-3-Clause */ | |
| 33 | |
| 34 #include <assert.h> | |
| 35 #include <stdio.h> | |
| 36 #include "common.h" | |
| 37 #include "code128.h" | |
| 38 #include "gs1.h" | |
| 39 | |
| 40 #define C128_SYMBOL_MAX 102 /* 102 * 10 + 10 (check digit) + 13 (Stop) = 1043 */ | |
| 41 #define C128_SYMBOL_MAX_S "102" /* String version of above */ | |
| 42 | |
| 43 #define C128_VALUES_MAX (C128_SYMBOL_MAX + 2) /* Allow for debug/test check digit and Stop */ | |
| 44 | |
| 45 /* Code 128 tables checked against ISO/IEC 15417:2007 */ | |
| 46 | |
| 47 /* Code 128 character encodation - Table 1 (with final CODE16K-only character in place of Stop character) */ | |
| 48 INTERNAL_DATA const char C128Table[107][6] = { /* Used by CODABLOCKF and CODE16K also */ | |
| 49 {'2','1','2','2','2','2'}, {'2','2','2','1','2','2'}, {'2','2','2','2','2','1'}, {'1','2','1','2','2','3'}, | |
| 50 {'1','2','1','3','2','2'}, {'1','3','1','2','2','2'}, {'1','2','2','2','1','3'}, {'1','2','2','3','1','2'}, | |
| 51 {'1','3','2','2','1','2'}, {'2','2','1','2','1','3'}, {'2','2','1','3','1','2'}, {'2','3','1','2','1','2'}, | |
| 52 {'1','1','2','2','3','2'}, {'1','2','2','1','3','2'}, {'1','2','2','2','3','1'}, {'1','1','3','2','2','2'}, | |
| 53 {'1','2','3','1','2','2'}, {'1','2','3','2','2','1'}, {'2','2','3','2','1','1'}, {'2','2','1','1','3','2'}, | |
| 54 {'2','2','1','2','3','1'}, {'2','1','3','2','1','2'}, {'2','2','3','1','1','2'}, {'3','1','2','1','3','1'}, | |
| 55 {'3','1','1','2','2','2'}, {'3','2','1','1','2','2'}, {'3','2','1','2','2','1'}, {'3','1','2','2','1','2'}, | |
| 56 {'3','2','2','1','1','2'}, {'3','2','2','2','1','1'}, {'2','1','2','1','2','3'}, {'2','1','2','3','2','1'}, | |
| 57 {'2','3','2','1','2','1'}, {'1','1','1','3','2','3'}, {'1','3','1','1','2','3'}, {'1','3','1','3','2','1'}, | |
| 58 {'1','1','2','3','1','3'}, {'1','3','2','1','1','3'}, {'1','3','2','3','1','1'}, {'2','1','1','3','1','3'}, | |
| 59 {'2','3','1','1','1','3'}, {'2','3','1','3','1','1'}, {'1','1','2','1','3','3'}, {'1','1','2','3','3','1'}, | |
| 60 {'1','3','2','1','3','1'}, {'1','1','3','1','2','3'}, {'1','1','3','3','2','1'}, {'1','3','3','1','2','1'}, | |
| 61 {'3','1','3','1','2','1'}, {'2','1','1','3','3','1'}, {'2','3','1','1','3','1'}, {'2','1','3','1','1','3'}, | |
| 62 {'2','1','3','3','1','1'}, {'2','1','3','1','3','1'}, {'3','1','1','1','2','3'}, {'3','1','1','3','2','1'}, | |
| 63 {'3','3','1','1','2','1'}, {'3','1','2','1','1','3'}, {'3','1','2','3','1','1'}, {'3','3','2','1','1','1'}, | |
| 64 {'3','1','4','1','1','1'}, {'2','2','1','4','1','1'}, {'4','3','1','1','1','1'}, {'1','1','1','2','2','4'}, | |
| 65 {'1','1','1','4','2','2'}, {'1','2','1','1','2','4'}, {'1','2','1','4','2','1'}, {'1','4','1','1','2','2'}, | |
| 66 {'1','4','1','2','2','1'}, {'1','1','2','2','1','4'}, {'1','1','2','4','1','2'}, {'1','2','2','1','1','4'}, | |
| 67 {'1','2','2','4','1','1'}, {'1','4','2','1','1','2'}, {'1','4','2','2','1','1'}, {'2','4','1','2','1','1'}, | |
| 68 {'2','2','1','1','1','4'}, {'4','1','3','1','1','1'}, {'2','4','1','1','1','2'}, {'1','3','4','1','1','1'}, | |
| 69 {'1','1','1','2','4','2'}, {'1','2','1','1','4','2'}, {'1','2','1','2','4','1'}, {'1','1','4','2','1','2'}, | |
| 70 {'1','2','4','1','1','2'}, {'1','2','4','2','1','1'}, {'4','1','1','2','1','2'}, {'4','2','1','1','1','2'}, | |
| 71 {'4','2','1','2','1','1'}, {'2','1','2','1','4','1'}, {'2','1','4','1','2','1'}, {'4','1','2','1','2','1'}, | |
| 72 {'1','1','1','1','4','3'}, {'1','1','1','3','4','1'}, {'1','3','1','1','4','1'}, {'1','1','4','1','1','3'}, | |
| 73 {'1','1','4','3','1','1'}, {'4','1','1','1','1','3'}, {'4','1','1','3','1','1'}, {'1','1','3','1','4','1'}, | |
| 74 {'1','1','4','1','3','1'}, {'3','1','1','1','4','1'}, {'4','1','1','1','3','1'}, {'2','1','1','4','1','2'}, | |
| 75 {'2','1','1','2','1','4'}, {'2','1','1','2','3','2'}, {/* Only used by CODE16K */ '2','1','1','1','3','3'} | |
| 76 }; | |
| 77 | |
| 78 /* Code Set states */ | |
| 79 #define C128_A0 1 | |
| 80 #define C128_B0 2 | |
| 81 #define C128_A1 3 | |
| 82 #define C128_B1 4 | |
| 83 #define C128_C0 5 | |
| 84 #define C128_C1 6 | |
| 85 #define C128_STATES 7 | |
| 86 | |
| 87 /* Helpers to characterize Code Set states */ | |
| 88 #define C128_A0B0(cset) ((cset) <= C128_B0) | |
| 89 #define C128_C0C1(cset) ((cset) >= C128_C0) | |
| 90 #define C128_A0A1(cset) ((cset) & 1) /* Assuming !C */ | |
| 91 #define C128_AB(cset) ((cset) >> ((cset) > C128_B0)) /* Assuming !C */ | |
| 92 | |
| 93 /* Code Set states are named A0, B0, A1, B1, C0 and C1. A, B, and C are the Code 128 Code Sets. | |
| 94 0 is for ASCII and 1 is for extended ASCII range */ | |
| 95 static const char c128_latch_seq[C128_STATES][C128_STATES][3] = { | |
| 96 /* Current: A0 B0 A1 B1 C0 C1 Prior */ | |
| 97 { {0} }, | |
| 98 { {0}, { 0 }, {100 }, {101,101 }, {100,100,100}, { 99 }, {101,101, 99} }, /* A0 */ | |
| 99 { {0}, {101 }, { 0 }, {101,101,101}, {100,100 }, { 99 }, {100,100, 99} }, /* B0 */ | |
| 100 { {0}, {101,101 }, {100,100,100}, { 0 }, {100 }, {101,101, 99}, { 99 } }, /* A1 */ | |
| 101 { {0}, {101,101,101}, {100,100 }, {101 }, { 0 }, {100,100, 99}, { 99 } }, /* B1 */ | |
| 102 { {0}, {101 }, {100 }, {101,101,101}, {100,100,100}, { 0 }, { 0 } }, /* C0 */ | |
| 103 { {0}, {101,101,101}, {100,100,100}, {101 }, {100 }, { 0 }, { 0 } }, /* C1 */ | |
| 104 }; | |
| 105 static const char c128_latch_len[C128_STATES][C128_STATES] = { /* Lengths of above */ | |
| 106 /* Current: A0 B0 A1 B1 C0 C1 Prior */ | |
| 107 { 0 }, | |
| 108 { 0, 0, 1, 2, 3, 1, 3 }, /* A0 */ | |
| 109 { 0, 1, 0, 3, 2, 1, 3 }, /* B0 */ | |
| 110 { 0, 2, 3, 0, 1, 3, 1 }, /* A1 */ | |
| 111 { 0, 3, 2, 1, 0, 3, 1 }, /* B1 */ | |
| 112 { 0, 1, 1, 3, 3, 0, 64 }, /* C0 */ | |
| 113 { 0, 3, 3, 1, 1, 64, 0 }, /* C1 */ | |
| 114 }; | |
| 115 | |
| 116 /* Start sequences for normal, GS1_MODE and READER_INIT (mutually exclusive) */ | |
| 117 static const char c128_start_latch_seq[3][C128_STATES][4] = { | |
| 118 /* A0 B0 A1 B1 C0 C1 (not used) */ | |
| 119 { {0}, {103 }, {104 }, {103,101,101 }, {104,100,100 }, {105 } }, /* Normal */ | |
| 120 { {0}, {103,102}, {104,102}, {103,102,101,101}, {104,102,100,100}, {105,102 } }, /* GS1_MODE */ | |
| 121 { {0}, {103, 96}, {104, 96}, {103, 96,101,101}, {104, 96,100,100}, {104, 96,99} }, /* READER_INIT */ | |
| 122 }; | |
| 123 static const char c128_start_latch_len[3][C128_STATES] = { /* Lengths of above */ | |
| 124 /* A0 B0 A1 B1 C0 C1 (not used) */ | |
| 125 { 0, 1, 1, 3, 3, 1 }, /* Normal */ | |
| 126 { 0, 2, 2, 4, 4, 2 }, /* GS1_MODE */ | |
| 127 { 0, 2, 2, 4, 4, 3 }, /* READER_INIT */ | |
| 128 }; | |
| 129 | |
| 130 /* Output cost (length) for Code Sets A/B */ | |
| 131 static int c128_cost_ab(const int cset, const unsigned char ch, int *p_mode) { | |
| 132 const unsigned char mask_0x60 = ch & 0x60; /* 0 for (ch & 0x7F) < 32, 0x60 for (ch & 0x7F) >= 96 */ | |
| 133 const int ga = C128_A0A1(cset); | |
| 134 int cost = 1; | |
| 135 | |
| 136 assert(!C128_C0C1(cset)); | |
| 137 | |
| 138 /* SHIFT */ | |
| 139 if ((ga && mask_0x60 == 0x60) || (!ga && !mask_0x60)) { /* A and (ch & 0x7F) >= 96, or B and (ch & 0x7F) < 32 */ | |
| 140 cost++; | |
| 141 *p_mode |= 0x10; | |
| 142 } | |
| 143 | |
| 144 /* FNC4 */ | |
| 145 if (C128_A0B0(cset) == (ch >= 128)) { /* If A0/B0 and extended ASCII, or A1/B1 and ASCII */ | |
| 146 cost++; | |
| 147 *p_mode |= 0x20; | |
| 148 } | |
| 149 | |
| 150 return cost; | |
| 151 } | |
| 152 | |
| 153 /* Calculate the cost of encoding from `i` starting in Code Set `prior_cset` (see `c128_set_values()` below) */ | |
| 154 static int c128_cost(const unsigned char source[], const int length, const int i, const int prior_cset, | |
| 155 const int start_idx, const char priority[C128_STATES], const char fncs[C128_MAX], | |
| 156 const char manuals[C128_MAX], short (*costs)[C128_STATES], char (*modes)[C128_STATES]) { | |
| 157 | |
| 158 const unsigned char ch = source[i]; | |
| 159 const char *const latch_len = prior_cset == 0 ? c128_start_latch_len[start_idx] : c128_latch_len[prior_cset]; | |
| 160 const int is_fnc1 = ch == '\x1D' && fncs[i]; | |
| 161 const int can_c = is_fnc1 || (z_isdigit(ch) && z_isdigit(source[i + 1])); /* Assumes source NUL-terminated */ | |
| 162 const int manual_c_fail = !can_c && manuals[i] == C128_C0; /* C requested but not doable */ | |
| 163 int min_cost = 999999; /* Max possible cost less than 2 * 256 */ | |
| 164 int min_mode = 0; | |
| 165 int p; | |
| 166 | |
| 167 for (p = 0; priority[p]; p++) { | |
| 168 const int cset = priority[p]; | |
| 169 if (C128_C0C1(cset)) { | |
| 170 if (can_c && (!manuals[i] || manuals[i] == C128_C0)) { | |
| 171 const int incr = 2 - is_fnc1; | |
| 172 int mode = prior_cset; | |
| 173 int cost = 1; | |
| 174 if (prior_cset != cset) { | |
| 175 cost += latch_len[cset]; | |
| 176 mode = cset; | |
| 177 } | |
| 178 if (i + incr < length) { | |
| 179 /* Check if memoized */ | |
| 180 if (costs[i + incr][cset]) { | |
| 181 cost += costs[i + incr][cset]; | |
| 182 } else { | |
| 183 cost += c128_cost(source, length, i + incr, cset, 0 /*start_idx*/, priority, fncs, manuals, | |
| 184 costs, modes); | |
| 185 } | |
| 186 } | |
| 187 if (cost < min_cost) { | |
| 188 min_cost = cost; | |
| 189 min_mode = mode; | |
| 190 } | |
| 191 } | |
| 192 } else { | |
| 193 if (!manuals[i] || manuals[i] == C128_AB(cset) || manual_c_fail) { | |
| 194 int mode = cset; | |
| 195 int cost = is_fnc1 ? 1 : c128_cost_ab(cset, ch, &mode); | |
| 196 if (prior_cset != cset) { | |
| 197 cost += latch_len[cset]; | |
| 198 } | |
| 199 if (i + 1 < length) { | |
| 200 /* Check if memoized */ | |
| 201 if (costs[i + 1][cset]) { | |
| 202 cost += costs[i + 1][cset]; | |
| 203 } else { | |
| 204 cost += c128_cost(source, length, i + 1, cset, 0 /*start_idx*/, priority, fncs, manuals, | |
| 205 costs, modes); | |
| 206 } | |
| 207 } | |
| 208 if (cost < min_cost) { | |
| 209 min_cost = cost; | |
| 210 min_mode = mode; | |
| 211 } | |
| 212 } | |
| 213 } | |
| 214 } | |
| 215 assert(min_cost != 999999); | |
| 216 | |
| 217 costs[i][prior_cset] = min_cost; | |
| 218 modes[i][prior_cset] = min_mode; | |
| 219 | |
| 220 return min_cost; | |
| 221 } | |
| 222 | |
| 223 /* Minimal encoding using Divide-And-Conquer with Memoization by Alex Geller - see | |
| 224 https://github.com/zxing/zxing/commit/94fb277607003c070ffd1413754a782f3f87cbcd | |
| 225 Many ideas, especially enabling extended ASCII, taken from BWIPP minimal encoding by Bue Jensen - see | |
| 226 https://github.com/bwipp/postscriptbarcode/pull/278 */ | |
| 227 static int c128_set_values(const unsigned char source[], const int length, const int start_idx, | |
| 228 const char priority[C128_STATES], const char fncs[C128_MAX], const char manuals[C128_MAX], | |
| 229 int values[C128_VALUES_MAX], int *p_final_cset) { | |
| 230 | |
| 231 short (*costs)[C128_STATES] = (short (*)[C128_STATES]) z_alloca(sizeof(*costs) * length); | |
| 232 char (*modes)[C128_STATES] = (char (*)[C128_STATES]) z_alloca(sizeof(*modes) * length); | |
| 233 int glyph_count = 0; | |
| 234 int cset = 0; | |
| 235 int i; | |
| 236 | |
| 237 memset(costs, 0, sizeof(*costs) * length); | |
| 238 | |
| 239 c128_cost(source, length, 0 /*i*/, 0 /*prior_cset*/, start_idx, priority, fncs, manuals, costs, modes); | |
| 240 | |
| 241 if (costs[0][0] > C128_SYMBOL_MAX) { /* Total minimal cost (glyph count) */ | |
| 242 return costs[0][0]; | |
| 243 } | |
| 244 | |
| 245 /* Output codewords into `values` */ | |
| 246 for (i = 0; i < length; i++) { | |
| 247 const unsigned char ch = source[i]; | |
| 248 const int is_fnc1 = ch == '\x1D' && fncs[i]; | |
| 249 const int mode = modes[i][cset]; | |
| 250 const int prior_cset = cset; | |
| 251 | |
| 252 cset = mode & 0x0F; | |
| 253 assert(cset); | |
| 254 if (cset != prior_cset) { | |
| 255 int j; | |
| 256 if (prior_cset == 0) { | |
| 257 for (j = 0; j < c128_start_latch_len[start_idx][cset]; j++) { | |
| 258 values[glyph_count++] = c128_start_latch_seq[start_idx][cset][j]; | |
| 259 } | |
| 260 } else { | |
| 261 for (j = 0; j < c128_latch_len[prior_cset][cset]; j++) { | |
| 262 values[glyph_count++] = c128_latch_seq[prior_cset][cset][j]; | |
| 263 } | |
| 264 } | |
| 265 } | |
| 266 if (mode >= 0x30) { | |
| 267 /* Extended Shift A/B */ | |
| 268 values[glyph_count++] = 100 + C128_A0A1(cset); /* FNC4 */ | |
| 269 values[glyph_count++] = 98; /* SHIFT */ | |
| 270 } else if (mode >= 0x20) { | |
| 271 /* Extended A/B */ | |
| 272 values[glyph_count++] = 100 + C128_A0A1(cset); /* FNC4 */ | |
| 273 } else if (mode >= 0x10) { | |
| 274 /* Shift A/B */ | |
| 275 values[glyph_count++] = 98; /* SHIFT */ | |
| 276 } | |
| 277 if (is_fnc1) { | |
| 278 values[glyph_count++] = 102; /* FNC1 */ | |
| 279 } else if (C128_C0C1(cset)) { | |
| 280 assert(i + 1 < length); /* Guaranteed by algorithm */ | |
| 281 values[glyph_count++] = (ch - '0') * 10 + source[++i] - '0'; | |
| 282 } else { | |
| 283 /* (ch & 0x7F) < 32 ? (ch & 0x7F) + 64 : (ch & 0x7F) - 32 */ | |
| 284 values[glyph_count++] = (ch & 0x7F) + 96 * !(ch & 0x60) - 32; | |
| 285 } | |
| 286 } | |
| 287 assert(glyph_count == costs[0][0]); | |
| 288 | |
| 289 if (p_final_cset) { | |
| 290 *p_final_cset = cset; | |
| 291 } | |
| 292 | |
| 293 return glyph_count; | |
| 294 } | |
| 295 | |
| 296 /* Helper to write out symbol, calculating check digit */ | |
| 297 static void c128_expand(struct zint_symbol *symbol, int values[C128_VALUES_MAX], int glyph_count) { | |
| 298 char dest[640]; /* (102 + 1 (check digit)) * 6 + 7 (Stop) = 625 */ | |
| 299 char *d = dest; | |
| 300 int total_sum; | |
| 301 int i; | |
| 302 | |
| 303 /* Destination setting and check digit calculation */ | |
| 304 memcpy(d, C128Table[values[0]], 6); | |
| 305 d += 6; | |
| 306 total_sum = values[0]; | |
| 307 | |
| 308 for (i = 1; i < glyph_count; i++, d += 6) { | |
| 309 memcpy(d, C128Table[values[i]], 6); | |
| 310 total_sum += values[i] * i; /* Note can't overflow as 106 * C128_SYMBOL_MAX * C128_SYMBOL_MAX = 1102824 */ | |
| 311 } | |
| 312 total_sum %= 103; | |
| 313 memcpy(d, C128Table[total_sum], 6); | |
| 314 d += 6; | |
| 315 values[glyph_count++] = total_sum; /* For debug/test */ | |
| 316 | |
| 317 /* Stop character */ | |
| 318 memcpy(d, "2331112", 7); | |
| 319 d += 7; | |
| 320 values[glyph_count++] = 106; /* For debug/test */ | |
| 321 | |
| 322 if (symbol->debug & ZINT_DEBUG_PRINT) { | |
| 323 fputs("Codewords:", stdout); | |
| 324 for (i = 0; i < glyph_count; i++) { | |
| 325 printf(" %d", values[i]); | |
| 326 } | |
| 327 printf(" (%d)\n", glyph_count); | |
| 328 printf("Barspaces: %.*s\n", (int) (d - dest), dest); | |
| 329 printf("Checksum: %d\n", total_sum); | |
| 330 } | |
| 331 #ifdef ZINT_TEST | |
| 332 if (symbol->debug & ZINT_DEBUG_TEST) { | |
| 333 debug_test_codeword_dump_int(symbol, values, glyph_count); | |
| 334 } | |
| 335 #endif | |
| 336 | |
| 337 expand(symbol, dest, d - dest); | |
| 338 } | |
| 339 | |
| 340 /* Helper to set `priority` array based on flags */ | |
| 341 static void c128_set_priority(char priority[C128_STATES], const int have_a, const int have_b, const int have_c, | |
| 342 const int have_extended) { | |
| 343 int i = 0; | |
| 344 if (have_c) { | |
| 345 priority[i++] = C128_C0; | |
| 346 } | |
| 347 if (have_b || !have_a) { | |
| 348 priority[i++] = C128_B0; | |
| 349 } | |
| 350 if (have_a) { | |
| 351 priority[i++] = C128_A0; | |
| 352 } | |
| 353 if (have_extended) { | |
| 354 if (have_c) { | |
| 355 priority[i++] = C128_C1; | |
| 356 } | |
| 357 if (have_b || !have_a) { | |
| 358 priority[i++] = C128_B1; | |
| 359 } | |
| 360 if (have_a) { | |
| 361 priority[i++] = C128_A1; | |
| 362 } | |
| 363 } | |
| 364 priority[i] = 0; | |
| 365 } | |
| 366 | |
| 367 /* Handle Code 128, 128AB and HIBC 128 */ | |
| 368 INTERNAL int code128(struct zint_symbol *symbol, unsigned char source[], int length) { | |
| 369 int i; | |
| 370 int error_number; | |
| 371 char manuals[C128_MAX] = {0}; | |
| 372 char fncs[C128_MAX] = {0}; /* Manual FNC1 positions */ | |
| 373 int have_fnc1 = 0; /* Whether have at least 1 manual FNC1 */ | |
| 374 int have_a = 0, have_b = 0, have_c = 0, have_extended = 0; | |
| 375 char priority[C128_STATES]; | |
| 376 int values[C128_VALUES_MAX] = {0}; | |
| 377 int glyph_count; | |
| 378 unsigned char src_buf[C128_MAX + 1]; | |
| 379 unsigned char *src = source; | |
| 380 const int ab_only = symbol->symbology == BARCODE_CODE128AB; | |
| 381 const int start_idx = (symbol->output_options & READER_INIT) ? 2 : 0; | |
| 382 | |
| 383 if (length > C128_MAX) { | |
| 384 /* This only blocks ridiculously long input - the actual length of the | |
| 385 resulting barcode depends on the type of data, so this is trapped later */ | |
| 386 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 340, "Input length %d too long (maximum " C128_MAX_S ")", length); | |
| 387 } | |
| 388 | |
| 389 /* Detect special Code Set escapes for Code 128 in extra escape mode only */ | |
| 390 if ((symbol->input_mode & EXTRA_ESCAPE_MODE) && symbol->symbology == BARCODE_CODE128) { | |
| 391 char manual = 0; | |
| 392 int j = 0; | |
| 393 for (i = 0; i < length; i++) { | |
| 394 if (source[i] == '\\' && i + 2 < length && source[i + 1] == '^' | |
| 395 && ((source[i + 2] >= '@' && source[i + 2] <= 'C') || source[i + 2] == '1' | |
| 396 || source[i + 2] == '^')) { | |
| 397 if (source[i + 2] == '^') { /* Escape sequence '\^^' */ | |
| 398 manuals[j] = manual; | |
| 399 src_buf[j++] = source[i++]; | |
| 400 manuals[j] = manual; | |
| 401 src_buf[j++] = source[i++]; | |
| 402 /* Drop second '^' */ | |
| 403 } else if (source[i + 2] == '1') { /* FNC1 */ | |
| 404 i += 2; | |
| 405 fncs[j] = have_fnc1 = 1; | |
| 406 manuals[j] = manual; | |
| 407 src_buf[j++] = '\x1D'; /* Manual FNC1 dummy */ | |
| 408 } else { /* Manual mode A/B/C/@ */ | |
| 409 i += 2; | |
| 410 manual = source[i] == 'C' ? C128_C0 : source[i] - '@'; /* Assuming A0 = 1, B0 = 2 */ | |
| 411 } | |
| 412 } else { | |
| 413 manuals[j] = manual; | |
| 414 src_buf[j++] = source[i]; | |
| 415 } | |
| 416 } | |
| 417 if (j != length) { | |
| 418 length = j; | |
| 419 if (length == 0) { | |
| 420 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 842, "No input data"); | |
| 421 } | |
| 422 src = src_buf; | |
| 423 src[length] = '\0'; | |
| 424 if (symbol->debug & ZINT_DEBUG_PRINT) { | |
| 425 fputs("Manuals: ", stdout); | |
| 426 for (i = 0; i < length; i++) { | |
| 427 printf("%c", manuals[i] == C128_C0 ? 'C' : '@' + manuals[i]); /* Assuming A0 = 1, B0 = 2 */ | |
| 428 } | |
| 429 fputc('\n', stdout); | |
| 430 } | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 /* Classify data to detect which Code Set states are needed */ | |
| 435 if (ab_only) { | |
| 436 for (i = 0; i < length; i++) { | |
| 437 const unsigned char ch = src[i]; | |
| 438 const unsigned char mask_0x60 = ch & 0x60; /* 0 for (ch & 0x7F) < 32, 0x60 for (ch & 0x7F) >= 96 */ | |
| 439 have_extended |= ch & 0x80; | |
| 440 have_a |= !mask_0x60; | |
| 441 have_b |= mask_0x60 == 0x60; | |
| 442 } | |
| 443 } else { | |
| 444 int prev_digit, digit = 0; | |
| 445 for (i = 0; i < length; i++) { | |
| 446 const unsigned char ch = src[i]; | |
| 447 const int is_fnc1 = ch == '\x1D' && fncs[i]; | |
| 448 if (!is_fnc1) { | |
| 449 const unsigned char mask_0x60 = ch & 0x60; /* 0 for (ch & 0x7F) < 32, 0x60 for (ch & 0x7F) >= 96 */ | |
| 450 const int manual = manuals[i]; | |
| 451 have_extended |= ch & 0x80; | |
| 452 have_a |= !mask_0x60 || manual == C128_A0; | |
| 453 have_b |= mask_0x60 == 0x60 || manual == C128_B0; | |
| 454 prev_digit = digit; | |
| 455 digit = z_isdigit(ch); | |
| 456 have_c |= prev_digit && digit; | |
| 457 } | |
| 458 } | |
| 459 } | |
| 460 c128_set_priority(priority, have_a, have_b, have_c, have_extended); | |
| 461 | |
| 462 glyph_count = c128_set_values(src, length, start_idx, priority, fncs, manuals, values, NULL /*p_final_cset*/); | |
| 463 | |
| 464 if (symbol->debug & ZINT_DEBUG_PRINT) { | |
| 465 printf("Data (%d): %.*s", length, length >= 100 ? 1 : length >= 10 ? 2 : 3, " "); | |
| 466 debug_print_escape(src, length, NULL); | |
| 467 printf("\nGlyphs: %d\n", glyph_count); | |
| 468 } | |
| 469 | |
| 470 /* Now we know how long the barcode is - stop it from being too long */ | |
| 471 if (glyph_count > C128_SYMBOL_MAX) { | |
| 472 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 341, | |
| 473 "Input too long, requires %d symbol characters (maximum " C128_SYMBOL_MAX_S ")", glyph_count); | |
| 474 } | |
| 475 | |
| 476 /* Now we can get on with it! */ | |
| 477 c128_expand(symbol, values, glyph_count); | |
| 478 | |
| 479 /* ISO/IEC 15417:2007 leaves dimensions/height as application specification */ | |
| 480 | |
| 481 /* HRT */ | |
| 482 if (have_fnc1) { | |
| 483 /* Remove any manual FNC1 dummies ('\x1D') */ | |
| 484 int j = 0; | |
| 485 for (i = 0; i < length; i++) { | |
| 486 if (!fncs[i]) { | |
| 487 src[j++] = src[i]; | |
| 488 } | |
| 489 } | |
| 490 length = j; | |
| 491 } | |
| 492 error_number = hrt_cpy_iso8859_1(symbol, src, length); | |
| 493 | |
| 494 return error_number; | |
| 495 } | |
| 496 | |
| 497 /* Handle GS1-128 (formerly known as EAN-128), and composite version if `cc_mode` set */ | |
| 498 INTERNAL int gs1_128_cc(struct zint_symbol *symbol, unsigned char source[], int length, const int cc_mode, | |
| 499 const int cc_rows) { | |
| 500 int i; | |
| 501 int error_number; | |
| 502 const char manuals[C128_MAX] = {0}; /* Dummy */ | |
| 503 char fncs[C128_MAX]; /* Dummy, set to all 1s */ | |
| 504 char priority[C128_STATES]; | |
| 505 int values[C128_VALUES_MAX] = {0}; | |
| 506 int glyph_count; | |
| 507 int final_cset; | |
| 508 int separator_row = 0; | |
| 509 int reduced_length; | |
| 510 unsigned char *reduced = (unsigned char *) z_alloca(length + 1); | |
| 511 | |
| 512 if (length > C128_MAX) { | |
| 513 /* This only blocks ridiculously long input - the actual length of the | |
| 514 resulting barcode depends on the type of data, so this is trapped later */ | |
| 515 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 342, "Input length %d too long (maximum " C128_MAX_S ")", length); | |
| 516 } | |
| 517 | |
| 518 /* If part of a composite symbol make room for the separator pattern */ | |
| 519 if (symbol->symbology == BARCODE_GS1_128_CC) { | |
| 520 separator_row = symbol->rows; | |
| 521 symbol->row_height[symbol->rows] = 1; | |
| 522 symbol->rows += 1; | |
| 523 } | |
| 524 | |
| 525 error_number = gs1_verify(symbol, source, length, reduced); | |
| 526 if (error_number >= ZINT_ERROR) { | |
| 527 return error_number; | |
| 528 } | |
| 529 | |
| 530 reduced_length = (int) ustrlen(reduced); | |
| 531 memset(fncs, 1, reduced_length); | |
| 532 | |
| 533 /* Control and extended chars not allowed so only have B/C (+FNC1) */ | |
| 534 c128_set_priority(priority, 0 /*have_a*/, 1 /*have_b*/, 1 /*have_c*/, 0 /*have_extended*/); | |
| 535 | |
| 536 glyph_count = c128_set_values(reduced, reduced_length, 1 /*start_idx*/, priority, fncs, manuals, values, | |
| 537 &final_cset); | |
| 538 | |
| 539 if (symbol->debug & ZINT_DEBUG_PRINT) { | |
| 540 printf("Data (%d): %.*s", reduced_length, reduced_length >= 100 ? 1 : reduced_length >= 10 ? 2 : 3, " "); | |
| 541 debug_print_escape(reduced, reduced_length, NULL); | |
| 542 printf("\nGlyphs: %d\n", glyph_count); | |
| 543 } | |
| 544 | |
| 545 /* Now we can calculate how long the barcode is going to be - and stop it from | |
| 546 being too long */ | |
| 547 if (glyph_count + (cc_mode != 0) > C128_SYMBOL_MAX) { | |
| 548 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 344, | |
| 549 "Input too long, requires %d symbol characters (maximum " C128_SYMBOL_MAX_S ")", | |
| 550 glyph_count + (cc_mode != 0)); | |
| 551 } | |
| 552 | |
| 553 /* "...note that the linkage flag is an extra Code Set character between | |
| 554 the last data character and the Symbol Check Character" (GS1 Specification) */ | |
| 555 | |
| 556 /* Linkage flags in GS1-128 are determined by ISO/IEC 24723 section 7.4 */ | |
| 557 assert(final_cset == C128_B0 || final_cset == C128_C0); | |
| 558 | |
| 559 switch (cc_mode) { | |
| 560 case 1: | |
| 561 case 2: | |
| 562 /* CC-A or CC-B 2D component */ | |
| 563 switch (final_cset) { | |
| 564 case C128_B0: | |
| 565 values[glyph_count++] = 99; | |
| 566 break; | |
| 567 case C128_C0: | |
| 568 values[glyph_count++] = 101; | |
| 569 break; | |
| 570 } | |
| 571 break; | |
| 572 case 3: | |
| 573 /* CC-C 2D component */ | |
| 574 switch (final_cset) { | |
| 575 case C128_B0: | |
| 576 values[glyph_count++] = 101; | |
| 577 break; | |
| 578 case C128_C0: | |
| 579 values[glyph_count++] = 100; | |
| 580 break; | |
| 581 } | |
| 582 break; | |
| 583 } | |
| 584 | |
| 585 c128_expand(symbol, values, glyph_count); | |
| 586 | |
| 587 /* Add the separator pattern for composite symbols */ | |
| 588 if (symbol->symbology == BARCODE_GS1_128_CC) { | |
| 589 for (i = 0; i < symbol->width; i++) { | |
| 590 if (!(module_is_set(symbol, separator_row + 1, i))) { | |
| 591 set_module(symbol, separator_row, i); | |
| 592 } | |
| 593 } | |
| 594 } | |
| 595 | |
| 596 if (reduced_length > 48) { /* GS1 General Specifications 5.4.4.3 */ | |
| 597 if (error_number == 0) { /* Don't overwrite any `gs1_verify()` warning */ | |
| 598 error_number = errtxtf(ZINT_WARN_NONCOMPLIANT, symbol, 843, | |
| 599 "Input too long, requires %d characters (maximum 48)", reduced_length); | |
| 600 } | |
| 601 } | |
| 602 | |
| 603 if (symbol->output_options & COMPLIANT_HEIGHT) { | |
| 604 /* GS1 General Specifications 21.0.1 5.12.3.2 table 2, including footnote (**): | |
| 605 same as ITF-14: "in case of further space constraints" height 5.8mm / 1.016mm (X max) ~ 5.7; | |
| 606 default 31.75mm / 0.495mm ~ 64.14 */ | |
| 607 const float min_height = 5.70866156f; /* 5.8 / 1.016 */ | |
| 608 const float default_height = 64.1414108f; /* 31.75 / 0.495 */ | |
| 609 if (symbol->symbology == BARCODE_GS1_128_CC) { | |
| 610 /* Pass back via temporary linear structure */ | |
| 611 symbol->height = symbol->height ? min_height : default_height; | |
| 612 } else { | |
| 613 if (error_number == 0) { /* Don't overwrite any `gs1_verify()` warning */ | |
| 614 error_number = set_height(symbol, min_height, default_height, 0.0f, 0 /*no_errtxt*/); | |
| 615 } else { | |
| 616 (void) set_height(symbol, min_height, default_height, 0.0f, 1 /*no_errtxt*/); | |
| 617 } | |
| 618 } | |
| 619 } else { | |
| 620 const float height = 50.0f; | |
| 621 if (symbol->symbology == BARCODE_GS1_128_CC) { | |
| 622 symbol->height = height - cc_rows * (cc_mode == 3 ? 3 : 2) - 1.0f; | |
| 623 } else { | |
| 624 (void) set_height(symbol, 0.0f, height, 0.0f, 1 /*no_errtxt*/); | |
| 625 } | |
| 626 } | |
| 627 | |
| 628 /* If no other warnings flag use of READER_INIT in GS1_MODE */ | |
| 629 if (error_number == 0 && (symbol->output_options & READER_INIT)) { | |
| 630 error_number = errtxt(ZINT_WARN_INVALID_OPTION, symbol, 845, | |
| 631 "Cannot use Reader Initialisation in GS1 mode, ignoring"); | |
| 632 } | |
| 633 | |
| 634 if (symbol->input_mode & GS1PARENS_MODE) { | |
| 635 i = length < (int) sizeof(symbol->text) ? length : (int) sizeof(symbol->text); | |
| 636 memcpy(symbol->text, source, i); | |
| 637 } else { | |
| 638 int bracket_level = 0; /* Non-compliant closing square brackets may be in text */ | |
| 639 for (i = 0; i < length && i < (int) sizeof(symbol->text); i++) { | |
| 640 if (source[i] == '[') { | |
| 641 symbol->text[i] = '('; | |
| 642 bracket_level++; | |
| 643 } else if (source[i] == ']' && bracket_level) { | |
| 644 symbol->text[i] = ')'; | |
| 645 bracket_level--; | |
| 646 } else { | |
| 647 symbol->text[i] = source[i]; | |
| 648 } | |
| 649 } | |
| 650 } | |
| 651 if (i == sizeof(symbol->text)) { | |
| 652 /* Trumps all other warnings */ | |
| 653 error_number = errtxt(ZINT_WARN_HRT_TRUNCATED, symbol, 844, "Human Readable Text truncated"); | |
| 654 i--; | |
| 655 } | |
| 656 symbol->text[i] = '\0'; | |
| 657 | |
| 658 return error_number; | |
| 659 } | |
| 660 | |
| 661 /* Handle GS1-128 (formerly known as EAN-128) */ | |
| 662 INTERNAL int gs1_128(struct zint_symbol *symbol, unsigned char source[], int length) { | |
| 663 return gs1_128_cc(symbol, source, length, 0 /*cc_mode*/, 0 /*cc_rows*/); | |
| 664 } | |
| 665 | |
| 666 /* Helper to do NVE18 or EAN14 */ | |
| 667 static int nve18_or_ean14(struct zint_symbol *symbol, unsigned char source[], const int length, const int data_len) { | |
| 668 static const char prefix[2][2][5] = { | |
| 669 { "(01)", "[01]" }, /* EAN14 */ | |
| 670 { "(00)", "[00]" }, /* NVE18 */ | |
| 671 }; | |
| 672 unsigned char ean128_equiv[23]; | |
| 673 int error_number, zeroes; | |
| 674 int i; | |
| 675 | |
| 676 if (length > data_len) { | |
| 677 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 345, "Input length %1$d too long (maximum %2$d)", length, | |
| 678 data_len); | |
| 679 } | |
| 680 | |
| 681 if ((i = not_sane(NEON_F, source, length))) { | |
| 682 /* Note: for all "at position" error messages, escape sequences not accounted for */ | |
| 683 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 346, | |
| 684 "Invalid character at position %d in input (digits only)", i); | |
| 685 } | |
| 686 | |
| 687 zeroes = data_len - length; | |
| 688 ustrcpy(ean128_equiv, prefix[data_len == 17][!(symbol->input_mode & GS1PARENS_MODE)]); | |
| 689 memset(ean128_equiv + 4, '0', zeroes); | |
| 690 ustrcpy(ean128_equiv + 4 + zeroes, source); | |
| 691 | |
| 692 ean128_equiv[data_len + 4] = gs1_check_digit(ean128_equiv + 4, data_len); | |
| 693 ean128_equiv[data_len + 5] = '\0'; | |
| 694 | |
| 695 error_number = gs1_128(symbol, ean128_equiv, data_len + 5); | |
| 696 | |
| 697 return error_number; | |
| 698 } | |
| 699 | |
| 700 | |
| 701 /* Add check digit if encoding an NVE18 symbol */ | |
| 702 INTERNAL int nve18(struct zint_symbol *symbol, unsigned char source[], int length) { | |
| 703 return nve18_or_ean14(symbol, source, length, 17 /*data_len*/); | |
| 704 } | |
| 705 | |
| 706 /* EAN-14 - A version of EAN-128 */ | |
| 707 INTERNAL int ean14(struct zint_symbol *symbol, unsigned char source[], int length) { | |
| 708 return nve18_or_ean14(symbol, source, length, 13 /*data_len*/); | |
| 709 } | |
| 710 | |
| 711 static const char KRSET[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
| 712 #define KRSET_F (IS_NUM_F | IS_UPR_F) | |
| 713 | |
| 714 /* DPD (Deutscher Paketdienst) Code */ | |
| 715 /* Specification at https://esolutions.dpd.com/dokumente/DPD_Parcel_Label_Specification_2.4.1_EN.pdf | |
| 716 * and identification tag info (Barcode ID) at https://esolutions.dpd.com/dokumente/DPD_Routing_Database_1.3_EN.pdf */ | |
| 717 INTERNAL int dpd(struct zint_symbol *symbol, unsigned char source[], int length) { | |
| 718 int error_number = 0; | |
| 719 int i, p; | |
| 720 unsigned char ident_tag; | |
| 721 unsigned char local_source_buf[29]; | |
| 722 unsigned char *local_source; | |
| 723 const int mod = 36; | |
| 724 const int relabel = symbol->option_2 == 1; /* A "relabel" has no identification tag */ | |
| 725 int cd; /* Check digit */ | |
| 726 | |
| 727 if ((length != 27 && length != 28) || (length == 28 && relabel)) { | |
| 728 if (relabel) { | |
| 729 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 830, "DPD relabel input length %d wrong (27 only)", length); | |
| 730 } | |
| 731 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 349, "DPD input length %d wrong (27 or 28 only)", length); | |
| 732 } | |
| 733 | |
| 734 if (length == 27 && !relabel) { | |
| 735 local_source_buf[0] = '%'; | |
| 736 ustrcpy(local_source_buf + 1, source); | |
| 737 local_source = local_source_buf; | |
| 738 length++; | |
| 739 } else { | |
| 740 local_source = source; | |
| 741 } | |
| 742 | |
| 743 ident_tag = local_source[0]; | |
| 744 | |
| 745 to_upper(local_source + !relabel, length - !relabel); | |
| 746 if ((i = not_sane(KRSET_F, local_source + !relabel, length - !relabel))) { | |
| 747 if (local_source == local_source_buf || relabel) { | |
| 748 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 300, | |
| 749 "Invalid character at position %d in input (alphanumerics only)", i); | |
| 750 } | |
| 751 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 299, | |
| 752 "Invalid character at position %d in input (alphanumerics only after first)", i); | |
| 753 } | |
| 754 | |
| 755 if ((ident_tag < 32) || (ident_tag > 127)) { | |
| 756 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 343, | |
| 757 "Invalid DPD identification tag (first character), ASCII values 32 to 127 only"); | |
| 758 } | |
| 759 | |
| 760 (void) code128(symbol, local_source, length); /* Only error returned is for large text which can't happen */ | |
| 761 | |
| 762 if (!(symbol->output_options & (BARCODE_BOX | BARCODE_BIND | BARCODE_BIND_TOP))) { | |
| 763 /* If no option has been selected then uses default bind top option */ | |
| 764 symbol->output_options |= BARCODE_BIND_TOP; /* Note won't extend over quiet zones for DPD */ | |
| 765 if (symbol->border_width == 0) { /* Allow override if non-zero */ | |
| 766 symbol->border_width = 3; /* From examples, not mentioned in spec */ | |
| 767 } | |
| 768 } | |
| 769 | |
| 770 if (symbol->output_options & COMPLIANT_HEIGHT) { | |
| 771 /* DPD Parcel Label Specification Version 2.4.1 (19.01.2021) Section 4.6.1.2 | |
| 772 25mm / 0.4mm (X max) = 62.5 min, 25mm / 0.375 (X) ~ 66.66 default */ | |
| 773 if (relabel) { /* If relabel then half-size */ | |
| 774 const float default_height = 33.3333321f; /* 12.5 / 0.375 */ | |
| 775 error_number = set_height(symbol, 31.25f, default_height, 0.0f, 0 /*no_errtxt*/); | |
| 776 } else { | |
| 777 const float default_height = 66.6666641f; /* 25.0 / 0.375 */ | |
| 778 error_number = set_height(symbol, 62.5f, default_height, 0.0f, 0 /*no_errtxt*/); | |
| 779 } | |
| 780 } else { | |
| 781 (void) set_height(symbol, 0.0f, relabel ? 25.0f : 50.0f, 0.0f, 1 /*no_errtxt*/); | |
| 782 } | |
| 783 | |
| 784 cd = mod; | |
| 785 | |
| 786 p = 0; | |
| 787 for (i = !relabel; i < length; i++) { | |
| 788 symbol->text[p] = local_source[i]; | |
| 789 p++; | |
| 790 | |
| 791 cd += posn(KRSET, local_source[i]); | |
| 792 if (cd > mod) cd -= mod; | |
| 793 cd *= 2; | |
| 794 if (cd >= (mod + 1)) cd -= mod + 1; | |
| 795 | |
| 796 switch (i + relabel) { | |
| 797 case 4: | |
| 798 case 7: | |
| 799 case 11: | |
| 800 case 15: | |
| 801 case 19: | |
| 802 case 21: | |
| 803 case 24: | |
| 804 case 27: | |
| 805 symbol->text[p++] = ' '; | |
| 806 break; | |
| 807 } | |
| 808 } | |
| 809 | |
| 810 cd = mod + 1 - cd; | |
| 811 if (cd == mod) cd = 0; | |
| 812 | |
| 813 if (cd < 10) { | |
| 814 symbol->text[p] = cd + '0'; | |
| 815 } else { | |
| 816 symbol->text[p] = (cd - 10) + 'A'; | |
| 817 } | |
| 818 p++; | |
| 819 | |
| 820 symbol->text[p] = '\0'; | |
| 821 | |
| 822 /* Some compliance checks */ | |
| 823 if (not_sane(NEON_F, local_source + length - 16, 16)) { | |
| 824 if (not_sane(NEON_F, local_source + length - 3, 3)) { /* 3-digit Country Code (ISO 3166-1) */ | |
| 825 errtxt(0, symbol, 831, "Destination Country Code (last 3 characters) should be numeric"); | |
| 826 } else if (not_sane(NEON_F, local_source + length - 6, 3)) { /* 3-digit Service Code */ | |
| 827 errtxt(0, symbol, 832, "Service Code (characters 6-4 from end) should be numeric"); | |
| 828 } else { /* Last 10 characters of Tracking No. */ | |
| 829 errtxt(0, symbol, 833, | |
| 830 "Last 10 characters of Tracking Number (characters 16-7 from end) should be numeric"); | |
| 831 } | |
| 832 error_number = ZINT_WARN_NONCOMPLIANT; | |
| 833 } | |
| 834 | |
| 835 return error_number; | |
| 836 } | |
| 837 | |
| 838 /* Universal Postal Union S10 */ | |
| 839 /* https://www.upu.int/UPU/media/upu/files/postalSolutions/programmesAndServices/standards/S10-12.pdf */ | |
| 840 INTERNAL int upu_s10(struct zint_symbol *symbol, unsigned char source[], int length) { | |
| 841 int i, j; | |
| 842 unsigned char local_source[13 + 1]; | |
| 843 unsigned char have_check_digit = '\0'; | |
| 844 int check_digit; | |
| 845 static const char weights[8] = { 8, 6, 4, 2, 3, 5, 9, 7 }; | |
| 846 int error_number = 0; | |
| 847 | |
| 848 if (length != 12 && length != 13) { | |
| 849 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 834, "Input length %d wrong (12 or 13 only)", length); | |
| 850 } | |
| 851 if (length == 13) { /* Includes check digit - remove for now */ | |
| 852 have_check_digit = source[10]; | |
| 853 memcpy(local_source, source, 10); | |
| 854 ustrcpy(local_source + 10, source + 11); | |
| 855 } else { | |
| 856 ustrcpy(local_source, source); | |
| 857 } | |
| 858 to_upper(local_source, length); | |
| 859 | |
| 860 if (!z_isupper(local_source[0]) || !z_isupper(local_source[1])) { | |
| 861 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 835, | |
| 862 "Invalid character in Service Indictor (first 2 characters) (alphabetic only)"); | |
| 863 } | |
| 864 if (not_sane(NEON_F, local_source + 2, 12 - 4) || (have_check_digit && !z_isdigit(have_check_digit))) { | |
| 865 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 836, | |
| 866 "Invalid character in Serial Number (middle %d characters) (digits only)", | |
| 867 have_check_digit ? 9 : 8); | |
| 868 } | |
| 869 if (!z_isupper(local_source[10]) || !z_isupper(local_source[11])) { | |
| 870 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 837, | |
| 871 "Invalid character in Country Code (last 2 characters) (alphabetic only)"); | |
| 872 } | |
| 873 | |
| 874 check_digit = 0; | |
| 875 for (i = 2; i < 10; i++) { /* Serial Number only */ | |
| 876 check_digit += ctoi(local_source[i]) * weights[i - 2]; | |
| 877 } | |
| 878 check_digit %= 11; | |
| 879 check_digit = 11 - check_digit; | |
| 880 if (check_digit == 10) { | |
| 881 check_digit = 0; | |
| 882 } else if (check_digit == 11) { | |
| 883 check_digit = 5; | |
| 884 } | |
| 885 if (have_check_digit && ctoi(have_check_digit) != check_digit) { | |
| 886 return errtxtf(ZINT_ERROR_INVALID_CHECK, symbol, 838, "Invalid check digit '%1$c', expecting '%2$c'", | |
| 887 have_check_digit, itoc(check_digit)); | |
| 888 } | |
| 889 /* Add in (back) check digit */ | |
| 890 local_source[12] = local_source[11]; | |
| 891 local_source[11] = local_source[10]; | |
| 892 local_source[10] = itoc(check_digit); | |
| 893 local_source[13] = '\0'; | |
| 894 | |
| 895 /* Do some checks on the Service Indicator (first char only) and Country Code */ | |
| 896 if (strchr("JKSTW", local_source[0]) != NULL) { /* These are reserved & cannot be assigned */ | |
| 897 error_number = errtxt(ZINT_WARN_NONCOMPLIANT, symbol, 839, | |
| 898 "Invalid Service Indicator (first character should not be any of \"JKSTW\")"); | |
| 899 } else if (strchr("FHIOXY", local_source[0]) != NULL) { /* These aren't allocated as of spec Oct 2017 */ | |
| 900 error_number = errtxt(ZINT_WARN_NONCOMPLIANT, symbol, 840, | |
| 901 "Non-standard Service Indicator (first 2 characters)"); | |
| 902 } else if (!gs1_iso3166_alpha2(local_source + 11)) { | |
| 903 error_number = errtxt(ZINT_WARN_NONCOMPLIANT, symbol, 841, | |
| 904 "Country code (last two characters) is not ISO 3166-1"); | |
| 905 } | |
| 906 | |
| 907 (void) code128(symbol, local_source, 13); /* Only error returned is for large text which can't happen */ | |
| 908 | |
| 909 j = 0; | |
| 910 for (i = 0; i < 13; i++) { | |
| 911 if (i == 2 || i == 5 || i == 8 || i == 11) { | |
| 912 symbol->text[j++] = ' '; | |
| 913 } | |
| 914 symbol->text[j++] = local_source[i]; | |
| 915 } | |
| 916 symbol->text[j] = '\0'; | |
| 917 | |
| 918 if (symbol->output_options & COMPLIANT_HEIGHT) { | |
| 919 /* Universal Postal Union S10 Section 8, using max X 0.51mm & minimum height 12.5mm or 15% of width */ | |
| 920 const float min_height_min = 24.5098038f; /* 12.5 / 0.51 */ | |
| 921 float min_height = stripf(symbol->width * 0.15f); | |
| 922 if (min_height < min_height_min) { | |
| 923 min_height = min_height_min; | |
| 924 } | |
| 925 /* Using 50 as default as none recommended */ | |
| 926 if (error_number == 0) { | |
| 927 error_number = set_height(symbol, min_height, min_height > 50.0f ? min_height : 50.0f, 0.0f, | |
| 928 0 /*no_errtxt*/); | |
| 929 } else { | |
| 930 (void) set_height(symbol, min_height, min_height > 50.0f ? min_height : 50.0f, 0.0f, 1 /*no_errtxt*/); | |
| 931 } | |
| 932 } else { | |
| 933 (void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/); | |
| 934 } | |
| 935 | |
| 936 return error_number; | |
| 937 } | |
| 938 | |
| 939 /* vim: set ts=4 sw=4 et : */ |
