Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/zint/backend/dmatrix.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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/zint/backend/dmatrix.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,1960 @@ +/* dmatrix.c Handles Data Matrix ECC 200 symbols */ +/* + libzint - the open source barcode library + Copyright (C) 2009-2024 Robin Stuart <rstuart114@gmail.com> + + developed from and including some functions from: + IEC16022 bar code generation + Adrian Kennard, Andrews & Arnold Ltd + with help from Cliff Hones on the RS coding + + (c) 2004 Adrian Kennard, Andrews & Arnold Ltd + (c) 2006 Stefan Schmidt <stefan@datenfreihafen.org> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + */ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include <assert.h> +#include <limits.h> +#include <stdio.h> +#include "common.h" +#include "reedsol.h" +#include "dmatrix.h" + +/* Annex F placement algorithm low level */ +static void dm_placementbit(int *array, const int NR, const int NC, int r, int c, const int p, const char b) { + if (r < 0) { + r += NR; + c += 4 - ((NR + 4) % 8); + } + if (c < 0) { + c += NC; + r += 4 - ((NC + 4) % 8); + } + /* Necessary for DMRE (ISO/IEC 21471:2020 Annex E) */ + if (r >= NR) { + r -= NR; + } + /* Check index limits */ + assert(r < NR); + assert(c < NC); + /* Check double-assignment */ + assert(0 == array[r * NC + c]); + array[r * NC + c] = (p << 3) + b; +} + +static void dm_placementblock(int *array, const int NR, const int NC, const int r, + const int c, const int p) { + dm_placementbit(array, NR, NC, r - 2, c - 2, p, 7); + dm_placementbit(array, NR, NC, r - 2, c - 1, p, 6); + dm_placementbit(array, NR, NC, r - 1, c - 2, p, 5); + dm_placementbit(array, NR, NC, r - 1, c - 1, p, 4); + dm_placementbit(array, NR, NC, r - 1, c - 0, p, 3); + dm_placementbit(array, NR, NC, r - 0, c - 2, p, 2); + dm_placementbit(array, NR, NC, r - 0, c - 1, p, 1); + dm_placementbit(array, NR, NC, r - 0, c - 0, p, 0); +} + +static void dm_placementcornerA(int *array, const int NR, const int NC, const int p) { + dm_placementbit(array, NR, NC, NR - 1, 0, p, 7); + dm_placementbit(array, NR, NC, NR - 1, 1, p, 6); + dm_placementbit(array, NR, NC, NR - 1, 2, p, 5); + dm_placementbit(array, NR, NC, 0, NC - 2, p, 4); + dm_placementbit(array, NR, NC, 0, NC - 1, p, 3); + dm_placementbit(array, NR, NC, 1, NC - 1, p, 2); + dm_placementbit(array, NR, NC, 2, NC - 1, p, 1); + dm_placementbit(array, NR, NC, 3, NC - 1, p, 0); +} + +static void dm_placementcornerB(int *array, const int NR, const int NC, const int p) { + dm_placementbit(array, NR, NC, NR - 3, 0, p, 7); + dm_placementbit(array, NR, NC, NR - 2, 0, p, 6); + dm_placementbit(array, NR, NC, NR - 1, 0, p, 5); + dm_placementbit(array, NR, NC, 0, NC - 4, p, 4); + dm_placementbit(array, NR, NC, 0, NC - 3, p, 3); + dm_placementbit(array, NR, NC, 0, NC - 2, p, 2); + dm_placementbit(array, NR, NC, 0, NC - 1, p, 1); + dm_placementbit(array, NR, NC, 1, NC - 1, p, 0); +} + +static void dm_placementcornerC(int *array, const int NR, const int NC, const int p) { + dm_placementbit(array, NR, NC, NR - 3, 0, p, 7); + dm_placementbit(array, NR, NC, NR - 2, 0, p, 6); + dm_placementbit(array, NR, NC, NR - 1, 0, p, 5); + dm_placementbit(array, NR, NC, 0, NC - 2, p, 4); + dm_placementbit(array, NR, NC, 0, NC - 1, p, 3); + dm_placementbit(array, NR, NC, 1, NC - 1, p, 2); + dm_placementbit(array, NR, NC, 2, NC - 1, p, 1); + dm_placementbit(array, NR, NC, 3, NC - 1, p, 0); +} + +static void dm_placementcornerD(int *array, const int NR, const int NC, const int p) { + dm_placementbit(array, NR, NC, NR - 1, 0, p, 7); + dm_placementbit(array, NR, NC, NR - 1, NC - 1, p, 6); + dm_placementbit(array, NR, NC, 0, NC - 3, p, 5); + dm_placementbit(array, NR, NC, 0, NC - 2, p, 4); + dm_placementbit(array, NR, NC, 0, NC - 1, p, 3); + dm_placementbit(array, NR, NC, 1, NC - 3, p, 2); + dm_placementbit(array, NR, NC, 1, NC - 2, p, 1); + dm_placementbit(array, NR, NC, 1, NC - 1, p, 0); +} + +/* Annex F placement algorithm main function */ +static void dm_placement(int *array, const int NR, const int NC) { + int r, c, p; + /* start */ + p = 1; + r = 4; + c = 0; + do { + /* check corner */ + if (r == NR && !c) + dm_placementcornerA(array, NR, NC, p++); + if (r == NR - 2 && !c && NC % 4) + dm_placementcornerB(array, NR, NC, p++); + if (r == NR - 2 && !c && (NC % 8) == 4) + dm_placementcornerC(array, NR, NC, p++); + if (r == NR + 4 && c == 2 && !(NC % 8)) + dm_placementcornerD(array, NR, NC, p++); + /* up/right */ + do { + if (r < NR && c >= 0 && !array[r * NC + c]) + dm_placementblock(array, NR, NC, r, c, p++); + r -= 2; + c += 2; + } while (r >= 0 && c < NC); + r++; + c += 3; + /* down/left */ + do { + if (r >= 0 && c < NC && !array[r * NC + c]) + dm_placementblock(array, NR, NC, r, c, p++); + r += 2; + c -= 2; + } while (r < NR && c >= 0); + r += 3; + c++; + } while (r < NR || c < NC); + /* unfilled corner */ + if (!array[NR * NC - 1]) + array[NR * NC - 1] = array[NR * NC - NC - 2] = 1; +} + +/* calculate and append ecc code, and if necessary interleave */ +static void dm_ecc(unsigned char *binary, const int bytes, const int datablock, const int rsblock, const int skew) { + int blocks = (bytes + 2) / datablock, b; + int rsblocks = rsblock * blocks; + int n; + rs_t rs; + + rs_init_gf(&rs, 0x12d); + rs_init_code(&rs, rsblock, 1); + for (b = 0; b < blocks; b++) { + unsigned char buf[256], ecc[256]; + int p = 0; + for (n = b; n < bytes; n += blocks) + buf[p++] = binary[n]; + rs_encode(&rs, p, buf, ecc); + if (skew) { + /* Rotate ecc data to make 144x144 size symbols acceptable */ + /* See http://groups.google.com/group/postscriptbarcode/msg/5ae8fda7757477da + or https://github.com/nu-book/zxing-cpp/issues/259 */ + for (n = b, p = 0; n < rsblocks; n += blocks, p++) { + if (b < 8) { + binary[bytes + n + 2] = ecc[p]; + } else { + binary[bytes + n - 8] = ecc[p]; + } + } + } else { + for (n = b, p = 0; n < rsblocks; n += blocks, p++) { + binary[bytes + n] = ecc[p]; + } + } + } +} + +/* Is basic (non-shifted) C40? */ +static int dm_isc40(const unsigned char input) { + if (input <= '9') { + return input >= '0' || input == ' '; + } + return z_isupper(input); +} + +/* Is basic (non-shifted) TEXT? */ +static int dm_istext(const unsigned char input) { + if (input <= '9') { + return input >= '0' || input == ' '; + } + return z_islower(input); +} + +/* Is basic (non-shifted) C40/TEXT? */ +static int dm_isc40text(const int current_mode, const unsigned char input) { + return current_mode == DM_C40 ? dm_isc40(input) : dm_istext(input); +} + +/* Return true (1) if a character is valid in X12 set */ +static int dm_isX12(const unsigned char input) { + return dm_isc40(input) || input == 13 || input == '*' || input == '>'; +} + +/* Return true (1) if a character is valid in EDIFACT set */ +static int dm_isedifact(const unsigned char input) { + return input >= ' ' && input <= '^'; +} + +/* Does Annex J section (r)(6)(ii)(I) apply? */ +static int dm_substep_r_6_2_1(const unsigned char source[], const int length, const int sp) { + /* Annex J section (r)(6)(ii)(I) + "If one of the three X12 terminator/separator characters first + occurs in the yet to be processed data before a non-X12 character..." + */ + int i; + + for (i = sp; i < length && dm_isX12(source[i]); i++) { + if (source[i] == 13 || source[i] == '*' || source[i] == '>') { + return 1; + } + } + + return 0; +} + +/* Count number of TEXT characters around `sp` between `position` and `length` + - helper to avoid exiting from Base 256 too early if have series of TEXT characters */ +static int dm_text_sp_cnt(const unsigned char source[], const int position, const int length, const int sp) { + int i; + int cnt = 0; + + /* Count from `sp` forward */ + for (i = sp; i < length && dm_istext(source[i]); i++, cnt++); + /* Count backwards from `sp` */ + for (i = sp - 1; i >= position && dm_istext(source[i]); i--, cnt++); + + return cnt; +} + +/* Character counts are multiplied by this, so as to be whole integer divisible by 2, 3 and 4 */ +#define DM_MULT 12 + +#define DM_MULT_1_DIV_2 6 +#define DM_MULT_2_DIV_3 8 +#define DM_MULT_3_DIV_4 9 +#define DM_MULT_1 12 +#define DM_MULT_4_DIV_3 16 +#define DM_MULT_2 24 +#define DM_MULT_8_DIV_3 32 +#define DM_MULT_3 26 +#define DM_MULT_13_DIV_4 39 +#define DM_MULT_10_DIV_3 40 +#define DM_MULT_4 48 +#define DM_MULT_17_DIV_4 51 +#define DM_MULT_13_DIV_3 52 + +#define DM_MULT_MINUS_1 11 +#define DM_MULT_CEIL(n) ((((n) + DM_MULT_MINUS_1) / DM_MULT) * DM_MULT) + +/* 'look ahead test' from Annex J */ +static int dm_look_ahead_test(const unsigned char source[], const int length, const int position, + const int current_mode, const int mode_arg, const int gs1, const int debug_print) { + int ascii_count, c40_count, text_count, x12_count, edf_count, b256_count; + int ascii_rnded, c40_rnded, text_rnded, x12_rnded, edf_rnded, b256_rnded; + int cnt_1; + int sp; + + /* step (j) */ + if (current_mode == DM_ASCII || current_mode == DM_BASE256) { /* Adjusted to use for DM_BASE256 also */ + ascii_count = 0; + c40_count = DM_MULT_1; + text_count = DM_MULT_1; + x12_count = DM_MULT_1; + edf_count = DM_MULT_1; + b256_count = DM_MULT_2; /* Adjusted from DM_MULT_5_DIV_4 (1.25) */ + } else { + ascii_count = DM_MULT_1; + c40_count = DM_MULT_2; + text_count = DM_MULT_2; + x12_count = DM_MULT_2; + edf_count = DM_MULT_2; + b256_count = DM_MULT_3; /* Adjusted from DM_MULT_9_DIV_4 (2.25) */ + } + + switch (current_mode) { + case DM_C40: c40_count = 0; + break; + case DM_TEXT: text_count = 0; + break; + case DM_X12: x12_count = 0; + break; + case DM_EDIFACT: edf_count = 0; + break; + case DM_BASE256: + b256_count = mode_arg == 249 ? DM_MULT_1 : 0; /* Adjusted to use no. of bytes written */ + break; + } + + for (sp = position; sp < length; sp++) { + const unsigned char c = source[sp]; + const int is_extended = c & 0x80; + + /* ascii ... step (l) */ + if (z_isdigit(c)) { + ascii_count += DM_MULT_1_DIV_2; /* (l)(1) */ + } else { + if (is_extended) { + ascii_count = DM_MULT_CEIL(ascii_count) + DM_MULT_2; /* (l)(2) */ + } else { + ascii_count = DM_MULT_CEIL(ascii_count) + DM_MULT_1; /* (l)(3) */ + } + } + + /* c40 ... step (m) */ + if (dm_isc40(c)) { + c40_count += DM_MULT_2_DIV_3; /* (m)(1) */ + } else { + if (is_extended) { + c40_count += DM_MULT_8_DIV_3; /* (m)(2) */ + } else { + c40_count += DM_MULT_4_DIV_3; /* (m)(3) */ + } + } + + /* text ... step (n) */ + if (dm_istext(c)) { + text_count += DM_MULT_2_DIV_3; /* (n)(1) */ + } else { + if (is_extended) { + text_count += DM_MULT_8_DIV_3; /* (n)(2) */ + } else { + text_count += DM_MULT_4_DIV_3; /* (n)(3) */ + } + } + + /* x12 ... step (o) */ + if (dm_isX12(c)) { + x12_count += DM_MULT_2_DIV_3; /* (o)(1) */ + } else { + if (is_extended) { + x12_count += DM_MULT_13_DIV_3; /* (o)(2) */ + } else { + x12_count += DM_MULT_10_DIV_3; /* (o)(3) */ + } + } + + /* edifact ... step (p) */ + if (dm_isedifact(c)) { + edf_count += DM_MULT_3_DIV_4; /* (p)(1) */ + } else { + if (is_extended) { + edf_count += DM_MULT_17_DIV_4; /* (p)(2) */ + } else { + edf_count += DM_MULT_13_DIV_4; /* (p)(3) */ + } + } + + /* base 256 ... step (q) */ + if (gs1 == 1 && c == '\x1D') { + /* FNC1 separator */ + b256_count += DM_MULT_4; /* (q)(1) */ + } else { + b256_count += DM_MULT_1; /* (q)(2) */ + } + + if (sp >= position + 3) { + /* At least 4 data characters processed ... step (r) */ + /* NOTE: previous behaviour was at least 5 (same as BWIPP) */ + + if (debug_print) { + printf("\n(m:%d, p:%d, sp:%d, a:%d): ascii_count %d, b256_count %d, edf_count %d, text_count %d" + ", x12_count %d, c40_count %d ", + current_mode, position, sp, mode_arg, ascii_count, b256_count, edf_count, text_count, + x12_count, c40_count); + } + + cnt_1 = ascii_count + DM_MULT_1; + /* Adjusted from <= b256_count */ + if (cnt_1 < b256_count && cnt_1 <= edf_count && cnt_1 <= text_count && cnt_1 <= x12_count + && cnt_1 <= c40_count) { + if (debug_print) fputs("ASC->", stdout); + return DM_ASCII; /* step (r)(1) */ + } + cnt_1 = b256_count + DM_MULT_1; + if (cnt_1 <= ascii_count || (cnt_1 < edf_count && cnt_1 < text_count && cnt_1 < x12_count + && cnt_1 < c40_count)) { + if (debug_print) fputs("BAS->", stdout); + return DM_BASE256; /* step (r)(2) */ + } + cnt_1 = edf_count + DM_MULT_1; + if (cnt_1 < ascii_count && cnt_1 < b256_count && cnt_1 < text_count && cnt_1 < x12_count + && cnt_1 < c40_count) { + if (debug_print) fputs("EDI->", stdout); + return DM_EDIFACT; /* step (r)(3) */ + } + cnt_1 = text_count + DM_MULT_1; + if (cnt_1 < ascii_count && cnt_1 < b256_count && cnt_1 < edf_count && cnt_1 < x12_count + && cnt_1 < c40_count) { + /* Adjusted to avoid early exit from Base 256 if have less than break-even sequence of TEXT chars */ + if (current_mode == DM_BASE256 && position + 6 < length) { + if (dm_text_sp_cnt(source, position, length, sp) >= 12) { + if (debug_print) fputs("TEX->", stdout); + return DM_TEXT; /* step (r)(4) */ + } + } else { + if (debug_print) fputs("TEX->", stdout); + return DM_TEXT; /* step (r)(4) */ + } + } + cnt_1 = x12_count + DM_MULT_1; + if (cnt_1 < ascii_count && cnt_1 < b256_count && cnt_1 < edf_count && cnt_1 < text_count + && cnt_1 < c40_count) { + if (debug_print) fputs("X12->", stdout); + return DM_X12; /* step (r)(5) */ + } + cnt_1 = c40_count + DM_MULT_1; + if (cnt_1 < ascii_count && cnt_1 < b256_count && cnt_1 < edf_count && cnt_1 < text_count) { + if (c40_count < x12_count) { + if (debug_print) fputs("C40->", stdout); + return DM_C40; /* step (r)(6)(i) */ + } + if (c40_count == x12_count) { + if (dm_substep_r_6_2_1(source, length, sp) == 1) { + if (debug_print) fputs("X12->", stdout); + return DM_X12; /* step (r)(6)(ii)(I) */ + } + if (debug_print) fputs("C40->", stdout); + return DM_C40; /* step (r)(6)(ii)(II) */ + } + } + } + } + + /* At the end of data ... step (k) */ + /* step (k)(1) */ + ascii_rnded = DM_MULT_CEIL(ascii_count); + b256_rnded = DM_MULT_CEIL(b256_count); + edf_rnded = DM_MULT_CEIL(edf_count); + text_rnded = DM_MULT_CEIL(text_count); + x12_rnded = DM_MULT_CEIL(x12_count); + c40_rnded = DM_MULT_CEIL(c40_count); + if (debug_print) { + printf("\nEOD(m:%d, p:%d, a:%d): ascii_rnded %d, b256_rnded %d, edf_rnded %d, text_rnded %d" + ", x12_rnded %d (%d), c40_rnded %d (%d) ", + current_mode, position, mode_arg, ascii_rnded, b256_rnded, edf_rnded, text_rnded, + x12_rnded, x12_count, c40_rnded, c40_count); + } + + if (ascii_rnded <= b256_rnded && ascii_rnded <= edf_rnded && ascii_rnded <= text_rnded && ascii_rnded <= x12_rnded + && ascii_rnded <= c40_rnded) { + if (debug_print) fputs("ASC->", stdout); + return DM_ASCII; /* step (k)(2) */ + } + if (b256_rnded < ascii_rnded && b256_rnded < edf_rnded && b256_rnded < text_rnded && b256_rnded < x12_rnded + && b256_rnded < c40_rnded) { + if (debug_print) fputs("BAS->", stdout); + return DM_BASE256; /* step (k)(3) */ + } + /* Adjusted from < x12_rnded */ + if (edf_rnded < ascii_rnded && edf_rnded < b256_rnded && edf_rnded < text_rnded && edf_rnded <= x12_rnded + && edf_rnded < c40_rnded) { + if (debug_print) fputs("EDI->", stdout); + return DM_EDIFACT; /* step (k)(4) */ + } + if (text_rnded < ascii_rnded && text_rnded < b256_rnded && text_rnded < edf_rnded && text_rnded < x12_rnded + && text_rnded < c40_rnded) { + if (debug_print) fputs("TEX->", stdout); + return DM_TEXT; /* step (k)(5) */ + } + /* Adjusted from < edf_rnded */ + if (x12_rnded < ascii_rnded && x12_rnded < b256_rnded && x12_rnded <= edf_rnded && x12_rnded < text_rnded + && x12_rnded < c40_rnded) { + if (debug_print) fputs("X12->", stdout); + return DM_X12; /* step (k)(6) */ + } + if (debug_print) fputs("C40->", stdout); + return DM_C40; /* step (k)(7) */ +} + +/* Copy C40/TEXT/X12 triplets from buffer to target. Returns elements left in buffer (< 3) */ +static int dm_ctx_buffer_xfer(int process_buffer[8], int process_p, unsigned char target[], int *p_tp, + const int debug_print) { + int i, process_e; + int tp = *p_tp; + + process_e = (process_p / 3) * 3; + + for (i = 0; i < process_e; i += 3) { + int iv = (1600 * process_buffer[i]) + (40 * process_buffer[i + 1]) + (process_buffer[i + 2]) + 1; + target[tp++] = (unsigned char) (iv >> 8); + target[tp++] = (unsigned char) (iv & 0xFF); + if (debug_print) { + printf("[%d %d %d (%d %d)] ", process_buffer[i], process_buffer[i + 1], process_buffer[i + 2], + target[tp - 2], target[tp - 1]); + } + } + + process_p -= process_e; + + if (process_p) { + memmove(process_buffer, process_buffer + process_e, sizeof(int) * process_p); + } + + *p_tp = tp; + + return process_p; +} + +/* Copy EDIFACT quadruplets from buffer to target. Returns elements left in buffer (< 4) */ +static int dm_edi_buffer_xfer(int process_buffer[8], int process_p, unsigned char target[], int *p_tp, + const int empty, const int debug_print) { + int i, process_e; + int tp = *p_tp; + + process_e = (process_p / 4) * 4; + + for (i = 0; i < process_e; i += 4) { + target[tp++] = (unsigned char) (process_buffer[i] << 2 | (process_buffer[i + 1] & 0x30) >> 4); + target[tp++] = (unsigned char) ((process_buffer[i + 1] & 0x0f) << 4 | (process_buffer[i + 2] & 0x3c) >> 2); + target[tp++] = (unsigned char) ((process_buffer[i + 2] & 0x03) << 6 | process_buffer[i + 3]); + if (debug_print) { + printf("[%d %d %d %d (%d %d %d)] ", process_buffer[i], process_buffer[i + 1], process_buffer[i + 2], + process_buffer[i + 3], target[tp - 3], target[tp - 2], target[tp - 1]); + } + } + + process_p -= process_e; + + if (process_p) { + memmove(process_buffer, process_buffer + process_e, sizeof(int) * process_p); + if (empty) { + if (process_p == 3) { + target[tp++] = (unsigned char) (process_buffer[i] << 2 | (process_buffer[i + 1] & 0x30) >> 4); + target[tp++] = (unsigned char) ((process_buffer[i + 1] & 0x0f) << 4 + | (process_buffer[i + 2] & 0x3c) >> 2); + target[tp++] = (unsigned char) ((process_buffer[i + 2] & 0x03) << 6); + if (debug_print) { + printf("[%d %d %d (%d %d %d)] ", process_buffer[i], process_buffer[i + 1], process_buffer[i + 2], + target[tp - 3], target[tp - 2], target[tp - 1]); + } + } else if (process_p == 2) { + target[tp++] = (unsigned char) (process_buffer[i] << 2 | (process_buffer[i + 1] & 0x30) >> 4); + target[tp++] = (unsigned char) ((process_buffer[i + 1] & 0x0f) << 4); + if (debug_print) { + printf("[%d %d (%d %d)] ", process_buffer[i], process_buffer[i + 1], target[tp - 2], + target[tp - 1]); + } + } else { + target[tp++] = (unsigned char) (process_buffer[i] << 2); + if (debug_print) printf("[%d (%d)] ", process_buffer[i], target[tp - 1]); + } + process_p = 0; + } + } + + *p_tp = tp; + + return process_p; +} + +/* Get index of symbol size in codewords array `dm_matrixbytes`, as specified or + else smallest containing `minimum` codewords */ +static int dm_get_symbolsize(struct zint_symbol *symbol, const int minimum) { + int i; + + if ((symbol->option_2 >= 1) && (symbol->option_2 <= DMSIZESCOUNT)) { + return dm_intsymbol[symbol->option_2 - 1]; + } + if (minimum > 1304) { + return minimum <= 1558 ? DMSIZESCOUNT - 1 : 0; + } + for (i = minimum >= 62 ? 23 : 0; minimum > dm_matrixbytes[i]; i++); + + if ((symbol->option_3 & 0x7F) == DM_DMRE) { + return i; + } + if ((symbol->option_3 & 0x7F) == DM_SQUARE) { + /* Skip rectangular symbols in square only mode */ + for (; dm_matrixH[i] != dm_matrixW[i]; i++); + return i; + } + /* Skip DMRE symbols in no dmre mode */ + for (; dm_isDMRE[i]; i++); + return i; +} + +/* Number of codewords remaining in a particular version (may be negative) */ +static int dm_codewords_remaining(struct zint_symbol *symbol, const int tp, const int process_p) { + int symbolsize = dm_get_symbolsize(symbol, tp + process_p); /* Allow for the remaining data characters */ + + return dm_matrixbytes[symbolsize] - tp; +} + +/* Number of C40/TEXT elements needed to encode `input` */ +static int dm_c40text_cnt(const int current_mode, const int gs1, unsigned char input) { + int cnt; + + if (gs1 && input == '\x1D') { + return 2; + } + cnt = 1; + if (input & 0x80) { + cnt += 2; + input = input - 128; + } + if ((current_mode == DM_C40 && dm_c40_shift[input]) || (current_mode == DM_TEXT && dm_text_shift[input])) { + cnt += 1; + } + + return cnt; +} + +/* Update Base 256 field length */ +static int dm_update_b256_field_length(unsigned char target[], int tp, int b256_start) { + int b256_count = tp - (b256_start + 1); + if (b256_count <= 249) { + target[b256_start] = b256_count; + } else { + /* Insert extra codeword */ + memmove(target + b256_start + 2, target + b256_start + 1, b256_count); + target[b256_start] = (unsigned char) (249 + (b256_count / 250)); + target[b256_start + 1] = (unsigned char) (b256_count % 250); + tp++; + } + + return tp; +} + +/* Switch from ASCII or Base 256 to another mode */ +static int dm_switch_mode(const int next_mode, unsigned char target[], int tp, int *p_b256_start, + const int debug_print) { + switch (next_mode) { + case DM_ASCII: + if (debug_print) fputs("ASC ", stdout); + break; + case DM_C40: target[tp++] = 230; + if (debug_print) fputs("C40 ", stdout); + break; + case DM_TEXT: target[tp++] = 239; + if (debug_print) fputs("TEX ", stdout); + break; + case DM_X12: target[tp++] = 238; + if (debug_print) fputs("X12 ", stdout); + break; + case DM_EDIFACT: target[tp++] = 240; + if (debug_print) fputs("EDI ", stdout); + break; + case DM_BASE256: target[tp++] = 231; + *p_b256_start = tp; + target[tp++] = 0; /* Byte count holder (may be expanded to 2 codewords) */ + if (debug_print) fputs("BAS ", stdout); + break; + } + + return tp; +} + +/* Minimal encoding using Dijkstra-based algorithm by Alex Geller + Note due to the complicated end-of-data (EOD) conditions that Data Matrix has, this may not be fully minimal; + however no counter-examples are known at present */ + +#define DM_NUM_MODES 6 + +static const char dm_smodes[DM_NUM_MODES + 1][6] = { "?", "ASCII", "C40", "TEXT", "X12", "EDF", "B256" }; + +/* The size of this structure could be significantly reduced using techniques pointed out by Alex Geller, + but not done currently to avoid the processing overhead */ +struct dm_edge { + unsigned char mode; + unsigned char endMode; /* Mode returned by `dm_getEndMode()` */ + unsigned short from; /* Position in input data, 0-based */ + unsigned short len; + unsigned short size; /* Cumulative number of codewords */ + unsigned short bytes; /* DM_BASE256 byte count, kept to avoid runtime calc */ + unsigned short previous; /* Index into edges array */ +}; + +/* Note 1st row of edges not used so valid previous cannot point there, i.e. won't be zero */ +#define DM_PREVIOUS(edges, edge) \ + ((edge)->previous ? (edges) + (edge)->previous : NULL) + +/* Determine if next 1 to 4 chars are at EOD and can be encoded as 1 or 2 ASCII codewords */ +static int dm_last_ascii(const unsigned char source[], const int length, const int from) { + if (length - from > 4 || from >= length) { + return 0; + } + if (length - from == 1) { + if (source[from] & 0x80) { + return 0; + } + return 1; + } + if (length - from == 2) { + if ((source[from] & 0x80) || (source[from + 1] & 0x80)) { + return 0; + } + if (z_isdigit(source[from]) && z_isdigit(source[from + 1])) { + return 1; + } + return 2; + } + if (length - from == 3) { + if (z_isdigit(source[from]) && z_isdigit(source[from + 1]) && !(source[from + 2] & 0x80)) { + return 2; + } + if (z_isdigit(source[from + 1]) && z_isdigit(source[from + 2]) && !(source[from] & 0x80)) { + return 2; + } + return 0; + } + if (z_isdigit(source[from]) && z_isdigit(source[from + 1]) && z_isdigit(source[from + 2]) + && z_isdigit(source[from + 3])) { + return 2; + } + return 0; +} + +/* Treat EDIFACT edges specially, returning DM_ASCII mode if not full (i.e. encoding < 4 chars), or if + full and at EOD where 1 or 2 ASCII chars can be encoded */ +static int dm_getEndMode(struct zint_symbol *symbol, const unsigned char *source, const int length, const int mode, + const int from, const int len, const int size) { + if (mode == DM_EDIFACT) { + int last_ascii; + if (len < 4) { + return DM_ASCII; + } + last_ascii = dm_last_ascii(source, length, from + len); + if (last_ascii) { /* At EOD with remaining chars ASCII-encodable in 1 or 2 codewords */ + const int symbols_left = dm_codewords_remaining(symbol, size + last_ascii, 0); + /* If no codewords left and 1 or 2 ASCII-encodables or 1 codeword left and 1 ASCII-encodable */ + if (symbols_left <= 2 - last_ascii) { + return DM_ASCII; + } + } + } + return mode; +} + +#if 0 +#define DM_TRACE +#endif +#include "dmatrix_trace.h" + +/* Return number of C40/TEXT codewords needed to encode characters in full batches of 3 (or less if EOD). + The number of characters encoded is returned in `len` */ +static int dm_getNumberOfC40Words(const unsigned char *source, const int length, const int from, const int mode, + int *len) { + int thirdsCount = 0; + int i; + + for (i = from; i < length; i++) { + const unsigned char ci = source[i]; + int remainder; + + if (dm_isc40text(mode, ci)) { + thirdsCount++; /* Native */ + } else if (!(ci & 0x80)) { + thirdsCount += 2; /* Shift */ + } else if (dm_isc40text(mode, (unsigned char) (ci & 0x7F))) { + thirdsCount += 3; /* Shift, Upper shift */ + } else { + thirdsCount += 4; /* Shift, Upper shift, shift */ + } + + remainder = thirdsCount % 3; + if (remainder == 0 || (remainder == 2 && i + 1 == length)) { + *len = i - from + 1; + return ((thirdsCount + 2) / 3) * 2; + } + } + *len = 0; + return 0; +} + +/* Initialize a new edge. Returns endMode */ +static int dm_new_Edge(struct zint_symbol *symbol, const unsigned char *source, const int length, + struct dm_edge *edges, const int mode, const int from, const int len, struct dm_edge *previous, + struct dm_edge *edge, const int cwds) { + int previousMode; + int size; + int last_ascii, symbols_left; + + edge->mode = mode; + edge->endMode = mode; + edge->from = from; + edge->len = len; + edge->bytes = 0; + if (previous) { + assert(previous->mode && previous->len && previous->size && previous->endMode); + previousMode = previous->endMode; + edge->previous = previous - edges; + size = previous->size; + } else { + previousMode = DM_ASCII; + edge->previous = 0; + size = 0; + } + + switch (mode) { + case DM_ASCII: + assert(previousMode != DM_EDIFACT); + size++; + if (source[from] & 0x80) { + size++; + } + if (previousMode != DM_ASCII && previousMode != DM_BASE256) { + size++; /* Unlatch to ASCII */ + } + break; + + case DM_BASE256: + assert(previousMode != DM_EDIFACT); + size++; + if (previousMode != DM_BASE256) { + size += 2; /* Byte count + latch to BASE256 */ + if (previousMode != DM_ASCII) { + size++; /* Unlatch to ASCII */ + } + edge->bytes = 1; + } else { + assert(previous); + edge->bytes = 1 + previous->bytes; + if (edge->bytes == 250) { + size++; /* Extra byte count */ + } + } + break; + + case DM_C40: + case DM_TEXT: + assert(previousMode != DM_EDIFACT); + size += cwds; + if (previousMode != mode) { + size++; /* Latch to this mode */ + if (previousMode != DM_ASCII && previousMode != DM_BASE256) { + size++; /* Unlatch to ASCII */ + } + } + if (from + len + 2 >= length) { /* If less than batch of 3 away from EOD */ + last_ascii = dm_last_ascii(source, length, from + len); + symbols_left = dm_codewords_remaining(symbol, size + last_ascii, 0); + if (symbols_left > 0) { + size++; /* We need an extra unlatch at the end */ + } + } + break; + + case DM_X12: + assert(previousMode != DM_EDIFACT); + size += 2; + if (previousMode != DM_X12) { + size++; /* Latch to this mode */ + if (previousMode != DM_ASCII && previousMode != DM_BASE256) { + size++; /* Unlatch to ASCII */ + } + } + if (from + len + 2 >= length) { /* If less than batch of 3 away from EOD */ + last_ascii = dm_last_ascii(source, length, from + len); + if (last_ascii == 2) { /* Only 1 ASCII-encodable allowed at EOD for X12, unlike C40/TEXT */ + size++; /* We need an extra unlatch at the end */ + } else { + symbols_left = dm_codewords_remaining(symbol, size + last_ascii, 0); + if (symbols_left > 0) { + size++; /* We need an extra unlatch at the end */ + } + } + } + break; + + case DM_EDIFACT: + size += 3; + if (previousMode != DM_EDIFACT) { + size++; /* Latch to this mode */ + if (previousMode != DM_ASCII && previousMode != DM_BASE256) { + size++; /* Unlatch to ASCII */ + } + } + edge->endMode = dm_getEndMode(symbol, source, length, mode, from, len, size); + break; + } + edge->size = size; + + return edge->endMode; +} + +/* Add an edge for a mode at a vertex if no existing edge or if more optimal than existing edge */ +static void dm_addEdge(struct zint_symbol *symbol, const unsigned char *source, const int length, + struct dm_edge *edges, const int mode, const int from, const int len, struct dm_edge *previous, + const int cwds) { + struct dm_edge edge; + const int endMode = dm_new_Edge(symbol, source, length, edges, mode, from, len, previous, &edge, cwds); + const int vertexIndex = from + len; + const int v_ij = vertexIndex * DM_NUM_MODES + endMode - 1; + + if (edges[v_ij].mode == 0 || edges[v_ij].size > edge.size) { + DM_TRACE_AddEdge(source, length, edges, previous, vertexIndex, &edge); + edges[v_ij] = edge; + } else { + DM_TRACE_NotAddEdge(source, length, edges, previous, vertexIndex, v_ij, &edge); + } +} + +/* Add edges for the various modes at a vertex */ +static void dm_addEdges(struct zint_symbol *symbol, const unsigned char source[], const int length, + struct dm_edge *edges, const int from, struct dm_edge *previous, const int gs1) { + int i, pos; + + /* Not possible to unlatch a full EDF edge to something else */ + if (previous == NULL || previous->endMode != DM_EDIFACT) { + + static const char c40text_modes[] = { DM_C40, DM_TEXT }; + + if (z_isdigit(source[from]) && from + 1 < length && z_isdigit(source[from + 1])) { + dm_addEdge(symbol, source, length, edges, DM_ASCII, from, 2, previous, 0); + /* If ASCII vertex, don't bother adding other edges as this will be optimal; suggested by Alex Geller */ + if (previous && previous->mode == DM_ASCII) { + return; + } + } else { + dm_addEdge(symbol, source, length, edges, DM_ASCII, from, 1, previous, 0); + } + + for (i = 0; i < ARRAY_SIZE(c40text_modes); i++) { + int len; + int cwds = dm_getNumberOfC40Words(source, length, from, c40text_modes[i], &len); + if (cwds) { + dm_addEdge(symbol, source, length, edges, c40text_modes[i], from, len, previous, cwds); + } + } + + if (from + 2 < length && dm_isX12(source[from]) && dm_isX12(source[from + 1]) && dm_isX12(source[from + 2])) { + dm_addEdge(symbol, source, length, edges, DM_X12, from, 3, previous, 0); + } + + if (gs1 != 1 || source[from] != '\x1D') { + dm_addEdge(symbol, source, length, edges, DM_BASE256, from, 1, previous, 0); + } + } + + if (dm_isedifact(source[from])) { + /* We create 3 EDF edges, 2, 3 or 4 characters length. The 4-char normally doesn't have a latch to ASCII + unless it is 2 characters away from the end of the input. */ + for (i = 1, pos = from + i; i < 4 && pos < length && dm_isedifact(source[pos]); i++, pos++) { + dm_addEdge(symbol, source, length, edges, DM_EDIFACT, from, i + 1, previous, 0); + } + } +} + +/* Calculate optimized encoding modes */ +static int dm_define_mode(struct zint_symbol *symbol, char modes[], const unsigned char source[], const int length, + const int gs1, const int debug_print) { + + int i, j, v_i; + int minimalJ, minimalSize; + struct dm_edge *edge; + int current_mode; + int mode_end, mode_len; + + struct dm_edge *edges = (struct dm_edge *) calloc((length + 1) * DM_NUM_MODES, sizeof(struct dm_edge)); + if (!edges) { + return 0; + } + dm_addEdges(symbol, source, length, edges, 0, NULL, gs1); + + DM_TRACE_Edges("DEBUG Initial situation\n", source, length, edges, 0); + + for (i = 1; i < length; i++) { + v_i = i * DM_NUM_MODES; + for (j = 0; j < DM_NUM_MODES; j++) { + if (edges[v_i + j].mode) { + dm_addEdges(symbol, source, length, edges, i, edges + v_i + j, gs1); + } + } + DM_TRACE_Edges("DEBUG situation after adding edges to vertices at position %d\n", source, length, edges, i); + } + + DM_TRACE_Edges("DEBUG Final situation\n", source, length, edges, length); + + v_i = length * DM_NUM_MODES; + minimalJ = -1; + minimalSize = INT_MAX; + for (j = 0; j < DM_NUM_MODES; j++) { + edge = edges + v_i + j; + if (edge->mode) { + if (debug_print) printf("edges[%d][%d][0] size %d\n", length, j, edge->size); + if (edge->size < minimalSize) { + minimalSize = edge->size; + minimalJ = j; + if (debug_print) printf(" set minimalJ %d\n", minimalJ); + } + } else { + if (debug_print) printf("edges[%d][%d][0] NULL\n", length, j); + } + } + assert(minimalJ >= 0); + + edge = edges + v_i + minimalJ; + mode_len = 0; + mode_end = length; + while (edge) { + current_mode = edge->mode; + mode_len += edge->len; + edge = DM_PREVIOUS(edges, edge); + if (!edge || edge->mode != current_mode) { + for (i = mode_end - mode_len; i < mode_end; i++) { + modes[i] = current_mode; + } + mode_end = mode_end - mode_len; + mode_len = 0; + } + } + + if (debug_print) { + printf("modes (%d): ", length); + for (i = 0; i < length; i++) printf("%c", dm_smodes[(int) modes[i]][0]); + fputc('\n', stdout); + } + assert(mode_end == 0); + + free(edges); + + return 1; +} + +/* Do default minimal encodation */ +static int dm_minimalenc(struct zint_symbol *symbol, const unsigned char source[], const int length, int *p_sp, + unsigned char target[], int *p_tp, int process_buffer[8], int *p_process_p, int *p_b256_start, + int *p_current_mode, const int gs1, const int debug_print) { + int sp = *p_sp; + int tp = *p_tp; + int process_p = *p_process_p; + int current_mode = *p_current_mode; + int last_ascii, symbols_left; + int i; + char *modes = (char *) z_alloca(length); + + assert(length <= 10921); /* Can only handle (10921 + 1) * 6 = 65532 < 65536 (2*16) due to sizeof(previous) */ + + if (!dm_define_mode(symbol, modes, source, length, gs1, debug_print)) { + return errtxt(ZINT_ERROR_MEMORY, symbol, 728, "Insufficient memory for mode buffers"); + } + + while (sp < length) { + + if (modes[sp] != current_mode) { + switch (current_mode) { + case DM_C40: + case DM_TEXT: + case DM_X12: + process_p = 0; /* Throw away buffer if any */ + target[tp++] = 254; /* Unlatch */ + break; + case DM_EDIFACT: + last_ascii = dm_last_ascii(source, length, sp); + if (!last_ascii) { + process_buffer[process_p++] = 31; /* Unlatch */ + } else { + symbols_left = dm_codewords_remaining(symbol, tp + last_ascii, process_p); + if (debug_print) { + printf("process_p %d, last_ascii %d, symbols_left %d\n", + process_p, last_ascii, symbols_left); + } + if (symbols_left > 2 - last_ascii) { + process_buffer[process_p++] = 31; /* Unlatch */ + } + } + process_p = dm_edi_buffer_xfer(process_buffer, process_p, target, &tp, 1 /*empty*/, debug_print); + break; + case DM_BASE256: + tp = dm_update_b256_field_length(target, tp, *p_b256_start); + /* B.2.1 255-state randomising algorithm */ + for (i = *p_b256_start; i < tp; i++) { + const int prn = ((149 * (i + 1)) % 255) + 1; + target[i] = (unsigned char) ((target[i] + prn) & 0xFF); + } + break; + } + tp = dm_switch_mode(modes[sp], target, tp, p_b256_start, debug_print); + } + + current_mode = modes[sp]; + assert(current_mode); + + if (current_mode == DM_ASCII) { + + if (is_twodigits(source, length, sp)) { + target[tp++] = (unsigned char) ((10 * ctoi(source[sp])) + ctoi(source[sp + 1]) + 130); + if (debug_print) printf("N%02d ", target[tp - 1] - 130); + sp += 2; + } else { + if (source[sp] & 0x80) { + target[tp++] = 235; /* FNC4 */ + target[tp++] = (source[sp] - 128) + 1; + if (debug_print) printf("FN4 A%02X ", target[tp - 1] - 1); + } else { + if (gs1 && source[sp] == '\x1D') { + if (gs1 == 2) { + target[tp++] = 29 + 1; /* GS */ + if (debug_print) fputs("GS ", stdout); + } else { + target[tp++] = 232; /* FNC1 */ + if (debug_print) fputs("FN1 ", stdout); + } + } else { + target[tp++] = source[sp] + 1; + if (debug_print) printf("A%02X ", target[tp - 1] - 1); + } + } + sp++; + } + + } else if (current_mode == DM_C40 || current_mode == DM_TEXT) { + + int shift_set, value; + const char *ct_shift, *ct_value; + + if (current_mode == DM_C40) { + ct_shift = dm_c40_shift; + ct_value = dm_c40_value; + } else { + ct_shift = dm_text_shift; + ct_value = dm_text_value; + } + + if (source[sp] & 0x80) { + process_buffer[process_p++] = 1; + process_buffer[process_p++] = 30; /* Upper Shift */ + shift_set = ct_shift[source[sp] - 128]; + value = ct_value[source[sp] - 128]; + } else { + if (gs1 && source[sp] == '\x1D') { + if (gs1 == 2) { + shift_set = ct_shift[29]; + value = ct_value[29]; /* GS */ + } else { + shift_set = 2; + value = 27; /* FNC1 */ + } + } else { + shift_set = ct_shift[source[sp]]; + value = ct_value[source[sp]]; + } + } + + if (shift_set != 0) { + process_buffer[process_p++] = shift_set - 1; + } + process_buffer[process_p++] = value; + + if (process_p >= 3) { + process_p = dm_ctx_buffer_xfer(process_buffer, process_p, target, &tp, debug_print); + } + sp++; + + } else if (current_mode == DM_X12) { + + static const char x12_nonalphanum_chars[] = "\015*> "; + int value = 0; + + if (z_isdigit(source[sp])) { + value = (source[sp] - '0') + 4; + } else if (z_isupper(source[sp])) { + value = (source[sp] - 'A') + 14; + } else { + value = posn(x12_nonalphanum_chars, source[sp]); + } + + process_buffer[process_p++] = value; + + if (process_p >= 3) { + process_p = dm_ctx_buffer_xfer(process_buffer, process_p, target, &tp, debug_print); + } + sp++; + + } else if (current_mode == DM_EDIFACT) { + + int value = source[sp]; + + if (value >= 64) { /* '@' */ + value -= 64; + } + + process_buffer[process_p++] = value; + sp++; + + if (process_p >= 4) { + process_p = dm_edi_buffer_xfer(process_buffer, process_p, target, &tp, 0 /*empty*/, debug_print); + } + + } else if (current_mode == DM_BASE256) { + + target[tp++] = source[sp++]; + if (debug_print) printf("B%02X ", target[tp - 1]); + } + + if (tp > 1558) { + return errtxt(ZINT_ERROR_TOO_LONG, symbol, 729, + "Input too long, requires too many codewords (maximum 1558)"); + } + + } /* while */ + + *p_sp = sp; + *p_tp = tp; + *p_process_p = process_p; + *p_current_mode = current_mode; + + return 0; +} + +/* Encode using algorithm based on ISO/IEC 21471:2020 Annex J (was ISO/IEC 21471:2006 Annex P) */ +static int dm_isoenc(struct zint_symbol *symbol, const unsigned char source[], const int length, int *p_sp, + unsigned char target[], int *p_tp, int process_buffer[8], int *p_process_p, int *p_b256_start, + int *p_current_mode, const int gs1, const int debug_print) { + const int mailmark = symbol->symbology == BARCODE_MAILMARK_2D; + int sp = *p_sp; + int tp = *p_tp; + int process_p = *p_process_p; + int current_mode = *p_current_mode; + int not_first = 0; + int i; + + /* step (a) */ + int next_mode = DM_ASCII; + + if (mailmark) { /* First 45 characters C40 */ + assert(length >= 45); + next_mode = DM_C40; + tp = dm_switch_mode(next_mode, target, tp, p_b256_start, debug_print); + while (sp < 45) { + assert(!(sp & 0x80)); + process_buffer[process_p++] = dm_c40_value[source[sp]]; + + if (process_p >= 3) { + process_p = dm_ctx_buffer_xfer(process_buffer, process_p, target, &tp, debug_print); + } + sp++; + } + current_mode = next_mode; + not_first = 1; + } + + while (sp < length) { + + current_mode = next_mode; + + /* step (b) - ASCII encodation */ + if (current_mode == DM_ASCII) { + next_mode = DM_ASCII; + + if (is_twodigits(source, length, sp)) { + target[tp++] = (unsigned char) ((10 * ctoi(source[sp])) + ctoi(source[sp + 1]) + 130); + if (debug_print) printf("N%02d ", target[tp - 1] - 130); + sp += 2; + } else { + next_mode = dm_look_ahead_test(source, length, sp, current_mode, 0, gs1, debug_print); + + if (next_mode != DM_ASCII) { + tp = dm_switch_mode(next_mode, target, tp, p_b256_start, debug_print); + not_first = 0; + } else { + if (source[sp] & 0x80) { + target[tp++] = 235; /* FNC4 */ + target[tp++] = (source[sp] - 128) + 1; + if (debug_print) printf("FN4 A%02X ", target[tp - 1] - 1); + } else { + if (gs1 && source[sp] == '\x1D') { + if (gs1 == 2) { + target[tp++] = 29 + 1; /* GS */ + if (debug_print) fputs("GS ", stdout); + } else { + target[tp++] = 232; /* FNC1 */ + if (debug_print) fputs("FN1 ", stdout); + } + } else { + target[tp++] = source[sp] + 1; + if (debug_print) printf("A%02X ", target[tp - 1] - 1); + } + } + sp++; + } + } + + /* step (c)/(d) C40/TEXT encodation */ + } else if (current_mode == DM_C40 || current_mode == DM_TEXT) { + + next_mode = current_mode; + if (process_p == 0 && not_first) { + next_mode = dm_look_ahead_test(source, length, sp, current_mode, process_p, gs1, debug_print); + } + + if (next_mode != current_mode) { + target[tp++] = 254; /* Unlatch */ + next_mode = DM_ASCII; + if (debug_print) fputs("ASC ", stdout); + } else { + int shift_set, value; + const char *ct_shift, *ct_value; + + if (current_mode == DM_C40) { + ct_shift = dm_c40_shift; + ct_value = dm_c40_value; + } else { + ct_shift = dm_text_shift; + ct_value = dm_text_value; + } + + if (source[sp] & 0x80) { + process_buffer[process_p++] = 1; + process_buffer[process_p++] = 30; /* Upper Shift */ + shift_set = ct_shift[source[sp] - 128]; + value = ct_value[source[sp] - 128]; + } else { + if (gs1 && source[sp] == '\x1D') { + if (gs1 == 2) { + shift_set = ct_shift[29]; + value = ct_value[29]; /* GS */ + } else { + shift_set = 2; + value = 27; /* FNC1 */ + } + } else { + shift_set = ct_shift[source[sp]]; + value = ct_value[source[sp]]; + } + } + + if (shift_set != 0) { + process_buffer[process_p++] = shift_set - 1; + } + process_buffer[process_p++] = value; + + if (process_p >= 3) { + process_p = dm_ctx_buffer_xfer(process_buffer, process_p, target, &tp, debug_print); + } + sp++; + not_first = 1; + } + + /* step (e) X12 encodation */ + } else if (current_mode == DM_X12) { + + if (!dm_isX12(source[sp])) { + next_mode = DM_ASCII; + } else { + next_mode = DM_X12; + if (process_p == 0 && not_first) { + next_mode = dm_look_ahead_test(source, length, sp, current_mode, process_p, gs1, debug_print); + } + } + + if (next_mode != DM_X12) { + sp -= process_p; /* About to throw away buffer, need to re-process input, cf Okapi commit [fb7981e] */ + process_p = 0; /* Throw away buffer if any */ + target[tp++] = 254; /* Unlatch */ + next_mode = DM_ASCII; + if (debug_print) fputs("ASC ", stdout); + } else { + static const char x12_nonalphanum_chars[] = "\015*> "; + int value = 0; + + if (z_isdigit(source[sp])) { + value = (source[sp] - '0') + 4; + } else if (z_isupper(source[sp])) { + value = (source[sp] - 'A') + 14; + } else { + value = posn(x12_nonalphanum_chars, source[sp]); + } + + process_buffer[process_p++] = value; + + if (process_p >= 3) { + process_p = dm_ctx_buffer_xfer(process_buffer, process_p, target, &tp, debug_print); + } + sp++; + not_first = 1; + } + + /* step (f) EDIFACT encodation */ + } else if (current_mode == DM_EDIFACT) { + + if (!dm_isedifact(source[sp])) { + next_mode = DM_ASCII; + } else { + next_mode = DM_EDIFACT; + if (process_p == 3) { + /* Note different than spec Step (f)(2), which suggests checking when 0, but this seems to + work better in many cases as the switch to ASCII is "free" */ + next_mode = dm_look_ahead_test(source, length, sp, current_mode, process_p, gs1, debug_print); + } + } + + if (next_mode != DM_EDIFACT) { + process_buffer[process_p++] = 31; + process_p = dm_edi_buffer_xfer(process_buffer, process_p, target, &tp, 1 /*empty*/, debug_print); + next_mode = DM_ASCII; + if (debug_print) fputs("ASC ", stdout); + } else { + int value = source[sp]; + + if (value >= 64) { /* '@' */ + value -= 64; + } + + process_buffer[process_p++] = value; + sp++; + not_first = 1; + + if (process_p >= 4) { + process_p = dm_edi_buffer_xfer(process_buffer, process_p, target, &tp, 0 /*empty*/, + debug_print); + } + } + + /* step (g) Base 256 encodation */ + } else if (current_mode == DM_BASE256) { + + if (gs1 == 1 && source[sp] == '\x1D') { + next_mode = DM_ASCII; + } else { + next_mode = DM_BASE256; + if (not_first) { + next_mode = dm_look_ahead_test(source, length, sp, current_mode, tp - (*p_b256_start + 1), gs1, + debug_print); + } + } + + if (next_mode != DM_BASE256) { + tp = dm_update_b256_field_length(target, tp, *p_b256_start); + /* B.2.1 255-state randomising algorithm */ + for (i = *p_b256_start; i < tp; i++) { + const int prn = ((149 * (i + 1)) % 255) + 1; + target[i] = (unsigned char) ((target[i] + prn) & 0xFF); + } + /* We switch directly here to avoid flipping back to Base 256 due to `dm_text_sp_cnt()` */ + tp = dm_switch_mode(next_mode, target, tp, p_b256_start, debug_print); + not_first = 0; + } else { + if (gs1 == 2 && source[sp] == '\x1D') { + target[tp++] = 29; /* GS */ + } else { + target[tp++] = source[sp]; + } + sp++; + not_first = 1; + if (debug_print) printf("B%02X ", target[tp - 1]); + } + } + + if (tp > 1558) { + return errtxt(ZINT_ERROR_TOO_LONG, symbol, 520, + "Input too long, requires too many codewords (maximum 1558)"); + } + + } /* while */ + + *p_sp = sp; + *p_tp = tp; + *p_process_p = process_p; + *p_current_mode = current_mode; + + return 0; +} + +/* Encodes data using ASCII, C40, Text, X12, EDIFACT or Base 256 modes as appropriate + Supports encoding FNC1 in supporting systems */ +static int dm_encode(struct zint_symbol *symbol, const unsigned char source[], const int length, const int eci, + const int gs1, unsigned char target[], int *p_tp) { + int sp = 0; + int tp = *p_tp; + int current_mode = DM_ASCII; + int i; + int process_buffer[8]; /* holds remaining data to finalised */ + int process_p = 0; /* number of characters left to finalise */ + int b256_start = 0; + int symbols_left; + int error_number; + const int debug_print = symbol->debug & ZINT_DEBUG_PRINT; + + if (eci > 0) { + /* Encode ECI numbers according to Table 6 */ + target[tp++] = 241; /* ECI Character */ + if (eci <= 126) { + target[tp++] = (unsigned char) (eci + 1); + } else if (eci <= 16382) { + target[tp++] = (unsigned char) ((eci - 127) / 254 + 128); + target[tp++] = (unsigned char) ((eci - 127) % 254 + 1); + } else { + target[tp++] = (unsigned char) ((eci - 16383) / 64516 + 192); + target[tp++] = (unsigned char) (((eci - 16383) / 254) % 254 + 1); + target[tp++] = (unsigned char) ((eci - 16383) % 254 + 1); + } + if (debug_print) printf("ECI %d ", eci + 1); + } + + /* If FAST_MODE or MAILMARK_2D, do Annex J-based encodation */ + if ((symbol->input_mode & FAST_MODE) || symbol->symbology == BARCODE_MAILMARK_2D) { + error_number = dm_isoenc(symbol, source, length, &sp, target, &tp, process_buffer, &process_p, + &b256_start, ¤t_mode, gs1, debug_print); + } else { /* Do default minimal encodation */ + error_number = dm_minimalenc(symbol, source, length, &sp, target, &tp, process_buffer, &process_p, + &b256_start, ¤t_mode, gs1, debug_print); + } + if (error_number != 0) { + return error_number; + } + + symbols_left = dm_codewords_remaining(symbol, tp, process_p); + + if (debug_print) printf("\nsymbols_left %d, tp %d, process_p %d ", symbols_left, tp, process_p); + + if (current_mode == DM_C40 || current_mode == DM_TEXT) { + /* NOTE: changed to follow spec exactly here, only using Shift 1 padded triplets when 2 symbol chars remain. + This matches the behaviour of BWIPP but not tec-it, nor figures 4.15.1-1 and 4.15-1-2 in GS1 General + Specifications 21.0.1. + */ + if (debug_print) printf("%s ", current_mode == DM_C40 ? "C40" : "TEX"); + if (process_p == 0) { + if (symbols_left > 0) { + target[tp++] = 254; /* Unlatch */ + if (debug_print) fputs("ASC ", stdout); + } + } else { + if (process_p == 2 && symbols_left == 2) { + /* 5.2.5.2 (b) */ + process_buffer[process_p++] = 0; /* Shift 1 */ + (void) dm_ctx_buffer_xfer(process_buffer, process_p, target, &tp, debug_print); + + } else if (process_p == 1 && symbols_left <= 2 && dm_isc40text(current_mode, source[length - 1])) { + /* 5.2.5.2 (c)/(d) */ + if (symbols_left > 1) { + /* 5.2.5.2 (c) */ + target[tp++] = 254; /* Unlatch and encode remaining data in ascii. */ + if (debug_print) fputs("ASC ", stdout); + } + target[tp++] = source[length - 1] + 1; + if (debug_print) printf("A%02X ", target[tp - 1] - 1); + + } else { + int cnt, total_cnt = 0; + /* Backtrack to last complete triplet (same technique as BWIPP) */ + while (sp > 0 && process_p % 3) { + sp--; + cnt = dm_c40text_cnt(current_mode, gs1, source[sp]); + total_cnt += cnt; + process_p -= cnt; + } + if (debug_print) printf("Mode %d, backtracked %d\n", current_mode, (total_cnt / 3) * 2); + tp -= (total_cnt / 3) * 2; + + target[tp++] = 254; /* Unlatch */ + if (debug_print) fputs("ASC ", stdout); + for (; sp < length; sp++) { + if (is_twodigits(source, length, sp)) { + target[tp++] = (unsigned char) ((10 * ctoi(source[sp])) + ctoi(source[sp + 1]) + 130); + if (debug_print) printf("N%02d ", target[tp - 1] - 130); + sp++; + } else if (source[sp] & 0x80) { + target[tp++] = 235; /* FNC4 */ + target[tp++] = (source[sp] - 128) + 1; + if (debug_print) printf("FN4 A%02X ", target[tp - 1] - 1); + } else if (gs1 && source[sp] == '\x1D') { + if (gs1 == 2) { + target[tp++] = 29 + 1; /* GS */ + if (debug_print) fputs("GS ", stdout); + } else { + target[tp++] = 232; /* FNC1 */ + if (debug_print) fputs("FN1 ", stdout); + } + } else { + target[tp++] = source[sp] + 1; + if (debug_print) printf("A%02X ", target[tp - 1] - 1); + } + } + } + } + + } else if (current_mode == DM_X12) { + if (debug_print) fputs("X12 ", stdout); + if ((symbols_left == 1) && (process_p == 1)) { + /* Unlatch not required! */ + target[tp++] = source[length - 1] + 1; + if (debug_print) printf("A%02X ", target[tp - 1] - 1); + } else { + if (symbols_left > 0) { + target[tp++] = (254); /* Unlatch. */ + if (debug_print) fputs("ASC ", stdout); + } + + if (process_p == 1) { + target[tp++] = source[length - 1] + 1; + if (debug_print) printf("A%02X ", target[tp - 1] - 1); + } else if (process_p == 2) { + target[tp++] = source[length - 2] + 1; + target[tp++] = source[length - 1] + 1; + if (debug_print) printf("A%02X A%02X ", target[tp - 2] - 1, target[tp - 1] - 1); + } + } + + } else if (current_mode == DM_EDIFACT) { + if (debug_print) fputs("EDI ", stdout); + if (symbols_left <= 2 && process_p <= symbols_left) { /* Unlatch not required! */ + if (process_p == 1) { + target[tp++] = source[length - 1] + 1; + if (debug_print) printf("A%02X ", target[tp - 1] - 1); + } else if (process_p == 2) { + target[tp++] = source[length - 2] + 1; + target[tp++] = source[length - 1] + 1; + if (debug_print) printf("A%02X A%02X ", target[tp - 2] - 1, target[tp - 1] - 1); + } + } else { + /* Append edifact unlatch value (31) and empty buffer */ + if (process_p <= 3) { + process_buffer[process_p++] = 31; + } + (void) dm_edi_buffer_xfer(process_buffer, process_p, target, &tp, 1 /*empty*/, debug_print); + } + + } else if (current_mode == DM_BASE256) { + if (symbols_left > 0) { + tp = dm_update_b256_field_length(target, tp, b256_start); + } + /* B.2.1 255-state randomising algorithm */ + for (i = b256_start; i < tp; i++) { + int prn = ((149 * (i + 1)) % 255) + 1; + target[i] = (unsigned char) ((target[i] + prn) & 0xFF); + } + } + + if (debug_print) { + printf("\nData (%d):", tp); + for (i = 0; i < tp; i++) + printf(" %d", target[i]); + + fputc('\n', stdout); + } + + *p_tp = tp; + + return 0; +} + +#ifdef ZINT_TEST /* Wrapper for direct testing */ +INTERNAL int dm_encode_test(struct zint_symbol *symbol, const unsigned char source[], const int length, const int eci, + const int gs1, unsigned char target[], int *p_tp) { + return dm_encode(symbol, source, length, eci, gs1, target, p_tp); +} +#endif + +/* Call `dm_encode()` for each segment, dealing with Structured Append, GS1, READER_INIT and macro headers + beforehand */ +static int dm_encode_segs(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count, + unsigned char target[], int *p_binlen) { + int error_number; + int i; + int tp = 0; + int gs1; + int in_macro = 0; + const struct zint_seg *last_seg = &segs[seg_count - 1]; + const int debug_print = symbol->debug & ZINT_DEBUG_PRINT; + + if ((i = segs_length(segs, seg_count)) > 3116) { /* Max is 3166 digits */ + return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 719, "Input length %d too long (maximum 3116)", i); + } + + if (symbol->structapp.count) { + int id1, id2; + + if (symbol->structapp.count < 2 || symbol->structapp.count > 16) { + return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 720, + "Structured Append count '%d' out of range (2 to 16)", symbol->structapp.count); + } + if (symbol->structapp.index < 1 || symbol->structapp.index > symbol->structapp.count) { + return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 721, + "Structured Append index '%1$d' out of range (1 to count %2$d)", + symbol->structapp.index, symbol->structapp.count); + } + if (symbol->structapp.id[0]) { + int id, id_len, id1_err, id2_err; + + for (id_len = 1; id_len < 7 && symbol->structapp.id[id_len]; id_len++); + + if (id_len > 6) { /* ID1 * 1000 + ID2 */ + return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 722, + "Structured Append ID length %d too long (6 digit maximum)", id_len); + } + + id = to_int((const unsigned char *) symbol->structapp.id, id_len); + if (id == -1) { + return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 723, "Invalid Structured Append ID (digits only)"); + } + id1 = id / 1000; + id2 = id % 1000; + id1_err = id1 < 1 || id1 > 254; + id2_err = id2 < 1 || id2 > 254; + if (id1_err || id2_err) { + if (id1_err && id2_err) { + return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 724, + "Structured Append ID1 '%1$03d' and ID2 '%2$03d' out of range (001 to 254)" + " (ID \"%3$03d%4$03d\")", + id1, id2, id1, id2); + } + if (id1_err) { + return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 725, + "Structured Append ID1 '%1$03d' out of range (001 to 254) (ID \"%2$03d%3$03d\")", + id1, id1, id2); + } + return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 726, + "Structured Append ID2 '%1$03d' out of range (001 to 254) (ID \"%2$03d%3$03d\")", + id2, id1, id2); + } + } else { + id1 = id2 = 1; + } + + target[tp++] = 233; + target[tp++] = (17 - symbol->structapp.count) | ((symbol->structapp.index - 1) << 4); + target[tp++] = id1; + target[tp++] = id2; + } + + /* gs1 flag values: 0: no gs1, 1: gs1 with FNC1 serparator, 2: GS separator */ + if ((symbol->input_mode & 0x07) == GS1_MODE) { + if (symbol->output_options & GS1_GS_SEPARATOR) { + gs1 = 2; + } else { + gs1 = 1; + } + } else { + gs1 = 0; + } + + if (gs1) { + target[tp++] = 232; + if (debug_print) fputs("FN1 ", stdout); + } /* FNC1 */ + + if (symbol->output_options & READER_INIT) { + if (gs1) { + return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 521, "Cannot use Reader Initialisation in GS1 mode"); + } + if (symbol->structapp.count) { + return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 727, + "Cannot have Structured Append and Reader Initialisation at the same time"); + } + target[tp++] = 234; /* Reader Programming */ + if (debug_print) fputs("RP ", stdout); + } + + /* Check for Macro05/Macro06 */ + /* "[)>[RS]05[GS]...[RS][EOT]" -> CW 236 */ + /* "[)>[RS]06[GS]...[RS][EOT]" -> CW 237 */ + if (tp == 0 && segs[0].length >= 9 && last_seg->length >= 2 + && segs[0].source[0] == '[' && segs[0].source[1] == ')' && segs[0].source[2] == '>' + && segs[0].source[3] == '\x1e' /*RS*/ && segs[0].source[4] == '0' + && (segs[0].source[5] == '5' || segs[0].source[5] == '6') + && segs[0].source[6] == '\x1d' /*GS*/ + && last_seg->source[last_seg->length - 1] == '\x04' /*EOT*/ + && last_seg->source[last_seg->length - 2] == '\x1e' /*RS*/) { + + /* Output macro Codeword */ + if (segs[0].source[5] == '5') { + target[tp++] = 236; + if (debug_print) fputs("Macro05 ", stdout); + } else { + target[tp++] = 237; + if (debug_print) fputs("Macro06 ", stdout); + } + /* Remove macro characters from input string */ + in_macro = 1; + } + + for (i = 0; i < seg_count; i++) { + int src_inc = 0, len_dec = 0; + if (in_macro) { + if (i == 0) { + src_inc = len_dec = 7; /* Skip over macro characters at beginning */ + } + if (i + 1 == seg_count) { + len_dec += 2; /* Remove RS + EOT from end */ + } + } + error_number = dm_encode(symbol, segs[i].source + src_inc, segs[i].length - len_dec, segs[i].eci, gs1, + target, &tp); + if (error_number != 0) { + return error_number; + } + } + + *p_binlen = tp; + + return 0; +} + +/* add pad bits */ +static void dm_add_tail(unsigned char target[], int tp, const int tail_length) { + int i, prn, temp; + + target[tp++] = 129; /* Pad */ + for (i = 1; i < tail_length; i++) { + /* B.1.1 253-state randomising algorithm */ + prn = ((149 * (tp + 1)) % 253) + 1; + temp = 129 + prn; + if (temp <= 254) { + target[tp++] = (unsigned char) (temp); + } else { + target[tp++] = (unsigned char) (temp - 254); + } + } +} + +static int dm_ecc200(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) { + int i, skew = 0; + unsigned char binary[2200]; + int binlen = 0; /* Suppress clang-tidy-20 uninitialized value false positive */ + int symbolsize; + int taillength, error_number; + int H, W, FH, FW, datablock, bytes, rsblock; + const int debug_print = symbol->debug & ZINT_DEBUG_PRINT; + + /* `length` may be decremented by 2 if macro character is used */ + error_number = dm_encode_segs(symbol, segs, seg_count, binary, &binlen); + if (error_number != 0) { + return error_number; + } + + symbolsize = dm_get_symbolsize(symbol, binlen); + + if (binlen > dm_matrixbytes[symbolsize]) { + if ((symbol->option_2 >= 1) && (symbol->option_2 <= DMSIZESCOUNT)) { + /* The symbol size was given by --ver (option_2) */ + return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 522, + "Input too long for Version %1$d, requires %2$d codewords (maximum %3$d)", + symbol->option_2, binlen, dm_matrixbytes[symbolsize]); + } + return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 523, "Input too long, requires %d codewords (maximum 1558)", + binlen); + } + + H = dm_matrixH[symbolsize]; + W = dm_matrixW[symbolsize]; + FH = dm_matrixFH[symbolsize]; + FW = dm_matrixFW[symbolsize]; + bytes = dm_matrixbytes[symbolsize]; + datablock = dm_matrixdatablock[symbolsize]; + rsblock = dm_matrixrsblock[symbolsize]; + + taillength = bytes - binlen; + + if (taillength != 0) { + dm_add_tail(binary, binlen, taillength); + } + if (debug_print) { + printf("Pads (%d): ", taillength); + for (i = binlen; i < binlen + taillength; i++) printf("%d ", binary[i]); + fputc('\n', stdout); + } + + /* ecc code */ + if (symbolsize == DMINTSYMBOL144 && !(symbol->option_3 & DM_ISO_144)) { + skew = 1; + } + dm_ecc(binary, bytes, datablock, rsblock, skew); + if (debug_print) { + printf("ECC (%d): ", rsblock * (bytes / datablock)); + for (i = bytes; i < bytes + rsblock * (bytes / datablock); i++) printf("%d ", binary[i]); + fputc('\n', stdout); + } + +#ifdef ZINT_TEST + if (symbol->debug & ZINT_DEBUG_TEST) { + debug_test_codeword_dump(symbol, binary, skew ? 1558 + 620 : bytes + rsblock * (bytes / datablock)); + } +#endif + { /* placement */ + const int NC = W - 2 * (W / FW); + const int NR = H - 2 * (H / FH); + int x, y, *places; + if (!(places = (int *) calloc(NC * NR, sizeof(int)))) { + return errtxt(ZINT_ERROR_MEMORY, symbol, 718, "Insufficient memory for placement array"); + } + dm_placement(places, NR, NC); + for (y = 0; y < H; y += FH) { + for (x = 0; x < W; x++) + set_module(symbol, (H - y) - 1, x); + for (x = 0; x < W; x += 2) + set_module(symbol, y, x); + } + for (x = 0; x < W; x += FW) { + for (y = 0; y < H; y++) + set_module(symbol, (H - y) - 1, x); + for (y = 0; y < H; y += 2) + set_module(symbol, (H - y) - 1, x + FW - 1); + } +#ifdef DM_DEBUG + /* Print position matrix as in standard */ + for (y = NR - 1; y >= 0; y--) { + for (x = 0; x < NC; x++) { + const int v = places[(NR - y - 1) * NC + x]; + if (x != 0) fprintf(stderr, "|"); + fprintf(stderr, "%3d.%2d", (v >> 3), 8 - (v & 7)); + } + fprintf(stderr, "\n"); + } +#endif + for (y = 0; y < NR; y++) { + for (x = 0; x < NC; x++) { + const int v = places[(NR - y - 1) * NC + x]; + if (v == 1 || (v > 7 && (binary[(v >> 3) - 1] & (1 << (v & 7))))) { + set_module(symbol, H - (1 + y + 2 * (y / (FH - 2))) - 1, 1 + x + 2 * (x / (FW - 2))); + } + } + } + for (y = 0; y < H; y++) { + symbol->row_height[y] = 1; + } + free(places); + } + + symbol->height = H; + symbol->rows = H; + symbol->width = W; + + return error_number; +} + +INTERNAL int datamatrix(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) { + + if (symbol->option_1 <= 1) { + /* ECC 200 */ + return dm_ecc200(symbol, segs, seg_count); + } + /* ECC 000 - 140 */ + return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 524, "Older Data Matrix standards are no longer supported"); +} + +/* vim: set ts=4 sw=4 et : */
