#include <assert.h>
#include <math.h>
#include <stdio.h>
#include "common.h"
#include "gs1.h"
#define DC_GF 113
#define DC_SCORE_UNLIT_EDGE -99999
static const unsigned short dc_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
};
static int dc_get_dot(const char Dots[], const int Hgt, const int Wid, const int x, const int y) {
return x >= 0 && x < Wid && y >= 0 && y < Hgt && Dots[(y * Wid) + x] == '1';
}
static int dc_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 (dc_get_dot(Dots, Hgt, Wid, x, y)) {
return 0;
}
}
return 1;
}
static int dc_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 (dc_get_dot(Dots, Hgt, Wid, x, y)) {
return 0;
}
}
return 1;
}
static int dc_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 (dc_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;
}
static int dc_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 (dc_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;
}
static int dc_score_array(const char Dots[], const int Hgt, const int Wid) {
int x, y, worstedge, first, last, sum;
int penalty;
penalty = dc_row_penalty(Dots, Hgt, Wid) + dc_col_penalty(Dots, Hgt, Wid);
sum = 0;
first = -1;
last = -1;
for (x = 0; x < Wid; x += 2) {
if (dc_get_dot(Dots, Hgt, Wid, x, 0)) {
if (first < 0) {
first = x;
}
last = x;
sum++;
}
}
if (sum == 0) {
return DC_SCORE_UNLIT_EDGE;
}
worstedge = sum + last - first;
worstedge *= Hgt;
sum = 0;
first = -1;
last = -1;
for (x = Wid & 1; x < Wid; x += 2) {
if (dc_get_dot(Dots, Hgt, Wid, x, Hgt - 1)) {
if (first < 0) {
first = x;
}
last = x;
sum++;
}
}
if (sum == 0) {
return DC_SCORE_UNLIT_EDGE;
}
sum += last - first;
sum *= Hgt;
if (sum < worstedge) {
worstedge = sum;
}
sum = 0;
first = -1;
last = -1;
for (y = 0; y < Hgt; y += 2) {
if (dc_get_dot(Dots, Hgt, Wid, 0, y)) {
if (first < 0) {
first = y;
}
last = y;
sum++;
}
}
if (sum == 0) {
return DC_SCORE_UNLIT_EDGE;
}
sum += last - first;
sum *= Wid;
if (sum < worstedge) {
worstedge = sum;
}
sum = 0;
first = -1;
last = -1;
for (y = Hgt & 1; y < Hgt; y += 2) {
if (dc_get_dot(Dots, Hgt, Wid, Wid - 1, y)) {
if (first < 0) {
first = y;
}
last = y;
sum++;
}
}
if (sum == 0) {
return DC_SCORE_UNLIT_EDGE;
}
sum += last - first;
sum *= Wid;
if (sum < worstedge) {
worstedge = sum;
}
sum = 0;
for (y = 0; y < Hgt; y++) {
for (x = y & 1; x < Wid; x += 2) {
if (!dc_get_dot(Dots, Hgt, Wid, x - 1, y - 1) && !dc_get_dot(Dots, Hgt, Wid, x + 1, y - 1)
&& !dc_get_dot(Dots, Hgt, Wid, x - 1, y + 1) && !dc_get_dot(Dots, Hgt, Wid, x + 1, y + 1)
&& (!dc_get_dot(Dots, Hgt, Wid, x, y)
|| (!dc_get_dot(Dots, Hgt, Wid, x - 2, y) && !dc_get_dot(Dots, Hgt, Wid, x, y - 2)
&& !dc_get_dot(Dots, Hgt, Wid, x + 2, y) && !dc_get_dot(Dots, Hgt, Wid, x, y + 2)))) {
sum++;
}
}
}
return (worstedge - sum * sum - penalty);
}
static void dc_rsencode(const int nd, const int nc, unsigned char *wd) {
static const char coefs[820 - 5] = {
1, 74, 12, 62,
1, 106, 7, 107, 63,
1, 89, 13, 101, 52, 59,
1, 38, 107, 3, 99, 6, 42,
1, 111, 56, 17, 92, 1, 28, 15,
1, 104, 70, 77, 86, 35, 21, 45, 8,
1, 83, 33, 76, 51, 37, 77, 56, 80, 58,
1, 20, 2, 31, 9, 101, 6, 64, 55, 103, 75,
1, 57, 64, 105, 26, 95, 14, 60, 50, 104, 44, 63,
1, 55, 63, 90, 42, 43, 50, 32, 43, 4, 62, 88, 100,
1, 49, 72, 51, 67, 17, 18, 71, 77, 85, 38, 55, 24, 78,
1, 31, 94, 111, 53, 54, 51, 86, 42, 55, 90, 49, 51, 98, 65,
1, 90, 2, 7, 48, 17, 73, 44, 31, 47, 58, 48, 4, 56, 84, 106,
1, 41, 112, 22, 44, 38, 31, 83, 22, 110, 15, 31, 25, 86, 52, 58, 4,
1, 7, 74, 56, 87, 11, 95, 46, 25, 40, 4, 86, 101, 27, 66, 98, 66, 90,
1, 18, 38, 79, 25, 64, 103, 74, 79, 89, 105, 17, 30, 8, 24, 33, 14, 25, 86,
1, 51, 67, 90, 33, 98, 68, 83, 35, 97, 104, 92, 26, 94, 62, 34, 86, 35, 7, 13,
1, 37, 31, 56, 16, 88, 52, 35, 3, 59, 102, 105, 94, 69, 102, 70, 62, 74, 82, 28, 44,
1, 108, 59, 110, 37, 94, 85, 111, 2, 46, 110, 2, 91, 76, 29, 80, 60, 69, 25, 87, 111, 73,
1, 95, 11, 21, 76, 65, 106, 23, 28, 20, 77, 41, 65, 23, 58, 42, 37, 80, 32, 101, 110, 99,
68,
1, 56, 35, 44, 48, 39, 57, 70, 35, 58, 88, 89, 48, 87, 65, 40, 94, 106, 76, 96, 13, 103,
49, 60,
1, 52, 37, 17, 98, 73, 14, 68, 94, 31, 82, 76, 31, 8, 56, 6, 47, 69, 104, 18, 81, 51,
89, 90, 99,
1, 40, 91, 25, 7, 27, 42, 13, 69, 33, 49, 109, 23, 88, 73, 12, 88, 70, 67, 13, 91, 96,
42, 39, 36, 55,
1, 4, 7, 26, 11, 1, 87, 83, 53, 35, 104, 40, 54, 51, 69, 96, 108, 66, 33, 87, 75, 97,
89, 109, 101, 2, 54,
1, 9, 27, 61, 28, 56, 92, 66, 16, 74, 53, 108, 28, 95, 98, 102, 23, 41, 24, 26, 58, 20,
9, 102, 81, 55, 64, 44,
1, 24, 49, 14, 39, 24, 28, 90, 102, 88, 33, 112, 66, 63, 54, 103, 84, 47, 74, 47, 109, 99,
83, 11, 29, 27, 98, 100, 95,
1, 69, 112, 72, 104, 84, 91, 107, 84, 45, 38, 15, 21, 95, 64, 47, 86, 98, 42, 100, 77, 32,
18, 17, 72, 89, 70, 103, 75, 94,
1, 91, 48, 50, 106, 112, 18, 75, 65, 85, 11, 60, 12, 105, 7, 99, 103, 69, 51, 7, 17, 31,
44, 74, 107, 91, 107, 61, 81, 49, 34,
1, 44, 65, 54, 16, 102, 65, 20, 43, 81, 84, 108, 17, 106, 44, 109, 83, 87, 85, 96, 27, 23,
56, 40, 19, 34, 11, 4, 39, 84, 104, 97,
1, 16, 76, 42, 86, 106, 34, 8, 48, 7, 76, 16, 44, 82, 14, 7, 82, 23, 22, 89, 51, 58,
90, 54, 29, 67, 76, 35, 40, 9, 12, 10, 109,
1, 45, 88, 99, 61, 1, 57, 90, 54, 43, 53, 73, 56, 2, 19, 74, 59, 28, 11, 49, 33, 68,
77, 65, 13, 4, 98, 92, 38, 39, 47, 19, 60, 110,
1, 19, 48, 71, 86, 110, 31, 77, 87, 108, 65, 51, 79, 15, 80, 32, 56, 76, 74, 102, 2, 1,
4, 97, 18, 5, 107, 30, 19, 68, 50, 40, 18, 19, 78,
1, 54, 35, 56, 85, 69, 39, 32, 70, 102, 3, 66, 56, 68, 40, 7, 46, 2, 22, 93, 69, 71,
39, 11, 23, 70, 56, 46, 52, 55, 57, 95, 62, 84, 65, 18,
1, 46, 55, 2, 89, 67, 52, 59, 40, 107, 91, 42, 93, 72, 61, 26, 103, 86, 6, 30, 3, 84,
36, 38, 48, 112, 61, 50, 23, 91, 69, 91, 93, 40, 71, 63, 82,
1, 22, 81, 38, 41, 78, 26, 54, 93, 51, 9, 5, 102, 100, 28, 31, 44, 100, 89, 112, 74, 12,
54, 78, 40, 90, 85, 55, 66, 104, 32, 17, 56, 68, 15, 54, 39, 66,
1, 63, 79, 82, 17, 64, 60, 103, 47, 22, 66, 35, 81, 101, 60, 49, 72, 96, 8, 32, 33, 108,
94, 32, 74, 35, 46, 37, 61, 98, 2, 86, 75, 104, 91, 104, 106, 83, 107,
1, 73, 31, 81, 46, 8, 22, 25, 60, 40, 60, 17, 92, 7, 53, 84, 110, 25, 64, 112, 14, 99,
44, 68, 55, 97, 57, 45, 92, 30, 78, 106, 31, 63, 1, 110, 16, 13, 33, 53,
};
static const short cinds[39 - 2] = {
0, 4, 9, 15, 22, 30, 39, 49, 60, 72, 85, 99, 114, 130, 147, 165, 184, 204, 225, 247, 270, 294,
319, 345, 372, 400, 429, 459, 490, 522, 555, 589, 624, 660, 697, 735, 774,
};
int i, j, k, nw, start, step;
const char *c;
nw = nd + nc;
step = (nw + DC_GF - 2) / (DC_GF - 1);
for (start = 0; start < step; start++) {
const int ND = (nd - start + step - 1) / step;
const int NW = (nw - start + step - 1) / step;
const int NC = NW - ND;
unsigned char *const e = wd + start + ND * step;
c = coefs + cinds[NC - 3];
for (i = 0; i < NC; i++) {
e[i * step] = 0;
}
for (i = 0; i < ND; i++) {
k = (wd[start + i * step] + e[0]) % DC_GF;
for (j = 0; j < NC - 1; j++) {
e[j * step] = (DC_GF - ((c[j + 1] * k) % DC_GF) + e[(j + 1) * step]) % DC_GF;
}
e[(NC - 1) * step] = (DC_GF - ((c[NC] * k) % DC_GF)) % DC_GF;
}
for (i = 0; i < NC; i++) {
if (e[i * step]) {
e[i * step] = DC_GF - e[i * step];
}
}
}
}
static int dc_datum_a(const unsigned char source[], const int length, const int position) {
return position < length && source[position] <= 95;
}
static int dc_datum_b(const unsigned char source[], const int length, const int position) {
if (position < length) {
if (source[position] >= 32 && source[position] <= 127) {
return 1;
}
switch (source[position]) {
case 9:
case 28:
case 29:
case 30:
return 1;
break;
}
if (position + 1 < length && source[position] == 13 && source[position + 1] == 10) {
return 2;
}
}
return 0;
}
static int dc_datum_c(const unsigned char source[], const int length, const int position) {
return z_is_twodigits(source, length, position);
}
static int dc_seventeen_ten(const unsigned char source[], const int length, const int position) {
if (position + 9 < length && source[position] == '1' && source[position + 1] == '7'
&& source[position + 8] == '1' && source[position + 9] == '0'
&& z_cnt_digits(source, length, position + 2, 6) >= 6) {
return 1;
}
return 0;
}
static int dc_ahead_c(const unsigned char source[], const int length, const int position) {
int count = 0;
int i;
for (i = position; (i < length) && dc_datum_c(source, length, i); i += 2) {
count++;
}
return count;
}
static int dc_try_c(const unsigned char source[], const int length, const int position) {
if (position < length && z_isdigit(source[position])) {
const int ahead_c_position = dc_ahead_c(source, length, position);
if (ahead_c_position > dc_ahead_c(source, length, position + 1)) {
return ahead_c_position;
}
}
return 0;
}
static int dc_ahead_a(const unsigned char source[], const int length, const int position) {
int count = 0;
int i;
for (i = position; i < length && dc_datum_a(source, length, i) && dc_try_c(source, length, i) < 2; i++) {
count++;
}
return count;
}
static int dc_ahead_b(const unsigned char source[], const int length, const int position, int *p_nx) {
int count = 0;
int i, incr;
for (i = position; i < length && (incr = dc_datum_b(source, length, i)) && dc_try_c(source, length, i) < 2;
i += incr) {
count++;
}
if (p_nx != NULL) {
*p_nx = count;
}
return i - position;
}
static int dc_binary(const unsigned char source[], const int length, const int position) {
return position < length && source[position] >= 128;
}
static int dc_empty_bin_buf(unsigned char *codeword_array, int ap, uint64_t *p_bin_buf, int *p_bin_buf_size) {
int i;
int lawrencium[6];
uint64_t bin_buf = *p_bin_buf;
int bin_buf_size = *p_bin_buf_size;
if (bin_buf_size) {
for (i = 0; i < bin_buf_size + 1; i++) {
lawrencium[i] = (int) (bin_buf % 103);
bin_buf /= 103;
}
for (i = 0; i < bin_buf_size + 1; i++) {
codeword_array[ap++] = lawrencium[bin_buf_size - i];
}
}
*p_bin_buf = 0;
*p_bin_buf_size = 0;
return ap;
}
static int dc_append_to_bin_buf(unsigned char *codeword_array, int ap, unsigned int val, uint64_t *p_bin_buf,
int *p_bin_buf_size) {
*p_bin_buf *= 259;
*p_bin_buf += val;
(*p_bin_buf_size)++;
if (*p_bin_buf_size == 5) {
ap = dc_empty_bin_buf(codeword_array, ap, p_bin_buf, p_bin_buf_size);
}
return ap;
}
static int dc_encode_message(struct zint_symbol *symbol, const unsigned char source[], const int length,
const int eci, const int last_seg, const int last_EOT, const int last_RSEOT,
int ap, unsigned char *codeword_array, char *p_encoding_mode, int *p_inside_macro,
uint64_t *p_bin_buf, int *p_bin_buf_size, unsigned char structapp_array[], int *p_structapp_size) {
static const char lead_specials[] = "\x09\x1C\x1D\x1E";
int i;
int position = 0;
char encoding_mode = *p_encoding_mode;
int inside_macro = *p_inside_macro;
uint64_t bin_buf = *p_bin_buf;
int bin_buf_size = *p_bin_buf_size;
int nx;
const int first_seg = ap == 0;
const int gs1 = (symbol->input_mode & 0x07) == GS1_MODE;
const int debug_print = (symbol->debug & ZINT_DEBUG_PRINT);
if (first_seg) {
if (symbol->output_options & READER_INIT) {
codeword_array[ap++] = 109;
} else if (!gs1 && eci == 0 && length > 2 && z_is_twodigits(source, length, 0)) {
codeword_array[ap++] = 107;
} else if (z_posn(lead_specials, source[0]) != -1) {
codeword_array[ap++] = 101;
codeword_array[ap++] = source[0] + 64;
encoding_mode = 'A';
position++;
} else if (length > 5) {
if (source[0] == '[' && source[1] == ')' && source[2] == '>' && source[3] == 30 && last_EOT) {
const int format_050612 = (source[4] == '0' && (source[5] == '5' || source[5] == '6'))
|| (source[4] == '1' && source[5] == '2');
inside_macro = 0;
if (length > 6 && format_050612 && source[6] == 29 && last_RSEOT) {
if (source[5] == '5') {
inside_macro = 97;
} else if (source[5] == '6') {
inside_macro = 98;
} else {
inside_macro = 99;
}
} else if (!format_050612 && z_is_twodigits(source, length, 4) ) {
inside_macro = 100;
}
if (inside_macro) {
codeword_array[ap++] = 106;
encoding_mode = 'B';
codeword_array[ap++] = inside_macro;
if (inside_macro == 100) {
codeword_array[ap++] = z_ctoi(source[4]) + 16;
codeword_array[ap++] = z_ctoi(source[5]) + 16;
position += 6;
} else {
position += 7;
}
if (debug_print) printf("C1/%d ", inside_macro - 96);
}
}
}
}
if (eci > 0) {
if (encoding_mode == 'X') {
if (eci <= 0xFF) {
ap = dc_append_to_bin_buf(codeword_array, ap, 256, &bin_buf, &bin_buf_size);
ap = dc_append_to_bin_buf(codeword_array, ap, eci, &bin_buf, &bin_buf_size);
} else if (eci <= 0xFFFF) {
ap = dc_append_to_bin_buf(codeword_array, ap, 257, &bin_buf, &bin_buf_size);
ap = dc_append_to_bin_buf(codeword_array, ap, eci >> 8, &bin_buf, &bin_buf_size);
ap = dc_append_to_bin_buf(codeword_array, ap, eci & 0xFF, &bin_buf, &bin_buf_size);
} else {
ap = dc_append_to_bin_buf(codeword_array, ap, 258, &bin_buf, &bin_buf_size);
ap = dc_append_to_bin_buf(codeword_array, ap, eci >> 16, &bin_buf, &bin_buf_size);
ap = dc_append_to_bin_buf(codeword_array, ap, (eci >> 8) & 0xFF, &bin_buf, &bin_buf_size);
ap = dc_append_to_bin_buf(codeword_array, ap, eci & 0xFF, &bin_buf, &bin_buf_size);
}
} else {
codeword_array[ap++] = 108;
if (eci <= 39) {
codeword_array[ap++] = eci;
} else {
int a, b, c;
a = (eci - 40) / 12769;
b = ((eci - 40) - (12769 * a)) / 113;
c = (eci - 40) - (12769 * a) - (113 * b);
codeword_array[ap++] = a + 40;
codeword_array[ap++] = b;
codeword_array[ap++] = c;
}
}
}
while (position < length) {
if (last_seg && position == length - 2 && inside_macro != 0 && inside_macro != 100) {
position += 2;
if (debug_print) fputs("A ", stdout);
continue;
}
if (last_seg && position == length - 1 && inside_macro == 100) {
position++;
if (debug_print) fputs("B ", stdout);
continue;
}
if (encoding_mode == 'C') {
if (dc_seventeen_ten(source, length, position)) {
codeword_array[ap++] = 100;
codeword_array[ap++] = z_to_int(source + position + 2, 2);
codeword_array[ap++] = z_to_int(source + position + 4, 2);
codeword_array[ap++] = z_to_int(source + position + 6, 2);
position += 10;
if (debug_print) fputs("C2/1 ", stdout);
continue;
}
if (dc_datum_c(source, length, position) || (gs1 && source[position] == '\x1D')) {
if (source[position] == '\x1D') {
codeword_array[ap++] = 107;
position++;
} else {
codeword_array[ap++] = z_to_int(source + position, 2);
position += 2;
}
if (debug_print) fputs("C2/2 ", stdout);
continue;
}
if (dc_binary(source, length, position)) {
if (position + 1 < length && z_isdigit(source[position + 1])) {
if (source[position] - 128 < 32) {
codeword_array[ap++] = 110;
codeword_array[ap++] = source[position] - 128 + 64;
} else {
codeword_array[ap++] = 111;
codeword_array[ap++] = source[position] - 128 - 32;
}
position++;
} else {
codeword_array[ap++] = 112;
encoding_mode = 'X';
}
if (debug_print) fputs("C3 ", stdout);
continue;
}
{
const int m = dc_ahead_a(source, length, position);
const int n = dc_ahead_b(source, length, position, &nx);
if (m > n) {
codeword_array[ap++] = 101;
encoding_mode = 'A';
} else {
if (nx >= 1 && nx <= 4) {
codeword_array[ap++] = 101 + nx;
for (i = 0; i < nx; i++) {
if (source[position] >= 32) {
codeword_array[ap++] = source[position] - 32;
} else if (source[position] == 13) {
codeword_array[ap++] = 96;
position++;
} else {
switch (source[position]) {
case 9: codeword_array[ap++] = 97; break;
case 28: codeword_array[ap++] = 98; break;
case 29: codeword_array[ap++] = 99; break;
case 30: codeword_array[ap++] = 100; break;
}
}
position++;
}
} else {
codeword_array[ap++] = 106;
encoding_mode = 'B';
}
}
if (debug_print) fputs("C4 ", stdout);
continue;
}
}
if (encoding_mode == 'B') {
const int n = dc_try_c(source, length, position);
if (n >= 2) {
if (n <= 4) {
codeword_array[ap++] = 103 + (n - 2);
for (i = 0; i < n; i++) {
codeword_array[ap++] = z_to_int(source + position, 2);
position += 2;
}
} else {
codeword_array[ap++] = 106;
encoding_mode = 'C';
}
if (debug_print) fputs("D1 ", stdout);
continue;
}
if (gs1 && source[position] == '\x1D') {
codeword_array[ap++] = 107;
position++;
if (debug_print) fputs("D2/1 ", stdout);
continue;
}
if (dc_datum_b(source, length, position)) {
int done = 0;
if (source[position] >= 32 && source[position] <= 127) {
codeword_array[ap++] = source[position] - 32;
done = 1;
} else if (source[position] == 13) {
codeword_array[ap++] = 96;
position++;
done = 1;
} else if (!first_seg || position != 0) {
switch (source[position]) {
case 9: codeword_array[ap++] = 97; break;
case 28: codeword_array[ap++] = 98; break;
case 29: codeword_array[ap++] = 99; break;
case 30: codeword_array[ap++] = 100; break;
}
done = 1;
}
if (done == 1) {
position++;
if (debug_print) fputs("D2/2 ", stdout);
continue;
}
}
if (dc_binary(source, length, position)) {
if (dc_datum_b(source, length, position + 1)) {
if (source[position] - 128 < 32) {
codeword_array[ap++] = 110;
codeword_array[ap++] = source[position] - 128 + 64;
} else {
codeword_array[ap++] = 111;
codeword_array[ap++] = source[position] - 128 - 32;
}
position++;
} else {
codeword_array[ap++] = 112;
encoding_mode = 'X';
}
if (debug_print) fputs("D3 ", stdout);
continue;
}
if (dc_ahead_a(source, length, position) == 1) {
codeword_array[ap++] = 101;
if (source[position] < 32) {
codeword_array[ap++] = source[position] + 64;
} else {
codeword_array[ap++] = source[position] - 32;
}
position++;
} else {
codeword_array[ap++] = 102;
encoding_mode = 'A';
}
if (debug_print) fputs("D4 ", stdout);
continue;
}
if (encoding_mode == 'A') {
const int n = dc_try_c(source, length, position);
if (n >= 2) {
if (n <= 4) {
codeword_array[ap++] = 103 + (n - 2);
for (i = 0; i < n; i++) {
codeword_array[ap++] = z_to_int(source + position, 2);
position += 2;
}
} else {
codeword_array[ap++] = 106;
encoding_mode = 'C';
}
if (debug_print) fputs("E1 ", stdout);
continue;
}
if (gs1 && source[position] == '\x1D') {
codeword_array[ap++] = 107;
position++;
if (debug_print) fputs("E2/1 ", stdout);
continue;
}
if (dc_datum_a(source, length, position)) {
if (source[position] < 32) {
codeword_array[ap++] = source[position] + 64;
} else {
codeword_array[ap++] = source[position] - 32;
}
position++;
if (debug_print) fputs("E2/2 ", stdout);
continue;
}
if (dc_binary(source, length, position)) {
if (dc_datum_a(source, length, position + 1)) {
if (source[position] - 128 < 32) {
codeword_array[ap++] = 110;
codeword_array[ap++] = source[position] - 128 + 64;
} else {
codeword_array[ap++] = 111;
codeword_array[ap++] = source[position] - 128 - 32;
}
position++;
} else {
codeword_array[ap++] = 112;
encoding_mode = 'X';
}
if (debug_print) fputs("E3 ", stdout);
continue;
}
dc_ahead_b(source, length, position, &nx);
if (nx >= 1 && nx <= 6) {
codeword_array[ap++] = 95 + nx;
for (i = 0; i < nx; i++) {
if (source[position] >= 32) {
codeword_array[ap++] = source[position] - 32;
} else if (source[position] == 13) {
codeword_array[ap++] = 96;
position++;
} else {
switch (source[position]) {
case 9: codeword_array[ap++] = 97; break;
case 28: codeword_array[ap++] = 98; break;
case 29: codeword_array[ap++] = 99; break;
case 30: codeword_array[ap++] = 100; break;
}
}
position++;
}
} else {
codeword_array[ap++] = 102;
encoding_mode = 'B';
}
if (debug_print) fputs("E4 ", stdout);
continue;
}
if (encoding_mode == 'X') {
const int n = dc_try_c(source, length, position);
if (n >= 2) {
ap = dc_empty_bin_buf(codeword_array, ap, &bin_buf, &bin_buf_size);
if (n <= 7) {
codeword_array[ap++] = 101 + n;
for (i = 0; i < n; i++) {
codeword_array[ap++] = z_to_int(source + position, 2);
position += 2;
}
} else {
codeword_array[ap++] = 111;
encoding_mode = 'C';
}
if (debug_print) fputs("F1 ", stdout);
continue;
}
if (dc_binary(source, length, position)
|| dc_binary(source, length, position + 1)
|| dc_binary(source, length, position + 2)
|| dc_binary(source, length, position + 3)) {
ap = dc_append_to_bin_buf(codeword_array, ap, source[position], &bin_buf, &bin_buf_size);
position++;
if (debug_print) fputs("F2 ", stdout);
continue;
}
ap = dc_empty_bin_buf(codeword_array, ap, &bin_buf, &bin_buf_size);
if (dc_ahead_a(source, length, position) > dc_ahead_b(source, length, position, NULL)) {
codeword_array[ap++] = 109;
encoding_mode = 'A';
} else {
codeword_array[ap++] = 110;
encoding_mode = 'B';
}
if (debug_print) fputs("F3 ", stdout);
}
}
if (last_seg) {
if (encoding_mode == 'X' && bin_buf_size != 0) {
ap = dc_empty_bin_buf(codeword_array, ap, &bin_buf, &bin_buf_size);
}
if (symbol->structapp.count) {
int sp = 0;
if (encoding_mode == 'C') {
structapp_array[sp++] = 101;
} else if (encoding_mode == 'X') {
structapp_array[sp++] = 109;
}
if (symbol->structapp.index < 10) {
structapp_array[sp++] = 16 + symbol->structapp.index;
} else {
structapp_array[sp++] = 33 + symbol->structapp.index - 10;
}
if (symbol->structapp.count < 10) {
structapp_array[sp++] = 16 + symbol->structapp.count;
} else {
structapp_array[sp++] = 33 + symbol->structapp.count - 10;
}
structapp_array[sp++] = 108;
*p_structapp_size = sp;
}
}
if (debug_print) {
fputc('\n', stdout);
}
*p_encoding_mode = encoding_mode;
*p_inside_macro = inside_macro;
*p_bin_buf = bin_buf;
*p_bin_buf_size = bin_buf_size;
return ap;
}
static void dc_encode_message_segs(struct zint_symbol *symbol, const struct zint_seg segs[], const int seg_count,
unsigned char *codeword_array, int *p_binary_finish, int *p_data_length, unsigned char structapp_array[],
int *p_structapp_size) {
int i;
int last_EOT = 0;
int last_RSEOT = 0;
int ap = 0;
char encoding_mode = 'C';
int inside_macro = 0;
uint64_t bin_buf = 0;
int bin_buf_size = 0;
const int content_segs = (symbol->input_mode & 0x07) != GS1_MODE
&& (symbol->output_options & BARCODE_CONTENT_SEGS);
const struct zint_seg *last_seg = &segs[seg_count - 1];
last_EOT = last_seg->source[last_seg->length - 1] == 4;
if (last_EOT && last_seg->length > 1) {
last_RSEOT = last_seg->source[last_seg->length - 2] == 30;
}
for (i = 0; i < seg_count; i++) {
ap = dc_encode_message(symbol, segs[i].source, segs[i].length, segs[i].eci, i == seg_count - 1 ,
last_EOT, last_RSEOT, ap, codeword_array, &encoding_mode, &inside_macro, &bin_buf, &bin_buf_size,
structapp_array, p_structapp_size);
if (content_segs && segs[i].eci) {
z_ct_set_seg_eci(symbol, i, segs[i].eci);
}
}
*p_binary_finish = encoding_mode == 'X';
*p_data_length = ap + *p_structapp_size;
}
static int dc_make_dotstream(const unsigned char masked_array[], const int array_length, char dot_stream[]) {
int i;
int bp = 0;
bp = z_bin_append_posn(masked_array[0], 2, dot_stream, bp);
for (i = 1; i < array_length; i++) {
bp = z_bin_append_posn(dc_dot_patterns[masked_array[i]], 9, dot_stream, bp);
}
return bp;
}
static int dc_is_corner(const int column, const int row, const int width, const int height) {
if (column == 0 && row == 0) {
return 1;
}
if (height & 1) {
if ((column == width - 2 && row == 0) || (column == width - 1 && row == 1)) {
return 1;
}
} else {
if (column == width - 1 && row == 0) {
return 1;
}
}
if (height & 1) {
if (column == 0 && row == height - 1) {
return 1;
}
} else {
if ((column == 0 && row == height - 2) || (column == 1 && row == height - 1)) {
return 1;
}
}
if ((column == width - 2 && row == height - 1) || (column == width - 1 && row == height - 2)) {
return 1;
}
return 0;
}
static void dc_fold_dotstream(const char dot_stream[], const int width, const int height, char dot_array[]) {
int column, row;
int position = 0;
if (height & 1) {
for (row = 0; row < height; row++) {
for (column = 0; column < width; column++) {
if (!((column + row) & 1)) {
if (dc_is_corner(column, row, width, height)) {
dot_array[(row * width) + column] = 'C';
} else {
dot_array[((height - row - 1) * width) + column] = dot_stream[position++];
}
} else {
dot_array[((height - row - 1) * width) + column] = ' ';
}
}
}
dot_array[width - 2] = dot_stream[position++];
dot_array[(height * width) - 2] = dot_stream[position++];
dot_array[(width * 2) - 1] = dot_stream[position++];
dot_array[((height - 1) * width) - 1] = dot_stream[position++];
dot_array[0] = dot_stream[position++];
dot_array[(height - 1) * width] = dot_stream[position];
} else {
for (column = 0; column < width; column++) {
for (row = 0; row < height; row++) {
if (!((column + row) & 1)) {
if (dc_is_corner(column, row, width, height)) {
dot_array[(row * width) + column] = 'C';
} else {
dot_array[(row * width) + column] = dot_stream[position++];
}
} else {
dot_array[(row * width) + column] = ' ';
}
}
}
dot_array[((height - 1) * width) - 1] = dot_stream[position++];
dot_array[(height - 2) * width] = dot_stream[position++];
dot_array[(height * width) - 2] = dot_stream[position++];
dot_array[((height - 1) * width) + 1] = dot_stream[position++];
dot_array[width - 1] = dot_stream[position++];
dot_array[0] = dot_stream[position];
}
}
static void dc_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);
assert(data_length > 0);
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;
}
dc_rsencode(data_length + 1, ecc_length, masked_codeword_array);
}
static void dc_force_corners(const int width, const int height, char *dot_array) {
if (width & 1) {
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 {
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 zint_dotcode(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
int warn_number = 0;
int i, j, k;
int jc, n_dots;
int data_length, ecc_length;
int min_dots, min_area;
int height, width = 0;
int mask_score[8];
int user_mask;
int dot_stream_length;
int high_score, best_mask;
int binary_finish = 0;
unsigned char structapp_array[5];
int structapp_size = 0;
int padding_dots;
const int gs1 = (symbol->input_mode & 0x07) == GS1_MODE;
const int debug_print = (symbol->debug & ZINT_DEBUG_PRINT);
const int codeword_array_len = z_segs_length(segs, seg_count) * 4 + 2 + seg_count * 4 + 2 + 5 + 52;
unsigned char *codeword_array = (unsigned char *) z_alloca(codeword_array_len);
char *dot_stream;
char *dot_array;
unsigned char *masked_codeword_array;
if (symbol->eci > 811799) {
return z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 525, "ECI code '%d' out of range (0 to 811799)",
symbol->eci);
}
if (symbol->option_2 > 0) {
if (symbol->option_2 < 5 || symbol->option_2 > 200) {
return ZEXT z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 527,
"Number of columns '%d' is out of range (5 to 200)", symbol->option_2);
}
width = symbol->option_2;
}
user_mask = (symbol->option_3 >> 8) & 0x0F;
if (user_mask > 8) {
user_mask = 0;
}
if (symbol->structapp.count) {
if (symbol->structapp.count < 2 || symbol->structapp.count > 35) {
return z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 730,
"Structured Append count '%d' out of range (2 to 35)", symbol->structapp.count);
}
if (symbol->structapp.index < 1 || symbol->structapp.index > symbol->structapp.count) {
return ZEXT z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 731,
"Structured Append index '%1$d' out of range (1 to count %2$d)",
symbol->structapp.index, symbol->structapp.count);
}
if (symbol->structapp.id[0]) {
return z_errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 732, "Structured Append ID not available for DotCode");
}
}
if (gs1) {
for (i = 0; i < seg_count; i++) {
if (segs[i].eci) {
warn_number = z_errtxt(ZINT_WARN_NONCOMPLIANT, symbol, 733,
"Using ECI in GS1 mode not supported by GS1 standards");
break;
}
}
if (warn_number == 0 && symbol->structapp.count) {
warn_number = z_errtxt(ZINT_WARN_NONCOMPLIANT, symbol, 734,
"Using Structured Append in GS1 mode not supported by GS1 standards");
}
}
dc_encode_message_segs(symbol, segs, seg_count, codeword_array, &binary_finish, &data_length, structapp_array,
&structapp_size);
assert(data_length > 0);
ecc_length = 3 + (data_length / 2);
min_dots = 9 * (data_length + 3 + (data_length / 2)) + 2;
min_area = min_dots * 2;
if (width == 0) {
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 {
if ((height = (min_area + (width - 1)) / width) < 5) {
height = 5;
}
if (!((width + height) & 1)) {
height++;
}
}
if (debug_print) {
printf("Width %d, Height %d\n", width, height);
}
if (height > 200 || width > 200) {
if (height > 200 && width > 200) {
ZEXT z_errtxtf(0, symbol, 526, "Resulting symbol size '%1$dx%2$d' (HxW) is too large (maximum 200x200)",
height, width);
} else if (width > 200) {
z_errtxtf(0, symbol, 528, "Resulting symbol width '%d' is too large (maximum 200)", width);
} else {
z_errtxtf(0, symbol, 735, "Resulting symbol height '%d' is too large (maximum 200)", height);
}
return ZINT_ERROR_INVALID_OPTION;
}
if (height < 5 || width < 5) {
assert(height >= 5 || width >= 5);
if (width < 5) {
z_errtxtf(0, symbol, 529, "Resulting symbol width '%d' is too small (minimum 5)", width);
} else {
z_errtxtf(0, symbol, 736, "Resulting symbol height '%d' is too small (minimum 5)", height);
}
return ZINT_ERROR_INVALID_OPTION;
}
n_dots = (height * width) / 2;
dot_stream = (char *) z_alloca(height * width * 3);
dot_array = (char *) z_alloca(width * height);
padding_dots = n_dots - min_dots;
if (debug_print) printf("Padding dots: %d\n", padding_dots);
if (padding_dots >= 9) {
int is_first = 1;
int padp = data_length - structapp_size;
while (padding_dots >= 9) {
if (padding_dots < 18 && (data_length & 1) == 0) {
padding_dots -= 9;
} else if (padding_dots >= 18) {
if ((data_length & 1) == 0) {
padding_dots -= 9;
} else {
padding_dots -= 18;
}
} else {
break;
}
if (is_first && binary_finish) {
codeword_array[padp++] = 109;
} else {
codeword_array[padp++] = 106;
}
data_length++;
is_first = 0;
}
if (structapp_size) {
if (structapp_array[0] == 109) {
structapp_array[0] = 106;
}
for (i = 0; i < structapp_size; i++) {
codeword_array[padp++] = structapp_array[i];
}
}
} else if (structapp_size) {
data_length -= structapp_size;
for (i = 0; i < structapp_size; i++) {
codeword_array[data_length++] = structapp_array[i];
}
}
if (debug_print) {
printf("Codeword length %d, ECC length %d\n", data_length, ecc_length);
fputs("Codewords:", stdout);
for (i = 0; i < data_length; i++) {
printf(" %d", codeword_array[i]);
}
fputc('\n', stdout);
}
#ifdef ZINT_TEST
if (symbol->debug & ZINT_DEBUG_TEST) {
z_debug_test_codeword_dump(symbol, codeword_array, data_length);
}
#endif
ecc_length = 3 + (data_length / 2);
masked_codeword_array = (unsigned char *) z_alloca(data_length + 1 + ecc_length);
if (user_mask) {
best_mask = user_mask - 1;
if (debug_print) {
printf("Applying mask %d (specified)\n", best_mask);
}
} else {
for (i = 0; i < 4; i++) {
dc_apply_mask(i, data_length, masked_codeword_array, codeword_array, ecc_length);
dot_stream_length = dc_make_dotstream(masked_codeword_array, (data_length + ecc_length + 1), dot_stream);
for (jc = dot_stream_length; jc < n_dots; jc++) {
dot_stream[dot_stream_length++] = '1';
}
dc_fold_dotstream(dot_stream, width, height, dot_array);
mask_score[i] = dc_score_array(dot_array, height, width);
if (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;
}
}
if (high_score <= (height * width) / 2) {
if (debug_print) {
printf("High score %d <= %d (height * width) / 2\n", high_score, (height * width) / 2);
}
for (i = 0; i < 4; i++) {
dc_apply_mask(i, data_length, masked_codeword_array, codeword_array, ecc_length);
dot_stream_length = dc_make_dotstream(masked_codeword_array, (data_length + ecc_length + 1),
dot_stream);
for (jc = dot_stream_length; jc < n_dots; jc++) {
dot_stream[dot_stream_length++] = '1';
}
dc_fold_dotstream(dot_stream, width, height, dot_array);
dc_force_corners(width, height, dot_array);
mask_score[i + 4] = dc_score_array(dot_array, height, width);
if (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_print) {
printf("Applying mask %d, high_score %d\n", best_mask, high_score);
}
}
dc_apply_mask(best_mask % 4, data_length, masked_codeword_array, codeword_array, ecc_length);
symbol->option_2 = width;
symbol->option_3 = (symbol->option_3 & 0xFF) | ((best_mask + 1) << 8);
if (debug_print) {
printf("Masked codewords (%d):", data_length);
for (i = 1; i < data_length + 1; i++) {
printf(" [%d]", masked_codeword_array[i]);
}
fputc('\n', stdout);
printf("Masked ECCs (%d):", ecc_length);
for (i = data_length + 1; i < data_length + ecc_length + 1; i++) {
printf(" [%d]", masked_codeword_array[i]);
}
fputc('\n', stdout);
}
dot_stream_length = dc_make_dotstream(masked_codeword_array, (data_length + ecc_length + 1), dot_stream);
for (jc = dot_stream_length; jc < n_dots; jc++) {
dot_stream[dot_stream_length++] = '1';
}
if (debug_print) printf("Binary (%d): %.*s\n", dot_stream_length, dot_stream_length, dot_stream);
dc_fold_dotstream(dot_stream, width, height, dot_array);
if (best_mask >= 4) {
dc_force_corners(width, height, dot_array);
}
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') {
z_set_module(symbol, k, j);
}
}
symbol->row_height[k] = 1;
}
symbol->height = height;
symbol->output_options |= BARCODE_DOTTY_MODE;
return warn_number;
}