comparison mupdf-source/thirdparty/zint/backend/tools/gen_gs1_lint.php @ 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 <?php
2 /* Generate GS1 verify include "backend/gs1_lint.h" for "backend/gs1.c" */
3 /*
4 libzint - the open source barcode library
5 Copyright (C) 2021-2024 <rstuart114@gmail.com>
6 */
7 /* SPDX-License-Identifier: BSD-3-Clause */
8
9 /* To create "backend/gs1_lint.h" (from project directory):
10 *
11 * php backend/tools/gen_gs1_lint.php > backend/gs1_lint.h
12 *
13 * or to use local copy of "gs1-syntax-dictionary.txt":
14 *
15 * php backend/tools/gen_gs1_lint.php -f <local-path>/gs1-syntax-dictionary.txt backend/gs1_lint.h
16 *
17 *************************************************************************************************
18 * NOTE: up-to-date version requires syntax dictionary available from
19 * https://ref.gs1.org/tools/gs1-barcode-syntax-resource/syntax-dictionary/
20 *************************************************************************************************
21 */
22
23 $basename = basename(__FILE__);
24 $dirname = dirname(__FILE__);
25 $dirdirname = basename(dirname($dirname)) . '/' . basename($dirname);
26
27 $opts = getopt('c:f:h:l:t:');
28
29 $print_copyright = isset($opts['c']) ? (bool) $opts['c'] : true;
30 $file = isset($opts['f']) ? $opts['f']
31 : 'https://raw.githubusercontent.com/gs1/gs1-syntax-dictionary/main/gs1-syntax-dictionary.txt';
32 $print_h_guard = isset($opts['h']) ? (bool) $opts['h'] : true;
33 $use_length_only = isset($opts['l']) ? (bool) $opts['l'] : true;
34 $tab = isset($opts['t']) ? $opts['t'] : ' ';
35
36 if (($get = file_get_contents($file)) === false) {
37 exit("$basename:" . __LINE__ . " ERROR: Could not read file \"$file\"" . PHP_EOL);
38 }
39 if (strncasecmp($file, "http", 4) != 0) {
40 // Strip to last 2 directories
41 $stripped_dir = dirname($file);
42 $stripped_file = basename(dirname($stripped_dir)) . '/' . basename($stripped_dir) . '/' . basename($file);
43 } else {
44 $stripped_file = $file;
45 }
46
47 $lines = explode("\n", $get);
48
49 $spec_ais = $spec_parts = $spec_funcs = $spec_comments = $fixed_ais = array();
50 $batches = array_fill(0, 100, array());
51
52 // Parse the lines into AIs and specs
53 $line_no = 0;
54 foreach ($lines as $line) {
55 $line_no++;
56 if ($line === '' || $line[0] === '#') {
57 continue;
58 }
59 if (!preg_match('/^([0-9]+(?:-[0-9]+)?) +([ *?]* )([NXYZ][0-9.][ NXYZ0-9.,a-z=|+\[\]]*)(?:# (.+))?$/',
60 $line, $matches)) {
61 print $line . PHP_EOL;
62 exit("$basename:" . __LINE__ . " ERROR: Could not parse line $line_no" . PHP_EOL);
63 }
64 $ai = $matches[1];
65 $flags = trim($matches[2]);
66 $fixed = strpos($flags, "*") !== false;
67 $spec = preg_replace('/ +req=[0-9,n+]*/', '', trim($matches[3])); // Strip mandatory association info
68 $spec = preg_replace('/ +ex=[0-9,n]*/', '', $spec); // Strip invalid pairings info
69 $spec = preg_replace('/ +dlpkey[=0-9,|]*/', '', $spec); // Strip Digital Link primary key info
70 $comment = isset($matches[4]) ? trim($matches[4]) : '';
71
72 if (isset($spec_ais[$spec])) {
73 $ais = $spec_ais[$spec];
74 } else {
75 $ais = array();
76 }
77
78 if (($hyphen = strpos($ai, '-')) !== false) {
79 if ($fixed !== '') {
80 $fixed_ais[substr($ai, 0, 2)] = true;
81 }
82 $ai_s = (int) substr($ai, 0, $hyphen);
83 $ai_e = (int) substr($ai, $hyphen + 1);
84 $ais[] = array($ai_s, $ai_e);
85
86 $batch_s_idx = (int) ($ai_s / 100);
87 $batch_e_idx = (int) ($ai_e / 100);
88 if ($batch_s_idx !== $batch_e_idx) {
89 if (!in_array($spec, $batches[$batch_s_idx])) {
90 $batches[$batch_s_idx][] = $spec;
91 }
92 if (!in_array($spec, $batches[$batch_e_idx])) {
93 $batches[$batch_e_idx][] = $spec;
94 }
95 } else {
96 if (!in_array($spec, $batches[$batch_s_idx])) {
97 $batches[$batch_s_idx][] = $spec;
98 }
99 }
100 } else {
101 if ($fixed !== '') {
102 $fixed_ais[substr($ai, 0, 2)] = true;
103 }
104 $ai = (int) $ai;
105 $ais[] = $ai;
106 $batch_idx = (int) ($ai / 100);
107 if (!in_array($spec, $batches[$batch_idx])) {
108 $batches[$batch_idx][] = $spec;
109 }
110 }
111
112 $spec_ais[$spec] = $ais;
113 if ($comment !== '') {
114 if (isset($spec_comments[$spec])) {
115 if (!in_array($comment, $spec_comments[$spec])) {
116 $spec_comments[$spec][] = $comment;
117 }
118 } else {
119 $spec_comments[$spec] = array($comment);
120 }
121 }
122
123 $spec_parts[$spec] = array();
124 $parts = explode(' ', $spec);
125 foreach ($parts as $part) {
126 $checkers = explode(',', $part);
127 $validator = array_shift($checkers);
128 if (preg_match('/^([NXYZ])([0-9]+)?(\.\.[0-9|]+)?$/', $validator, $matches)) {
129 if (count($matches) === 3) {
130 $min = $max = (int) $matches[2];
131 } else {
132 $min = $matches[2] === '' ? 1 : (int) $matches[2];
133 $max = (int) substr($matches[3], 2);
134 }
135 if ($matches[1] === 'N') {
136 $validator = "numeric";
137 } elseif ($matches[1] === 'X') {
138 $validator = "cset82";
139 } elseif ($matches[1] === 'Y') {
140 $validator = "cset39";
141 } else { // 'Z'
142 $validator = "cset64";
143 }
144 } else if (preg_match('/^\[([NXYZ])([1-9]+)?(\.\.[0-9|]+)?\]$/', $validator, $matches)) {
145 if (count($matches) === 3) {
146 $min = 0;
147 $max = (int) $matches[2];
148 } else {
149 $min = $matches[2] === '' ? 0 : (int) $matches[2];
150 $max = (int) substr($matches[3], 2);
151 }
152 if ($matches[1] === 'N') {
153 $validator = "numeric";
154 } elseif ($matches[1] === 'X') {
155 $validator = "cset82";
156 } elseif ($matches[1] === 'Y') {
157 $validator = "cset39";
158 } else { // 'Z'
159 $validator = "cset64";
160 }
161 } else {
162 exit("$basename:" . __LINE__ . " ERROR: Could not parse validator \"$validator\" line $line_no"
163 . PHP_EOL);
164 }
165 $spec_parts[$spec][] = array($min, $max, $validator, $checkers);
166 }
167 }
168
169 // Calculate total min/maxs and convert the AIs into ranges
170
171 foreach ($spec_ais as $spec => $ais) {
172 // Total min/maxs
173 $total_min = $total_max = 0;
174 foreach ($spec_parts[$spec] as list($min, $max)) {
175 $total_min += $min;
176 $total_max += $max;
177 }
178
179 // Sort the AIs
180 $sort_ais = array();
181 foreach ($ais as $ai) {
182 if (is_array($ai)) {
183 $sort_ais[] = $ai[0];
184 } else {
185 $sort_ais[] = $ai;
186 }
187 }
188 array_multisort($sort_ais, $ais);
189
190 // Consolidate contiguous AIs into ranges
191 $tmp_ais = array();
192 foreach ($ais as $ai) {
193 $cnt = count($tmp_ais);
194 if ($cnt === 0) {
195 $tmp_ais[] = $ai;
196 } else {
197 $prev_ai = $tmp_ais[$cnt - 1];
198 if (is_array($prev_ai)) {
199 $prev_s = $prev_ai[0];
200 $prev_e = $prev_ai[1];
201 } else {
202 $prev_e = $prev_s = $prev_ai;
203 }
204 if (is_array($ai)) {
205 $this_s = $ai[0];
206 $this_e = $ai[1];
207 } else {
208 $this_s = $this_e = $ai;
209 }
210 if ($this_s === $prev_e + 1 && $this_e - $prev_s < 100) { // Confine to batches of 100
211 $tmp_ais[$cnt - 1] = array($prev_s, $this_e);
212 } else {
213 $tmp_ais[] = $ai;
214 }
215 }
216 }
217
218 // Unconsolidate ranges of 1 into separate entries
219 $ais = array();
220 foreach ($tmp_ais as $ai) {
221 if (is_array($ai) && $ai[1] === $ai[0] + 1) {
222 $ais[] = $ai[0];
223 $ais[] = $ai[1];
224 } else {
225 $ais[] = $ai;
226 }
227 }
228
229 $spec_ais[$spec] = array($total_min, $total_max, $ais);
230 }
231
232 // Print output
233
234 print <<<EOD
235 /*
236 * GS1 AI checker generated by "$dirdirname/$basename" from
237 * $stripped_file
238 */
239
240 EOD;
241
242 if ($print_copyright) {
243 print <<<'EOD'
244 /*
245 libzint - the open source barcode library
246 Copyright (C) 2021-2024 Robin Stuart <rstuart114@gmail.com>
247
248 Redistribution and use in source and binary forms, with or without
249 modification, are permitted provided that the following conditions
250 are met:
251
252 1. Redistributions of source code must retain the above copyright
253 notice, this list of conditions and the following disclaimer.
254 2. Redistributions in binary form must reproduce the above copyright
255 notice, this list of conditions and the following disclaimer in the
256 documentation and/or other materials provided with the distribution.
257 3. Neither the name of the project nor the names of its contributors
258 may be used to endorse or promote products derived from this software
259 without specific prior written permission.
260
261 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
262 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
263 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
264 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
265 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
266 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
267 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
268 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
270 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271 SUCH DAMAGE.
272 */
273 /* SPDX-License-Identifier: BSD-3-Clause */
274
275
276 EOD;
277 }
278
279 if ($print_h_guard) {
280 print <<<'EOD'
281 #ifndef Z_GS1_LINT_H
282 #define Z_GS1_LINT_H
283
284
285 EOD;
286 }
287
288 // Print the spec validator/checkers functions
289
290 foreach ($spec_parts as $spec => $spec_part) {
291 $spec_funcs[$spec] = $spec_func = str_replace(array(' ', '.', ',', '[', ']'), '_', strtolower($spec));
292 $comment = '';
293 if (isset($spec_comments[$spec])) {
294 $comment = ' (Used by';
295 foreach ($spec_comments[$spec] as $i => $spec_comment) {
296 if ($i) {
297 $comment .= ', ';
298 } else {
299 $comment .= ' ';
300 }
301 $comment .= $spec_comment;
302 }
303 if (strlen($comment) > 118 - 3 /*start comment*/ - 4 /*)end comment*/ - strlen($spec)) {
304 $comment = substr($comment, 0, 118 - 3 - 4 - strlen($spec) - 3) . '...';
305 }
306 $comment .= ')';
307 }
308 print <<<EOD
309 /* $spec$comment */
310 static int $spec_func(const unsigned char *data,
311 $tab$tab{$tab}const int data_len, int *p_err_no, int *p_err_posn, char err_msg[50]) {
312 {$tab}return
313 EOD;
314
315 list($total_min, $total_max) = $spec_ais[$spec];
316 if ($total_min === $total_max) {
317 print "data_len == $total_max";
318 } else {
319 print "data_len >= $total_min && data_len <= $total_max";
320 }
321
322 if ($use_length_only) {
323 // Call checkers checking for length only first
324 $length_only_arg = ", 1 /*length_only*/";
325 $offset = 0;
326 foreach ($spec_part as list($min, $max, $validator, $checkers)) {
327 foreach ($checkers as $checker) {
328 print <<<EOD
329
330 $tab$tab{$tab}&& $checker(data, data_len, $offset, $min, $max, p_err_no, p_err_posn, err_msg$length_only_arg)
331 EOD;
332 }
333
334 $offset += $max;
335 }
336 }
337
338 // Validator and full checkers
339 $length_only_arg = $use_length_only ? ", 0" : "";
340 $offset = 0;
341 foreach ($spec_part as list($min, $max, $validator, $checkers)) {
342 print <<<EOD
343
344 $tab$tab{$tab}&& $validator(data, data_len, $offset, $min, $max, p_err_no, p_err_posn, err_msg)
345 EOD;
346
347 foreach ($checkers as $checker) {
348 print <<<EOD
349
350 $tab$tab{$tab}&& $checker(data, data_len, $offset, $min, $max, p_err_no, p_err_posn, err_msg$length_only_arg)
351 EOD;
352 }
353
354 $offset += $max;
355 }
356 print ";\n}\n\n";
357 }
358
359 // Print main routine
360
361 print <<<EOD
362 /* Entry point. Returns 1 on success, 0 on failure: `*p_err_no` set to 1 if unknown AI, 2 if bad data length */
363 static int gs1_lint(const int ai, const unsigned char *data, const int data_len, int *p_err_no, int *p_err_posn,
364 $tab$tab{$tab}char err_msg[50]) {
365
366 $tab/* Assume data length failure */
367 $tab*p_err_no = 2;
368
369 EOD;
370
371 // Split AIs into batches of 100 to lessen the number of comparisons
372
373 $not_first_batch = false;
374 $last_batch_e = -1;
375 foreach ($batches as $batch => $batch_specs) {
376 if (empty($batch_specs)) {
377 continue;
378 }
379 $batch_s = $batch * 100;
380 $batch_e = $batch_s + 100;
381 if ($not_first_batch) {
382 print "\n$tab} else if (ai < $batch_e) {\n\n";
383 } else {
384 print "\n{$tab}if (ai < $batch_e) {\n\n";
385 $not_first_batch = true;
386 }
387 foreach ($batch_specs as $spec) {
388 $total_min = $spec_ais[$spec][0];
389 $total_max = $spec_ais[$spec][1];
390 $ais = $spec_ais[$spec][2];
391
392 $str = "$tab{$tab}if (";
393 print $str;
394 $width = strlen($str);
395
396 // Count the applicable AIs
397 $ais_cnt = 0;
398 foreach ($ais as $ai) {
399 if (is_array($ai)) {
400 if ($ai[1] < $batch_s || $ai[0] >= $batch_e) {
401 continue;
402 }
403 } else {
404 if ($ai < $batch_s || $ai >= $batch_e) {
405 continue;
406 }
407 }
408 $ais_cnt++;
409 }
410
411 // Output
412 $not_first_ai = false;
413 foreach ($ais as $ai) {
414 if (is_array($ai)) {
415 if ($ai[1] < $batch_s || $ai[0] >= $batch_e) {
416 continue;
417 }
418 } else {
419 if ($ai < $batch_s || $ai >= $batch_e) {
420 continue;
421 }
422 }
423
424 $str = '';
425 if ($not_first_ai) {
426 $str .= " || ";
427 } else {
428 $not_first_ai = true;
429 }
430 if (is_array($ai)) {
431 if ($ai[0] === $last_batch_e) { // Don't need 1st element of range if excluded by previous batch
432 $str .= "ai <= " . $ai[1];
433 } else if ($ai[1] + 1 == $batch_e) { // Don't need 2nd element of range if excluded by this batch
434 $str .= "ai >= " . $ai[0];
435 } else {
436 if ($ais_cnt > 1) {
437 $str .= "(ai >= " . $ai[0] . " && ai <= " . $ai[1] . ")";
438 } else {
439 $str .= "ai >= " . $ai[0] . " && ai <= " . $ai[1];
440 }
441 }
442 } else {
443 $str .= "ai == " . $ai;
444 }
445 if ($width + strlen($str) > 118) {
446 print "\n";
447 $str2 = "$tab$tab$tab ";
448 print $str2;
449 $width = strlen($str2);
450 }
451 print $str;
452 $width += strlen($str);
453 }
454 $spec_func = $spec_funcs[$spec];
455 $str = "$tab$tab{$tab}return $spec_func(data, data_len, p_err_no, p_err_posn, err_msg);";
456 if (strlen($str) > 118) {
457 print ") {\n$tab$tab{$tab}return $spec_func(data,\n";
458 print "$tab$tab$tab$tab$tab{$tab}data_len, p_err_no, p_err_posn, err_msg);\n";
459 } else {
460 print ") {\n$str\n";
461 }
462 print <<<EOD
463 $tab$tab}
464
465 EOD;
466 }
467 $last_batch_e = $batch_e;
468 }
469
470 print <<<EOD
471 $tab}
472
473 {$tab}/* Unknown AI */
474 {$tab}*p_err_no = 1;
475 {$tab}return 0;
476 }
477
478 EOD;
479
480 if ($print_h_guard) {
481 print <<<'EOD'
482
483 #endif /* Z_GS1_LINT_H */
484
485 EOD;
486 }
487
488 /* vim: set ts=4 sw=4 et : */