comparison mupdf-source/thirdparty/zint/backend/mailmark.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 /* mailmark.c - Royal Mail 4-state and 2D Mailmark 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 /*
34 * Developed in accordance with "Royal Mail Mailmark barcode C encoding and deconding instructions"
35 * (https://www.royalmail.com/sites/default/files/
36 * Mailmark-4-state-barcode-C-encoding-and-decoding-instructions-Sept-2015.pdf)
37 * and "Royal Mail Mailmark barcode L encoding and decoding"
38 * (https://www.royalmail.com/sites/default/files/
39 * Mailmark-4-state-barcode-L-encoding-and-decoding-instructions-Sept-2015.pdf)
40 *
41 */
42
43 #include <stdio.h>
44 #include "common.h"
45 #include "large.h"
46 #include "reedsol.h"
47
48 #define RUBIDIUM_F (IS_NUM_F | IS_UPR_F | IS_SPC_F) /* RUBIDIUM "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ " */
49
50 /* Allowed character values from Table 3 */
51 #define SET_F "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
52 #define SET_L "ABDEFGHJLNPQRSTUWXYZ"
53 #define SET_N "0123456789"
54 #define SET_S " "
55
56 static const char mailmark_postcode_format[6][9] = {
57 {'F','N','F','N','L','L','N','L','S'}, {'F','F','N','N','L','L','N','L','S'},
58 {'F','F','N','N','N','L','L','N','L'}, {'F','F','N','F','N','L','L','N','L'},
59 {'F','N','N','L','L','N','L','S','S'}, {'F','N','N','N','L','L','N','L','S'}
60 };
61
62 /* Data/Check Symbols from Table 5 */
63 static const unsigned char mailmark_data_symbol_odd[32] = {
64 0x01, 0x02, 0x04, 0x07, 0x08, 0x0B, 0x0D, 0x0E, 0x10, 0x13, 0x15, 0x16,
65 0x19, 0x1A, 0x1C, 0x1F, 0x20, 0x23, 0x25, 0x26, 0x29, 0x2A, 0x2C, 0x2F,
66 0x31, 0x32, 0x34, 0x37, 0x38, 0x3B, 0x3D, 0x3E
67 };
68
69 static const unsigned char mailmark_data_symbol_even[30] = {
70 0x03, 0x05, 0x06, 0x09, 0x0A, 0x0C, 0x0F, 0x11, 0x12, 0x14, 0x17, 0x18,
71 0x1B, 0x1D, 0x1E, 0x21, 0x22, 0x24, 0x27, 0x28, 0x2B, 0x2D, 0x2E, 0x30,
72 0x33, 0x35, 0x36, 0x39, 0x3A, 0x3C
73 };
74
75 static const unsigned char mailmark_extender_group_c[22] = {
76 3, 5, 7, 11, 13, 14, 16, 17, 19, 0, 1, 2, 4, 6, 8, 9, 10, 12, 15, 18, 20, 21
77 };
78
79 static const unsigned char mailmark_extender_group_l[26] = {
80 2, 5, 7, 8, 13, 14, 15, 16, 21, 22, 23, 0, 1, 3, 4, 6, 9, 10, 11, 12, 17, 18, 19, 20, 24, 25
81 };
82
83 static int mailmark_verify_character(char input, char type) {
84 int val = 0;
85
86 switch (type) {
87 case 'F':
88 val = posn(SET_F, input);
89 break;
90 case 'L':
91 val = posn(SET_L, input);
92 break;
93 case 'N':
94 val = posn(SET_N, input);
95 break;
96 case 'S':
97 val = posn(SET_S, input);
98 break;
99 }
100
101 if (val == -1) {
102 return 0;
103 } else {
104 return 1;
105 }
106 }
107
108 static int mailmark_verify_postcode(const char postcode[10], int *p_postcode_type) {
109 int postcode_type;
110
111 /* Detect postcode type */
112 /* postcode_type is used to select which format of postcode
113 *
114 * 1 = FNFNLLNLS
115 * 2 = FFNNLLNLS
116 * 3 = FFNNNLLNL
117 * 4 = FFNFNLLNL
118 * 5 = FNNLLNLSS
119 * 6 = FNNNLLNLS
120 * 7 = International designation
121 */
122
123 if (strcmp(postcode, "XY11 ") == 0) {
124 postcode_type = 7;
125 } else {
126 if (postcode[7] == ' ') {
127 postcode_type = 5;
128 } else {
129 if (postcode[8] == ' ') {
130 /* Types 1, 2 and 6 */
131 if (z_isdigit(postcode[1])) {
132 if (z_isdigit(postcode[2])) {
133 postcode_type = 6;
134 } else {
135 postcode_type = 1;
136 }
137 } else {
138 postcode_type = 2;
139 }
140 } else {
141 /* Types 3 and 4 */
142 if (z_isdigit(postcode[3])) {
143 postcode_type = 3;
144 } else {
145 postcode_type = 4;
146 }
147 }
148 }
149 }
150
151 if (p_postcode_type) {
152 *p_postcode_type = postcode_type;
153 }
154
155 /* Verify postcode type */
156 if (postcode_type != 7) {
157 int i;
158 const char *const pattern = mailmark_postcode_format[postcode_type - 1];
159 for (i = 0; i < 9; i++) {
160 if (!(mailmark_verify_character(postcode[i], pattern[i]))) {
161 return 1;
162 }
163 }
164 }
165
166 return 0;
167 }
168
169 INTERNAL int daft_set_height(struct zint_symbol *symbol, const float min_height, const float max_height);
170
171 /* Royal Mail 4-state Mailmark */
172 INTERNAL int mailmark_4s(struct zint_symbol *symbol, unsigned char source[], int length) {
173
174 char local_source[28];
175 int format;
176 int version_id;
177 int mail_class;
178 int supply_chain_id;
179 unsigned int item_id;
180 char postcode[10];
181 int postcode_type;
182 large_uint destination_postcode;
183 large_uint b;
184 large_uint cdv;
185 unsigned char data[26];
186 int data_top, data_step;
187 unsigned char check[7];
188 unsigned int extender[27];
189 char bar[80];
190 char *d = bar;
191 int check_count;
192 int i, j, len;
193 rs_t rs;
194 int error_number = 0;
195
196 if (length > 26) {
197 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 580, "Input length %d too long (maximum 26)", length);
198 }
199
200 ustrcpy(local_source, source);
201
202 if (length < 22) {
203 if (length < 14) {
204 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 588, "Input length %d too short (minimum 14)", length);
205 }
206 for (i = length; i <= 22; i++) {
207 strcat(local_source, " ");
208 }
209 length = 22;
210 } else if ((length > 22) && (length < 26)) {
211 for (i = length; i <= 26; i++) {
212 strcat(local_source, " ");
213 }
214 length = 26;
215 }
216
217 to_upper((unsigned char *) local_source, length);
218
219 if (symbol->debug & ZINT_DEBUG_PRINT) {
220 printf("Producing 4-state Mailmark (%d): %s<end>\n", length, local_source);
221 }
222
223 if ((i = not_sane(RUBIDIUM_F, (unsigned char *) local_source, length))) {
224 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 581,
225 "Invalid character at position %d in input (alphanumerics and space only)", i);
226 }
227
228 /* Format is in the range 0-4 */
229 format = ctoi(local_source[0]);
230 if ((format < 0) || (format > 4)) {
231 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 582, "Format (1st character) out of range (0 to 4)");
232 }
233
234 /* Version ID is in the range 1-4 */
235 version_id = ctoi(local_source[1]) - 1;
236 if ((version_id < 0) || (version_id > 3)) {
237 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 583, "Version ID (2nd character) out of range (1 to 4)");
238 }
239
240 /* Class is in the range 0-9,A-E */
241 mail_class = ctoi(local_source[2]);
242 if ((mail_class < 0) || (mail_class > 14)) {
243 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 584, "Class (3rd character) out of range (0 to 9 and A to E)");
244 }
245
246 /* Supply Chain ID is 2 digits for barcode C and 6 digits for barcode L */
247 supply_chain_id = 0;
248 for (i = 3; i < (length - 17); i++) {
249 if (z_isdigit(local_source[i])) {
250 supply_chain_id *= 10;
251 supply_chain_id += ctoi(local_source[i]);
252 } else {
253 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 585,
254 "Invalid Supply Chain ID at character %d (digits only)", i);
255 }
256 }
257
258 /* Item ID is 8 digits */
259 item_id = 0;
260 for (i = length - 17; i < (length - 9); i++) {
261 if (z_isdigit(local_source[i])) {
262 item_id *= 10;
263 item_id += ctoi(local_source[i]);
264 } else {
265 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 586, "Invalid Item ID at character %d (digits only)", i);
266 }
267 }
268
269 /* Separate Destination Post Code plus DPS field */
270 for (i = 0; i < 9; i++) {
271 postcode[i] = local_source[(length - 9) + i];
272 }
273 postcode[9] = '\0';
274 if (mailmark_verify_postcode(postcode, &postcode_type) != 0) {
275 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 587, "Invalid postcode \"%s\"", postcode);
276 }
277
278 /* Convert postcode to internal user field */
279
280 large_load_u64(&destination_postcode, 0);
281
282 if (postcode_type != 7) {
283 const char *const pattern = mailmark_postcode_format[postcode_type - 1];
284
285 large_load_u64(&b, 0);
286
287 for (i = 0; i < 9; i++) {
288 switch (pattern[i]) {
289 case 'F':
290 large_mul_u64(&b, 26);
291 large_add_u64(&b, posn(SET_F, postcode[i]));
292 break;
293 case 'L':
294 large_mul_u64(&b, 20);
295 large_add_u64(&b, posn(SET_L, postcode[i]));
296 break;
297 case 'N':
298 large_mul_u64(&b, 10);
299 large_add_u64(&b, posn(SET_N, postcode[i]));
300 break;
301 /* case 'S' ignored as value is 0 */
302 }
303 }
304
305 large_load(&destination_postcode, &b);
306
307 /* destination_postcode = a + b */
308 large_load_u64(&b, 1);
309 if (postcode_type == 1) {
310 large_add(&destination_postcode, &b);
311 }
312 large_add_u64(&b, 5408000000);
313 if (postcode_type == 2) {
314 large_add(&destination_postcode, &b);
315 }
316 large_add_u64(&b, 5408000000);
317 if (postcode_type == 3) {
318 large_add(&destination_postcode, &b);
319 }
320 large_add_u64(&b, 54080000000);
321 if (postcode_type == 4) {
322 large_add(&destination_postcode, &b);
323 }
324 large_add_u64(&b, 140608000000);
325 if (postcode_type == 5) {
326 large_add(&destination_postcode, &b);
327 }
328 large_add_u64(&b, 208000000);
329 if (postcode_type == 6) {
330 large_add(&destination_postcode, &b);
331 }
332 }
333
334 /* Conversion from Internal User Fields to Consolidated Data Value */
335 /* Set CDV to 0 */
336 large_load_u64(&cdv, 0);
337
338 /* Add Destination Post Code plus DPS */
339 large_add(&cdv, &destination_postcode);
340
341 /* Multiply by 100,000,000 */
342 large_mul_u64(&cdv, 100000000);
343
344 /* Add Item ID */
345 large_add_u64(&cdv, item_id);
346
347 if (length == 22) {
348 /* Barcode C - Multiply by 100 */
349 large_mul_u64(&cdv, 100);
350 } else {
351 /* Barcode L - Multiply by 1,000,000 */
352 large_mul_u64(&cdv, 1000000);
353 }
354
355 /* Add Supply Chain ID */
356 large_add_u64(&cdv, supply_chain_id);
357
358 /* Multiply by 15 */
359 large_mul_u64(&cdv, 15);
360
361 /* Add Class */
362 large_add_u64(&cdv, mail_class);
363
364 /* Multiply by 5 */
365 large_mul_u64(&cdv, 5);
366
367 /* Add Format */
368 large_add_u64(&cdv, format);
369
370 /* Multiply by 4 */
371 large_mul_u64(&cdv, 4);
372
373 /* Add Version ID */
374 large_add_u64(&cdv, version_id);
375
376 if (symbol->debug & ZINT_DEBUG_PRINT) {
377 printf("DPC type %d\n", postcode_type);
378 fputs("CDV: ", stdout);
379 large_print(&cdv);
380 }
381
382 if (length == 22) {
383 data_top = 15;
384 data_step = 8;
385 check_count = 6;
386 } else {
387 data_top = 18;
388 data_step = 10;
389 check_count = 7;
390 }
391
392 /* Conversion from Consolidated Data Value to Data Numbers */
393
394 for (j = data_top; j >= (data_step + 1); j--) {
395 data[j] = (unsigned char) large_div_u64(&cdv, 32);
396 }
397
398 for (j = data_step; j >= 0; j--) {
399 data[j] = (unsigned char) large_div_u64(&cdv, 30);
400 }
401
402 /* Generation of Reed-Solomon Check Numbers */
403 rs_init_gf(&rs, 0x25);
404 rs_init_code(&rs, check_count, 1);
405 data_top++;
406 rs_encode(&rs, data_top, data, check);
407
408 /* Append check digits to data */
409 memcpy(data + data_top, check, check_count);
410
411 if (symbol->debug & ZINT_DEBUG_PRINT) {
412 fputs("Codewords:", stdout);
413 for (i = 0; i < data_top + check_count; i++) {
414 printf(" %d", (int) data[i]);
415 }
416 fputc('\n', stdout);
417 }
418
419 /* Conversion from Data Numbers and Check Numbers to Data Symbols and Check Symbols */
420 for (i = 0; i <= data_step; i++) {
421 data[i] = mailmark_data_symbol_even[data[i]];
422 }
423 for (i = data_step + 1; i < (data_top + check_count); i++) {
424 data[i] = mailmark_data_symbol_odd[data[i]];
425 }
426
427 /* Conversion from Data Symbols and Check Symbols to Extender Groups */
428 for (i = 0; i < length; i++) {
429 if (length == 22) {
430 extender[mailmark_extender_group_c[i]] = data[i];
431 } else {
432 extender[mailmark_extender_group_l[i]] = data[i];
433 }
434 }
435
436 /* Conversion from Extender Groups to Bar Identifiers */
437
438 for (i = 0; i < length; i++) {
439 for (j = 0; j < 3; j++) {
440 switch (extender[i] & 0x24) {
441 case 0x24:
442 *d++ = 'F';
443 break;
444 case 0x20:
445 if (i % 2) {
446 *d++ = 'D';
447 } else {
448 *d++ = 'A';
449 }
450 break;
451 case 0x04:
452 if (i % 2) {
453 *d++ = 'A';
454 } else {
455 *d++ = 'D';
456 }
457 break;
458 default:
459 *d++ = 'T';
460 break;
461 }
462 extender[i] = extender[i] << 1;
463 }
464 }
465
466 if (symbol->debug & ZINT_DEBUG_PRINT) {
467 printf("Bar pattern: %.*s\n", (int) (d - bar), bar);
468 }
469
470 /* Translate 4-state data pattern to symbol */
471 j = 0;
472 for (i = 0, len = d - bar; i < len; i++) {
473 if ((bar[i] == 'F') || (bar[i] == 'A')) {
474 set_module(symbol, 0, j);
475 }
476 set_module(symbol, 1, j);
477 if ((bar[i] == 'F') || (bar[i] == 'D')) {
478 set_module(symbol, 2, j);
479 }
480 j += 2;
481 }
482
483 if (symbol->output_options & COMPLIANT_HEIGHT) {
484 /* Royal Mail Mailmark Barcode Definition Document (15 Sept 2015) Section 3.5.1
485 (https://www.royalmail.com/sites/default/files/
486 Royal-Mail-Mailmark-barcode-definition-document-September-2015.pdf)
487 Using bar pitch as X (25.4mm / 42.3) ~ 0.6mm based on 21.2 bars + 21.1 spaces per 25.4mm (bar width
488 0.38mm - 0.63mm)
489 Using recommended 1.9mm and 1.3mm heights for Ascender/Descenders and Trackers resp. as defaults
490 Min height 4.22mm * 39 (max pitch) / 25.4mm ~ 6.47, max height 5.84mm * 47 (min pitch) / 25.4mm ~ 10.8
491 */
492 const float min_height = 6.47952747f; /* (4.22 * 39) / 25.4 */
493 const float max_height = 10.8062992f; /* (5.84 * 47) / 25.4 */
494 symbol->row_height[0] = 3.16417313f; /* (1.9 * 42.3) / 25.4 */
495 symbol->row_height[1] = 2.16496062f; /* (1.3 * 42.3) / 25.4 */
496 /* Note using max X for minimum and min X for maximum */
497 error_number = daft_set_height(symbol, min_height, max_height);
498 } else {
499 symbol->row_height[0] = 4.0f;
500 symbol->row_height[1] = 2.0f;
501 (void) daft_set_height(symbol, 0.0f, 0.0f);
502 }
503 symbol->rows = 3;
504 symbol->width = j - 1;
505
506 return error_number;
507 }
508
509 INTERNAL int datamatrix(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count);
510
511 /* Royal Mail 2D Mailmark (CMDM) (Data Matrix) */
512 /* https://www.royalmailtechnical.com/rmt_docs/User_Guides_2021/Mailmark_Barcode_definition_document_20210215.pdf */
513 INTERNAL int mailmark_2d(struct zint_symbol *symbol, unsigned char source[], int length) {
514
515 unsigned char local_source[90 + 1];
516 char postcode[10];
517 int i;
518 struct zint_seg segs[1];
519
520 if (length > 90) {
521 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 589, "Input length %d too long (maximum 90)", length);
522 }
523
524 if (length < 28) { /* After adding prefix (4), blank Return to Sender Post Code (7), Reserved (6): 28 + 17 = 45 */
525 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 860, "Input length %d too short (minimum 28)", length);
526 }
527
528 /* Add prefix if missing */
529 memcpy(local_source, source, 4);
530 to_upper(local_source, 3);
531 if (memcmp(local_source, "JGB ", 4) != 0) {
532 if (length > 86) {
533 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 861, "Input length %d too long (maximum 86)", length);
534 }
535 ustrcpy(local_source, "JGB ");
536 ustrcpy(local_source + 4, source);
537 length += 4;
538 } else {
539 ustrcpy(local_source, source);
540 }
541
542 if (length < 32) {
543 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 862, "Input length %d too short (minimum 32)", length);
544 }
545 if (length < 39) { /* Space-pad Return to Sender Post Code */
546 memset(local_source + length, ' ', 39 - length);
547 local_source[39] = '\0';
548 length = 39;
549 }
550 to_upper(local_source, 39);
551
552 if (length < 45) { /* Space-pad Reserved */
553 memset(local_source + length, ' ', 45 - length);
554 local_source[45] = '\0';
555 length = 45;
556 }
557
558 /* 8: 24 x 24, 10: 32 x 32, 30: 16 x 48 */
559 if (symbol->option_2) {
560 if (symbol->option_2 != 8 && symbol->option_2 != 10 && symbol->option_2 != 30) {
561 return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 863, "Invalid Version '%d' (8, 10 or 30 only)",
562 symbol->option_2);
563 }
564 if (symbol->option_2 == 8) {
565 if (length > 51) {
566 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 864,
567 "Input length %d too long for Version 8 (maximum 51)", length);
568 }
569 } else if (symbol->option_2 == 30) {
570 if (length > 70) {
571 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 865,
572 "Input length %d too long for Version 30 (maximum 70)", length);
573 }
574 }
575 } else {
576 if (length <= 51) {
577 symbol->option_2 = 8;
578 } else if (length <= 70 && (symbol->option_3 & 0x7F) != DM_SQUARE) {
579 symbol->option_2 = 30;
580 } else {
581 symbol->option_2 = 10;
582 }
583 }
584
585 if (symbol->debug & ZINT_DEBUG_PRINT) {
586 printf("Producing 2D Mailmark %d (%d): %s<end>\n", symbol->option_2, length, local_source);
587 }
588
589 if ((i = not_sane(RUBIDIUM_F, local_source, 45))) {
590 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 866,
591 "Invalid character at position %d in input (alphanumerics and space only in first 45)", i);
592 }
593
594 /* Information Type ID */
595 /* Not checking that matches values listed in Mailmark Definition Document as contradicted by Mailmark Mailing
596 Requirements Section 5.7 which says 'P' for poll card is valid, which isn't listed */
597 if (local_source[4] == ' ') {
598 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 867, "Invalid Information Type ID (cannot be space)");
599 }
600 /* Version ID */
601 if (local_source[5] != '1') {
602 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 868, "Invalid Version ID (\"1\" only)");
603 }
604 /* Class */
605 if (local_source[6] == ' ') {
606 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 869, "Invalid Class (cannot be space)");
607 }
608 /* Supply Chain ID */
609 if (cnt_digits(local_source, length, 7, 7) != 7) {
610 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 870, "Invalid Supply Chain ID (7 digits only)");
611 }
612 /* Item ID */
613 if (cnt_digits(local_source, length, 14, 8) != 8) {
614 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 871, "Invalid Item ID (8 digits only)");
615 }
616
617 /* Destination Post Code plus DPS field */
618 for (i = 0; i < 9; i++) {
619 postcode[i] = local_source[22 + i];
620 }
621 postcode[9] = '\0';
622 if (mailmark_verify_postcode(postcode, NULL) != 0) {
623 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 872, "Invalid Destination Post Code plus DPS");
624 }
625
626 /* Service Type */
627 if (local_source[31] < '0' || local_source[31] > '6') {
628 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 873, "Invalid Service Type (\"0\" to \"6\" only)");
629 }
630
631 /* Return to Sender Post Code */
632 if (memcmp(local_source + 32, " ", 7) != 0) { /* If not blank (allowed) */
633 for (i = 0; i < 7; i++) {
634 postcode[i] = local_source[32 + i];
635 }
636 /* Add dummy DPS */
637 for (i = 6; postcode[i] == ' '; i--); /* Skip any terminal spaces */
638 i++;
639 postcode[i++] = '1';
640 postcode[i++] = 'A';
641 while (i != 9) {
642 postcode[i++] = ' ';
643 }
644 postcode[9] = '\0';
645 if (mailmark_verify_postcode(postcode, NULL) != 0) {
646 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 874, "Invalid Return to Sender Post Code");
647 }
648 }
649
650 /* Reserved */
651 if (memcmp(local_source + 39, " ", 6) != 0) {
652 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 875, "Invalid Reserved field (must be spaces only)");
653 }
654
655 segs[0].eci = 0;
656 segs[0].source = local_source;
657 segs[0].length = length;
658
659 return datamatrix(symbol, segs, 1);
660 }
661
662 /* vim: set ts=4 sw=4 et : */