comparison mupdf-source/thirdparty/zint/backend/2of5.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 /* 2of5.c - Handles Code 2 of 5 barcodes */
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 #include "gs1.h"
36
37 /* First 5 of each entry Interleaved also */
38 static const char C25MatrixTable[10][6] = {
39 {'1','1','3','3','1','1'}, {'3','1','1','1','3','1'}, {'1','3','1','1','3','1'}, {'3','3','1','1','1','1'},
40 {'1','1','3','1','3','1'}, {'3','1','3','1','1','1'}, {'1','3','3','1','1','1'}, {'1','1','1','3','3','1'},
41 {'3','1','1','3','1','1'}, {'1','3','1','3','1','1'}
42 };
43
44 static const char C25IndustTable[10][10] = {
45 {'1','1','1','1','3','1','3','1','1','1'}, {'3','1','1','1','1','1','1','1','3','1'},
46 {'1','1','3','1','1','1','1','1','3','1'}, {'3','1','3','1','1','1','1','1','1','1'},
47 {'1','1','1','1','3','1','1','1','3','1'}, {'3','1','1','1','3','1','1','1','1','1'},
48 {'1','1','3','1','3','1','1','1','1','1'}, {'1','1','1','1','1','1','3','1','3','1'},
49 {'3','1','1','1','1','1','3','1','1','1'}, {'1','1','3','1','1','1','3','1','1','1'}
50 };
51
52 /* Note `c25_common()` assumes Stop string length one less than Start */
53 static const char C25MatrixStartStop[2][6] = { {'4', '1', '1', '1', '1', '1'}, {'4', '1', '1', '1', '1'} };
54 static const char C25IndustStartStop[2][6] = { {'3', '1', '3', '1', '1', '1'}, {'3', '1', '1', '1', '3'} };
55 static const char C25IataLogicStartStop[2][6] = { {'1', '1', '1', '1'}, {'3', '1', '1'} };
56
57 /* Common to Standard (Matrix), Industrial, IATA, and Data Logic */
58 static int c25_common(struct zint_symbol *symbol, const unsigned char source[], int length, const int max,
59 const int is_matrix, const char start_stop[2][6], const int start_length, const int error_base) {
60
61 int i;
62 char dest[818]; /* Largest destination 4 + (80 + 1) * 10 + 3 + 1 = 818 */
63 char *d = dest;
64 unsigned char temp[113 + 1 + 1]; /* Largest maximum 113 + optional check digit */
65 const int have_checkdigit = symbol->option_2 == 1 || symbol->option_2 == 2;
66
67 if (length > max) {
68 /* errtxt 301: 303: 305: 307: */
69 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, error_base, "Input length %1$d too long (maximum %2$d)", length,
70 max);
71 }
72 if ((i = not_sane(NEON_F, source, length))) {
73 /* Note: for all "at position" error messages, escape sequences not accounted for */
74 /* errtxt 302: 304: 306: 308: */
75 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, error_base + 1,
76 "Invalid character at position %d in input (digits only)", i);
77 }
78
79 ustrcpy(temp, source);
80
81 if (have_checkdigit) {
82 /* Add standard GS1 check digit */
83 temp[length] = gs1_check_digit(source, length);
84 temp[++length] = '\0';
85 if (symbol->debug & ZINT_DEBUG_PRINT) printf("Check digit: %c\n", temp[length - 1]);
86 }
87
88 /* Start character */
89 memcpy(d, start_stop[0], start_length);
90 d += start_length;
91
92 if (is_matrix) {
93 for (i = 0; i < length; i++, d += 6) {
94 memcpy(d, C25MatrixTable[temp[i] - '0'], 6);
95 }
96 } else {
97 for (i = 0; i < length; i++, d += 10) {
98 memcpy(d, C25IndustTable[temp[i] - '0'], 10);
99 }
100 }
101
102 /* Stop character */
103 memcpy(d, start_stop[1], start_length - 1);
104 d += start_length - 1;
105
106 expand(symbol, dest, d - dest);
107
108 ustrcpy(symbol->text, temp);
109 if (symbol->option_2 == 2) {
110 /* Remove check digit from HRT */
111 symbol->text[length - 1] = '\0';
112 }
113
114 return 0;
115 }
116
117 /* Code 2 of 5 Standard (Code 2 of 5 Matrix) */
118 INTERNAL int c25standard(struct zint_symbol *symbol, unsigned char source[], int length) {
119 /* 9 + (112 + 1) * 10 + 8 = 1147 */
120 return c25_common(symbol, source, length, 112, 1 /*is_matrix*/, C25MatrixStartStop, 6, 301);
121 }
122
123 /* Code 2 of 5 Industrial */
124 INTERNAL int c25ind(struct zint_symbol *symbol, unsigned char source[], int length) {
125 /* 10 + (79 + 1) * 14 + 9 = 1139 */
126 return c25_common(symbol, source, length, 79, 0 /*is_matrix*/, C25IndustStartStop, 6, 303);
127 }
128
129 /* Code 2 of 5 IATA */
130 INTERNAL int c25iata(struct zint_symbol *symbol, unsigned char source[], int length) {
131 /* 4 + (80 + 1) * 14 + 5 = 1143 */
132 return c25_common(symbol, source, length, 80, 0 /*is_matrix*/, C25IataLogicStartStop, 4, 305);
133 }
134
135 /* Code 2 of 5 Data Logic */
136 INTERNAL int c25logic(struct zint_symbol *symbol, unsigned char source[], int length) {
137 /* 4 + (113 + 1) * 10 + 5 = 1149 */
138 return c25_common(symbol, source, length, 113, 1 /*is_matrix*/, C25IataLogicStartStop, 4, 307);
139 }
140
141 /* Common to Interleaved, ITF-14, DP Leitcode, DP Identcode */
142 static int c25_inter_common(struct zint_symbol *symbol, unsigned char source[], int length,
143 const int checkdigit_option, const int dont_set_height) {
144 int i, j, error_number = 0;
145 char dest[638]; /* 4 + (125 + 1) * 5 + 3 + 1 = 638 */
146 char *d = dest;
147 unsigned char temp[125 + 1 + 1];
148 const int have_checkdigit = checkdigit_option == 1 || checkdigit_option == 2;
149
150 if (length > 125) { /* 4 + (125 + 1) * 9 + 5 = 1143 */
151 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 309, "Input length %d too long (maximum 125)", length);
152 }
153 if ((i = not_sane(NEON_F, source, length))) {
154 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 310,
155 "Invalid character at position %d in input (digits only)", i);
156 }
157
158 /* Input must be an even number of characters for Interlaced 2 of 5 to work:
159 if an odd number of characters has been entered and no check digit or an even number and have check digit
160 then add a leading zero */
161 if (have_checkdigit == !(length & 1)) {
162 temp[0] = '0';
163 memcpy(temp + 1, source, length++);
164 } else {
165 memcpy(temp, source, length);
166 }
167 temp[length] = '\0';
168
169 if (have_checkdigit) {
170 /* Add standard GS1 check digit */
171 temp[length] = gs1_check_digit(temp, length);
172 temp[++length] = '\0';
173 }
174
175 /* Start character */
176 memcpy(d, "1111", 4);
177 d += 4;
178
179 for (i = 0; i < length; i += 2) {
180 /* Look up the bars and the spaces */
181 const char *const bars = C25MatrixTable[temp[i] - '0'];
182 const char *const spaces = C25MatrixTable[temp[i + 1] - '0'];
183
184 /* Then merge (interlace) the strings together */
185 for (j = 0; j < 5; j++) {
186 *d++ = bars[j];
187 *d++ = spaces[j];
188 }
189 }
190
191 /* Stop character */
192 memcpy(d, "311", 3);
193 d += 3;
194
195 expand(symbol, dest, d - dest);
196
197 ustrcpy(symbol->text, temp);
198 if (checkdigit_option == 2) {
199 /* Remove check digit from HRT */
200 symbol->text[length - 1] = '\0';
201 }
202
203 if (!dont_set_height) {
204 if (symbol->output_options & COMPLIANT_HEIGHT) {
205 /* ISO/IEC 16390:2007 Section 4.4 min height 5mm or 15% of symbol width whichever greater where
206 (P = character pairs, N = wide/narrow ratio = 3)
207 width = (P(4N + 6) + N + 6)X = (length / 2) * 18 + 9 */
208 /* Taking min X = 0.330mm from Annex D.3.1 (application specification) */
209 const float min_height_min = 15.151515f; /* 5.0 / 0.33 */
210 float min_height = stripf((18.0f * (length / 2) + 9.0f) * 0.15f);
211 if (min_height < min_height_min) {
212 min_height = min_height_min;
213 }
214 /* Using 50 as default as none recommended */
215 error_number = set_height(symbol, min_height, min_height > 50.0f ? min_height : 50.0f, 0.0f,
216 0 /*no_errtxt*/);
217 } else {
218 (void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/);
219 }
220 }
221
222 return error_number;
223 }
224
225 /* Code 2 of 5 Interleaved ISO/IEC 16390:2007 */
226 INTERNAL int c25inter(struct zint_symbol *symbol, unsigned char source[], int length) {
227 return c25_inter_common(symbol, source, length, symbol->option_2 /*checkdigit_option*/, 0 /*dont_set_height*/);
228 }
229
230 /* Interleaved 2-of-5 (ITF-14) */
231 INTERNAL int itf14(struct zint_symbol *symbol, unsigned char source[], int length) {
232 int i, error_number, zeroes;
233 unsigned char localstr[16] = {0};
234
235 if (length > 13) {
236 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 311, "Input length %d too long (maximum 13)", length);
237 }
238
239 if ((i = not_sane(NEON_F, source, length))) {
240 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 312,
241 "Invalid character at position %d in input (digits only)", i);
242 }
243
244 /* Add leading zeros as required */
245 zeroes = 13 - length;
246 for (i = 0; i < zeroes; i++) {
247 localstr[i] = '0';
248 }
249 ustrcpy(localstr + zeroes, source);
250
251 /* Calculate the check digit - the same method used for EAN-13 */
252 localstr[13] = gs1_check_digit(localstr, 13);
253 localstr[14] = '\0';
254 error_number = c25_inter_common(symbol, localstr, 14, 0 /*checkdigit_option*/, 1 /*dont_set_height*/);
255 ustrcpy(symbol->text, localstr);
256
257 if (error_number < ZINT_ERROR) {
258 if (!(symbol->output_options & (BARCODE_BOX | BARCODE_BIND | BARCODE_BIND_TOP))) {
259 /* If no option has been selected then uses default box option */
260 symbol->output_options |= BARCODE_BOX;
261 if (symbol->border_width == 0) { /* Allow override if non-zero */
262 /* GS1 General Specifications 21.0.1 Sections 5.3.2.4 & 5.3.6 (4.83 / 1.016 ~ 4.75) */
263 symbol->border_width = 5; /* Note change from previous value 8 */
264 }
265 }
266
267 if (symbol->output_options & COMPLIANT_HEIGHT) {
268 /* GS1 General Specifications 21.0.1 5.12.3.2 table 2, including footnote (**): (note bind/box additional
269 to symbol->height), same as GS1-128: "in case of further space constraints"
270 height 5.8mm / 1.016mm (X max) ~ 5.7; default 31.75mm / 0.495mm ~ 64.14 */
271 const float min_height = 5.70866156f; /* 5.8 / 1.016 */
272 const float default_height = 64.1414108f; /* 31.75 / 0.495 */
273 error_number = set_height(symbol, min_height, default_height, 0.0f, 0 /*no_errtxt*/);
274 } else {
275 (void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/);
276 }
277 }
278
279 return error_number;
280 }
281
282 /* Deutsche Post check digit */
283 static char c25_dp_check_digit(const unsigned int count) {
284 return itoc((10 - (count % 10)) % 10);
285 }
286
287 /* Deutsche Post Leitcode */
288 /* Documentation (of a very incomplete and non-technical type):
289 https://www.deutschepost.de/content/dam/dpag/images/D_d/dialogpost-schwer/dp-dialogpost-schwer-broschuere-072021.pdf
290 */
291 INTERNAL int dpleit(struct zint_symbol *symbol, unsigned char source[], int length) {
292 int i, j, error_number;
293 unsigned int count;
294 int factor;
295 unsigned char localstr[16] = {0};
296 int zeroes;
297
298 count = 0;
299 if (length > 13) {
300 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 313, "Input length %d too long (maximum 13)", length);
301 }
302 if ((i = not_sane(NEON_F, source, length))) {
303 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 314,
304 "Invalid character at position %d in input (digits only)", i);
305 }
306
307 zeroes = 13 - length;
308 for (i = 0; i < zeroes; i++)
309 localstr[i] = '0';
310 ustrcpy(localstr + zeroes, source);
311
312 factor = 4;
313 for (i = 12; i >= 0; i--) {
314 count += factor * ctoi(localstr[i]);
315 factor ^= 0x0D; /* Toggles 4 and 9 */
316 }
317 localstr[13] = c25_dp_check_digit(count);
318 localstr[14] = '\0';
319 error_number = c25_inter_common(symbol, localstr, 14, 0 /*checkdigit_option*/, 1 /*dont_set_height*/);
320
321 /* HRT formatting as per DIALOGPOST SCHWER brochure but TEC-IT differs as do examples at
322 https://www.philaseiten.de/cgi-bin/index.pl?ST=8615&CP=0&F=1#M147 */
323 for (i = 0, j = 0; i <= 14; i++) {
324 symbol->text[j++] = localstr[i];
325 if (i == 4 || i == 7 || i == 10) {
326 symbol->text[j++] = '.';
327 }
328 }
329
330 /* TODO: Find documentation on BARCODE_DPLEIT dimensions/height */
331 /* Based on eyeballing DIALOGPOST SCHWER, using 72X as default */
332 (void) set_height(symbol, 0.0f, 72.0f, 0.0f, 1 /*no_errtxt*/);
333
334 return error_number;
335 }
336
337 /* Deutsche Post Identcode */
338 /* See dpleit() for (sort of) documentation reference */
339 INTERNAL int dpident(struct zint_symbol *symbol, unsigned char source[], int length) {
340 int i, j, error_number, zeroes;
341 unsigned int count;
342 int factor;
343 unsigned char localstr[16] = {0};
344
345 count = 0;
346 if (length > 11) {
347 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 315, "Input length %d too long (maximum 11)", length);
348 }
349 if ((i = not_sane(NEON_F, source, length))) {
350 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 316,
351 "Invalid character at position %d in input (digits only)", i);
352 }
353
354 zeroes = 11 - length;
355 for (i = 0; i < zeroes; i++)
356 localstr[i] = '0';
357 ustrcpy(localstr + zeroes, source);
358
359 factor = 4;
360 for (i = 10; i >= 0; i--) {
361 count += factor * ctoi(localstr[i]);
362 factor ^= 0x0D; /* Toggles 4 and 9 */
363 }
364 localstr[11] = c25_dp_check_digit(count);
365 localstr[12] = '\0';
366 error_number = c25_inter_common(symbol, localstr, 12, 0 /*checkdigit_option*/, 1 /*dont_set_height*/);
367
368 /* HRT formatting as per DIALOGPOST SCHWER brochure but TEC-IT differs as do other examples (see above) */
369 for (i = 0, j = 0; i <= 12; i++) {
370 symbol->text[j++] = localstr[i];
371 if (i == 1 || i == 4 || i == 7) {
372 symbol->text[j++] = '.';
373 } else if (i == 3 || i == 10) {
374 symbol->text[j++] = ' ';
375 }
376 }
377
378 /* TODO: Find documentation on BARCODE_DPIDENT dimensions/height */
379 /* Based on eyeballing DIALOGPOST SCHWER, using 72X as default */
380 (void) set_height(symbol, 0.0f, 72.0f, 0.0f, 1 /*no_errtxt*/);
381
382 return error_number;
383 }
384
385 /* vim: set ts=4 sw=4 et : */