/* dotcode.c - Handles DotCode */ /* libzint - the open source barcode library Copyright (C) 2017-2020 Robin Stuart 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. */ /* vim: set ts=4 sw=4 et : */ /* * Attempts to encode DotCode according to (AIMD013) ISS DotCode Rev. 4.0, DRAFT 0.15, TSC Pre-PR #5, dated May 28, 2019 * Incorporating suggestions from Terry Burton at BWIPP */ #include #include #include #ifndef _MSC_VER #include #else #include "ms_stdint.h" #include #endif #include "common.h" #include "gs1.h" #define GF 113 #define PM 3 #define SCORE_UNLIT_EDGE -99999 /* DotCode symbol character dot patterns, from Annex C */ static const unsigned short int dot_patterns[113] = { 0x155, 0x0ab, 0x0ad, 0x0b5, 0x0d5, 0x156, 0x15a, 0x16a, 0x1aa, 0x0ae, 0x0b6, 0x0ba, 0x0d6, 0x0da, 0x0ea, 0x12b, 0x12d, 0x135, 0x14b, 0x14d, 0x153, 0x159, 0x165, 0x169, 0x195, 0x1a5, 0x1a9, 0x057, 0x05b, 0x05d, 0x06b, 0x06d, 0x075, 0x097, 0x09b, 0x09d, 0x0a7, 0x0b3, 0x0b9, 0x0cb, 0x0cd, 0x0d3, 0x0d9, 0x0e5, 0x0e9, 0x12e, 0x136, 0x13a, 0x14e, 0x15c, 0x166, 0x16c, 0x172, 0x174, 0x196, 0x19a, 0x1a6, 0x1ac, 0x1b2, 0x1b4, 0x1ca, 0x1d2, 0x1d4, 0x05e, 0x06e, 0x076, 0x07a, 0x09e, 0x0bc, 0x0ce, 0x0dc, 0x0e6, 0x0ec, 0x0f2, 0x0f4, 0x117, 0x11b, 0x11d, 0x127, 0x133, 0x139, 0x147, 0x163, 0x171, 0x18b, 0x18d, 0x193, 0x199, 0x1a3, 0x1b1, 0x1c5, 0x1c9, 0x1d1, 0x02f, 0x037, 0x03b, 0x03d, 0x04f, 0x067, 0x073, 0x079, 0x08f, 0x0c7, 0x0e3, 0x0f1, 0x11e, 0x13c, 0x178, 0x18e, 0x19c, 0x1b8, 0x1c6, 0x1cc }; // Printed() routine from Annex A adapted to char array of ASCII 1's and 0's static int get_dot(char Dots[], const int Hgt, const int Wid, const int x, const int y) { int retval = 0; if ((x >= 0) && (x < Wid) && (y >= 0) && (y < Hgt)) { if (Dots[(y * Wid) + x] == '1') { retval = 1; } } return retval; } static int clr_col(char *Dots, const int Hgt, const int Wid, const int x) { int y; for (y = x & 1; y < Hgt; y += 2) { if (get_dot(Dots, Hgt, Wid, x, y)) { return 0; } } return 1; } static int clr_row(char *Dots, const int Hgt, const int Wid, const int y) { int x; for (x = y & 1; x < Wid; x += 2) { if (get_dot(Dots, Hgt, Wid, x, y)) { return 0; } } return 1; } // calc penalty for empty interior columns static int col_penalty(char *Dots, int Hgt, int Wid) { int x, penalty = 0, penalty_local = 0; for (x = 1; x < Wid - 1; x++) { if (clr_col(Dots, Hgt, Wid, x)) { if (penalty_local == 0) { penalty_local = Hgt; } else { penalty_local *= Hgt; } } else { if (penalty_local) { penalty += penalty_local; penalty_local = 0; } } } return penalty + penalty_local; } // calc penalty for empty interior rows static int row_penalty(char *Dots, int Hgt, int Wid) { int y, penalty = 0, penalty_local = 0; for (y = 1; y < Hgt - 1; y++) { if (clr_row(Dots, Hgt, Wid, y)) { if (penalty_local == 0) { penalty_local = Wid; } else { penalty_local *= Wid; } } else { if (penalty_local) { penalty += penalty_local; penalty_local = 0; } } } return penalty + penalty_local; } /* Dot pattern scoring routine from Annex A */ static int score_array(char Dots[], int Hgt, int Wid) { int x, y, worstedge, first, last, sum; int penalty = 0; // first, guard against "pathelogical" gaps in the array // subtract a penalty score for empty rows/columns from total code score for each mask, // where the penalty is Sum(N ^ n), where N is the number of positions in a column/row, // and n is the number of consecutive empty rows/columns penalty = row_penalty(Dots, Hgt, Wid) + col_penalty(Dots, Hgt, Wid); sum = 0; first = -1; last = -1; // across the top edge, count printed dots and measure their extent for (x = 0; x < Wid; x += 2) { if (get_dot(Dots, Hgt, Wid, x, 0)) { if (first < 0) { first = x; } last = x; sum++; } } if (sum == 0) { return SCORE_UNLIT_EDGE; // guard against empty top edge } worstedge = sum + last - first; worstedge *= Hgt; sum = 0; first = -1; last = -1; // across the bottom edge, ditto for (x = Wid & 1; x < Wid; x += 2) { if (get_dot(Dots, Hgt, Wid, x, Hgt - 1)) { if (first < 0) { first = x; } last = x; sum++; } } if (sum == 0) { return SCORE_UNLIT_EDGE; // guard against empty bottom edge } sum += last - first; sum *= Hgt; if (sum < worstedge) { worstedge = sum; } sum = 0; first = -1; last = -1; // down the left edge, ditto for (y = 0; y < Hgt; y += 2) { if (get_dot(Dots, Hgt, Wid, 0, y)) { if (first < 0) { first = y; } last = y; sum++; } } if (sum == 0) { return SCORE_UNLIT_EDGE; // guard against empty left edge } sum += last - first; sum *= Wid; if (sum < worstedge) { worstedge = sum; } sum = 0; first = -1; last = -1; // down the right edge, ditto for (y = Hgt & 1; y < Hgt; y += 2) { if (get_dot(Dots, Hgt, Wid, Wid - 1, y)) { if (first < 0) { first = y; } last = y; sum++; } } if (sum == 0) { return SCORE_UNLIT_EDGE; // guard against empty right edge } sum += last - first; sum *= Wid; if (sum < worstedge) { worstedge = sum; } // throughout the array, count the # of unprinted 5-somes (cross patterns) // plus the # of printed dots surrounded by 8 unprinted neighbors sum = 0; for (y = 0; y < Hgt; y++) { for (x = y & 1; x < Wid; x += 2) { if ((!get_dot(Dots, Hgt, Wid, x - 1, y - 1)) && (!get_dot(Dots, Hgt, Wid, x + 1, y - 1)) && (!get_dot(Dots, Hgt, Wid, x - 1, y + 1)) && (!get_dot(Dots, Hgt, Wid, x + 1, y + 1)) && ((!get_dot(Dots, Hgt, Wid, x, y)) || ((!get_dot(Dots, Hgt, Wid, x - 2, y)) && (!get_dot(Dots, Hgt, Wid, x, y - 2)) && (!get_dot(Dots, Hgt, Wid, x + 2, y)) && (!get_dot(Dots, Hgt, Wid, x, y + 2))))) { sum++; } } } return (worstedge - sum * sum - penalty); } //------------------------------------------------------------------------- // "rsencode(nd,nc)" adds "nc" R-S check words to "nd" data words in wd[] // employing Galois Field GF, where GF is prime, with a prime modulus of PM //------------------------------------------------------------------------- static void rsencode(int nd, int nc, unsigned char *wd) { // roots (antilogs): root[0] = 1; for (i = 1; i < GF - 1; i++) root[i] = (PM * root[i - 1]) % GF; static int root[GF - 1] = { 1, 3, 9, 27, 81, 17, 51, 40, 7, 21, 63, 76, 2, 6, 18, 54, 49, 34, 102, 80, 14, 42, 13, 39, 4, 12, 36, 108, 98, 68, 91, 47, 28, 84, 26, 78, 8, 24, 72, 103, 83, 23, 69, 94, 56, 55, 52, 43, 16, 48, 31, 93, 53, 46, 25, 75, 112, 110, 104, 86, 32, 96, 62, 73, 106, 92, 50, 37, 111, 107, 95, 59, 64, 79, 11, 33, 99, 71, 100, 74, 109, 101, 77, 5, 15, 45, 22, 66, 85, 29, 87, 35, 105, 89, 41, 10, 30, 90, 44, 19, 57, 58, 61, 70, 97, 65, 82, 20, 60, 67, 88, 38 }; int i, j, k, nw, start, step, c[GF]; // Here we compute how many interleaved R-S blocks will be needed nw = nd + nc; step = (nw + GF - 2) / (GF - 1); // ...& then for each such block: for (start = 0; start < step; start++) { int ND = (nd - start + step - 1) / step; int NW = (nw - start + step - 1) / step; int NC = NW - ND; // first compute the generator polynomial "c" of order "NC": memset(c, 0, GF * sizeof(int)); // Keep clang-tidy happy (as far as UndefinedBinaryOperatorResult warning below at least) c[0] = 1; for (i = 1; i <= NC; i++) { for (j = NC; j >= 1; j--) { c[j] = (GF + c[j] - (root[i] * c[j - 1]) % GF) % GF; } } // & then compute the corresponding checkword values into wd[] // ... (a) starting at wd[start] & (b) stepping by step for (i = ND; i < NW; i++) { wd[start + i * step] = 0; } for (i = 0; i < ND; i++) { k = (wd[start + i * step] + wd[start + ND * step]) % GF; for (j = 0; j < NC - 1; j++) { wd[start + (ND + j) * step] = (GF - ((c[j + 1] * k) % GF) + wd[start + (ND + j + 1) * step]) % GF; } wd[start + (ND + NC - 1) * step] = (GF - ((c[NC] * k) % GF)) % GF; } for (i = ND; i < NW; i++) { wd[start + i * step] = (GF - wd[start + i * step]) % GF; } } } /* Check if the next character is directly encodable in code set A (Annex F.II.D) */ static int datum_a(const unsigned char source[], int position, int length) { int retval = 0; if (position < length) { if (source[position] <= 95) { retval = 1; } } return retval; } /* Check if the next character is directly encodable in code set B (Annex F.II.D). Note changed to return 2 if CR/LF */ static int datum_b(const unsigned char source[], int position, int length) { int retval = 0; if (position < length) { if ((source[position] >= 32) && (source[position] <= 127)) { retval = 1; } switch (source[position]) { case 9: // HT case 28: // FS case 29: // GS case 30: // RS retval = 1; } if (position + 1 < length) { if ((source[position] == 13) && (source[position + 1] == 10)) { // CRLF retval = 2; } } } return retval; } /* Check if the next characters are directly encodable in code set C (Annex F.II.D) */ static int datum_c(const unsigned char source[], int position, int length) { int retval = 0; if (position <= length - 2) { if (((source[position] >= '0') && (source[position] <= '9')) && ((source[position + 1] >= '0') && (source[position + 1] <= '9'))) retval = 1; } return retval; } /* Returns how many consecutive digits lie immediately ahead (Annex F.II.A) */ static int n_digits(const unsigned char source[], int position, int length) { int i; for (i = position; (i < length) && ((source[i] >= '0') && (source[i] <= '9')); i++); return i - position; } /* checks ahead for 10 or more digits starting "17xxxxxx10..." (Annex F.II.B) */ static int seventeen_ten(const unsigned char source[], int position, int length) { int found = 0; if (n_digits(source, position, length) >= 10) { if (((source[position] == '1') && (source[position + 1] == '7')) && ((source[position + 8] == '1') && (source[position + 9] == '0'))) { found = 1; } } return found; } /* checks how many characters ahead can be reached while datum_c is true, * returning the resulting number of codewords (Annex F.II.E) */ static int ahead_c(const unsigned char source[], int position, int length) { int count = 0; int i; for (i = position; (i < length) && datum_c(source, i, length); i += 2) { count++; } return count; } /* Annex F.II.F */ static int try_c(const unsigned char source[], int position, int length) { int retval = 0; if (n_digits(source, position, length) > 0) { if (ahead_c(source, position, length) > ahead_c(source, position + 1, length)) { retval = ahead_c(source, position, length); } } return retval; } /* Annex F.II.G */ static int ahead_a(const unsigned char source[], int position, int length) { int count = 0; int i; for (i = position; ((i < length) && datum_a(source, i, length)) && (try_c(source, i, length) < 2); i++) { count++; } return count; } /* Annex F.II.H Note: changed to return number of chars encodable. Number of codewords returned in *p_nx. */ static int ahead_b(const unsigned char source[], int position, int length, int *p_nx) { int count = 0; int i, incr; for (i = position; (i < length) && (incr = datum_b(source, i, length)) && (try_c(source, i, length) < 2); i += incr) { count++; } if (p_nx != NULL) { *p_nx = count; } return i - position; } /* checks if the next character is in the range 128 to 255 (Annex F.II.I) */ static int binary(const unsigned char source[], int length, int position) { int retval = 0; if (position < length && source[position] >= 128) { retval = 1; } return retval; } /* Analyse input data stream and encode using algorithm from Annex F */ static int dotcode_encode_message(struct zint_symbol *symbol, const unsigned char source[], int length, unsigned char *codeword_array, int *binary_finish) { static char lead_specials[] = "\x09\x1C\x1D\x1E"; // HT, FS, GS, RS int input_position, array_length, i; char encoding_mode; int inside_macro; int debug = (symbol->debug & ZINT_DEBUG_PRINT); int binary_buffer_size = 0; int lawrencium[6]; // Reversed radix 103 values int nx; uint64_t binary_buffer = 0; input_position = 0; array_length = 0; encoding_mode = 'C'; inside_macro = 0; if (symbol->output_options & READER_INIT) { codeword_array[array_length] = 109; // FNC3 array_length++; } if ((symbol->input_mode & 0x07) != GS1_MODE) { if (length > 2) { if (((source[input_position] >= '0') && (source[input_position] <= '9')) && ((source[input_position + 1] >= '0') && (source[input_position + 1] <= '9'))) { codeword_array[array_length] = 107; // FNC1 array_length++; } } } if (symbol->eci > 0) { codeword_array[array_length] = 108; // FNC2 array_length++; if (symbol->eci <= 39) { codeword_array[array_length] = symbol->eci; array_length++; } else { // the next three codewords valued A, B & C encode the ECI value of // (A - 40) * 12769 + B * 113 + C + 40 (Section 5.2.1) int a, b, c; a = (symbol->eci - 40) / 12769; b = ((symbol->eci - 40) - (12769 * a)) / 113; c = (symbol->eci - 40) - (12769 * a) - (113 * b); codeword_array[array_length] = a + 40; array_length++; codeword_array[array_length] = b; array_length++; codeword_array[array_length] = c; array_length++; } } // Prevent encodation as a macro if a special character is in first position if (strchr(lead_specials, source[input_position]) != NULL) { codeword_array[array_length] = 101; // Latch A array_length++; codeword_array[array_length] = source[input_position] + 64; array_length++; encoding_mode = 'A'; input_position++; } while (input_position < length) { int done = 0; /* Step A */ if ((input_position == length - 2) && (inside_macro != 0) && (inside_macro != 100)) { // inside_macro only gets set to 97, 98 or 99 if the last two characters are RS/EOT input_position += 2; done = 1; if (debug) { printf("A "); } } /* Step B */ if ((input_position == length - 1) && (inside_macro == 100)) { // inside_macro only gets set to 100 if the last character is EOT input_position++; done = 1; if (debug) { printf("B "); } } /* Step C1 */ if ((!done) && (encoding_mode == 'C')) { if ((array_length == 0) && (length > 6)) { if ((source[input_position] == '[') && (source[input_position + 1] == ')') && (source[input_position + 2] == '>') && (source[input_position + 3] == 30) // RS && (source[length - 1] == 4)) { // EOT if ((source[input_position + 6] == 29) && (source[length - 2] == 30)) { // GS/RS if ((source[input_position + 4] == '0') && (source[input_position + 5] == '5')) { codeword_array[array_length] = 106; // Latch B array_length++; encoding_mode = 'B'; codeword_array[array_length] = 97; // Macro array_length++; input_position += 7; inside_macro = 97; done = 1; if (debug) { printf("C1/1 "); } } if ((!done) && (source[input_position + 4] == '0') && (source[input_position + 5] == '6')) { codeword_array[array_length] = 106; // Latch B array_length++; encoding_mode = 'B'; codeword_array[array_length] = 98; // Macro array_length++; input_position += 7; inside_macro = 98; done = 1; if (debug) { printf("C1/2 "); } } if ((!done) && (source[input_position + 4] == '1') && (source[input_position + 5] == '2')) { codeword_array[array_length] = 106; // Latch B array_length++; encoding_mode = 'B'; codeword_array[array_length] = 99; // Macro array_length++; input_position += 7; inside_macro = 99; done = 1; if (debug) { printf("C1/3 "); } } } if ((!done) && (source[input_position + 4] >= '0') && (source[input_position + 4] <= '9') && (source[input_position + 5] >= '0') && (source[input_position + 5] <= '9')) { codeword_array[array_length] = 106; // Latch B array_length++; encoding_mode = 'B'; codeword_array[array_length] = 100; // Macro array_length++; input_position += 4; inside_macro = 100; done = 1; if (debug) { printf("C1/4 "); } } } } } /* Step C2 */ if ((!done) && (encoding_mode == 'C')) { if (seventeen_ten(source, input_position, length)) { codeword_array[array_length] = 100; // (17)...(10) array_length++; codeword_array[array_length] = ((source[input_position + 2] - '0') * 10) + (source[input_position + 3] - '0'); array_length++; codeword_array[array_length] = ((source[input_position + 4] - '0') * 10) + (source[input_position + 5] - '0'); array_length++; codeword_array[array_length] = ((source[input_position + 6] - '0') * 10) + (source[input_position + 7] - '0'); array_length++; input_position += 10; done = 1; if (debug) { printf("C2/1 "); } } } if ((!done) && (encoding_mode == 'C')) { if (datum_c(source, input_position, length) || ((source[input_position] == '[') && ((symbol->input_mode & 0x07) == GS1_MODE))) { if (source[input_position] == '[') { codeword_array[array_length] = 107; // FNC1 input_position++; } else { codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0'); input_position += 2; } array_length++; done = 1; if (debug) { printf("C2/2 "); } } } /* Step C3 */ if ((!done) && (encoding_mode == 'C')) { if (binary(source, length, input_position)) { if (n_digits(source, input_position + 1, length) > 0) { if ((source[input_position] - 128) < 32) { codeword_array[array_length] = 110; // Upper Shift A array_length++; codeword_array[array_length] = source[input_position] - 128 + 64; array_length++; } else { codeword_array[array_length] = 111; // Upper Shift B array_length++; codeword_array[array_length] = source[input_position] - 128 - 32; array_length++; } input_position++; } else { codeword_array[array_length] = 112; // Bin Latch array_length++; encoding_mode = 'X'; } done = 1; if (debug) { printf("C3 "); } } } /* Step C4 */ if ((!done) && (encoding_mode == 'C')) { int m = ahead_a(source, input_position, length); int n = ahead_b(source, input_position, length, &nx); if (m > n) { codeword_array[array_length] = 101; // Latch A array_length++; encoding_mode = 'A'; } else { if (nx >= 1 && nx <= 4) { codeword_array[array_length] = 101 + nx; // nx Shift B array_length++; for (i = 0; i < nx; i++) { if (source[input_position] >= 32) { codeword_array[array_length] = source[input_position] - 32; } else if (source[input_position] == 13) { // CR/LF codeword_array[array_length] = 96; input_position++; } else { switch(source[input_position]) { case 9: codeword_array[array_length] = 97; break; // HT case 28: codeword_array[array_length] = 98; break; // FS case 29: codeword_array[array_length] = 99; break; // GS case 30: codeword_array[array_length] = 100; break; // RS } } array_length++; input_position++; } } else { codeword_array[array_length] = 106; // Latch B array_length++; encoding_mode = 'B'; } } done = 1; if (debug) { printf("C4 "); } } /* Step D1 */ if ((!done) && (encoding_mode == 'B')) { int n = try_c(source, input_position, length); if (n >= 2) { if (n <= 4) { codeword_array[array_length] = 103 + (n - 2); // nx Shift C array_length++; for (i = 0; i < n; i++) { codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0'); array_length++; input_position += 2; } } else { codeword_array[array_length] = 106; // Latch C array_length++; encoding_mode = 'C'; } done = 1; if (debug) { printf("D1 "); } } } /* Step D2 */ if ((!done) && (encoding_mode == 'B')) { if ((source[input_position] == '[') && ((symbol->input_mode & 0x07) == GS1_MODE)) { codeword_array[array_length] = 107; // FNC1 array_length++; input_position++; done = 1; if (debug) { printf("D2/1 "); } } else { if (datum_b(source, input_position, length)) { if ((source[input_position] >= 32) && (source[input_position] <= 127)) { codeword_array[array_length] = source[input_position] - 32; done = 1; } else if (source[input_position] == 13) { /* CR/LF */ codeword_array[array_length] = 96; input_position++; done = 1; } else if (input_position != 0) { /* HT, FS, GS and RS in the first data position would be interpreted as a macro (see table 2) */ switch(source[input_position]) { case 9: // HT codeword_array[array_length] = 97; break; case 28: // FS codeword_array[array_length] = 98; break; case 29: // GS codeword_array[array_length] = 99; break; case 30: // RS codeword_array[array_length] = 100; break; } done = 1; } if (done == 1) { array_length++; input_position++; if (debug) { printf("D2/2 "); } } } } } /* Step D3 */ if ((!done) && (encoding_mode == 'B')) { if (binary(source, length, input_position)) { if (datum_b(source, input_position + 1, length)) { if ((source[input_position] - 128) < 32) { codeword_array[array_length] = 110; // Bin Shift A array_length++; codeword_array[array_length] = source[input_position] - 128 + 64; array_length++; } else { codeword_array[array_length] = 111; // Bin Shift B array_length++; codeword_array[array_length] = source[input_position] - 128 - 32; array_length++; } input_position++; } else { codeword_array[array_length] = 112; // Bin Latch array_length++; encoding_mode = 'X'; } done = 1; if (debug) { printf("D3 "); } } } /* Step D4 */ if ((!done) && (encoding_mode == 'B')) { if (ahead_a(source, input_position, length) == 1) { codeword_array[array_length] = 101; // Shift A array_length++; if (source[input_position] < 32) { codeword_array[array_length] = source[input_position] + 64; } else { codeword_array[array_length] = source[input_position] - 32; } array_length++; input_position++; } else { codeword_array[array_length] = 102; // Latch A array_length++; encoding_mode = 'A'; } done = 1; if (debug) { printf("D4 "); } } /* Step E1 */ if ((!done) && (encoding_mode == 'A')) { int n = try_c(source, input_position, length); if (n >= 2) { if (n <= 4) { codeword_array[array_length] = 103 + (n - 2); // nx Shift C array_length++; for (i = 0; i < n; i++) { codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0'); array_length++; input_position += 2; } } else { codeword_array[array_length] = 106; // Latch C array_length++; encoding_mode = 'C'; } done = 1; if (debug) { printf("E1 "); } } } /* Step E2 */ if ((!done) && (encoding_mode == 'A')) { if ((source[input_position] == '[') && ((symbol->input_mode & 0x07) == GS1_MODE)) { // Note: this branch probably never reached as no reason to be in Code Set A for GS1 data codeword_array[array_length] = 107; // FNC1 array_length++; input_position++; done = 1; if (debug) { printf("E2/1 "); } } else { if (datum_a(source, input_position, length)) { if (source[input_position] < 32) { codeword_array[array_length] = source[input_position] + 64; } else { codeword_array[array_length] = source[input_position] - 32; } array_length++; input_position++; done = 1; if (debug) { printf("E2/2 "); } } } } /* Step E3 */ if ((!done) && (encoding_mode == 'A')) { if (binary(source, length, input_position)) { if (datum_a(source, input_position + 1, length)) { if ((source[input_position] - 128) < 32) { codeword_array[array_length] = 110; // Bin Shift A array_length++; codeword_array[array_length] = source[input_position] - 128 + 64; array_length++; } else { codeword_array[array_length] = 111; // Bin Shift B array_length++; codeword_array[array_length] = source[input_position] - 128 - 32; array_length++; } input_position++; } else { codeword_array[array_length] = 112; // Bin Latch array_length++; encoding_mode = 'X'; } done = 1; if (debug) { printf("E3 "); } } } /* Step E4 */ if ((!done) && (encoding_mode == 'A')) { ahead_b(source, input_position, length, &nx); if (nx >= 1 && nx <= 6) { codeword_array[array_length] = 95 + nx; // nx Shift B array_length++; for (i = 0; i < nx; i++) { if (source[input_position] >= 32) { codeword_array[array_length] = source[input_position] - 32; } else if (source[input_position] == 13) { // CR/LF codeword_array[array_length] = 96; input_position++; } else { switch(source[input_position]) { case 9: codeword_array[array_length] = 97; break; // HT case 28: codeword_array[array_length] = 98; break; // FS case 29: codeword_array[array_length] = 99; break; // GS case 30: codeword_array[array_length] = 100; break; // RS } } array_length++; input_position++; } } else { codeword_array[array_length] = 102; // Latch B array_length++; encoding_mode = 'B'; } done = 1; if (debug) { printf("E4 "); } } /* Step F1 */ if ((!done) && (encoding_mode == 'X')) { int n = try_c(source, input_position, length); if (n >= 2) { /* Empty binary buffer */ for (i = 0; i < (binary_buffer_size + 1); i++) { lawrencium[i] = (int) (binary_buffer % 103); binary_buffer /= 103; } for (i = 0; i < (binary_buffer_size + 1); i++) { codeword_array[array_length] = lawrencium[binary_buffer_size - i]; array_length++; } binary_buffer = 0; binary_buffer_size = 0; if (n <= 7) { codeword_array[array_length] = 101 + n; // Interrupt for nx Shift C array_length++; for (i = 0; i < n; i++) { codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0'); array_length++; input_position += 2; } } else { codeword_array[array_length] = 111; // Terminate with Latch to C array_length++; encoding_mode = 'C'; } done = 1; if (debug) { printf("F1 "); } } } /* Step F2 */ /* Section 5.2.1.1 para D.2.i states: * "Groups of six codewords, each valued between 0 and 102, are radix converted from * base 103 into five base 259 values..." */ if ((!done) && (encoding_mode == 'X')) { if (binary(source, length, input_position) || binary(source, length, input_position + 1) || binary(source, length, input_position + 2) || binary(source, length, input_position + 3)) { binary_buffer *= 259; binary_buffer += source[input_position]; binary_buffer_size++; if (binary_buffer_size == 5) { for (i = 0; i < 6; i++) { lawrencium[i] = (int) (binary_buffer % 103); binary_buffer /= 103; } for (i = 0; i < 6; i++) { codeword_array[array_length] = lawrencium[5 - i]; array_length++; } binary_buffer = 0; binary_buffer_size = 0; } input_position++; done = 1; if (debug) { printf("F2 "); } } } /* Step F3 */ if ((!done) && (encoding_mode == 'X')) { /* Empty binary buffer */ for (i = 0; i < (binary_buffer_size + 1); i++) { lawrencium[i] = (int) (binary_buffer % 103); binary_buffer /= 103; } for (i = 0; i < (binary_buffer_size + 1); i++) { codeword_array[array_length] = lawrencium[binary_buffer_size - i]; array_length++; } binary_buffer = 0; binary_buffer_size = 0; if (ahead_a(source, input_position, length) > ahead_b(source, input_position, length, NULL)) { codeword_array[array_length] = 109; // Terminate with Latch to A encoding_mode = 'A'; } else { codeword_array[array_length] = 110; // Terminate with Latch to B encoding_mode = 'B'; } array_length++; // done = 1 // As long as last branch not needed if (debug) { printf("F3 "); } } } if (encoding_mode == 'X') { if (binary_buffer_size != 0) { /* Empty binary buffer */ for (i = 0; i < (binary_buffer_size + 1); i++) { lawrencium[i] = (int) (binary_buffer % 103); binary_buffer /= 103; } for (i = 0; i < (binary_buffer_size + 1); i++) { codeword_array[array_length] = lawrencium[binary_buffer_size - i]; array_length++; } } *(binary_finish) = 1; } if (debug) { printf("\n"); } return array_length; } /* Convert codewords to binary data stream */ static int make_dotstream(const unsigned char masked_array[], const int array_length, char dot_stream[]) { int i; int bp = 0; /* Mask value is encoded as two dots */ bp = bin_append_posn(masked_array[0], 2, dot_stream, bp); /* The rest of the data uses 9-bit dot patterns from Annex C */ for (i = 1; i < array_length; i++) { bp = bin_append_posn(dot_patterns[masked_array[i]], 9, dot_stream, bp); } dot_stream[bp] = '\0'; return bp; } /* Determines if a given dot is a reserved corner dot * to be used by one of the last six bits */ static int is_corner(int column, int row, int width, int height) { int corner = 0; /* Top Left */ if ((column == 0) && (row == 0)) { corner = 1; } /* Top Right */ if (height % 2) { if (((column == width - 2) && (row == 0)) || ((column == width - 1) && (row == 1))) { corner = 1; } } else { if ((column == width - 1) && (row == 0)) { corner = 1; } } /* Bottom Left */ if (height % 2) { if ((column == 0) && (row == height - 1)) { corner = 1; } } else { if (((column == 0) && (row == height - 2)) || ((column == 1) && (row == height - 1))) { corner = 1; } } /* Bottom Right */ if (((column == width - 2) && (row == height - 1)) || ((column == width - 1) && (row == height - 2))) { corner = 1; } return corner; } /* Place the dots in the symbol*/ static void fold_dotstream(char dot_stream[], int width, int height, char dot_array[]) { int column, row; int input_position = 0; if (height % 2) { /* Horizontal folding */ for (row = 0; row < height; row++) { for (column = 0; column < width; column++) { if (!((column + row) % 2)) { if (is_corner(column, row, width, height)) { dot_array[(row * width) + column] = 'C'; } else { dot_array[((height - row - 1) * width) + column] = dot_stream[input_position]; input_position++; } } else { dot_array[((height - row - 1) * width) + column] = ' '; // Non-data position } } } /* Corners */ dot_array[width - 2] = dot_stream[input_position]; input_position++; dot_array[(height * width) - 2] = dot_stream[input_position]; input_position++; dot_array[(width * 2) - 1] = dot_stream[input_position]; input_position++; dot_array[((height - 1) * width) - 1] = dot_stream[input_position]; input_position++; dot_array[0] = dot_stream[input_position]; input_position++; dot_array[(height - 1) * width] = dot_stream[input_position]; } else { /* Vertical folding */ for (column = 0; column < width; column++) { for (row = 0; row < height; row++) { if (!((column + row) % 2)) { if (is_corner(column, row, width, height)) { dot_array[(row * width) + column] = 'C'; } else { dot_array[(row * width) + column] = dot_stream[input_position]; input_position++; } } else { dot_array[(row * width) + column] = ' '; // Non-data position } } } /* Corners */ dot_array[((height - 1) * width) - 1] = dot_stream[input_position]; input_position++; dot_array[(height - 2) * width] = dot_stream[input_position]; input_position++; dot_array[(height * width) - 2] = dot_stream[input_position]; input_position++; dot_array[((height - 1) * width) + 1] = dot_stream[input_position]; input_position++; dot_array[width - 1] = dot_stream[input_position]; input_position++; dot_array[0] = dot_stream[input_position]; } } static void apply_mask(int mask, int data_length, unsigned char *masked_codeword_array, unsigned char *codeword_array, int ecc_length) { int weight = 0; int j; switch (mask) { case 0: masked_codeword_array[0] = 0; for (j = 0; j < data_length; j++) { masked_codeword_array[j + 1] = codeword_array[j]; } break; case 1: masked_codeword_array[0] = 1; for (j = 0; j < data_length; j++) { masked_codeword_array[j + 1] = (weight + codeword_array[j]) % 113; weight += 3; } break; case 2: masked_codeword_array[0] = 2; for (j = 0; j < data_length; j++) { masked_codeword_array[j + 1] = (weight + codeword_array[j]) % 113; weight += 7; } break; case 3: masked_codeword_array[0] = 3; for (j = 0; j < data_length; j++) { masked_codeword_array[j + 1] = (weight + codeword_array[j]) % 113; weight += 17; } break; } rsencode(data_length + 1, ecc_length, masked_codeword_array); } static void force_corners(int width, int height, char *dot_array) { if (width % 2) { // "Vertical" symbol dot_array[0] = '1'; dot_array[width - 1] = '1'; dot_array[(height - 2) * width] = '1'; dot_array[((height - 1) * width) - 1] = '1'; dot_array[((height - 1) * width) + 1] = '1'; dot_array[(height * width) - 2] = '1'; } else { // "Horizontal" symbol dot_array[0] = '1'; dot_array[width - 2] = '1'; dot_array[(2 * width) - 1] = '1'; dot_array[((height - 1) * width) - 1] = '1'; dot_array[(height - 1) * width] = '1'; dot_array[(height * width) - 2] = '1'; } } INTERNAL int dotcode(struct zint_symbol *symbol, unsigned char source[], int length) { int i, j, k; int jc, n_dots; int data_length, ecc_length; int min_dots, min_area; int height, width; int mask_score[8]; int dot_stream_length; int high_score, best_mask; int binary_finish = 0; int debug = symbol->debug; int padding_dots, is_first; int codeword_array_len = length * 4 + 8; /* Allow up to 4 codewords per input + 2 (FNC) + 4 (ECI) + 2 (special char 1st position) */ #ifdef _MSC_VER unsigned char* masked_codeword_array; #endif #ifndef _MSC_VER unsigned char codeword_array[codeword_array_len]; #else char* dot_stream; char* dot_array; unsigned char* codeword_array = (unsigned char *) _alloca(codeword_array_len); #endif /* _MSC_VER */ if (symbol->eci > 811799) { strcpy(symbol->errtxt, "525: Invalid ECI"); return ZINT_ERROR_INVALID_OPTION; } data_length = dotcode_encode_message(symbol, source, length, codeword_array, &binary_finish); /* Suppresses clang-tidy clang-analyzer-core.UndefinedBinaryOperatorResult/uninitialized.ArraySubscript warnings */ assert(data_length > 0); ecc_length = 3 + (data_length / 2); if (debug & ZINT_DEBUG_PRINT) { printf("Codeword length = %d, ECC length = %d\n", data_length, ecc_length); printf("Codewords: "); for (i = 0; i < data_length; i++) { printf("[%d] ",codeword_array[i]); } printf("\n"); } #ifdef ZINT_TEST if (debug & ZINT_DEBUG_TEST) { debug_test_codeword_dump(symbol, codeword_array, data_length); } #endif min_dots = 9 * (data_length + 3 + (data_length / 2)) + 2; min_area = min_dots * 2; if (symbol->option_2 == 0) { /* Automatic sizing */ /* Following Rule 3 (Section 5.2.2) and applying a recommended width to height ratio 3:2 */ /* Eliminates under sized symbols */ float h = (float) (sqrt(min_area * 0.666)); float w = (float) (sqrt(min_area * 1.5)); height = (int) h; width = (int) w; if (((width + height) & 1) == 1) { if ((width * height) < min_area) { width++; height++; } } else { if ((h * width) < (w * height)) { width++; if ((width * height) < min_area) { width--; height++; if ((width * height) < min_area) { width += 2; } } } else { height++; if ((width * height) < min_area) { width++; height--; if ((width * height) < min_area) { height += 2; } } } } } else { /* User defined width */ /* Eliminates under sized symbols */ width = symbol->option_2; height = (min_area + (width - 1)) / width; if (!((width + height) % 2)) { height++; } } if (debug & ZINT_DEBUG_PRINT) { printf("Width = %d, Height = %d\n", width, height); } if ((height > 200) || (width > 200)) { strcpy(symbol->errtxt, "526: Specified symbol size is too large"); return ZINT_ERROR_INVALID_OPTION; } if ((height < 5) || (width < 5)) { strcpy(symbol->errtxt, "527: Specified symbol size has a dimension which is too small"); return ZINT_ERROR_INVALID_OPTION; } n_dots = (height * width) / 2; #ifndef _MSC_VER char dot_stream[height * width * 3]; char dot_array[width * height * sizeof (char) ]; #else dot_stream = (char *) _alloca(height * width * 3); if (!dot_stream) return ZINT_ERROR_MEMORY; dot_array = (char *) _alloca(width * height * sizeof (char)); if (!dot_array) return ZINT_ERROR_MEMORY; #endif /* Add pad characters */ padding_dots = n_dots - min_dots; /* get the number of free dots available for padding */ is_first = 1; /* first padding character flag */ while (padding_dots >= 9) { if (padding_dots < 18 && ((data_length % 2) == 0)) padding_dots -= 9; else if (padding_dots >= 18) { if ((data_length % 2) == 0) padding_dots -= 9; else padding_dots -= 18; } else break; /* not enough padding dots left for padding */ if ((is_first == 1) && (binary_finish == 1)) codeword_array[data_length] = 109; else codeword_array[data_length] = 106; data_length++; is_first = 0; } ecc_length = 3 + (data_length / 2); #ifndef _MSC_VER unsigned char masked_codeword_array[data_length + 1 + ecc_length]; #else masked_codeword_array = (unsigned char *) _alloca((data_length + 1 + ecc_length) * sizeof (unsigned char)); #endif /* _MSC_VER */ /* Evaluate data mask options */ for (i = 0; i < 4; i++) { apply_mask(i, data_length, masked_codeword_array, codeword_array, ecc_length); dot_stream_length = make_dotstream(masked_codeword_array, (data_length + ecc_length + 1), dot_stream); /* Add pad bits */ for (jc = dot_stream_length; jc < n_dots; jc++) { strcat(dot_stream, "1"); } fold_dotstream(dot_stream, width, height, dot_array); mask_score[i] = score_array(dot_array, height, width); if (debug & ZINT_DEBUG_PRINT) { printf("Mask %d score is %d\n", i, mask_score[i]); } } high_score = mask_score[0]; best_mask = 0; for (i = 1; i < 4; i++) { if (mask_score[i] >= high_score) { high_score = mask_score[i]; best_mask = i; } } /* Re-evaluate using forced corners if needed */ if (best_mask <= (height * width) / 2) { for (i = 0; i < 4; i++) { apply_mask(i, data_length, masked_codeword_array, codeword_array, ecc_length); dot_stream_length = make_dotstream(masked_codeword_array, (data_length + ecc_length + 1), dot_stream); /* Add pad bits */ for (jc = dot_stream_length; jc < n_dots; jc++) { strcat(dot_stream, "1"); } fold_dotstream(dot_stream, width, height, dot_array); force_corners(width, height, dot_array); mask_score[i + 4] = score_array(dot_array, height, width); if (debug & ZINT_DEBUG_PRINT) { printf("Mask %d score is %d\n", i + 4, mask_score[i + 4]); } } for (i = 4; i < 8; i++) { if (mask_score[i] >= high_score) { high_score = mask_score[i]; best_mask = i; } } } if (debug & ZINT_DEBUG_PRINT) { printf("Applying mask %d, high_score %d\n", best_mask, high_score); } /* Apply best mask */ apply_mask(best_mask % 4, data_length, masked_codeword_array, codeword_array, ecc_length); dot_stream_length = make_dotstream(masked_codeword_array, (data_length + ecc_length + 1), dot_stream); /* Add pad bits */ for (jc = dot_stream_length; jc < n_dots; jc++) { strcat(dot_stream, "1"); } fold_dotstream(dot_stream, width, height, dot_array); if (best_mask >= 4) { force_corners(width, height, dot_array); } /* Copy values to symbol */ symbol->width = width; symbol->rows = height; for (k = 0; k < height; k++) { for (j = 0; j < width; j++) { if (dot_array[(k * width) + j] == '1') { set_module(symbol, k, j); } } symbol->row_height[k] = 1; } if (!(symbol->output_options & BARCODE_DOTTY_MODE)) { symbol->output_options += BARCODE_DOTTY_MODE; } return 0; }