GRIDMATRIX codeword fixes, Project Nayuki optimized encoding modes

This commit is contained in:
gitlost 2019-12-04 13:45:01 +00:00
parent 8295883987
commit b1f4a12c78
6 changed files with 678 additions and 334 deletions

View File

@ -54,9 +54,17 @@ int ctoi(const char source) {
/* Convert an integer value to a string representing its binary equivalent */ /* Convert an integer value to a string representing its binary equivalent */
void bin_append(const int arg, const int length, char *binary) { void bin_append(const int arg, const int length, char *binary) {
size_t posn = strlen(binary);
bin_append_posn(arg, length, binary, posn);
binary[posn + length] = '\0';
}
/* Convert an integer value to a string representing its binary equivalent at a set position */
void bin_append_posn(const int arg, const int length, char *binary, size_t posn) {
int i; int i;
int start; int start;
size_t posn = strlen(binary);
start = 0x01 << (length - 1); start = 0x01 << (length - 1);
@ -66,9 +74,6 @@ void bin_append(const int arg, const int length, char *binary) {
binary[posn + i] = '1'; binary[posn + i] = '1';
} }
} }
binary[posn + length] = '\0';
return;
} }
/* Converts an integer value to its hexadecimal character */ /* Converts an integer value to its hexadecimal character */
@ -330,8 +335,8 @@ int utf8toutf16(struct zint_symbol *symbol, const unsigned char source[], int va
vals[jpos] = codepoint; vals[jpos] = codepoint;
jpos++; jpos++;
} }
*length = jpos; *length = jpos;
return 0; return 0;

View File

@ -62,6 +62,7 @@ extern "C" {
extern int is_sane(const char test_string[], const unsigned char source[], const size_t length); extern int is_sane(const char test_string[], const unsigned char source[], const size_t length);
extern void lookup(const char set_string[], const char *table[], const char data, char dest[]); extern void lookup(const char set_string[], const char *table[], const char data, char dest[]);
extern void bin_append(const int arg, const int length, char *binary); extern void bin_append(const int arg, const int length, char *binary);
extern void bin_append_posn(const int arg, const int length, char *binary, size_t posn);
extern int posn(const char set_string[], const char data); extern int posn(const char set_string[], const char data);
extern int ustrchr_cnt(const unsigned char string[], const size_t length, const unsigned char c); extern int ustrchr_cnt(const unsigned char string[], const size_t length, const unsigned char c);
extern int module_is_set(const struct zint_symbol *symbol, const int y_coord, const int x_coord); extern int module_is_set(const struct zint_symbol *symbol, const int y_coord, const int x_coord);

View File

@ -44,285 +44,247 @@
#include "gridmtx.h" #include "gridmtx.h"
#include "gb2312.h" #include "gb2312.h"
int number_lat(unsigned int gbdata[], const size_t length, const size_t position) { /* Bits multiplied by this for costs, so as to be whole integer divisible by 2 and 3 */
/* Attempt to calculate the 'cost' of using numeric mode from a given position in number of bits */ #define GM_MULT 6
static char numeral_nondigits[] = " +-.,"; /* Non-digit numeral set, excluding EOL (carriage return/linefeed) */
/* Whether in numeral or not. If in numeral, *p_numeral_end is set to position after numeral, and *p_numeral_cost is set to per-numeral cost */
static int numeral_lat(unsigned int gbdata[], const size_t length, const int posn, int* p_numeral_end, int* p_numeral_cost) {
int i, nondigit, nondigit_posn, digit_cnt;
if (posn < *p_numeral_end) {
return 1;
}
/* Attempt to calculate the average 'cost' of using numeric mode in number of bits (times GM_MULT) */
/* Also ensures that numeric mode is not selected when it cannot be used: for example in /* Also ensures that numeric mode is not selected when it cannot be used: for example in
a string which has "2.2.0" (cannot have more than one non-numeric character for each a string which has "2.2.0" (cannot have more than one non-numeric character for each
block of three numeric characters) */ block of three numeric characters) */
size_t sp; for (i = posn, nondigit = 0, digit_cnt = 0; i < length && i < posn + 4 && digit_cnt < 3; i++) {
int numb = 0, nonum = 0; if (gbdata[i] >= '0' && gbdata[i] <= '9') {
int tally = 0; digit_cnt++;
} else if (strchr(numeral_nondigits, gbdata[i])) {
sp = position; if (nondigit) {
break;
do {
int done = 0;
if ((gbdata[sp] >= '0') && (gbdata[sp] <= '9')) {
numb++;
done = 1;
}
switch (gbdata[sp]) {
case ' ':
case '+':
case '-':
case '.':
case ',':
nonum++;
done = 1;
}
if ((sp + 1) < length) {
if ((gbdata[sp] == 0x13) && (gbdata[sp + 1] == 0x10)) {
nonum++;
done = 1;
sp++;
} }
} nondigit = 1;
nondigit_posn = i;
if (done == 0) { } else if (i < length - 1 && gbdata[i] == 13 && gbdata[i + 1] == 10) {
tally += 80; if (nondigit) {
break;
}
i++;
nondigit = 2;
nondigit_posn = i;
} else { } else {
if (numb == 3) { break;
if (nonum == 0) {
tally += 10;
}
if (nonum == 1) {
tally += 20;
}
if (nonum > 1) {
tally += 80;
}
numb = 0;
nonum = 0;
}
}
sp++;
} while ((sp < length) && (sp <= (position + 8)));
if (numb == 0) {
tally += 80;
}
if (numb > 1) {
if (nonum == 0) {
tally += 10;
}
if (nonum == 1) {
tally += 20;
}
if (nonum > 1) {
tally += 80;
} }
} }
if (digit_cnt == 0) { /* Must have at least one digit */
return tally; *p_numeral_end = -1;
return 0;
}
if (nondigit && nondigit_posn == i - 1) { /* Non-digit can't be at end */
nondigit = 0;
}
*p_numeral_end = posn + digit_cnt + nondigit;
/* Calculate per-numeral cost where 120 == (10 + 10) * GM_MULT, 60 == 10 * GM_MULT */
if (digit_cnt == 3) {
*p_numeral_cost = nondigit == 2 ? 24 /* (120 / 5) */ : nondigit == 1 ? 30 /* (120 / 4) */ : 20 /* (60 / 3) */;
} else if (digit_cnt == 2) {
*p_numeral_cost = nondigit == 2 ? 30 /* (120 / 4) */ : nondigit == 1 ? 40 /* (120 / 3) */ : 30 /* (60 / 2) */;
} else {
*p_numeral_cost = nondigit == 2 ? 40 /* (120 / 3) */ : nondigit == 1 ? 60 /* (120 / 2) */ : 60 /* (60 / 1) */;
}
return 1;
} }
static int seek_forward(unsigned int gbdata[], const size_t length, const size_t position, int current_mode, int debug) { /* Encoding modes */
/* In complete contrast to the method recommended in Annex D of the ANSI standard this #define GM_CHINESE 'H'
code uses a look-ahead test in the same manner as Data Matrix. This decision was made #define GM_NUMBER 'N'
because the "official" algorithm does not provide clear methods for dealing with all #define GM_LOWER 'L'
possible combinations of input data */ #define GM_UPPER 'U'
#define GM_MIXED 'M'
#define GM_BYTE 'B'
/* Note Control is a submode of Lower, Upper and Mixed modes */
int number_count, byte_count, mixed_count, upper_count, lower_count, chinese_count; /* Indexes into mode_types array */
int best_mode; #define GM_H 0 /* Chinese (Hanzi) */
size_t sp; #define GM_N 1 /* Numeral */
int best_count, last = -1; #define GM_L 2 /* Lower case */
#define GM_U 3 /* Upper case */
#define GM_M 4 /* Mixed */
#define GM_B 5 /* Byte */
if (gbdata[position] > 0xff) { #define GM_NUM_MODES 6
return GM_CHINESE;
}
switch (current_mode) { /* Calculate optimized encoding modes. Adapted from Project Nayuki */
case GM_CHINESE: /*
number_count = 13; * Copyright (c) Project Nayuki. (MIT License)
byte_count = 13; * https://www.nayuki.io/page/qr-code-generator-library
mixed_count = 13; *
upper_count = 13; * Permission is hereby granted, free of charge, to any person obtaining a copy of
lower_count = 13; * this software and associated documentation files (the "Software"), to deal in
chinese_count = 0; * the Software without restriction, including without limitation the rights to
break; * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
case GM_NUMBER: * the Software, and to permit persons to whom the Software is furnished to do so,
number_count = 0; * subject to the following conditions:
byte_count = 10; * - The above copyright notice and this permission notice shall be included in
mixed_count = 10; * all copies or substantial portions of the Software.
upper_count = 10; */
lower_count = 10; static void define_mode(unsigned int gbdata[], const size_t length, char* mode, int debug) {
chinese_count = 10; static char mode_types[] = { GM_CHINESE, GM_NUMBER, GM_LOWER, GM_UPPER, GM_MIXED, GM_BYTE }; /* Must be in same order as GM_H etc */
break;
case GM_LOWER: /* Initial mode costs */
number_count = 5; static unsigned int head_costs[GM_NUM_MODES] = {
byte_count = 7; /* H N (+pad prefix) L U M B (+byte count) */
mixed_count = 7; 4 * GM_MULT, (4 + 2) * GM_MULT, 4 * GM_MULT, 4 * GM_MULT, 4 * GM_MULT, (4 + 9) * GM_MULT
upper_count = 5; };
lower_count = 0;
chinese_count = 5; /* Costs of switching modes - see AIMD014 Rev. 1.63 Table 9 Type conversion codes */
break; static unsigned int switch_costs[GM_NUM_MODES][GM_NUM_MODES] = {
case GM_UPPER: /* H N L U M B */
number_count = 5; /*H*/ { 0, (13 + 2) * GM_MULT, 13 * GM_MULT, 13 * GM_MULT, 13 * GM_MULT, (13 + 9) * GM_MULT },
byte_count = 7; /*N*/ { 10 * GM_MULT, 0, 10 * GM_MULT, 10 * GM_MULT, 10 * GM_MULT, (10 + 9) * GM_MULT },
mixed_count = 7; /*L*/ { 5 * GM_MULT, (5 + 2) * GM_MULT, 0, 5 * GM_MULT, 7 * GM_MULT, (7 + 9) * GM_MULT },
upper_count = 0; /*U*/ { 5 * GM_MULT, (5 + 2) * GM_MULT, 5 * GM_MULT, 0, 7 * GM_MULT, (7 + 9) * GM_MULT },
lower_count = 5; /*M*/ { 10 * GM_MULT, (10 + 2) * GM_MULT, 10 * GM_MULT, 10 * GM_MULT, 0, (10 + 9) * GM_MULT },
chinese_count = 5; /*B*/ { 4 * GM_MULT, (4 + 2) * GM_MULT, 4 * GM_MULT, 4 * GM_MULT, 4 * GM_MULT, 0 },
break; };
case GM_MIXED:
number_count = 10; /* Final end-of-data costs - see AIMD014 Rev. 1.63 Table 9 Type conversion codes */
byte_count = 10; static unsigned int eod_costs[GM_NUM_MODES] = {
mixed_count = 0; /* H N L U M B */
upper_count = 10; 13 * GM_MULT, 10 * GM_MULT, 5 * GM_MULT, 5 * GM_MULT, 10 * GM_MULT, 4 * GM_MULT
lower_count = 10; };
chinese_count = 10;
break; unsigned int prev_costs[GM_NUM_MODES];
case GM_BYTE: int i, j, k;
number_count = 4; int byte_count = 0;
int numeral_end = -1, numeral_cost;
int cur_mode_index;
unsigned int min_cost;
/* char_modes[i][j] represents the mode to encode the code point at index i such that the final segment ends in mode_types[j] and the
* total number of bits is minimized over all possible choices */
#ifndef _MSC_VER
char char_modes[length][GM_NUM_MODES];
#else
char* char_modes = (char*) _alloca(length * GM_NUM_MODES);
#endif
memset(char_modes, 0, length * GM_NUM_MODES);
/* At the beginning of each iteration of the loop below, prev_costs[j] is the minimum number of 1/6 (1/GM_MULT) bits needed
* to encode the entire string prefix of length i, and end in mode_types[j] */
memcpy(prev_costs, head_costs, sizeof(head_costs));
/* Calculate costs using dynamic programming */
for (i = 0; i < length; i++) {
int double_byte, space, numeric, lower, upper, control, double_digit, eol;
unsigned int cur_costs[GM_NUM_MODES] = { 0, 0, 0, 0, 0, 0 };
double_byte = gbdata[i] > 0xFF;
space = gbdata[i] == ' ';
numeric = gbdata[i] >= '0' && gbdata[i] <= '9';
lower = gbdata[i] >= 'a' && gbdata[i] <= 'z';
upper = gbdata[i] >= 'A' && gbdata[i] <= 'Z';
control = !space && !numeric && !lower && !upper && gbdata[i] < 0x7F; /* Exclude DEL */
double_digit = i < length - 1 && numeric && gbdata[i + 1] >= '0' && gbdata[i + 1] <= '9';
eol = i < length - 1 && gbdata[i] == 13 && gbdata[i + 1] == 10;
/* Hanzi mode can encode anything */
cur_costs[GM_H] = prev_costs[GM_H] + (double_digit || eol ? 39 : 78); /* (6.5 : 13) * GM_MULT */
char_modes[i][GM_H] = 'H';
/* Byte mode can encode anything */
if (byte_count == 512 || (double_byte && byte_count == 511)) {
cur_costs[GM_B] = head_costs[GM_B];
if (double_byte && byte_count == 511) {
double_byte = 0; /* Splitting double-byte so mark as single */
}
byte_count = 0; byte_count = 0;
mixed_count = 4; }
upper_count = 4; cur_costs[GM_B] += prev_costs[GM_B] + (double_byte ? 96 : 48); /* (16 : 8) * GM_MULT */
lower_count = 4; char_modes[i][GM_B] = 'B';
chinese_count = 4; byte_count += double_byte ? 2 : 1;
break;
default: /* Start of symbol */
number_count = 4;
byte_count = 4;
mixed_count = 4;
upper_count = 4;
lower_count = 4;
chinese_count = 4;
}
for (sp = position; (sp < length) && (sp <= (position + 8)); sp++) { if (numeral_lat(gbdata, length, i, &numeral_end, &numeral_cost)) {
cur_costs[GM_N] = prev_costs[GM_N] + numeral_cost;
int done = 0; char_modes[i][GM_N] = 'N';
if (gbdata[sp] >= 0xff) {
byte_count += 17;
mixed_count += 23;
upper_count += 18;
lower_count += 18;
chinese_count += 13;
done = 1;
} }
if ((gbdata[sp] >= 'a') && (gbdata[sp] <= 'z')) { if (control) {
byte_count += 8; cur_costs[GM_L] = prev_costs[GM_L] + 78; /* (7 + 6) * GM_MULT */
mixed_count += 6; char_modes[i][GM_L] = 'L';
upper_count += 10; cur_costs[GM_U] = prev_costs[GM_U] + 78; /* (7 + 6) * GM_MULT */
lower_count += 5; char_modes[i][GM_U] = 'U';
chinese_count += 13; cur_costs[GM_M] = prev_costs[GM_M] + 96; /* (10 + 6) * GM_MULT */
done = 1; char_modes[i][GM_M] = 'M';
} } else {
if (lower || space) {
if ((gbdata[sp] >= 'A') && (gbdata[sp] <= 'Z')) { cur_costs[GM_L] = prev_costs[GM_L] + 30; /* 5 * GM_MULT */
byte_count += 8; char_modes[i][GM_L] = 'L';
mixed_count += 6; }
upper_count += 5; if (upper || space) {
lower_count += 10; cur_costs[GM_U] = prev_costs[GM_U] + 30; /* 5 * GM_MULT */
chinese_count += 13; char_modes[i][GM_U] = 'U';
done = 1; }
} if (numeric || lower || upper || space) {
cur_costs[GM_M] = prev_costs[GM_M] + 36; /* 6 * GM_MULT */
if ((gbdata[sp] >= '0') && (gbdata[sp] <= '9')) { char_modes[i][GM_M] = 'M';
byte_count += 8;
mixed_count += 6;
upper_count += 8;
lower_count += 8;
chinese_count += 13;
done = 1;
}
if (gbdata[sp] == ' ') {
byte_count += 8;
mixed_count += 6;
upper_count += 5;
lower_count += 5;
chinese_count += 13;
done = 1;
}
if (done == 0) {
/* Control character */
byte_count += 8;
mixed_count += 16;
upper_count += 13;
lower_count += 13;
chinese_count += 13;
}
if (gbdata[sp] >= 0x7f) {
mixed_count += 20;
upper_count += 20;
lower_count += 20;
}
}
/* Adjust for <end of line> */
for (sp = position; (sp < (length - 1)) && (sp <= (position + 7)); sp++) {
if ((gbdata[sp] == 0x13) && (gbdata[sp + 1] == 0x10)) {
chinese_count -= 13;
}
}
/* Adjust for double digits */
for (sp = position; (sp < (length - 1)) && (sp <= (position + 7)); sp++) {
if (sp != last) {
if (((gbdata[sp] >= '0') && (gbdata[sp] <= '9')) && ((gbdata[sp + 1] >= '0') && (gbdata[sp + 1] <= '9'))) {
chinese_count -= 13;
last = (int)(sp + 1);
} }
} }
if (i == length - 1) { /* Add end of data costs if last character */
for (j = 0; j < GM_NUM_MODES; j++) {
if (char_modes[i][j]) {
cur_costs[j] += eod_costs[j];
}
}
}
/* Start new segment at the end to switch modes */
for (j = 0; j < GM_NUM_MODES; j++) { /* To mode */
for (k = 0; k < GM_NUM_MODES; k++) { /* From mode */
if (j != k && char_modes[i][k]) {
unsigned int new_cost = cur_costs[k] + switch_costs[k][j];
if (!char_modes[i][j] || new_cost < cur_costs[j]) {
cur_costs[j] = new_cost;
char_modes[i][j] = mode_types[k];
}
}
}
}
memcpy(prev_costs, cur_costs, sizeof(cur_costs));
} }
/* Numeric mode is more complex */ /* Find optimal ending mode */
number_count += number_lat(gbdata, length, position); cur_mode_index = 0;
min_cost = prev_costs[0];
for (i = 1; i < GM_NUM_MODES; i++) {
if (prev_costs[i] < min_cost) {
min_cost = prev_costs[i];
cur_mode_index = i;
}
}
/* Get optimal mode for each code point by tracing backwards */
for (i = length - 1; i >= 0; i--) {
char cur_mode = char_modes[i][cur_mode_index];
cur_mode_index = strchr(mode_types, cur_mode) - mode_types;
mode[i] = cur_mode;
}
if (debug & ZINT_DEBUG_PRINT) { if (debug & ZINT_DEBUG_PRINT) {
printf("C %d / B %d / M %d / U %d / L %d / N %d\n", chinese_count, byte_count, mixed_count, upper_count, lower_count, number_count); printf(" Mode: %.*s\n", (int)length, mode);
} }
best_count = chinese_count;
best_mode = GM_CHINESE;
if (byte_count <= best_count) {
best_count = byte_count;
best_mode = GM_BYTE;
}
if (mixed_count <= best_count) {
best_count = mixed_count;
best_mode = GM_MIXED;
}
if (upper_count <= best_count) {
best_count = upper_count;
best_mode = GM_UPPER;
}
if (lower_count <= best_count) {
best_count = lower_count;
best_mode = GM_LOWER;
}
if (number_count <= best_count) {
best_count = number_count;
best_mode = GM_NUMBER;
}
return best_mode;
} }
/* Add the length indicator for byte encoded blocks */ /* Add the length indicator for byte encoded blocks */
static void add_byte_count(char binary[], const size_t byte_count_posn, const int byte_count) { static void add_byte_count(char binary[], const size_t byte_count_posn, const int byte_count) {
int p; bin_append_posn(byte_count - 1, 9, binary, byte_count_posn);
for (p = 0; p < 9; p++) {
if (byte_count & (0x100 >> p)) {
binary[byte_count_posn + p] = '0';
} else {
binary[byte_count_posn + p] = '1';
}
}
} }
/* Add a control character to the data stream */ /* Add a control character to the data stream */
@ -348,13 +310,19 @@ static int gm_encode(unsigned int gbdata[], const size_t length, char binary[],
/* Create a binary stream representation of the input data. /* Create a binary stream representation of the input data.
7 sets are defined - Chinese characters, Numerals, Lower case letters, Upper case letters, 7 sets are defined - Chinese characters, Numerals, Lower case letters, Upper case letters,
Mixed numerals and latters, Control characters and 8-bit binary data */ Mixed numerals and latters, Control characters and 8-bit binary data */
int sp, current_mode, last_mode, glyph = 0; int sp, current_mode, last_mode;
unsigned int glyph = 0;
int c1, c2, done; int c1, c2, done;
int p = 0, ppos; int p = 0, ppos;
int numbuf[3], punt = 0; int numbuf[3], punt = 0;
size_t number_pad_posn, byte_count_posn = 0; size_t number_pad_posn, byte_count_posn = 0;
int byte_count = 0; int byte_count = 0;
int shift; int shift;
#ifndef _MSC_VER
char mode[length];
#else
char* mode = (char*) _alloca(length);
#endif
strcpy(binary, ""); strcpy(binary, "");
@ -383,8 +351,10 @@ static int gm_encode(unsigned int gbdata[], const size_t length, char binary[],
} }
} }
define_mode(gbdata, length, mode, debug);
do { do {
int next_mode = seek_forward(gbdata, length, sp, current_mode, debug); int next_mode = mode[sp];
if (next_mode != current_mode) { if (next_mode != current_mode) {
switch (current_mode) { switch (current_mode) {
@ -529,7 +499,7 @@ static int gm_encode(unsigned int gbdata[], const size_t length, char binary[],
} }
if (!(done)) { if (!(done)) {
if (sp != (length - 1)) { if (sp != (length - 1)) {
if ((gbdata[sp] == 0x13) && (gbdata[sp + 1] == 0x10)) { if ((gbdata[sp] == 13) && (gbdata[sp + 1] == 10)) {
/* End of Line */ /* End of Line */
glyph = 7776; glyph = 7776;
sp++; sp++;
@ -579,29 +549,26 @@ static int gm_encode(unsigned int gbdata[], const size_t length, char binary[],
do { do {
if ((gbdata[sp] >= '0') && (gbdata[sp] <= '9')) { if ((gbdata[sp] >= '0') && (gbdata[sp] <= '9')) {
numbuf[p] = gbdata[sp]; numbuf[p] = gbdata[sp];
sp++;
p++; p++;
} } else if (strchr(numeral_nondigits, gbdata[sp])) {
switch (gbdata[sp]) { if (ppos != -1) {
case ' ':
case '+':
case '-':
case '.':
case ',':
punt = gbdata[sp];
sp++;
ppos = p;
break; break;
}
if (sp < (length - 1)) {
if ((gbdata[sp] == 0x13) && (gbdata[sp + 1] == 0x10)) {
/* <end of line> */
punt = gbdata[sp];
sp += 2;
ppos = p;
} }
punt = gbdata[sp];
ppos = p;
} else if (sp < (length - 1) && (gbdata[sp] == 13) && (gbdata[sp + 1] == 10)) {
/* <end of line> */
if (ppos != -1) {
break;
}
punt = gbdata[sp];
sp++;
ppos = p;
} else {
break;
} }
} while ((p < 3) && (sp < length)); sp++;
} while ((p < 3) && (sp < length) && mode[sp] == GM_NUMBER);
if (ppos != -1) { if (ppos != -1) {
switch (punt) { switch (punt) {
@ -615,7 +582,7 @@ static int gm_encode(unsigned int gbdata[], const size_t length, char binary[],
break; break;
case ',': glyph = 12; case ',': glyph = 12;
break; break;
case 0x13: glyph = 15; case 13: glyph = 15;
break; break;
} }
glyph += ppos; glyph += ppos;
@ -642,8 +609,14 @@ static int gm_encode(unsigned int gbdata[], const size_t length, char binary[],
byte_count_posn = strlen(binary); byte_count_posn = strlen(binary);
strcat(binary, "LLLLLLLLL"); strcat(binary, "LLLLLLLLL");
} }
if (byte_count == 512) { glyph = gbdata[sp];
if (byte_count == 512 || (glyph > 0xFF && byte_count == 511)) {
/* Maximum byte block size is 512 bytes. If longer is needed then start a new block */ /* Maximum byte block size is 512 bytes. If longer is needed then start a new block */
if (glyph > 0xFF && byte_count == 511) { /* Split double-byte */
bin_append(glyph >> 8, 8, binary);
glyph &= 0xFF;
byte_count++;
}
add_byte_count(binary, byte_count_posn, byte_count); add_byte_count(binary, byte_count_posn, byte_count);
bin_append(7, 4, binary); bin_append(7, 4, binary);
byte_count_posn = strlen(binary); byte_count_posn = strlen(binary);
@ -651,13 +624,15 @@ static int gm_encode(unsigned int gbdata[], const size_t length, char binary[],
byte_count = 0; byte_count = 0;
} }
glyph = gbdata[sp];
if (debug & ZINT_DEBUG_PRINT) { if (debug & ZINT_DEBUG_PRINT) {
printf("[%d] ", glyph); printf("[%d] ", glyph);
} }
bin_append(glyph, 8, binary); bin_append(glyph, glyph > 0xFF ? 16 : 8, binary);
sp++; sp++;
byte_count++; byte_count++;
if (glyph > 0xFF) {
byte_count++;
}
break; break;
case GM_MIXED: case GM_MIXED:
@ -798,9 +773,17 @@ static int gm_encode(unsigned int gbdata[], const size_t length, char binary[],
} }
static void gm_test_codeword_dump(struct zint_symbol *symbol, int* codewords, int length) { static void gm_test_codeword_dump(struct zint_symbol *symbol, int* codewords, int length) {
int i; int i, max, cnt_len;
for (i = 0; i < length && i < 33; i++) { /* 33*3 < errtxt 100 chars */ if (length >= 33) {
sprintf(symbol->errtxt + i * 3, "%02X ", codewords[i]); sprintf(symbol->errtxt, "(%d) ", length); /* Place the number of codewords at the front */
cnt_len = strlen(symbol->errtxt);
max = 33 - (cnt_len + 2) / 3;
} else {
max = length > 33 ? 33 : length;
cnt_len = 0;
}
for (i = 0; i < max; i++) { /* 33*3 < errtxt 100 chars */
sprintf(symbol->errtxt + cnt_len + i * 3, "%02X ", codewords[i]);
} }
symbol->errtxt[strlen(symbol->errtxt) - 1] = '\0'; /* Zap last space */ symbol->errtxt[strlen(symbol->errtxt) - 1] = '\0'; /* Zap last space */
} }
@ -1079,25 +1062,10 @@ int grid_matrix(struct zint_symbol *symbol, const unsigned char source[], size_t
} }
} }
layers = auto_layers; layers = auto_layers;
auto_ecc_level = 3;
if (layers == 1) {
auto_ecc_level = 5;
}
if ((layers == 2) || (layers == 3)) {
auto_ecc_level = 4;
}
min_ecc_level = 1;
if (layers == 1) {
min_ecc_level = 4;
}
if ((layers == 2) || (layers == 3)) {
min_ecc_level = 2;
}
ecc_level = auto_ecc_level;
if ((symbol->option_2 >= 1) && (symbol->option_2 <= 13)) { if ((symbol->option_2 >= 1) && (symbol->option_2 <= 13)) {
input_latch = 1; input_latch = 1;
if (symbol->option_2 > min_layers) { if (symbol->option_2 >= min_layers) {
layers = symbol->option_2; layers = symbol->option_2;
} else { } else {
strcpy(symbol->errtxt, "534: Input data too long for selected symbol size"); strcpy(symbol->errtxt, "534: Input data too long for selected symbol size");
@ -1105,32 +1073,41 @@ int grid_matrix(struct zint_symbol *symbol, const unsigned char source[], size_t
} }
} }
if (input_latch == 1) { auto_ecc_level = 3;
auto_ecc_level = 3; if (layers == 1) {
if (layers == 1) { auto_ecc_level = 5;
auto_ecc_level = 5; }
} if ((layers == 2) || (layers == 3)) {
if ((layers == 2) || (layers == 3)) { auto_ecc_level = 4;
auto_ecc_level = 4; }
} ecc_level = auto_ecc_level;
ecc_level = auto_ecc_level;
if (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) { min_ecc_level = 1;
layers++; if (layers == 1) {
} min_ecc_level = 4;
}
if (layers == 2) {
min_ecc_level = 2;
} }
if (input_latch == 0) { if ((symbol->option_1 >= 1) && (symbol->option_1 <= 5)) {
if ((symbol->option_1 >= 1) && (symbol->option_1 <= 5)) { if (symbol->option_1 >= min_ecc_level) {
if (symbol->option_1 > min_ecc_level) { ecc_level = symbol->option_1;
ecc_level = symbol->option_1; } else {
} else { ecc_level = min_ecc_level;
ecc_level = min_ecc_level;
}
} }
if (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) { }
if (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) {
if (input_latch && ecc_level > min_ecc_level) { /* If layers user-specified (option_2), try reducing ECC level first */
do { do {
layers++; ecc_level--;
} while ((data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) && (layers <= 13)); } while ((data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) && (ecc_level > min_ecc_level));
}
while (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)] && (layers < 13)) {
layers++;
}
while (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)] && ecc_level > 1) { /* ECC min level 1 for layers > 2 */
ecc_level--;
} }
} }

View File

@ -28,14 +28,7 @@
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE. SUCH DAMAGE.
*/ */
/* vim: set ts=4 sw=4 et : */
#define GM_NUMBER 1
#define GM_LOWER 2
#define GM_UPPER 3
#define GM_MIXED 4
#define GM_CONTROL 5
#define GM_BYTE 6
#define GM_CHINESE 7
#define EUROPIUM "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz " #define EUROPIUM "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz "

View File

@ -401,7 +401,7 @@ static void qr_binary(unsigned char datastream[], const int version, const int t
case 'N': case 'N':
/* Numeric mode */ /* Numeric mode */
/* Mode indicator */ /* Mode indicator */
if (version >= RMQR_VERSION) { if (version < RMQR_VERSION) {
strcat(binary, "0001"); strcat(binary, "0001");
} else { } else {
strcat(binary, "001"); strcat(binary, "001");

View File

@ -0,0 +1,368 @@
/*
libzint - the open source barcode library
Copyright (C) 2008-2019 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 : */
#include "testcommon.h"
//#define TEST_INPUT_GENERATE_EXPECTED 1
//#define TEST_ENCODE_GENERATE_EXPECTED 1
static void test_options(void)
{
testStart("");
int ret;
struct item {
unsigned char* data;
int option_1;
int option_2;
int ret_encode;
int ret_vector;
int expected_size;
};
// s/\/\*[ 0-9]*\*\//\=printf("\/*%3d*\/", line(".") - line("'<"))
struct item data[] = {
/* 0*/ { "12345", 0, 0, 0, 0, 18 },
/* 1*/ { "12345", 0, 1, 0, 0, 18 },
/* 2*/ { "12345", 0, 2, 0, 0, 30 },
/* 3*/ { "12345", 0, 14, 0, 0, 18 }, // Version > max version 13 so ignored
/* 4*/ { "12345", 0, 13, 0, 0, 162 },
/* 5*/ { "1234567890123456789", 0, 1, ZINT_ERROR_TOO_LONG, -1, -1 },
/* 6*/ { "1234567890123456789", 0, 2, 0, 0, 30 },
/* 7*/ { "123456789012345678", 0, 0, 0, 0, 30 }, // Version auto-set to 2
/* 8*/ { "123456789012345678", 0, 1, 0, 0, 18 },
/* 9*/ { "123456789012345678", 5, 1, 0, 0, 18 }, // Version specified so overrides ECC level which gets reduced to 4
/* 10*/ { "123456789012345678", 5, 0, 0, 0, 30 }, // Version not specified so increased to allow for ECC level
/* 11*/ { "123456789012345678", 6, 0, 0, 0, 30 }, // ECC > max ECC 5 so ignored and auto-settings version 2, ECC 4 used
};
int data_size = sizeof(data) / sizeof(struct item);
for (int i = 0; i < data_size; i++) {
struct zint_symbol* symbol = ZBarcode_Create();
assert_nonnull(symbol, "Symbol not created\n");
symbol->symbology = BARCODE_GRIDMATRIX;
symbol->option_1 = data[i].option_1;
symbol->option_2 = data[i].option_2;
int length = strlen(data[i].data);
ret = ZBarcode_Encode(symbol, data[i].data, length);
assert_equal(ret, data[i].ret_encode, "i:%d ZBarcode_Encode ret %d != %d (%s)\n", i, ret, data[i].ret_encode, symbol->errtxt);
if (data[i].ret_vector != -1) {
ret = ZBarcode_Buffer_Vector(symbol, 0);
assert_equal(ret, data[i].ret_vector, "i:%d ZBarcode_Buffer_Vector ret %d != %d\n", i, ret, data[i].ret_vector);
assert_equal(symbol->width, data[i].expected_size, "i:%d symbol->width %d != %d\n", i, symbol->width, data[i].expected_size);
assert_equal(symbol->rows, data[i].expected_size, "i:%d symbol->rows %d != %d\n", i, symbol->rows, data[i].expected_size);
}
ZBarcode_Delete(symbol);
}
testFinish();
}
static void test_input(void)
{
testStart("");
int ret;
struct item {
int input_mode;
int eci;
unsigned char* data;
int ret;
int expected_eci;
char* expected;
char* comment;
};
// é U+00E9 in ISO 8859-1 plus other ISO 8859 (but not in ISO 8859-7 or ISO 8859-11), Win 1250 plus other Win, in GB 2312 0xA8A6, UTF-8 C3A9
// β U+03B2 in ISO 8859-7 Greek (but not other ISO 8859 or Win page), in GB 2312 0xA6C2, UTF-8 CEB2
// ÿ U+00FF in ISO 8859-1 0xFF, not in GB 2312, outside first byte and second byte range, UTF-8 C3BF
// ㈩ U+3229 in GB 2312 0x226E
// 一 U+4E00 in GB 2312 0x523B
struct item data[] = {
/* 0*/ { UNICODE_MODE, 0, "é", 0, 0, "30 01 69 00", "B1 (ISO 8859-1)" },
/* 1*/ { UNICODE_MODE, 3, "é", 0, 3, "60 01 58 00 74 40", "ECI-3 B1 (ISO 8859-1)" },
/* 2*/ { UNICODE_MODE, 29, "é", 0, 29, "60 0E 44 2A 37 7C 00", "ECI-29 H1 (GB 2312)" },
/* 3*/ { UNICODE_MODE, 26, "é", 0, 26, "60 0D 05 28 4F 7C 00", "ECI-26 H1 (UTF-8)" },
/* 4*/ { DATA_MODE, 0, "é", 0, 0, "0A 51 1F 78 00", "H1 (UTF-8)" },
/* 5*/ { DATA_MODE, 0, "\351", 0, 0, "30 01 69 00", "B1 (ISO 8859-1) (0xE9)" },
/* 6*/ { UNICODE_MODE, 0, "β", 0, 0, "08 40 2F 78 00", "H1 (GB 2312)" },
/* 7*/ { UNICODE_MODE, 9, "β", 0, 9, "60 04 58 00 71 00", "ECI-9 B1 (ISO 8859-7)" },
/* 8*/ { UNICODE_MODE, 29, "β", 0, 29, "60 0E 44 20 17 7C 00", "ECI-29 H1 (GB 2312)" },
/* 9*/ { UNICODE_MODE, 26, "β", 0, 26, "60 0D 05 6B 17 7C 00", "ECI-26 H1 (UTF-8)" },
/* 10*/ { DATA_MODE, 0, "β", 0, 0, "0B 56 2F 78 00", "H1 (UTF-8)" },
/* 11*/ { UNICODE_MODE, 0, "ÿ", 0, 0, "30 01 7F 00", "B1 (ISO 8859-1)" },
/* 12*/ { UNICODE_MODE, 0, "ÿÿÿ", 0, 0, "30 05 7F 7F 7F 60", "B3 (ISO 8859-1)" },
/* 13*/ { UNICODE_MODE, 0, "㈩一", 0, 0, "08 15 68 0E 7F 70 00", "H2 (GB 2312)" },
/* 14*/ { UNICODE_MODE, 29, "㈩一", 0, 29, "60 0E 44 0A 74 07 3F 78 00", "ECI-29 H2 (GB 2312)" },
/* 15*/ { DATA_MODE, 0, "\177\177", 0, 0, "30 02 7F 3F 40", "B2 (ASCII)" },
/* 16*/ { DATA_MODE, 0, "\177\177\177", 0, 0, "30 04 7F 3F 5F 60", "B3 (ASCII)" },
/* 17*/ { UNICODE_MODE, 0, "123", 0, 0, "10 1E 7F 68", "N3 (ASCII)" },
/* 18*/ { UNICODE_MODE, 0, " 123", 0, 0, "11 7A 03 6F 7D 00", "N4 (ASCII)" },
/* 19*/ { UNICODE_MODE, 0, "1+23", 0, 0, "11 7B 03 6F 7D 00", "N4 (ASCII)" },
/* 20*/ { UNICODE_MODE, 0, "12.3", 0, 0, "11 7C 63 6F 7D 00", "N4 (ASCII)" },
/* 21*/ { UNICODE_MODE, 0, "123,", 0, 0, "10 1E 7F 73 76 5E 60", "N3 L1 (ASCII)" },
/* 22*/ { UNICODE_MODE, 0, "123,4", 0, 0, "14 1E 7F 51 48 3F 50", "N5 (ASCII)" },
/* 23*/ { UNICODE_MODE, 0, "\015\012123", 0, 0, "11 7D 63 6F 7D 00", "N4 (ASCII) (EOL)" },
/* 24*/ { UNICODE_MODE, 0, "1\015\01223", 0, 0, "11 7E 03 6F 7D 00", "N4 (ASCII) (EOL)" },
/* 25*/ { UNICODE_MODE, 0, "12\015\0123", 0, 0, "11 7E 23 6F 7D 00", "N4 (ASCII) (EOL)" },
/* 26*/ { UNICODE_MODE, 0, "123\015\012", 0, 0, "10 1E 7F 7C 01 06 42 40", "N3 B2 (ASCII) (EOL)" },
/* 27*/ { UNICODE_MODE, 0, "123\015\0124", 0, 0, "14 1E 7F 5D 48 3F 50", "N5 (ASCII) (EOL)" },
/* 28*/ { UNICODE_MODE, 0, "2.2.0", 0, 0, "15 7C 46 73 78 40 07 7A", "N5 (ASCII)" },
/* 29*/ { UNICODE_MODE, 0, "2.2.0.5", 0, 0, "30 0C 32 17 0C 45 63 01 38 6A 00", "B7 (ASCII)" },
/* 30*/ { UNICODE_MODE, 0, "2.2.0.56", 0, 0, "13 7C 46 73 78 40 07 71 46 0F 74", "N8 (ASCII)" },
/* 31*/ { UNICODE_MODE, 0, "1 1234ABCD12.2abcd-12", 0, 0, "13 7A 23 41 2A 3F 68 01 08 3E 4F 66 1E 5F 70 00 44 1F 2F 6E 0F 0F 74", "N6 U4 N4 L4 N3 (ASCII)" },
/* 32*/ { UNICODE_MODE, 0, "1 123ABCDE12.2abcd-12", 0, 0, "28 1F 40 42 06 28 59 43 27 01 05 7D 56 42 49 16 34 7F 6D 30 08 2F 60", "M21 (ASCII)" },
/* 33*/ { UNICODE_MODE, 0, "国外通信教材 Matlab6.5", 0, 0, "09 63 27 20 4E 24 1F 05 21 58 22 13 7E 1E 4C 78 09 56 00 3D 3F 4A 45 3F 50", "H6 U2 L5 N3 (GB 2312) (Same as D.2 example)" },
/* 34*/ { UNICODE_MODE, 0, "AAT", 0, 0, "20 00 4F 30", "U3 (ASCII)" },
/* 35*/ { UNICODE_MODE, 0, "aat", 0, 0, "18 00 4F 30", "L3 (ASCII)" },
/* 36*/ { UNICODE_MODE, 0, "AAT2556", 0, 0, "20 00 4F 58 7F 65 47 7A", "U3 N4 (ASCII) (note same bit count as M7)" },
/* 37*/ { UNICODE_MODE, 0, "AAT2556 ", 0, 0, "29 22 4E 42 0A 14 37 6F 60", "M8 (ASCII)" },
/* 38*/ { UNICODE_MODE, 0, "AAT2556 电", 0, 0, "29 22 4E 42 0A 14 37 6F 62 2C 1F 7E 00", "M8 H1 (GB 2312)" },
/* 39*/ { UNICODE_MODE, 0, " 200", 0, 0, "11 7A 06 23 7D 00", "N4 (ASCII)" },
/* 40*/ { UNICODE_MODE, 0, " 200mA至", 0, 0, "2F 60 40 00 60 2B 78 63 41 7F 40", "M6 H1 (GB 2312)" },
/* 41*/ { UNICODE_MODE, 0, "2A tel:86 019 82512738", 0, 0, "28 22 5F 4F 29 48 5F 6D 7E 6F 55 57 1F 28 63 0F 5A 11 64 0F 74", "M2 L5(with control) N15 (ASCII)" },
/* 42*/ { UNICODE_MODE, 0, "至2A tel:86 019 82512738", 0, 0, "30 07 56 60 4C 48 13 6A 32 17 7B 3F 5B 75 35 67 6A 18 63 76 44 39 03 7D 00", "B4 L5(with control) N15 (GB 2312)" },
/* 43*/ { UNICODE_MODE, 0, "AAT2556 电池充电器+降压转换器 200mA至2A tel:86 019 82512738", 0, 0, "(62) 29 22 22 1C 4E 41 42 7E 0A 40 14 00 37 7E 6F 00 62 7E 2C 00 1C 7E 4B 00 41 7E 18 00 42 7E 61", "M8 H11 M6 B4 L5(with control) N15 (GB 2312) (*NOT SAME* as D3 example, M8 H11 M6 H1 M3 L4(with control) N15, which uses a few more bits)" },
/* 44*/ { UNICODE_MODE, 0, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::", 0, 0, "(588) 37 68 68 68 68 68 74 7E 74 74 74 74 74 3A 3A 3A 3A 3A 3A 3A 1D 1D 1D 1D 1D 1D 1D 0E 0E 0E 0E", "B512 (ASCII)" },
/* 45*/ { UNICODE_MODE, 0, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\177", 0, 0, "(591) 37 68 68 68 68 68 74 7E 74 74 74 74 74 3A 3A 3A 3A 3A 3A 3A 1D 1D 1D 1D 1D 1D 1D 0E 0E 0E 0E", "B513 (ASCII)" },
/* 46*/ { UNICODE_MODE, 0, ":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::至", 0, 0, "(591) 37 68 68 68 68 68 74 7E 74 74 74 74 74 3A 3A 3A 3A 3A 3A 3A 1D 1D 1D 1D 1D 1D 1D 0E 0E 0E 0E", "B513 (GB 2312)" },
};
int data_size = sizeof(data) / sizeof(struct item);
char escaped[1024];
for (int i = 0; i < data_size; i++) {
struct zint_symbol* symbol = ZBarcode_Create();
assert_nonnull(symbol, "Symbol not created\n");
symbol->symbology = BARCODE_GRIDMATRIX;
symbol->input_mode = data[i].input_mode;
symbol->eci = data[i].eci;
symbol->debug = ZINT_DEBUG_TEST; // Needed to get codeword dump in errtxt
int length = strlen(data[i].data);
ret = ZBarcode_Encode(symbol, data[i].data, length);
assert_equal(ret, data[i].ret, "i:%d ZBarcode_Encode ret %d != %d\n", i, ret, data[i].ret);
#ifdef TEST_INPUT_GENERATE_EXPECTED
printf(" /*%3d*/ { %s, %d, \"%s\", %s, %d, \"%s\", \"%s\" },\n",
i, testUtilInputModeName(data[i].input_mode), data[i].eci, testUtilEscape(data[i].data, escaped, sizeof(escaped)), testUtilErrorName(data[i].ret),
ret < 5 ? symbol->eci : -1, symbol->errtxt, data[i].comment);
#else
if (ret < 5) {
assert_equal(symbol->eci, data[i].expected_eci, "i:%d eci %d != %d\n", i, symbol->eci, data[i].expected_eci);
assert_zero(strcmp(symbol->errtxt, data[i].expected), "i:%d strcmp(%s, %s) != 0\n", i, symbol->errtxt, data[i].expected);
}
#endif
ZBarcode_Delete(symbol);
}
testFinish();
}
static void test_encode(void)
{
testStart("");
int ret;
struct item {
unsigned char* data;
int input_mode;
int option_1;
int option_2;
int ret;
int expected_rows;
int expected_width;
char* comment;
char* expected;
};
struct item data[] = {
/* 0*/ { "1234", UNICODE_MODE, -1, -1, 0, 18, 18, "",
"111111000000111111"
"101111001100101001"
"100101000010100001"
"111011011100101111"
"101011011000111011"
"111111000000111111"
"000000111111000000"
"001100100001001110"
"001110111111011110"
"001010100011000000"
"010100101001000000"
"000000111111000000"
"111111000000111111"
"101001001100101001"
"111001000000101111"
"111001000100110001"
"111111000000100001"
"111111000000111111"
},
/* 1*/ { "Grid Matrix", UNICODE_MODE, 5, -1, 0, 30, 30, "",
"111111000000111111000000111111"
"110111010110110111010110110011"
"100011011110111111011110111111"
"110001000000100001000000100001"
"101101000000100001000000100001"
"111111000000111111000000111111"
"000000111111000000111111000000"
"010000101111001000101101010000"
"000100111111000000110011010110"
"001110100001011000111111001000"
"001010100001001010111011000010"
"000000111111000000111111000000"
"111111000000111111000000111111"
"110001001110100101001000110001"
"111111011110111111000010111111"
"110111000000100101000010100101"
"100111000000100011011100100011"
"111111000000111111000000111111"
"000000111111000000111111000000"
"010100101111001110101101010110"
"011100111111000000110001001100"
"001010100001000110110111011110"
"000010100001011100110001000000"
"000000111111000000111111000000"
"111111000000111111000000111111"
"110101010000110101010010110101"
"110111000000110011001010101111"
"111101010010110101010010111011"
"101111010010100001010010110111"
"111111000000111111000000111111"
},
/* 2*/ { "AAT2556 电池充电器+降压转换器 200mA至2A tel:86 019 82512738", UNICODE_MODE, 3, 3, 0, 42, 42, "",
"111111000000111111000000111111000000111111"
"101101001100101111001010101011001100101101"
"110001011010110101010000100011000000100001"
"110001001110111111010000101111010010101111"
"101101010100100111011000110111011000111101"
"111111000000111111000000111111000000111111"
"000000111111000000111111000000111111000000"
"001010100111000000100011000110100101001110"
"011010101101001100101011000110110001011110"
"000010111011001110100101001110110101000000"
"000110111111011010100011011100100001000000"
"000000111111000000111111000000111111000000"
"111111000000111111000000111111000000111111"
"101101000110111111011100111001000010101111"
"110111011100111011000010110101010100111111"
"100011010000111101001000100001011110100001"
"100111010110110111011100110101000110100001"
"111111000000111111000000111111000000111111"
"000000111111000000111111000000111111000000"
"001010100001011110110011011110100111001110"
"010010111001000000100011001110111111011110"
"010100111111011000100101010110111111000000"
"001010101011000100110011001110111011000000"
"000000111111000000111111000000111111000000"
"111111000000111111000000111111000000111111"
"101001000000111001011100111011000010101101"
"111011001000111001001010101101011110111101"
"110011010100101001010010101101001110100001"
"100001001000100011011000100101000100100001"
"111111000000111111000000111111000000111111"
"000000111111000000111111000000111111000000"
"001110100011000110100101000000100001001110"
"010000100111001010111101000010100001010100"
"010100100111010000101011000000100001000100"
"001110100001000110111111001100101001000100"
"000000111111000000111111000000111111000000"
"111111000000111111000000111111000000111111"
"101011001110101001001100101011001010101111"
"100011011010100001011100101001010000110101"
"111101000110110001000100100111010110110011"
"100001001000110011011110110001000100100101"
"111111000000111111000000111111000000111111"
},
};
int data_size = sizeof(data) / sizeof(struct item);
for (int i = 0; i < data_size; i++) {
struct zint_symbol* symbol = ZBarcode_Create();
assert_nonnull(symbol, "Symbol not created\n");
symbol->symbology = BARCODE_GRIDMATRIX;
symbol->input_mode = data[i].input_mode;
if (data[i].option_1 != -1) {
symbol->option_1 = data[i].option_1;
}
if (data[i].option_2 != -1) {
symbol->option_2 = data[i].option_2;
}
int length = strlen(data[i].data);
ret = ZBarcode_Encode(symbol, data[i].data, length);
assert_equal(ret, data[i].ret, "i:%d ZBarcode_Encode ret %d != %d (%s)\n", i, ret, data[i].ret, symbol->errtxt);
#ifdef TEST_ENCODE_GENERATE_EXPECTED
printf(" /*%3d*/ { \"%s\", %s, %d, %d, %s, %d, %d, \"%s\",\n",
i, data[i].data, testUtilInputModeName(data[i].input_mode), data[i].option_1, data[i].option_2, testUtilErrorName(data[i].ret),
symbol->rows, symbol->width, data[i].comment);
testUtilModulesDump(symbol, " ", "\n");
printf(" },\n");
#else
if (ret < 5) {
assert_equal(symbol->rows, data[i].expected_rows, "i:%d symbol->rows %d != %d (%s)\n", i, symbol->rows, data[i].expected_rows, data[i].data);
assert_equal(symbol->width, data[i].expected_width, "i:%d symbol->width %d != %d (%s)\n", i, symbol->width, data[i].expected_width, data[i].data);
if (ret == 0) {
int width, row;
ret = testUtilModulesCmp(symbol, data[i].expected, &width, &row);
assert_zero(ret, "i:%d testUtilModulesCmp ret %d != 0 width %d row %d (%s)\n", i, ret, width, row, data[i].data);
}
}
#endif
ZBarcode_Delete(symbol);
}
testFinish();
}
int main()
{
test_options();
test_input();
test_encode();
testReport();
return 0;
}