zint-barcode-generator/backend/dotcode.c

1602 lines
54 KiB
C

/* dotcode.c - Handles DotCode */
/*
libzint - the open source barcode library
Copyright (C) 2017-2020 Robin Stuart <rstuart114@gmail.com>
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 <stdio.h>
#include <assert.h>
#include <math.h>
#ifndef _MSC_VER
#include <stdint.h>
#else
#include "ms_stdint.h"
#include <malloc.h>
#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(const 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(const 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(const 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(const char *Dots, const int Hgt, const 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(const char *Dots, const int Hgt, const 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(const char Dots[], const int Hgt, const 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(const int nd, const 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":
// Keep clang-tidy happy (as far as UndefinedBinaryOperatorResult warning below at least)
memset(c, 0, GF * sizeof(int));
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[], const int position, const 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[], const int position, const 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[], const int position, const int length) {
int retval = 0;
if (position + 1 < length) {
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[], const int position, const 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[], const int position, const 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[], const int position, const 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[], const int position, const 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[], const int position, const 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[], const int position, const 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[], const int length, const 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[], const 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] = to_int(source + input_position + 2, 2);
array_length++;
codeword_array[array_length] = to_int(source + input_position + 4, 2);
array_length++;
codeword_array[array_length] = to_int(source + input_position + 6, 2);
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] = to_int(source + input_position, 2);
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] = to_int(source + input_position, 2);
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] = to_int(source + input_position, 2);
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] = to_int(source + input_position, 2);
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(const int column, const int row, const int width, const 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(const char dot_stream[], const int width, const 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(const int mask, const int data_length, unsigned char *masked_codeword_array,
const unsigned char *codeword_array, const int ecc_length) {
int weight = 0;
int j;
assert(mask >= 0 && mask <= 3); /* Suppress clang-analyzer taking default branch */
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(const int width, const 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 user_mask;
int dot_stream_length;
int high_score, best_mask;
int binary_finish = 0;
int debug = symbol->debug;
int padding_dots, is_first;
/* Allow up to 4 codewords per input + 2 (FNC) + 4 (ECI) + 2 (special char 1st position) */
int codeword_array_len = length * 4 + 8;
#ifndef _MSC_VER
unsigned char codeword_array[codeword_array_len];
#else
unsigned char *codeword_array = (unsigned char *) _alloca(codeword_array_len);
char *dot_stream;
char *dot_array;
unsigned char *masked_codeword_array;
#endif /* _MSC_VER */
if (symbol->eci > 811799) {
strcpy(symbol->errtxt, "525: Invalid ECI");
return ZINT_ERROR_INVALID_OPTION;
}
user_mask = (symbol->option_3 >> 8) & 0x0F; /* User mask is mask + 1, so >= 1 and <= 8 */
if (user_mask > 8) {
user_mask = 0; /* Ignore */
}
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)) {
if (height > 200 && width > 200) {
sprintf(symbol->errtxt, "526: Symbol size %dx%d (WxH) is too large", width, height);
} else {
sprintf(symbol->errtxt, "528: Symbol %s %d is too large",
width > 200 ? "width" : "height", width > 200 ? width : height);
}
return ZINT_ERROR_INVALID_OPTION;
}
if ((height < 5) || (width < 5)) {
if (height < 5 && width < 5) {
sprintf(symbol->errtxt, "527: Symbol size %dx%d (WxH) is too small", width, height);
} else {
sprintf(symbol->errtxt, "529: Symbol %s %d is too small",
width < 5 ? "width" : "height", width < 5 ? width : height);
}
return ZINT_ERROR_INVALID_OPTION;
}
n_dots = (height * width) / 2;
#ifndef _MSC_VER
char dot_stream[height * width * 3];
char dot_array[width * height];
#else
dot_stream = (char *) _alloca(height * width * 3);
dot_array = (char *) _alloca(width * height);
#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);
#endif /* _MSC_VER */
if (user_mask) {
best_mask = user_mask - 1;
if (debug & ZINT_DEBUG_PRINT) {
printf("Applying mask %d (specified)\n", best_mask);
}
} else {
/* 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 (high_score <= (height * width) / 2) {
if (debug & ZINT_DEBUG_PRINT) {
printf("High score %d <= %d (height * width) / 2\n", high_score, (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;
}
symbol->height = height;
if (!(symbol->output_options & BARCODE_DOTTY_MODE)) {
symbol->output_options += BARCODE_DOTTY_MODE;
}
return 0;
}