Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/zint/backend/medical.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 /* medical.c - Handles 1 track and 2 track pharmacode and Codabar */ | |
| 2 /* | |
| 3 libzint - the open source barcode library | |
| 4 Copyright (C) 2008-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 <stdio.h> | |
| 34 #include "common.h" | |
| 35 | |
| 36 INTERNAL int code39(struct zint_symbol *symbol, unsigned char source[], int length); | |
| 37 | |
| 38 static const char CALCIUM[] = "0123456789-$:/.+ABCD"; | |
| 39 #define CALCIUM_INNER_F (IS_NUM_F | IS_MNS_F | IS_CLI_F | IS_PLS_F) /* CALCIUM_INNER "0123456789-$:/.+" */ | |
| 40 | |
| 41 /* Codabar table checked against EN 798:1995 */ | |
| 42 static const char CodaTable[20][8] = { | |
| 43 {'1','1','1','1','1','2','2','1'}, {'1','1','1','1','2','2','1','1'}, {'1','1','1','2','1','1','2','1'}, | |
| 44 {'2','2','1','1','1','1','1','1'}, {'1','1','2','1','1','2','1','1'}, {'2','1','1','1','1','2','1','1'}, | |
| 45 {'1','2','1','1','1','1','2','1'}, {'1','2','1','1','2','1','1','1'}, {'1','2','2','1','1','1','1','1'}, | |
| 46 {'2','1','1','2','1','1','1','1'}, {'1','1','1','2','2','1','1','1'}, {'1','1','2','2','1','1','1','1'}, | |
| 47 {'2','1','1','1','2','1','2','1'}, {'2','1','2','1','1','1','2','1'}, {'2','1','2','1','2','1','1','1'}, | |
| 48 {'1','1','2','1','2','1','2','1'}, {'1','1','2','2','1','2','1','1'}, {'1','2','1','2','1','1','2','1'}, | |
| 49 {'1','1','1','2','1','2','2','1'}, {'1','1','1','2','2','2','1','1'} | |
| 50 }; | |
| 51 | |
| 52 INTERNAL int pharma(struct zint_symbol *symbol, unsigned char source[], int length) { | |
| 53 /* "Pharmacode can represent only a single integer from 3 to 131070. Unlike other | |
| 54 commonly used one-dimensional barcode schemes, pharmacode does not store the data in a | |
| 55 form corresponding to the human-readable digits; the number is encoded in binary, rather | |
| 56 than decimal. Pharmacode is read from right to left: with n as the bar position starting | |
| 57 at 0 on the right, each narrow bar adds 2^n to the value and each wide bar adds 2(2^n). | |
| 58 The minimum barcode is 2 bars and the maximum 16, so the smallest number that could | |
| 59 be encoded is 3 (2 narrow bars) and the biggest is 131070 (16 wide bars)." | |
| 60 - http://en.wikipedia.org/wiki/Pharmacode */ | |
| 61 | |
| 62 /* This code uses the One Track Pharamacode calculating algorithm as recommended by | |
| 63 the specification at http://www.laetus.com/laetus.php?request=file&id=69 | |
| 64 (http://www.gomaro.ch/ftproot/Laetus_PHARMA-CODE.pdf) */ | |
| 65 | |
| 66 int i; | |
| 67 int tester; | |
| 68 int counter, error_number = 0, h; | |
| 69 char inter[18] = {0}; /* 131070 -> 17 bits */ | |
| 70 char *in = inter; | |
| 71 char dest[64]; /* 17 * 2 + 1 */ | |
| 72 char *d = dest; | |
| 73 | |
| 74 if (length > 6) { | |
| 75 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 350, "Input length %d too long (maximum 6)", length); | |
| 76 } | |
| 77 if ((i = not_sane(NEON_F, source, length))) { | |
| 78 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 351, | |
| 79 "Invalid character at position %d in input (digits only)", i); | |
| 80 } | |
| 81 | |
| 82 tester = to_int(source, length); | |
| 83 if ((tester < 3) || (tester > 131070)) { | |
| 84 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 352, "Input value '%d' out of range (3 to 131070)", tester); | |
| 85 } | |
| 86 | |
| 87 do { | |
| 88 if (!(tester & 1)) { | |
| 89 *in++ = 'W'; | |
| 90 tester = (tester - 2) / 2; | |
| 91 } else { | |
| 92 *in++ = 'N'; | |
| 93 tester = (tester - 1) / 2; | |
| 94 } | |
| 95 } while (tester != 0); | |
| 96 | |
| 97 h = in - inter; | |
| 98 for (counter = h - 1; counter >= 0; counter--) { | |
| 99 *d++ = inter[counter] == 'W' ? '3' : '1'; | |
| 100 *d++ = '2'; | |
| 101 } | |
| 102 *--d = '\0'; /* Chop off final bar */ | |
| 103 | |
| 104 expand(symbol, dest, d - dest); | |
| 105 | |
| 106 if (symbol->output_options & COMPLIANT_HEIGHT) { | |
| 107 /* Laetus Pharmacode Guide 1.2 Standard one-track height 8mm / 0.5mm (X) */ | |
| 108 error_number = set_height(symbol, 16.0f, 0.0f, 0.0f, 0 /*no_errtxt*/); | |
| 109 } else { | |
| 110 (void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/); | |
| 111 } | |
| 112 | |
| 113 return error_number; | |
| 114 } | |
| 115 | |
| 116 static int pharma_two_calc(int tester, char *d) { | |
| 117 /* This code uses the Two Track Pharamacode defined in the document at | |
| 118 http://www.laetus.com/laetus.php?request=file&id=69 and using a modified | |
| 119 algorithm from the One Track system. This standard accepts integet values | |
| 120 from 4 to 64570080. */ | |
| 121 | |
| 122 int counter, h; | |
| 123 char inter[17]; | |
| 124 char *in = inter; | |
| 125 | |
| 126 do { | |
| 127 switch (tester % 3) { | |
| 128 case 0: | |
| 129 *in++ = '3'; | |
| 130 tester = (tester - 3) / 3; | |
| 131 break; | |
| 132 case 1: | |
| 133 *in++ = '1'; | |
| 134 tester = (tester - 1) / 3; | |
| 135 break; | |
| 136 case 2: | |
| 137 *in++ = '2'; | |
| 138 tester = (tester - 2) / 3; | |
| 139 break; | |
| 140 } | |
| 141 } while (tester != 0); | |
| 142 | |
| 143 h = in - inter; | |
| 144 for (counter = h - 1; counter >= 0; counter--) { | |
| 145 *d++ = inter[counter]; | |
| 146 } | |
| 147 *d = '\0'; | |
| 148 | |
| 149 return h; | |
| 150 } | |
| 151 | |
| 152 INTERNAL int pharma_two(struct zint_symbol *symbol, unsigned char source[], int length) { | |
| 153 /* Draws the patterns for two track pharmacode */ | |
| 154 int i; | |
| 155 int tester; | |
| 156 char height_pattern[200]; | |
| 157 unsigned int loopey, h; | |
| 158 int writer; | |
| 159 int error_number = 0; | |
| 160 | |
| 161 if (length > 8) { | |
| 162 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 354, "Input length %d too long (maximum 8)", length); | |
| 163 } | |
| 164 if ((i = not_sane(NEON_F, source, length))) { | |
| 165 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 355, | |
| 166 "Invalid character at position %d in input (digits only)", i); | |
| 167 } | |
| 168 | |
| 169 tester = to_int(source, length); | |
| 170 if ((tester < 4) || (tester > 64570080)) { | |
| 171 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 353, "Input value '%d' out of range (4 to 64570080)", tester); | |
| 172 } | |
| 173 h = pharma_two_calc(tester, height_pattern); | |
| 174 | |
| 175 writer = 0; | |
| 176 for (loopey = 0; loopey < h; loopey++) { | |
| 177 if ((height_pattern[loopey] == '2') || (height_pattern[loopey] == '3')) { | |
| 178 set_module(symbol, 0, writer); | |
| 179 } | |
| 180 if ((height_pattern[loopey] == '1') || (height_pattern[loopey] == '3')) { | |
| 181 set_module(symbol, 1, writer); | |
| 182 } | |
| 183 writer += 2; | |
| 184 } | |
| 185 symbol->rows = 2; | |
| 186 symbol->width = writer - 1; | |
| 187 | |
| 188 if (symbol->output_options & COMPLIANT_HEIGHT) { | |
| 189 /* Laetus Pharmacode Guide 1.4 | |
| 190 Two-track height min 8mm / 2mm (X max) = 4X (2X per row), standard 8mm / 1mm = 8X, | |
| 191 max 12mm / 0.8mm (X min) = 15X */ | |
| 192 error_number = set_height(symbol, 2.0f, 8.0f, 15.0f, 0 /*no_errtxt*/); | |
| 193 } else { | |
| 194 (void) set_height(symbol, 0.0f, 10.0f, 0.0f, 1 /*no_errtxt*/); | |
| 195 } | |
| 196 | |
| 197 return error_number; | |
| 198 } | |
| 199 | |
| 200 /* The Codabar system consisting of simple substitution */ | |
| 201 INTERNAL int codabar(struct zint_symbol *symbol, unsigned char source[], int length) { | |
| 202 | |
| 203 int i, error_number = 0; | |
| 204 int posns[103]; | |
| 205 char dest[833]; /* (103 + 1) * 8 + 1 == 833 */ | |
| 206 char *d = dest; | |
| 207 int add_checksum, count = 0, checksum = 0; | |
| 208 int d_chars = 0; | |
| 209 | |
| 210 if (length > 103) { /* No stack smashing please (103 + 1) * 11 = 1144 */ | |
| 211 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 356, "Input length %d too long (maximum 103)", length); | |
| 212 } | |
| 213 /* BS EN 798:1995 4.2 "'Codabar' symbols shall consist of ... b) start character; | |
| 214 c) one or more symbol characters representing data ... d) stop character ..." */ | |
| 215 if (length < 3) { | |
| 216 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 362, "Input length %d too short (minimum 3)", length); | |
| 217 } | |
| 218 to_upper(source, length); | |
| 219 | |
| 220 /* Codabar must begin and end with the characters A, B, C or D */ | |
| 221 if ((source[0] != 'A') && (source[0] != 'B') && (source[0] != 'C') | |
| 222 && (source[0] != 'D')) { | |
| 223 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 358, "Does not begin with \"A\", \"B\", \"C\" or \"D\""); | |
| 224 } | |
| 225 if ((source[length - 1] != 'A') && (source[length - 1] != 'B') && | |
| 226 (source[length - 1] != 'C') && (source[length - 1] != 'D')) { | |
| 227 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 359, "Does not end with \"A\", \"B\", \"C\" or \"D\""); | |
| 228 } | |
| 229 if ((i = not_sane_lookup(CALCIUM, sizeof(CALCIUM) - 1, source, length, posns))) { | |
| 230 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 357, | |
| 231 "Invalid character at position %1$d in input (\"%2$s\" only)", i, CALCIUM); | |
| 232 } | |
| 233 /* And must not use A, B, C or D otherwise (BS EN 798:1995 4.3.2) */ | |
| 234 if ((i = not_sane(CALCIUM_INNER_F, source + 1, length - 2))) { | |
| 235 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 363, | |
| 236 "Invalid character at position %d in input (cannot contain \"A\", \"B\", \"C\" or \"D\")", i); | |
| 237 } | |
| 238 | |
| 239 /* Add check character: 1 don't show to HRT, 2 do show to HRT | |
| 240 (unfortunately to maintain back-compatibility, this is reverse of C25) */ | |
| 241 add_checksum = symbol->option_2 == 1 || symbol->option_2 == 2; | |
| 242 | |
| 243 for (i = 0; i < length; i++, d += 8) { | |
| 244 if (add_checksum) { | |
| 245 /* BS EN 798:1995 A.3 suggests using ISO 7064 algorithm but leaves it application defined. | |
| 246 Following BWIPP and TEC-IT, use this simple mod-16 algorithm (not in ISO 7064) */ | |
| 247 count += posns[i]; | |
| 248 if (i + 1 == length) { | |
| 249 checksum = count % 16; | |
| 250 if (checksum) { | |
| 251 checksum = 16 - checksum; | |
| 252 } | |
| 253 if (symbol->debug & ZINT_DEBUG_PRINT) { | |
| 254 printf("Codabar: %s, count %d, checksum %d (%c)\n", source, count, checksum, CALCIUM[checksum]); | |
| 255 } | |
| 256 memcpy(d, CodaTable[checksum], 8); | |
| 257 d += 8; | |
| 258 } | |
| 259 } | |
| 260 memcpy(d, CodaTable[posns[i]], 8); | |
| 261 if (source[i] == '/' || source[i] == ':' || source[i] == '.' || source[i] == '+') { /* Wide data characters */ | |
| 262 d_chars++; | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 expand(symbol, dest, d - dest); | |
| 267 | |
| 268 if (symbol->output_options & COMPLIANT_HEIGHT) { | |
| 269 /* BS EN 798:1995 4.4.1 (d) max of 5mm / 0.43mm (X max) ~ 11.628 or 15% of width where (taking N = | |
| 270 narrow/wide ratio as 2 and I = X) width = ((2 * N + 5) * C + (N – 1) * (D + 2)) * X + I * (C – 1) + 2Q | |
| 271 = ((4 + 5) * C + (D + 2) + C - 1 + 2 * 10) * X = (10 * C + D + 21) * X | |
| 272 Length (C) includes start/stop chars */ | |
| 273 const float min_height_min = 11.6279068f; /* 5.0 / 0.43 */ | |
| 274 float min_height = stripf((10.0f * ((add_checksum ? length + 1 : length) + 2.0f) + d_chars + 21.0f) * 0.15f); | |
| 275 if (min_height < min_height_min) { | |
| 276 min_height = min_height_min; | |
| 277 } | |
| 278 /* Using 50 as default as none recommended */ | |
| 279 error_number = set_height(symbol, min_height, min_height > 50.0f ? min_height : 50.0f, 0.0f, 0 /*no_errtxt*/); | |
| 280 } else { | |
| 281 (void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/); | |
| 282 } | |
| 283 | |
| 284 ustrcpy(symbol->text, source); | |
| 285 if (symbol->option_2 == 2) { | |
| 286 symbol->text[length - 1] = CALCIUM[checksum]; /* Place before final A/B/C/D character (BS EN 798:1995 A.3) */ | |
| 287 symbol->text[length] = source[length - 1]; | |
| 288 symbol->text[length + 1] = '\0'; | |
| 289 } | |
| 290 | |
| 291 return error_number; | |
| 292 } | |
| 293 | |
| 294 /* Italian Pharmacode */ | |
| 295 INTERNAL int code32(struct zint_symbol *symbol, unsigned char source[], int length) { | |
| 296 static const char TABELLA[] = "0123456789BCDFGHJKLMNPQRSTUVWXYZ"; | |
| 297 int i, zeroes, error_number = 0, checksum, checkpart, checkdigit; | |
| 298 char localstr[10], risultante[7]; | |
| 299 unsigned int pharmacode, devisor; | |
| 300 int codeword[6]; | |
| 301 | |
| 302 /* Validate the input */ | |
| 303 if (length > 8) { | |
| 304 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 360, "Input length %d too long (maximum 8)", length); | |
| 305 } | |
| 306 if ((i = not_sane(NEON_F, source, length))) { | |
| 307 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 361, | |
| 308 "Invalid character at position %d in input (digits only)", i); | |
| 309 } | |
| 310 | |
| 311 /* Add leading zeros as required */ | |
| 312 zeroes = 8 - length; | |
| 313 memset(localstr, '0', zeroes); | |
| 314 ustrcpy(localstr + zeroes, source); | |
| 315 | |
| 316 /* Calculate the check digit */ | |
| 317 checksum = 0; | |
| 318 for (i = 0; i < 4; i++) { | |
| 319 checkpart = ctoi(localstr[i * 2]); | |
| 320 checksum += checkpart; | |
| 321 checkpart = 2 * (ctoi(localstr[(i * 2) + 1])); | |
| 322 if (checkpart >= 10) { | |
| 323 checksum += (checkpart - 10) + 1; | |
| 324 } else { | |
| 325 checksum += checkpart; | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 /* Add check digit to data string */ | |
| 330 checkdigit = checksum % 10; | |
| 331 localstr[8] = itoc(checkdigit); | |
| 332 localstr[9] = '\0'; | |
| 333 | |
| 334 /* Convert string into an integer value */ | |
| 335 pharmacode = atoi(localstr); | |
| 336 | |
| 337 /* Convert from decimal to base-32 */ | |
| 338 devisor = 33554432; | |
| 339 for (i = 5; i >= 0; i--) { | |
| 340 unsigned int remainder; | |
| 341 codeword[i] = pharmacode / devisor; | |
| 342 remainder = pharmacode % devisor; | |
| 343 pharmacode = remainder; | |
| 344 devisor /= 32; | |
| 345 } | |
| 346 | |
| 347 /* Look up values in 'Tabella di conversione' */ | |
| 348 for (i = 5; i >= 0; i--) { | |
| 349 risultante[5 - i] = TABELLA[codeword[i]]; | |
| 350 } | |
| 351 risultante[6] = '\0'; | |
| 352 /* Plot the barcode using Code 39 */ | |
| 353 error_number = code39(symbol, (unsigned char *) risultante, 6); | |
| 354 if (error_number != 0) { /* Should never happen */ | |
| 355 return error_number; /* Not reached */ | |
| 356 } | |
| 357 | |
| 358 if (symbol->output_options & COMPLIANT_HEIGHT) { | |
| 359 /* Allegato A Caratteristiche tecniche del bollino farmaceutico | |
| 360 (https://www.gazzettaufficiale.it/do/atto/serie_generale/caricaPdf?cdimg=14A0566800100010110001 | |
| 361 &dgu=2014-07-18&art.dataPubblicazioneGazzetta=2014-07-18&art.codiceRedazionale=14A05668&art.num=1 | |
| 362 &art.tiposerie=SG) | |
| 363 X given as 0.250mm; height (and quiet zones) left to ISO/IEC 16388:2007 (Code 39) | |
| 364 So min height 5mm = 5mm / 0.25mm = 20 > 15% of width, i.e. (10 * 8 + 19) * 0.15 = 14.85 */ | |
| 365 error_number = set_height(symbol, 20.0f, 20.0f, 0.0f, 0 /*no_errtxt*/); /* Use as default also */ | |
| 366 } else { | |
| 367 (void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/); | |
| 368 } | |
| 369 | |
| 370 /* Override the normal text output with the Pharmacode number */ | |
| 371 ustrcpy(symbol->text, "A"); | |
| 372 ustrcat(symbol->text, localstr); | |
| 373 | |
| 374 return error_number; | |
| 375 } | |
| 376 | |
| 377 /* vim: set ts=4 sw=4 et : */ |
