comparison mupdf-source/thirdparty/zint/backend/maxicode.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 /* maxicode.c - Handles MaxiCode */
2 /*
3 libzint - the open source barcode library
4 Copyright (C) 2010-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 /* Includes corrections thanks to Monica Swanson @ Source Technologies */
34 #include <assert.h>
35 #include <stdio.h>
36 #include "common.h"
37 #include "maxicode.h"
38 #include "reedsol.h"
39
40 /* Code Set states. Those with PAD (i.e. A, B and E) are first pick */
41 #define MX_A 0
42 #define MX_B 1
43 #define MX_E 2
44 #define MX_C 3
45 #define MX_D 4
46 #define MX_STATES 5
47
48 /* Prior:A B E C D Later */
49 static const char mx_latch_seq[MX_STATES][MX_STATES][2] = {
50 { { 0 }, {63 }, {58 }, {58 }, {58 } }, /* A */
51 { {63 }, { 0 }, {63 }, {63 }, {63 } }, /* B */
52 { {62,62}, {62,62}, { 0 }, {62,62}, {62,62} }, /* E */
53 { {60,60}, {60,60}, {60,60}, { 0 }, {60,60} }, /* C */
54 { {61,61}, {61,61}, {61,61}, {61,61}, { 0 } }, /* D */
55
56 };
57 static const char mx_latch_len[MX_STATES][MX_STATES] = { /* Lengths of above */
58 { 0, 1, 1, 1, 1 }, /* A */
59 { 1, 0, 1, 1, 1 }, /* B */
60 { 2, 2, 0, 2, 2 }, /* E */
61 { 2, 2, 2, 0, 2 }, /* C */
62 { 2, 2, 2, 2, 0 }, /* D */
63 };
64
65 /* Op */
66 struct mx_op {
67 unsigned char op;
68 unsigned char intake; /* `output` calculated from this */
69 };
70
71 /* Op table ops */
72 #define MX_OP_DGTS 0
73 #define MX_OP_SETA 0x01 /* If change these, must change `maxiCodeSet` */
74 #define MX_OP_SETB 0x02
75 #define MX_OP_SETE 0x04
76 #define MX_OP_SETC 0x08
77 #define MX_OP_SETD 0x10
78 #define MX_OP_SHA (0x20 | MX_OP_SETA)
79 #define MX_OP_2SHA (0x40 | MX_OP_SETA)
80 #define MX_OP_3SHA (0x80 | MX_OP_SETA)
81 #define MX_OP_SHB (0x20 | MX_OP_SETB)
82 #define MX_OP_SHE (0x20 | MX_OP_SETE)
83 #define MX_OP_SHC (0x20 | MX_OP_SETC)
84 #define MX_OP_SHD (0x20 | MX_OP_SETD)
85
86 /* Op table indexes */
87 #define MX_OP_DGTS_IDX 0
88 #define MX_OP_SETA_IDX 1
89 #define MX_OP_SETB_IDX 2
90 #define MX_OP_SETE_IDX 3
91 #define MX_OP_SETC_IDX 4
92 #define MX_OP_SETD_IDX 5
93 #define MX_OP_SHA_IDX 6
94 #define MX_OP_2SHA_IDX 7
95 #define MX_OP_3SHA_IDX 8
96 #define MX_OP_SHB_IDX 9
97 #define MX_OP_SHE_IDX 10
98 #define MX_OP_SHC_IDX 11
99 #define MX_OP_SHD_IDX 12
100 #define MX_OP_IDXS 13
101
102 /* Op table (order must match indexes above) */
103 static const struct mx_op mx_op_tab[MX_OP_IDXS] = {
104 /* op intake */
105 { MX_OP_DGTS, 9 },
106 { MX_OP_SETA, 1 },
107 { MX_OP_SETB, 1 },
108 { MX_OP_SETE, 1 },
109 { MX_OP_SETC, 1 },
110 { MX_OP_SETD, 1 },
111 { MX_OP_SHA, 1 },
112 { MX_OP_2SHA, 2 },
113 { MX_OP_3SHA, 3 },
114 { MX_OP_SHB, 1 },
115 { MX_OP_SHE, 1 },
116 { MX_OP_SHC, 1 },
117 { MX_OP_SHD, 1 },
118 };
119
120 /* Indexes of op sets relevant to each state - MX_OP_DGTS dealt with separately */
121 static const signed char mx_code_set_op_idxs[MX_STATES][8] = {
122 { MX_OP_SETA_IDX, MX_OP_SHB_IDX, MX_OP_SHE_IDX, MX_OP_SHC_IDX, MX_OP_SHD_IDX, -1 }, /* MX_A */
123 { MX_OP_SETB_IDX, MX_OP_SHA_IDX, MX_OP_2SHA_IDX, MX_OP_3SHA_IDX, MX_OP_SHE_IDX, MX_OP_SHC_IDX, /* MX_B */
124 MX_OP_SHD_IDX, -1 },
125 { MX_OP_SETE_IDX, MX_OP_SHC_IDX, MX_OP_SHD_IDX, -1 }, /* MX_E */
126 { MX_OP_SETC_IDX, MX_OP_SHE_IDX, MX_OP_SHD_IDX, -1 }, /* MX_C */
127 { MX_OP_SETD_IDX, MX_OP_SHE_IDX, MX_OP_SHC_IDX, -1 }, /* MX_D */
128 };
129
130 /* Whether can encode character `ch` with `op` - MX_OP_DGTS dealt with separately */
131 static int mx_can(const unsigned char op, const unsigned char ch, const int num_a) {
132 if (op == MX_OP_2SHA || op == MX_OP_3SHA) {
133 return num_a >= 2 + (op == MX_OP_3SHA);
134 }
135 return maxiCodeSet[ch] & op;
136 }
137
138 /* Get the symbol value for `ch` in Code Set of `op`, accounting for chars in multiple Code Sets */
139 static int mx_symbol_ch(const unsigned char op, const unsigned char ch) {
140 if (maxiCodeSet[ch] == (op & 0x1F) || (op & MX_OP_SETA)) { /* Non-multiple or Code Set A */
141 return maxiSymbolChar[ch];
142 }
143 if (op & MX_OP_SETB) {
144 const int p = posn(" ,./:", ch);
145 if (p >= 0) {
146 return 47 + p;
147 }
148 }
149 if (op & MX_OP_SETE) {
150 if (ch >= 28 && ch <= 30) { /* FS GS RS */
151 return ch + 4;
152 }
153 }
154 return ch == ' ' ? 59 : ch; /* SP CR FS GS RS */
155 }
156
157 /* Encode according to operation `op` (note done backwards) */
158 static int mx_enc(unsigned char codewords[144], int ci, const unsigned char source[], const int i,
159 const unsigned char op) {
160 if (op == MX_OP_DGTS) {
161 const int value = (source[i] - '0') * 100000000 + (source[i + 1] - '0') * 10000000
162 + (source[i + 2] - '0') * 1000000 + (source[i + 3] - '0') * 100000
163 + (source[i + 4] - '0') * 10000 + (source[i + 5] - '0') * 1000
164 + (source[i + 6] - '0') * 100 + (source[i + 7] - '0') * 10 + source[i + 8] - '0';
165 assert(ci >= 6);
166 codewords[--ci] = (value & 0x3F);
167 codewords[--ci] = (value & 0xFC0) >> 6;
168 codewords[--ci] = (value & 0x3F000) >> 12;
169 codewords[--ci] = (value & 0xFC0000) >> 18;
170 codewords[--ci] = (value & 0x3F000000) >> 24;
171 codewords[--ci] = 31; /* NS */
172 } else if (op == MX_OP_2SHA) {
173 assert(ci >= 3);
174 codewords[--ci] = mx_symbol_ch(op, source[i + 1]);
175 codewords[--ci] = mx_symbol_ch(op, source[i]);
176 codewords[--ci] = 56;
177 } else if (op == MX_OP_3SHA) {
178 assert(ci >= 4);
179 codewords[--ci] = mx_symbol_ch(op, source[i + 2]);
180 codewords[--ci] = mx_symbol_ch(op, source[i + 1]);
181 codewords[--ci] = mx_symbol_ch(op, source[i]);
182 codewords[--ci] = 57;
183 } else {
184 assert(ci >= 1);
185 codewords[--ci] = mx_symbol_ch(op, source[i]);
186
187 if (op & 0x20) { /* Shift */
188 assert(ci >= 1);
189 codewords[--ci] = 59 + 1 * (op == MX_OP_SHC) + 2 * (op == MX_OP_SHD) + 3 * (op == MX_OP_SHE);
190 }
191 }
192 return ci;
193 }
194
195 /* Encoding length of ECI */
196 static int mx_eci_len(const int eci) {
197 return eci == 0 ? 0 : 2 + (eci > 31) + (eci > 1023) + (eci > 32767);
198 }
199
200 /* Encode ECI (`eci` non-zero) */
201 static int mx_enc_eci(const int eci, unsigned char codewords[144], int ci) {
202 codewords[--ci] = eci & 0x3F;
203 if (eci > 31) {
204 if (eci > 1023) {
205 codewords[--ci] = (eci & 0xFC0) >> 6;
206 if (eci > 32767) {
207 codewords[--ci] = (eci & 0x3F000) >> 12;
208 codewords[--ci] = 0x38 | ((eci & 0xC0000) >> 18);
209 } else {
210 codewords[--ci] = 0x30 | ((eci & 0x7000) >> 12);
211 }
212 } else {
213 codewords[--ci] = 0x20 | ((eci & 0x3C0) >> 6);
214 }
215 }
216 codewords[--ci] = 27; /* ECI */
217
218 return ci;
219 }
220
221 /* Get the shortest encoded length for the Code Set (state) and plot the path */
222 static int mx_get_best_length(const int state, const int i, const unsigned char ch, const int digits, const int num_a,
223 const int best_lengths[16][MX_STATES], const char best_origins[16][MX_STATES],
224 unsigned char *const path_op, char *const prior_code_set) {
225 const char *const latch_length_s = mx_latch_len[state];
226 int min_len = 999999;
227 int j;
228
229 if (digits >= 9) { /* Nothing can beat digits */
230 const int m = (i - 9) & 0x0F;
231 const int org = best_origins[m][state];
232 const int len = best_lengths[m][org] + latch_length_s[org] + 6;
233 if (len < min_len) {
234 path_op[state] = MX_OP_DGTS_IDX;
235 prior_code_set[state] = org;
236 min_len = len;
237 }
238 } else {
239 const signed char *const op_idx_s = mx_code_set_op_idxs[state];
240 for (j = 0; op_idx_s[j] != -1; j++) {
241 const int op_idx = op_idx_s[j];
242 const struct mx_op *const op = &mx_op_tab[op_idx];
243 if (mx_can(op->op, ch, num_a)) {
244 const int m = (i - op->intake) & 0x0F;
245 const int org = best_origins[m][state];
246 const int len = best_lengths[m][org] + latch_length_s[org] + op->intake + (op_idx >= MX_OP_SHA_IDX);
247 if (len < min_len) {
248 path_op[state] = op_idx;
249 prior_code_set[state] = org;
250 min_len = len;
251 }
252 }
253 }
254 }
255 return min_len;
256 }
257
258 /* Loop to get the best prior Code Set using a row of best encoded lengths */
259 static int mx_get_best_origin(const int state, const int *const best_length) {
260
261 const char *const latch_length_s = mx_latch_len[state];
262 int orglen = best_length[0] + latch_length_s[0];
263 int best_org = 0;
264 int org;
265
266 for (org = 1; org < MX_STATES; org++) {
267 const int len = best_length[org] + latch_length_s[org];
268 if (len < orglen) {
269 best_org = org;
270 orglen = len;
271 }
272 }
273 return best_org;
274 }
275
276 /* Minimal encoding using backtracking by Bue Jensen, taken from BWIPP - see
277 https://github.com/bwipp/postscriptbarcode/pull/279 */
278 static int mx_text_process_segs(unsigned char codewords[144], const int mode, struct zint_seg segs[],
279 const int seg_count, const int structapp_cw, const int scm_vv, const int debug_print) {
280
281 /* The encoder needs 10 history rows. The circular history buffers are 16 long for convenience */
282 int best_lengths[16][MX_STATES] = {0};
283 char best_origins[16][MX_STATES] = {0};
284 int *best_length = NULL; /* Suppress clang-tidy-20 warning */
285
286 int cp = 20 - 9 * (mode > 3); /* Offset the initial codewords index to minimize copying */
287 const int max_len = (mode == 5 ? 77 : 93) + 11; /* 11 added to adjust for above offset */
288 int ci, ci_top;
289
290 /* Backtracking information */
291 const int segs_len = segs_length(segs, seg_count);
292 char (*prior_code_sets)[MX_STATES] = (char (*)[MX_STATES]) z_alloca(sizeof(*prior_code_sets) * (segs_len + 9));
293 unsigned char (*path_ops)[MX_STATES]
294 = (unsigned char (*)[MX_STATES]) z_alloca(sizeof(*path_ops) * (segs_len + 9));
295
296 int digits = 0;
297 int num_a = 0;
298
299 int min_len = 999999;
300 int min_state = 0;
301 int state;
302
303 unsigned char *source_scm_vv; /* For SCM prefix `scm_vv` if any */
304 int have_eci_scm = 0; /* Set if have ECI and SCM prefix */
305
306 int seg;
307 int si = 0; /* Segment offset to `source` position */
308 int i, j;
309
310 if (scm_vv != -1) { /* Add SCM prefix */
311 source_scm_vv = (unsigned char *) z_alloca(segs[0].length + 9);
312 sprintf((char *) source_scm_vv, "[)>\03601\035%02d", scm_vv); /* [)>\R01\Gvv */
313 memcpy(source_scm_vv + 9, segs[0].source, segs[0].length);
314 segs[0].source = source_scm_vv;
315 segs[0].length += 9;
316 have_eci_scm = segs[0].eci;
317 } else if (segs[0].length >= 9 && memcmp(segs[0].source, "[)>\03601\035", 7) == 0
318 && z_isdigit(segs[0].source[7]) && z_isdigit(segs[0].source[8])) {
319 have_eci_scm = segs[0].eci;
320 }
321
322 /* Insert Structured Append at beginning if needed */
323 if (structapp_cw) {
324 codewords[cp++] = 33; /* PAD */
325 codewords[cp++] = structapp_cw;
326 }
327
328 /* Make a table of best path options */
329 ci = cp;
330 for (seg = 0; seg < seg_count; seg++) {
331 /* Suppress NS compaction for SCM prefix if have ECI so can place ECI after it when encoding */
332 const int no_eci_scm_check = !have_eci_scm || seg != 0;
333 const unsigned char *const source = segs[seg].source;
334 const int length = segs[seg].length;
335 const int eci_len = mx_eci_len(segs[seg].eci);
336 if (eci_len) {
337 ci += eci_len;
338 if (ci > max_len) {
339 return ZINT_ERROR_TOO_LONG;
340 }
341 digits = 0;
342 }
343
344 for (i = 0; i < length; i++) {
345 const unsigned char ch = source[i];
346 const int si_i = i + si;
347
348 /* Get rows of interest */
349 unsigned char *const path_op = path_ops[si_i];
350 char *const prior_code_set = prior_code_sets[si_i];
351 char *const best_origin = best_origins[(si_i) & 0x0F];
352 best_length = best_lengths[(si_i) & 0x0F];
353
354 /* Keep tabs on digits and characters in Code Set A */
355 digits = z_isdigit(ch) && (no_eci_scm_check || i >= 9) ? digits + 1 : 0;
356 num_a = maxiCodeSet[ch] & MX_OP_SETA ? num_a + 1 : 0;
357
358 /* Get best encoded lengths, then best prior Code Sets */
359 for (state = 0; state < MX_STATES; state++) {
360 best_length[state] = mx_get_best_length(state, si_i, ch, digits, num_a, best_lengths, best_origins,
361 path_op, prior_code_set);
362 }
363 for (state = 0; state < MX_STATES; state++) {
364 best_origin[state] = mx_get_best_origin(state, best_length);
365 }
366 }
367 si += length;
368 }
369 assert(best_length == best_lengths[(segs_len + 9 * (scm_vv != -1) - 1) & 0x0F]); /* Set to last char */
370
371 /* Get the best Code Set to end with */
372 for (state = 0; state < MX_STATES; state++) {
373 const int len = best_length[state];
374 if (len < min_len) {
375 min_state = state;
376 min_len = len;
377 }
378 }
379 if (ci + min_len > max_len) {
380 return ZINT_ERROR_TOO_LONG;
381 }
382
383 /* Follow the best path back to the start of the message */
384 ci += min_len;
385 ci_top = ci;
386 state = min_state;
387 for (seg = seg_count - 1; seg >= 0; seg--) {
388 const unsigned char *const source = segs[seg].source;
389 const int length = segs[seg].length;
390 const int eci_scm_check = have_eci_scm && seg == 0;
391
392 si -= length;
393 assert(si >= 0);
394
395 i = length;
396 while (i > 0) {
397 const int ch_i = (i + si) - 1;
398 const int pcs = prior_code_sets[ch_i][state];
399 const int op_idx = path_ops[ch_i][state];
400 const struct mx_op *const op = &mx_op_tab[op_idx];
401
402 if (eci_scm_check && i == 9) { /* Place ECI after SCM prefix */
403 assert(ci >= cp + mx_eci_len(segs[0].eci));
404 ci = mx_enc_eci(segs[0].eci, codewords, ci);
405 segs[0].eci = 0;
406 }
407
408 i -= op->intake;
409 assert(i >= 0);
410 ci = mx_enc(codewords, ci, source, i, op->op);
411
412 if (state != pcs) {
413 const int latch_len = mx_latch_len[state][pcs];
414 assert(ci >= cp + latch_len);
415 for (j = 0; j < latch_len; j++) {
416 codewords[--ci] = mx_latch_seq[state][pcs][j];
417 }
418 state = pcs;
419 }
420 }
421 if (segs[seg].eci) {
422 assert(ci >= cp + mx_eci_len(segs[seg].eci));
423 ci = mx_enc_eci(segs[seg].eci, codewords, ci);
424 }
425 }
426 assert(ci == cp);
427
428 cp = ci_top;
429
430 /* If end in Code Set C or D, switch to A for padding */
431 if (cp < max_len && (min_state == MX_C || min_state == MX_D)) {
432 codewords[cp++] = 58; /* Latch A */
433 }
434
435 if (debug_print) {
436 if (cp < max_len) {
437 printf("Pads: %d\n", max_len - cp);
438 } else {
439 fputs("No Pads\n", stdout);
440 }
441 }
442
443 if (cp < max_len) {
444 /* Add the padding */
445 memset(codewords + cp, min_state == MX_E ? 28 : 33, max_len - cp);
446 }
447
448 if (debug_print) printf("Length: %d\n", cp);
449
450 if (cp > max_len) {
451 return ZINT_ERROR_TOO_LONG;
452 }
453
454 /* Adjust the codeword array */
455 if (mode > 3) {
456 memcpy(codewords + 1, codewords + 20 - 9, 9); /* Primary */
457 }
458
459 return 0;
460 }
461
462 /* Handles error correction of primary message */
463 static void mx_do_primary_ecc(unsigned char codewords[144]) {
464 const int datalen = 10, eclen = 10;
465 unsigned char ecc[10];
466 int j;
467 rs_t rs;
468
469 rs_init_gf(&rs, 0x43);
470 rs_init_code(&rs, eclen, 1);
471
472 rs_encode(&rs, datalen, codewords, ecc);
473
474 for (j = 0; j < eclen; j++) {
475 codewords[datalen + j] = ecc[j];
476 }
477 }
478
479 /* Handles error correction of characters in secondary */
480 static void mx_do_secondary_ecc(unsigned char codewords[144], const int datalen, const int eclen) {
481 unsigned char data[42]; /* Half max `datalen` (84) */
482 unsigned char ecc[28]; /* Half max `eclen` (56) */
483 int j;
484 rs_t rs;
485
486 rs_init_gf(&rs, 0x43);
487 rs_init_code(&rs, eclen, 1);
488
489 /* Even */
490 for (j = 0; j < datalen; j += 2) {
491 data[j >> 1] = codewords[j + 20];
492 }
493
494 rs_encode(&rs, datalen >> 1, data, ecc);
495
496 for (j = 0; j < eclen; j++) {
497 codewords[datalen + (j << 1) + 20] = ecc[j];
498 }
499
500 /* Odd */
501 for (j = 0; j < datalen; j += 2) {
502 data[j >> 1] = codewords[j + 1 + 20];
503 }
504
505 rs_encode(&rs, datalen >> 1, data, ecc);
506
507 for (j = 0; j < eclen; j++) {
508 codewords[datalen + (j << 1) + 1 + 20] = ecc[j];
509 }
510 }
511
512 /* Format structured primary for Mode 2 */
513 static void mx_do_primary_2(unsigned char codewords[144], const unsigned char postcode[],
514 const int postcode_length, const int country, const int service) {
515
516 const int postcode_num = to_int(postcode, postcode_length);
517
518 codewords[0] = ((postcode_num & 0x03) << 4) | 2;
519 codewords[1] = ((postcode_num & 0xFC) >> 2);
520 codewords[2] = ((postcode_num & 0x3F00) >> 8);
521 codewords[3] = ((postcode_num & 0xFC000) >> 14);
522 codewords[4] = ((postcode_num & 0x3F00000) >> 20);
523 codewords[5] = ((postcode_num & 0x3C000000) >> 26) | ((postcode_length & 0x03) << 4);
524 codewords[6] = ((postcode_length & 0x3C) >> 2) | ((country & 0x03) << 4);
525 codewords[7] = (country & 0xFC) >> 2;
526 codewords[8] = ((country & 0x300) >> 8) | ((service & 0x0F) << 2);
527 codewords[9] = ((service & 0x3F0) >> 4);
528 }
529
530 /* Format structured primary for Mode 3 */
531 static void mx_do_primary_3(unsigned char codewords[144], unsigned char postcode[], const int country,
532 const int service) {
533 int i;
534
535 /* Convert to Code Set A */
536 for (i = 0; i < 6; i++) {
537 postcode[i] = maxiSymbolChar[postcode[i]];
538 }
539
540 codewords[0] = ((postcode[5] & 0x03) << 4) | 3;
541 codewords[1] = ((postcode[4] & 0x03) << 4) | ((postcode[5] & 0x3C) >> 2);
542 codewords[2] = ((postcode[3] & 0x03) << 4) | ((postcode[4] & 0x3C) >> 2);
543 codewords[3] = ((postcode[2] & 0x03) << 4) | ((postcode[3] & 0x3C) >> 2);
544 codewords[4] = ((postcode[1] & 0x03) << 4) | ((postcode[2] & 0x3C) >> 2);
545 codewords[5] = ((postcode[0] & 0x03) << 4) | ((postcode[1] & 0x3C) >> 2);
546 codewords[6] = ((postcode[0] & 0x3C) >> 2) | ((country & 0x03) << 4);
547 codewords[7] = (country & 0xFC) >> 2;
548 codewords[8] = ((country & 0x300) >> 8) | ((service & 0x0F) << 2);
549 codewords[9] = ((service & 0x3F0) >> 4);
550 }
551
552 INTERNAL int maxicode(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
553 int i, j, mode, lp = 0;
554 int error_number;
555 unsigned char codewords[144];
556 int scm_vv = -1;
557 int structapp_cw = 0;
558 const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
559
560 mode = symbol->option_1;
561
562 if (mode <= 0) { /* If mode is unspecified (-1) or to be auto-determined (0) between 2 and 3 */
563 lp = (int) strlen(symbol->primary);
564 if (lp == 0) {
565 if (mode == 0) { /* Require primary message to auto-determine between 2 and 3 */
566 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 554, "Primary Message empty");
567 }
568 mode = 4;
569 } else {
570 mode = 2;
571 for (i = 0; i < lp - 6; i++) {
572 if (!z_isdigit(symbol->primary[i]) && (symbol->primary[i] != ' ')) {
573 mode = 3;
574 break;
575 }
576 }
577 }
578 }
579
580 if (mode < 2 || mode > 6) { /* Only codes 2 to 6 supported */
581 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 550, "Mode '%d' out of range (2 to 6)", mode);
582 }
583
584 if (mode <= 3) { /* Modes 2 and 3 need data in symbol->primary */
585 unsigned char postcode[10];
586 int countrycode;
587 int service;
588 int postcode_len;
589 if (lp == 0) { /* Mode set manually means lp doesn't get set */
590 lp = (int) strlen(symbol->primary);
591 }
592 if (lp < 7 || lp > 15) { /* 1 to 9 character postcode + 3 digit country code + 3 digit service class */
593 if (lp == 0) {
594 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 548, "Primary Message empty");
595 }
596 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 551, "Primary Message length %d wrong (7 to 15 only)",
597 lp);
598 }
599 postcode_len = lp - 6;
600
601 countrycode = to_int((const unsigned char *) (symbol->primary + postcode_len), 3);
602 service = to_int((const unsigned char *) (symbol->primary + postcode_len + 3), 3);
603
604 if (countrycode == -1 || service == -1) { /* Check that country code and service are numeric */
605 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 552,
606 "Non-numeric country code or service class in Primary Message");
607 }
608
609 memcpy(postcode, symbol->primary, postcode_len);
610 postcode[postcode_len] = '\0';
611
612 if (mode == 2) {
613 for (i = 0; i < postcode_len; i++) {
614 if (postcode[i] == ' ') {
615 postcode[i] = '\0';
616 postcode_len = i;
617 break;
618 }
619 if (!z_isdigit(postcode[i])) {
620 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 555, "Non-numeric postcode in Primary Message");
621 }
622 }
623 if (countrycode == 840 && postcode_len == 5) {
624 /* Annex B, section B.1, paragraph 4.a, "In the case of country code 840, if the "+4" is unknown,
625 then fill with zeroes" (adapted from OkaiBarcode, stricter interpretation, props Daniel Gredler) */
626 memcpy(postcode + 5, "0000", 5); /* Include NUL char */
627 postcode_len = 9;
628 }
629 mx_do_primary_2(codewords, postcode, postcode_len, countrycode, service);
630 } else {
631 /* Just truncate and space-pad */
632 postcode[6] = '\0';
633 for (i = postcode_len; i < 6; i++) {
634 postcode[i] = ' ';
635 }
636 /* Upper-case and check for Code Set A characters only */
637 to_upper(postcode, postcode_len);
638 for (i = 0; i < 6; i++) {
639 /* Don't allow control chars (CR FS GS RS for Code Set A) */
640 if (postcode[i] < ' ' || !(maxiCodeSet[postcode[i]] & MX_OP_SETA)) {
641 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 556,
642 "Invalid character in postcode in Primary Message");
643 }
644 }
645 mx_do_primary_3(codewords, postcode, countrycode, service);
646 }
647
648 if (symbol->option_2) { /* Check for option_2 = vv + 1, where vv is version of SCM prefix "[)>\R01\Gvv" */
649 if (symbol->option_2 < 0 || symbol->option_2 > 100) {
650 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 557,
651 "SCM prefix version '%d' out of range (1 to 100)", symbol->option_2);
652 }
653 if (symbol->eci == 25 || (symbol->eci >= 33 && symbol->eci <= 35)) { /* UTF-16/32 */
654 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 547,
655 "SCM prefix can not be used with ECI %d (ECI must be ASCII compatible)", symbol->eci);
656 }
657 scm_vv = symbol->option_2 - 1;
658 }
659
660 if (debug_print) {
661 printf("Postcode: %s, Country Code: %d, Service Class: %d\n", postcode, countrycode, service);
662 }
663 } else {
664 codewords[0] = mode;
665 }
666
667 if (debug_print) {
668 printf("Mode: %d\n", mode);
669 }
670
671 if (symbol->structapp.count) {
672 if (symbol->structapp.count < 2 || symbol->structapp.count > 8) {
673 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 558,
674 "Structured Append count '%d' out of range (2 to 8)", symbol->structapp.count);
675 }
676 if (symbol->structapp.index < 1 || symbol->structapp.index > symbol->structapp.count) {
677 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 559,
678 "Structured Append index '%1$d' out of range (1 to count %2$d)",
679 symbol->structapp.index, symbol->structapp.count);
680 }
681 if (symbol->structapp.id[0]) {
682 return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 549, "Structured Append ID not available for MaxiCode");
683 }
684 structapp_cw = (symbol->structapp.count - 1) | ((symbol->structapp.index - 1) << 3);
685 }
686
687 error_number = mx_text_process_segs(codewords, mode, segs, seg_count, structapp_cw, scm_vv, debug_print);
688 if (error_number == ZINT_ERROR_TOO_LONG) {
689 return errtxt(error_number, symbol, 553, "Input too long, requires too many codewords (maximum 144)");
690 }
691
692 /* All the data is sorted - now do error correction */
693 mx_do_primary_ecc(codewords); /* Always Enhanced ECC (EEC) 10 data + 10 error correction */
694
695 if (mode == 5) {
696 /* Enhanced ECC (EEC) 68 data + 56 error correction */
697 mx_do_secondary_ecc(codewords, 68, 28); /* ECC halved for even/odd */
698 } else {
699 /* Standard ECC (SEC) 84 data + 40 error correction */
700 mx_do_secondary_ecc(codewords, 84, 20); /* ECC halved for even/odd */
701 }
702
703 if (debug_print) {
704 fputs("Codewords:", stdout);
705 for (i = 0; i < 144; i++) printf(" %d", codewords[i]);
706 fputc('\n', stdout);
707 }
708 #ifdef ZINT_TEST
709 if (symbol->debug & ZINT_DEBUG_TEST) {
710 debug_test_codeword_dump(symbol, codewords, 144);
711 }
712 #endif
713
714 /* Copy data into symbol grid */
715 for (i = 0; i < 33; i++) {
716 for (j = 0; j < 30; j++) {
717 const int mod_seq = maxiGrid[(i * 30) + j] + 5;
718 const int block = mod_seq / 6;
719
720 if (block != 0) {
721 if ((codewords[block - 1] >> (5 - (mod_seq % 6))) & 1) {
722 set_module(symbol, i, j);
723 }
724 }
725 }
726 }
727
728 /* Add orientation markings */
729 set_module(symbol, 0, 28); /* Top right filler */
730 set_module(symbol, 0, 29);
731 set_module(symbol, 9, 10); /* Top left marker */
732 set_module(symbol, 9, 11);
733 set_module(symbol, 10, 11);
734 set_module(symbol, 15, 7); /* Left hand marker */
735 set_module(symbol, 16, 8);
736 set_module(symbol, 16, 20); /* Right hand marker */
737 set_module(symbol, 17, 20);
738 set_module(symbol, 22, 10); /* Bottom left marker */
739 set_module(symbol, 23, 10);
740 set_module(symbol, 22, 17); /* Bottom right marker */
741 set_module(symbol, 23, 17);
742
743 symbol->width = 30;
744 symbol->rows = 33;
745
746 /* Note MaxiCode fixed size so symbol height ignored but set anyway */
747 (void) set_height(symbol, 5.0f, 0.0f, 0.0f, 1 /*no_errtxt*/);
748
749 return error_number;
750 }
751
752 /* vim: set ts=4 sw=4 et : */