Mercurial > hgrepos > Python2 > PyMuPDF
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 : */ |
