#include <assert.h>
#include <stdio.h>
#include "common.h"
#include "code128.h"
#include "gs1.h"
#define C128_SYMBOL_MAX 102
#define C128_SYMBOL_MAX_S "102"
#define C128_VALUES_MAX (C128_SYMBOL_MAX + 2)
INTERNAL_DATA const char zint_C128Table[107][6] = {
{'2','1','2','2','2','2'}, {'2','2','2','1','2','2'}, {'2','2','2','2','2','1'}, {'1','2','1','2','2','3'},
{'1','2','1','3','2','2'}, {'1','3','1','2','2','2'}, {'1','2','2','2','1','3'}, {'1','2','2','3','1','2'},
{'1','3','2','2','1','2'}, {'2','2','1','2','1','3'}, {'2','2','1','3','1','2'}, {'2','3','1','2','1','2'},
{'1','1','2','2','3','2'}, {'1','2','2','1','3','2'}, {'1','2','2','2','3','1'}, {'1','1','3','2','2','2'},
{'1','2','3','1','2','2'}, {'1','2','3','2','2','1'}, {'2','2','3','2','1','1'}, {'2','2','1','1','3','2'},
{'2','2','1','2','3','1'}, {'2','1','3','2','1','2'}, {'2','2','3','1','1','2'}, {'3','1','2','1','3','1'},
{'3','1','1','2','2','2'}, {'3','2','1','1','2','2'}, {'3','2','1','2','2','1'}, {'3','1','2','2','1','2'},
{'3','2','2','1','1','2'}, {'3','2','2','2','1','1'}, {'2','1','2','1','2','3'}, {'2','1','2','3','2','1'},
{'2','3','2','1','2','1'}, {'1','1','1','3','2','3'}, {'1','3','1','1','2','3'}, {'1','3','1','3','2','1'},
{'1','1','2','3','1','3'}, {'1','3','2','1','1','3'}, {'1','3','2','3','1','1'}, {'2','1','1','3','1','3'},
{'2','3','1','1','1','3'}, {'2','3','1','3','1','1'}, {'1','1','2','1','3','3'}, {'1','1','2','3','3','1'},
{'1','3','2','1','3','1'}, {'1','1','3','1','2','3'}, {'1','1','3','3','2','1'}, {'1','3','3','1','2','1'},
{'3','1','3','1','2','1'}, {'2','1','1','3','3','1'}, {'2','3','1','1','3','1'}, {'2','1','3','1','1','3'},
{'2','1','3','3','1','1'}, {'2','1','3','1','3','1'}, {'3','1','1','1','2','3'}, {'3','1','1','3','2','1'},
{'3','3','1','1','2','1'}, {'3','1','2','1','1','3'}, {'3','1','2','3','1','1'}, {'3','3','2','1','1','1'},
{'3','1','4','1','1','1'}, {'2','2','1','4','1','1'}, {'4','3','1','1','1','1'}, {'1','1','1','2','2','4'},
{'1','1','1','4','2','2'}, {'1','2','1','1','2','4'}, {'1','2','1','4','2','1'}, {'1','4','1','1','2','2'},
{'1','4','1','2','2','1'}, {'1','1','2','2','1','4'}, {'1','1','2','4','1','2'}, {'1','2','2','1','1','4'},
{'1','2','2','4','1','1'}, {'1','4','2','1','1','2'}, {'1','4','2','2','1','1'}, {'2','4','1','2','1','1'},
{'2','2','1','1','1','4'}, {'4','1','3','1','1','1'}, {'2','4','1','1','1','2'}, {'1','3','4','1','1','1'},
{'1','1','1','2','4','2'}, {'1','2','1','1','4','2'}, {'1','2','1','2','4','1'}, {'1','1','4','2','1','2'},
{'1','2','4','1','1','2'}, {'1','2','4','2','1','1'}, {'4','1','1','2','1','2'}, {'4','2','1','1','1','2'},
{'4','2','1','2','1','1'}, {'2','1','2','1','4','1'}, {'2','1','4','1','2','1'}, {'4','1','2','1','2','1'},
{'1','1','1','1','4','3'}, {'1','1','1','3','4','1'}, {'1','3','1','1','4','1'}, {'1','1','4','1','1','3'},
{'1','1','4','3','1','1'}, {'4','1','1','1','1','3'}, {'4','1','1','3','1','1'}, {'1','1','3','1','4','1'},
{'1','1','4','1','3','1'}, {'3','1','1','1','4','1'}, {'4','1','1','1','3','1'}, {'2','1','1','4','1','2'},
{'2','1','1','2','1','4'}, {'2','1','1','2','3','2'}, { '2','1','1','1','3','3'}
};
#define C128_A0 1
#define C128_B0 2
#define C128_A1 3
#define C128_B1 4
#define C128_C0 5
#define C128_C1 6
#define C128_STATES 7
#define C128_A0B0(cset) ((cset) <= C128_B0)
#define C128_C0C1(cset) ((cset) >= C128_C0)
#define C128_A0A1(cset) ((cset) & 1)
#define C128_AB(cset) ((cset) >> ((cset) > C128_B0))
static const char c128_latch_seq[C128_STATES][C128_STATES][3] = {
{ {0} },
{ {0}, { 0 }, {100 }, {101,101 }, {100,100,100}, { 99 }, {101,101, 99} },
{ {0}, {101 }, { 0 }, {101,101,101}, {100,100 }, { 99 }, {100,100, 99} },
{ {0}, {101,101 }, {100,100,100}, { 0 }, {100 }, {101,101, 99}, { 99 } },
{ {0}, {101,101,101}, {100,100 }, {101 }, { 0 }, {100,100, 99}, { 99 } },
{ {0}, {101 }, {100 }, {101,101,101}, {100,100,100}, { 0 }, { 0 } },
{ {0}, {101,101,101}, {100,100,100}, {101 }, {100 }, { 0 }, { 0 } },
};
static const char c128_latch_len[C128_STATES][C128_STATES] = {
{ 0 },
{ 0, 0, 1, 2, 3, 1, 3 },
{ 0, 1, 0, 3, 2, 1, 3 },
{ 0, 2, 3, 0, 1, 3, 1 },
{ 0, 3, 2, 1, 0, 3, 1 },
{ 0, 1, 1, 3, 3, 0, 64 },
{ 0, 3, 3, 1, 1, 64, 0 },
};
static const char c128_start_latch_seq[3][C128_STATES][4] = {
{ {0}, {103 }, {104 }, {103,101,101 }, {104,100,100 }, {105 } },
{ {0}, {103,102}, {104,102}, {103,102,101,101}, {104,102,100,100}, {105,102 } },
{ {0}, {103, 96}, {104, 96}, {103, 96,101,101}, {104, 96,100,100}, {104, 96,99} },
};
static const char c128_start_latch_len[3][C128_STATES] = {
{ 0, 1, 1, 3, 3, 1, 64 },
{ 0, 2, 2, 4, 4, 2, 64 },
{ 0, 2, 2, 4, 4, 3, 64 },
};
static int c128_cost_ab(const int cset, const unsigned char ch, int *p_mode) {
const unsigned char mask_0x60 = ch & 0x60;
const int ga = C128_A0A1(cset);
int cost = 1;
assert(!C128_C0C1(cset));
if ((ga && mask_0x60 == 0x60) || (!ga && !mask_0x60)) {
cost++;
*p_mode |= 0x10;
}
if (C128_A0B0(cset) == !z_isascii(ch)) {
cost++;
*p_mode |= 0x20;
}
return cost;
}
static int c128_cost(const unsigned char source[], const int length, const int i, const int prior_cset,
const int start_idx, const char priority[C128_STATES], const char fncs[C128_MAX],
const char manuals[C128_MAX], short (*costs)[C128_STATES], char (*modes)[C128_STATES]) {
const unsigned char ch = source[i];
const char *const latch_len = prior_cset == 0 ? c128_start_latch_len[start_idx] : c128_latch_len[prior_cset];
const int is_fnc1 = ch == '\x1D' && fncs[i];
const int can_c = is_fnc1 || (z_isdigit(ch) && z_isdigit(source[i + 1]));
const int manual_c_fail = !can_c && manuals[i] == C128_C0;
int min_cost = 999999;
int min_mode = 0;
int p;
for (p = 0; priority[p]; p++) {
const int cset = priority[p];
if (C128_C0C1(cset)) {
if (can_c && (!manuals[i] || manuals[i] == C128_C0)) {
const int incr = 2 - is_fnc1;
int mode = prior_cset;
int cost = 1;
if (prior_cset != cset) {
cost += latch_len[cset];
mode = cset;
}
if (i + incr < length) {
if (costs[i + incr][cset]) {
cost += costs[i + incr][cset];
} else {
cost += c128_cost(source, length, i + incr, cset, 0 , priority, fncs, manuals,
costs, modes);
}
}
if (cost < min_cost) {
min_cost = cost;
min_mode = mode;
}
}
} else {
if (!manuals[i] || manuals[i] == C128_AB(cset) || manual_c_fail) {
int mode = cset;
int cost = is_fnc1 ? 1 : c128_cost_ab(cset, ch, &mode);
if (prior_cset != cset) {
cost += latch_len[cset];
}
if (i + 1 < length) {
if (costs[i + 1][cset]) {
cost += costs[i + 1][cset];
} else {
cost += c128_cost(source, length, i + 1, cset, 0 , priority, fncs, manuals,
costs, modes);
}
}
if (cost < min_cost) {
min_cost = cost;
min_mode = mode;
}
}
}
}
assert(min_cost != 999999);
costs[i][prior_cset] = min_cost;
modes[i][prior_cset] = min_mode;
return min_cost;
}
static int c128_set_values(const unsigned char source[], const int length, const int start_idx,
const char priority[C128_STATES], const char fncs[C128_MAX], const char manuals[C128_MAX],
int values[C128_VALUES_MAX], int *p_final_cset) {
short (*costs)[C128_STATES] = (short (*)[C128_STATES]) z_alloca(sizeof(*costs) * length);
char (*modes)[C128_STATES] = (char (*)[C128_STATES]) z_alloca(sizeof(*modes) * length);
int glyph_count = 0;
int cset = 0;
int i;
memset(costs, 0, sizeof(*costs) * length);
assert(source[length] == '\0');
c128_cost(source, length, 0 , 0 , start_idx, priority, fncs, manuals, costs, modes);
if (costs[0][0] > C128_SYMBOL_MAX) {
return costs[0][0];
}
for (i = 0; i < length; i++) {
const unsigned char ch = source[i];
const int is_fnc1 = ch == '\x1D' && fncs[i];
const int mode = modes[i][cset];
const int prior_cset = cset;
cset = mode & 0x0F;
assert(cset);
if (cset != prior_cset) {
int j;
if (prior_cset == 0) {
assert(cset != C128_C1);
for (j = 0; j < c128_start_latch_len[start_idx][cset]; j++) {
values[glyph_count++] = c128_start_latch_seq[start_idx][cset][j];
}
} else {
for (j = 0; j < c128_latch_len[prior_cset][cset]; j++) {
values[glyph_count++] = c128_latch_seq[prior_cset][cset][j];
}
}
}
if (mode >= 0x30) {
values[glyph_count++] = 100 + C128_A0A1(cset);
values[glyph_count++] = 98;
} else if (mode >= 0x20) {
values[glyph_count++] = 100 + C128_A0A1(cset);
} else if (mode >= 0x10) {
values[glyph_count++] = 98;
}
if (is_fnc1) {
values[glyph_count++] = 102;
} else if (C128_C0C1(cset)) {
assert(i + 1 < length);
values[glyph_count++] = (ch - '0') * 10 + source[++i] - '0';
} else {
values[glyph_count++] = (ch & 0x7F) + 96 * !(ch & 0x60) - 32;
}
}
assert(glyph_count == costs[0][0]);
if (p_final_cset) {
*p_final_cset = cset;
}
return glyph_count;
}
static void c128_expand(struct zint_symbol *symbol, int values[C128_VALUES_MAX], int glyph_count) {
char dest[640];
char *d = dest;
int total_sum;
int i;
memcpy(d, zint_C128Table[values[0]], 6);
d += 6;
total_sum = values[0];
for (i = 1; i < glyph_count; i++, d += 6) {
memcpy(d, zint_C128Table[values[i]], 6);
total_sum += values[i] * i;
}
total_sum %= 103;
memcpy(d, zint_C128Table[total_sum], 6);
d += 6;
values[glyph_count++] = total_sum;
memcpy(d, "2331112", 7);
d += 7;
values[glyph_count++] = 106;
if (symbol->debug & ZINT_DEBUG_PRINT) {
fputs("Codewords:", stdout);
for (i = 0; i < glyph_count; i++) {
printf(" %d", values[i]);
}
printf(" (%d)\n", glyph_count);
printf("Barspaces: %.*s\n", (int) (d - dest), dest);
printf("Checksum: %d\n", total_sum);
}
#ifdef ZINT_TEST
if (symbol->debug & ZINT_DEBUG_TEST) {
z_debug_test_codeword_dump_int(symbol, values, glyph_count);
}
#endif
z_expand(symbol, dest, d - dest);
}
static void c128_set_priority(char priority[C128_STATES], const int have_a, const int have_b, const int have_c,
const int have_extended) {
int i = 0;
if (have_c) {
priority[i++] = C128_C0;
}
if (have_b || !have_a) {
priority[i++] = C128_B0;
}
if (have_a) {
priority[i++] = C128_A0;
}
if (have_extended) {
if (have_c) {
priority[i++] = C128_C1;
}
if (have_b || !have_a) {
priority[i++] = C128_B1;
}
if (have_a) {
priority[i++] = C128_A1;
}
}
priority[i] = 0;
}
INTERNAL int zint_code128(struct zint_symbol *symbol, unsigned char source[], int length) {
int i;
int error_number;
char manuals[C128_MAX] = {0};
char fncs[C128_MAX] = {0};
int have_fnc1 = 0;
int have_a = 0, have_b = 0, have_c = 0, have_extended = 0;
char priority[C128_STATES];
int values[C128_VALUES_MAX] = {0};
int glyph_count;
unsigned char src_buf[C128_MAX + 1];
unsigned char *src = source;
const int ab_only = symbol->symbology == BARCODE_CODE128AB;
const int start_idx = (symbol->output_options & READER_INIT) ? 2 : 0;
const int content_segs = symbol->output_options & BARCODE_CONTENT_SEGS;
assert(length);
if (length > C128_MAX) {
return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 340, "Input length %d too long (maximum " C128_MAX_S ")",
length);
}
if ((symbol->input_mode & EXTRA_ESCAPE_MODE) && symbol->symbology == BARCODE_CODE128) {
char manual = 0;
int j = 0;
for (i = 0; i < length; i++) {
if (source[i] == '\\' && i + 2 < length && source[i + 1] == '^'
&& ((source[i + 2] >= '@' && source[i + 2] <= 'C') || source[i + 2] == '1'
|| source[i + 2] == '^')) {
if (source[i + 2] == '^') {
manuals[j] = manual;
src_buf[j++] = source[i++];
manuals[j] = manual;
src_buf[j++] = source[i++];
} else if (source[i + 2] == '1') {
i += 2;
fncs[j] = have_fnc1 = 1;
manuals[j] = manual;
src_buf[j++] = '\x1D';
} else {
i += 2;
manual = source[i] == 'C' ? C128_C0 : source[i] - '@';
}
} else {
manuals[j] = manual;
src_buf[j++] = source[i];
}
}
if (j != length) {
length = j;
if (length == 0) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 842, "No input data");
}
src = src_buf;
src[length] = '\0';
if (symbol->debug & ZINT_DEBUG_PRINT) {
fputs("Manuals: ", stdout);
for (i = 0; i < length; i++) {
printf("%c", manuals[i] == C128_C0 ? 'C' : '@' + manuals[i]);
}
fputc('\n', stdout);
}
}
}
if (ab_only) {
for (i = 0; i < length; i++) {
const unsigned char ch = src[i];
const unsigned char mask_0x60 = ch & 0x60;
have_extended |= ch & 0x80;
have_a |= !mask_0x60;
have_b |= mask_0x60 == 0x60;
}
} else {
int prev_digit, digit = 0;
for (i = 0; i < length; i++) {
const unsigned char ch = src[i];
const int is_fnc1 = ch == '\x1D' && fncs[i];
if (!is_fnc1) {
const unsigned char mask_0x60 = ch & 0x60;
const int manual = manuals[i];
have_extended |= ch & 0x80;
have_a |= !mask_0x60 || manual == C128_A0;
have_b |= mask_0x60 == 0x60 || manual == C128_B0;
prev_digit = digit;
digit = z_isdigit(ch);
have_c |= prev_digit && digit;
}
}
}
c128_set_priority(priority, have_a, have_b, have_c, have_extended);
glyph_count = c128_set_values(src, length, start_idx, priority, fncs, manuals, values, NULL );
if (symbol->debug & ZINT_DEBUG_PRINT) {
printf("Data (%d): %.*s", length, length >= 100 ? 1 : length >= 10 ? 2 : 3, " ");
z_debug_print_escape(src, length, NULL);
printf("\nGlyphs: %d\n", glyph_count);
}
if (glyph_count > C128_SYMBOL_MAX) {
return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 341,
"Input too long, requires %d symbol characters (maximum " C128_SYMBOL_MAX_S ")", glyph_count);
}
c128_expand(symbol, values, glyph_count);
if (have_fnc1) {
int j = 0;
for (i = 0; i < length; i++) {
if (!fncs[i]) {
src[j++] = src[i];
}
}
length = j;
}
error_number = z_hrt_cpy_iso8859_1(symbol, src, length);
if (content_segs) {
if ((symbol->input_mode & 0x07) == DATA_MODE) {
if (z_ct_cpy(symbol, src, length)) {
return ZINT_ERROR_MEMORY;
}
} else if (z_ct_cpy_iso8859_1(symbol, src, length)) {
return ZINT_ERROR_MEMORY;
}
}
return error_number;
}
INTERNAL int zint_gs1_128_cc(struct zint_symbol *symbol, unsigned char source[], int length, const int cc_mode,
const int cc_rows) {
int i;
int error_number;
const char manuals[C128_MAX] = {0};
char fncs[C128_MAX];
char priority[C128_STATES];
int values[C128_VALUES_MAX] = {0};
int glyph_count;
int final_cset;
int separator_row = 0;
int reduced_length;
unsigned char *reduced = (unsigned char *) z_alloca(length + 1);
const int content_segs = symbol->output_options & BARCODE_CONTENT_SEGS;
if (length > C128_MAX) {
return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 342, "Input length %d too long (maximum " C128_MAX_S ")",
length);
}
if (symbol->symbology == BARCODE_GS1_128_CC) {
separator_row = symbol->rows++;
symbol->row_height[separator_row] = 1;
}
error_number = zint_gs1_verify(symbol, source, length, reduced, &reduced_length);
if (error_number >= ZINT_ERROR) {
return error_number;
}
memset(fncs, 1, reduced_length);
c128_set_priority(priority, 0 , 1 , 1 , 0 );
glyph_count = c128_set_values(reduced, reduced_length, 1 , priority, fncs, manuals, values,
&final_cset);
if (symbol->debug & ZINT_DEBUG_PRINT) {
printf("Data (%d): %.*s", reduced_length, reduced_length >= 100 ? 1 : reduced_length >= 10 ? 2 : 3, " ");
z_debug_print_escape(reduced, reduced_length, NULL);
printf("\nGlyphs: %d\n", glyph_count);
}
if (glyph_count + (cc_mode != 0) > C128_SYMBOL_MAX) {
return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 344,
"Input too long, requires %d symbol characters (maximum " C128_SYMBOL_MAX_S ")",
glyph_count + (cc_mode != 0));
}
assert(final_cset == C128_B0 || final_cset == C128_C0);
switch (cc_mode) {
case 1:
case 2:
switch (final_cset) {
case C128_B0:
values[glyph_count++] = 99;
break;
case C128_C0:
values[glyph_count++] = 101;
break;
}
break;
case 3:
switch (final_cset) {
case C128_B0:
values[glyph_count++] = 101;
break;
case C128_C0:
values[glyph_count++] = 100;
break;
}
break;
}
c128_expand(symbol, values, glyph_count);
if (symbol->symbology == BARCODE_GS1_128_CC) {
for (i = 0; i < symbol->width; i++) {
if (!(z_module_is_set(symbol, separator_row + 1, i))) {
z_set_module(symbol, separator_row, i);
}
}
}
if (reduced_length > 48) {
if (error_number == 0) {
error_number = z_errtxtf(ZINT_WARN_NONCOMPLIANT, symbol, 843,
"Input too long, requires %d characters (maximum 48)", reduced_length);
}
}
if (symbol->output_options & COMPLIANT_HEIGHT) {
const float min_height = 5.70866156f;
const float default_height = 64.1414108f;
if (symbol->symbology == BARCODE_GS1_128_CC) {
symbol->height = symbol->height ? min_height : default_height;
} else {
if (error_number == 0) {
error_number = z_set_height(symbol, min_height, default_height, 0.0f, 0 );
} else {
(void) z_set_height(symbol, min_height, default_height, 0.0f, 1 );
}
}
} else {
const float height = 50.0f;
if (symbol->symbology == BARCODE_GS1_128_CC) {
symbol->height = height - cc_rows * (cc_mode == 3 ? 3 : 2) - 1.0f;
} else {
(void) z_set_height(symbol, 0.0f, height, 0.0f, 1 );
}
}
if (error_number == 0 && (symbol->output_options & READER_INIT)) {
error_number = z_errtxt(ZINT_WARN_INVALID_OPTION, symbol, 845,
"Cannot use Reader Initialisation in GS1 mode, ignoring");
}
if (symbol->input_mode & GS1PARENS_MODE) {
z_hrt_cpy_nochk(symbol, source, length);
} else {
z_hrt_conv_gs1_brackets_nochk(symbol, source, length);
}
if (content_segs && z_ct_cpy(symbol, reduced, reduced_length)) {
return ZINT_ERROR_MEMORY;
}
return error_number;
}
INTERNAL int zint_gs1_128(struct zint_symbol *symbol, unsigned char source[], int length) {
return zint_gs1_128_cc(symbol, source, length, 0 , 0 );
}