diff --git a/backend/dotcode.c b/backend/dotcode.c index 7284c432..fec910e6 100644 --- a/backend/dotcode.c +++ b/backend/dotcode.c @@ -32,7 +32,8 @@ /* vim: set ts=4 sw=4 et : */ /* - * Attempts to encode DotCode according to (AIMD013) ISS DotCode Rev. 4.0, DRAFT 0.15, TSC Pre-PR #5, dated May 28, 2019 + * Attempts to encode DotCode according to (AIMD013) ISS DotCode Rev. 4.0, DRAFT 0.15, TSC Pre-PR #5, + * dated May 28, 2019 * Incorporating suggestions from Terry Burton at BWIPP */ @@ -69,7 +70,7 @@ static const unsigned short int dot_patterns[113] = { }; // Printed() routine from Annex A adapted to char array of ASCII 1's and 0's -static int get_dot(char Dots[], const int Hgt, const int Wid, const int x, const int y) { +static int get_dot(const char Dots[], const int Hgt, const int Wid, const int x, const int y) { int retval = 0; if ((x >= 0) && (x < Wid) && (y >= 0) && (y < Hgt)) { @@ -81,7 +82,7 @@ static int get_dot(char Dots[], const int Hgt, const int Wid, const int x, const return retval; } -static int clr_col(char *Dots, const int Hgt, const int Wid, const int x) { +static int clr_col(const char *Dots, const int Hgt, const int Wid, const int x) { int y; for (y = x & 1; y < Hgt; y += 2) { if (get_dot(Dots, Hgt, Wid, x, y)) { @@ -92,7 +93,7 @@ static int clr_col(char *Dots, const int Hgt, const int Wid, const int x) { return 1; } -static int clr_row(char *Dots, const int Hgt, const int Wid, const int y) { +static int clr_row(const char *Dots, const int Hgt, const int Wid, const int y) { int x; for (x = y & 1; x < Wid; x += 2) { if (get_dot(Dots, Hgt, Wid, x, y)) { @@ -104,7 +105,7 @@ static int clr_row(char *Dots, const int Hgt, const int Wid, const int y) { } // calc penalty for empty interior columns -static int col_penalty(char *Dots, int Hgt, int Wid) { +static int col_penalty(const char *Dots, const int Hgt, const int Wid) { int x, penalty = 0, penalty_local = 0; for (x = 1; x < Wid - 1; x++) { @@ -126,7 +127,7 @@ static int col_penalty(char *Dots, int Hgt, int Wid) { } // calc penalty for empty interior rows -static int row_penalty(char *Dots, int Hgt, int Wid) { +static int row_penalty(const char *Dots, const int Hgt, const int Wid) { int y, penalty = 0, penalty_local = 0; for (y = 1; y < Hgt - 1; y++) { @@ -148,7 +149,7 @@ static int row_penalty(char *Dots, int Hgt, int Wid) { } /* Dot pattern scoring routine from Annex A */ -static int score_array(char Dots[], int Hgt, int Wid) { +static int score_array(const char Dots[], const int Hgt, const int Wid) { int x, y, worstedge, first, last, sum; int penalty = 0; @@ -278,7 +279,7 @@ static int score_array(char Dots[], int Hgt, int Wid) { // employing Galois Field GF, where GF is prime, with a prime modulus of PM //------------------------------------------------------------------------- -static void rsencode(int nd, int nc, unsigned char *wd) { +static void rsencode(const int nd, const int nc, unsigned char *wd) { // roots (antilogs): root[0] = 1; for (i = 1; i < GF - 1; i++) root[i] = (PM * root[i - 1]) % GF; static int root[GF - 1] = { 1, 3, 9, 27, 81, 17, 51, 40, 7, 21, @@ -307,7 +308,9 @@ static void rsencode(int nd, int nc, unsigned char *wd) { int NC = NW - ND; // first compute the generator polynomial "c" of order "NC": - memset(c, 0, GF * sizeof(int)); // Keep clang-tidy happy (as far as UndefinedBinaryOperatorResult warning below at least) + + // Keep clang-tidy happy (as far as UndefinedBinaryOperatorResult warning below at least) + memset(c, 0, GF * sizeof(int)); c[0] = 1; for (i = 1; i <= NC; i++) { @@ -335,7 +338,7 @@ static void rsencode(int nd, int nc, unsigned char *wd) { } /* Check if the next character is directly encodable in code set A (Annex F.II.D) */ -static int datum_a(const unsigned char source[], int position, int length) { +static int datum_a(const unsigned char source[], const int position, const int length) { int retval = 0; if (position < length) { @@ -347,8 +350,9 @@ static int datum_a(const unsigned char source[], int position, int length) { return retval; } -/* Check if the next character is directly encodable in code set B (Annex F.II.D). Note changed to return 2 if CR/LF */ -static int datum_b(const unsigned char source[], int position, int length) { +/* Check if the next character is directly encodable in code set B (Annex F.II.D). + * Note changed to return 2 if CR/LF */ +static int datum_b(const unsigned char source[], const int position, const int length) { int retval = 0; if (position < length) { @@ -375,10 +379,10 @@ static int datum_b(const unsigned char source[], int position, int length) { } /* Check if the next characters are directly encodable in code set C (Annex F.II.D) */ -static int datum_c(const unsigned char source[], int position, int length) { +static int datum_c(const unsigned char source[], const int position, const int length) { int retval = 0; - if (position <= length - 2) { + if (position + 1 < length) { if (((source[position] >= '0') && (source[position] <= '9')) && ((source[position + 1] >= '0') && (source[position + 1] <= '9'))) retval = 1; @@ -388,7 +392,7 @@ static int datum_c(const unsigned char source[], int position, int length) { } /* Returns how many consecutive digits lie immediately ahead (Annex F.II.A) */ -static int n_digits(const unsigned char source[], int position, int length) { +static int n_digits(const unsigned char source[], const int position, const int length) { int i; for (i = position; (i < length) && ((source[i] >= '0') && (source[i] <= '9')); i++); @@ -397,7 +401,7 @@ static int n_digits(const unsigned char source[], int position, int length) { } /* checks ahead for 10 or more digits starting "17xxxxxx10..." (Annex F.II.B) */ -static int seventeen_ten(const unsigned char source[], int position, int length) { +static int seventeen_ten(const unsigned char source[], const int position, const int length) { int found = 0; if (n_digits(source, position, length) >= 10) { @@ -413,7 +417,7 @@ static int seventeen_ten(const unsigned char source[], int position, int length) /* checks how many characters ahead can be reached while datum_c is true, * returning the resulting number of codewords (Annex F.II.E) */ -static int ahead_c(const unsigned char source[], int position, int length) { +static int ahead_c(const unsigned char source[], const int position, const int length) { int count = 0; int i; @@ -425,7 +429,7 @@ static int ahead_c(const unsigned char source[], int position, int length) { } /* Annex F.II.F */ -static int try_c(const unsigned char source[], int position, int length) { +static int try_c(const unsigned char source[], const int position, const int length) { int retval = 0; if (n_digits(source, position, length) > 0) { @@ -438,7 +442,7 @@ static int try_c(const unsigned char source[], int position, int length) { } /* Annex F.II.G */ -static int ahead_a(const unsigned char source[], int position, int length) { +static int ahead_a(const unsigned char source[], const int position, const int length) { int count = 0; int i; @@ -451,7 +455,7 @@ static int ahead_a(const unsigned char source[], int position, int length) { } /* Annex F.II.H Note: changed to return number of chars encodable. Number of codewords returned in *p_nx. */ -static int ahead_b(const unsigned char source[], int position, int length, int *p_nx) { +static int ahead_b(const unsigned char source[], const int position, const int length, int *p_nx) { int count = 0; int i, incr; @@ -468,7 +472,7 @@ static int ahead_b(const unsigned char source[], int position, int length, int * } /* checks if the next character is in the range 128 to 255 (Annex F.II.I) */ -static int binary(const unsigned char source[], int length, int position) { +static int binary(const unsigned char source[], const int length, const int position) { int retval = 0; if (position < length && source[position] >= 128) { @@ -479,7 +483,8 @@ static int binary(const unsigned char source[], int length, int position) { } /* Analyse input data stream and encode using algorithm from Annex F */ -static int dotcode_encode_message(struct zint_symbol *symbol, const unsigned char source[], int length, unsigned char *codeword_array, int *binary_finish) { +static int dotcode_encode_message(struct zint_symbol *symbol, const unsigned char source[], const int length, + unsigned char *codeword_array, int *binary_finish) { static char lead_specials[] = "\x09\x1C\x1D\x1E"; // HT, FS, GS, RS int input_position, array_length, i; @@ -644,11 +649,11 @@ static int dotcode_encode_message(struct zint_symbol *symbol, const unsigned cha if (seventeen_ten(source, input_position, length)) { codeword_array[array_length] = 100; // (17)...(10) array_length++; - codeword_array[array_length] = ((source[input_position + 2] - '0') * 10) + (source[input_position + 3] - '0'); + codeword_array[array_length] = to_int(source + input_position + 2, 2); array_length++; - codeword_array[array_length] = ((source[input_position + 4] - '0') * 10) + (source[input_position + 5] - '0'); + codeword_array[array_length] = to_int(source + input_position + 4, 2); array_length++; - codeword_array[array_length] = ((source[input_position + 6] - '0') * 10) + (source[input_position + 7] - '0'); + codeword_array[array_length] = to_int(source + input_position + 6, 2); array_length++; input_position += 10; done = 1; @@ -659,12 +664,13 @@ static int dotcode_encode_message(struct zint_symbol *symbol, const unsigned cha } if ((!done) && (encoding_mode == 'C')) { - if (datum_c(source, input_position, length) || ((source[input_position] == '[') && ((symbol->input_mode & 0x07) == GS1_MODE))) { + if (datum_c(source, input_position, length) + || ((source[input_position] == '[') && ((symbol->input_mode & 0x07) == GS1_MODE))) { if (source[input_position] == '[') { codeword_array[array_length] = 107; // FNC1 input_position++; } else { - codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0'); + codeword_array[array_length] = to_int(source + input_position, 2); input_position += 2; } array_length++; @@ -754,7 +760,7 @@ static int dotcode_encode_message(struct zint_symbol *symbol, const unsigned cha codeword_array[array_length] = 103 + (n - 2); // nx Shift C array_length++; for (i = 0; i < n; i++) { - codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0'); + codeword_array[array_length] = to_int(source + input_position, 2); array_length++; input_position += 2; } @@ -794,7 +800,8 @@ static int dotcode_encode_message(struct zint_symbol *symbol, const unsigned cha done = 1; } else if (input_position != 0) { - /* HT, FS, GS and RS in the first data position would be interpreted as a macro (see table 2) */ + /* HT, FS, GS and RS in the first data position would be interpreted as a macro + * (see table 2) */ switch(source[input_position]) { case 9: // HT codeword_array[array_length] = 97; @@ -882,7 +889,7 @@ static int dotcode_encode_message(struct zint_symbol *symbol, const unsigned cha codeword_array[array_length] = 103 + (n - 2); // nx Shift C array_length++; for (i = 0; i < n; i++) { - codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0'); + codeword_array[array_length] = to_int(source + input_position, 2); array_length++; input_position += 2; } @@ -1011,7 +1018,7 @@ static int dotcode_encode_message(struct zint_symbol *symbol, const unsigned cha codeword_array[array_length] = 101 + n; // Interrupt for nx Shift C array_length++; for (i = 0; i < n; i++) { - codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0'); + codeword_array[array_length] = to_int(source + input_position, 2); array_length++; input_position += 2; } @@ -1136,7 +1143,7 @@ static int make_dotstream(const unsigned char masked_array[], const int array_le /* Determines if a given dot is a reserved corner dot * to be used by one of the last six bits */ -static int is_corner(int column, int row, int width, int height) { +static int is_corner(const int column, const int row, const int width, const int height) { int corner = 0; /* Top Left */ @@ -1178,7 +1185,7 @@ static int is_corner(int column, int row, int width, int height) { } /* Place the dots in the symbol*/ -static void fold_dotstream(char dot_stream[], int width, int height, char dot_array[]) { +static void fold_dotstream(const char dot_stream[], const int width, const int height, char dot_array[]) { int column, row; int input_position = 0; @@ -1243,10 +1250,12 @@ static void fold_dotstream(char dot_stream[], int width, int height, char dot_ar } } -static void apply_mask(int mask, int data_length, unsigned char *masked_codeword_array, unsigned char *codeword_array, int ecc_length) { +static void apply_mask(const int mask, const int data_length, unsigned char *masked_codeword_array, + const unsigned char *codeword_array, const int ecc_length) { int weight = 0; int j; + assert(mask >= 0 && mask <= 3); /* Suppress clang-analyzer taking default branch */ switch (mask) { case 0: masked_codeword_array[0] = 0; @@ -1280,7 +1289,7 @@ static void apply_mask(int mask, int data_length, unsigned char *masked_codeword rsencode(data_length + 1, ecc_length, masked_codeword_array); } -static void force_corners(int width, int height, char *dot_array) { +static void force_corners(const int width, const int height, char *dot_array) { if (width % 2) { // "Vertical" symbol dot_array[0] = '1'; @@ -1307,22 +1316,22 @@ INTERNAL int dotcode(struct zint_symbol *symbol, unsigned char source[], int len int min_dots, min_area; int height, width; int mask_score[8]; + int user_mask; int dot_stream_length; int high_score, best_mask; int binary_finish = 0; int debug = symbol->debug; int padding_dots, is_first; - int codeword_array_len = length * 4 + 8; /* Allow up to 4 codewords per input + 2 (FNC) + 4 (ECI) + 2 (special char 1st position) */ -#ifdef _MSC_VER - unsigned char* masked_codeword_array; -#endif + /* Allow up to 4 codewords per input + 2 (FNC) + 4 (ECI) + 2 (special char 1st position) */ + int codeword_array_len = length * 4 + 8; #ifndef _MSC_VER unsigned char codeword_array[codeword_array_len]; #else - char* dot_stream; - char* dot_array; - unsigned char* codeword_array = (unsigned char *) _alloca(codeword_array_len); + unsigned char *codeword_array = (unsigned char *) _alloca(codeword_array_len); + char *dot_stream; + char *dot_array; + unsigned char *masked_codeword_array; #endif /* _MSC_VER */ if (symbol->eci > 811799) { @@ -1330,9 +1339,15 @@ INTERNAL int dotcode(struct zint_symbol *symbol, unsigned char source[], int len return ZINT_ERROR_INVALID_OPTION; } + user_mask = (symbol->option_3 >> 8) & 0x0F; /* User mask is mask + 1, so >= 1 and <= 8 */ + if (user_mask > 8) { + user_mask = 0; /* Ignore */ + } + data_length = dotcode_encode_message(symbol, source, length, codeword_array, &binary_finish); - /* Suppresses clang-tidy clang-analyzer-core.UndefinedBinaryOperatorResult/uninitialized.ArraySubscript warnings */ + /* Suppresses clang-tidy clang-analyzer-core.UndefinedBinaryOperatorResult/uninitialized.ArraySubscript + * warnings */ assert(data_length > 0); ecc_length = 3 + (data_length / 2); @@ -1422,13 +1437,10 @@ INTERNAL int dotcode(struct zint_symbol *symbol, unsigned char source[], int len #ifndef _MSC_VER char dot_stream[height * width * 3]; - char dot_array[width * height * sizeof (char) ]; + char dot_array[width * height]; #else dot_stream = (char *) _alloca(height * width * 3); - if (!dot_stream) return ZINT_ERROR_MEMORY; - - dot_array = (char *) _alloca(width * height * sizeof (char)); - if (!dot_array) return ZINT_ERROR_MEMORY; + dot_array = (char *) _alloca(width * height); #endif /* Add pad characters */ @@ -1458,46 +1470,19 @@ INTERNAL int dotcode(struct zint_symbol *symbol, unsigned char source[], int len ecc_length = 3 + (data_length / 2); - #ifndef _MSC_VER unsigned char masked_codeword_array[data_length + 1 + ecc_length]; #else - masked_codeword_array = (unsigned char *) _alloca((data_length + 1 + ecc_length) * sizeof (unsigned char)); + masked_codeword_array = (unsigned char *) _alloca(data_length + 1 + ecc_length); #endif /* _MSC_VER */ - /* Evaluate data mask options */ - for (i = 0; i < 4; i++) { - - apply_mask(i, data_length, masked_codeword_array, codeword_array, ecc_length); - - dot_stream_length = make_dotstream(masked_codeword_array, (data_length + ecc_length + 1), dot_stream); - - /* Add pad bits */ - for (jc = dot_stream_length; jc < n_dots; jc++) { - strcat(dot_stream, "1"); - } - - fold_dotstream(dot_stream, width, height, dot_array); - - mask_score[i] = score_array(dot_array, height, width); - + if (user_mask) { + best_mask = user_mask - 1; if (debug & ZINT_DEBUG_PRINT) { - printf("Mask %d score is %d\n", i, mask_score[i]); + printf("Applying mask %d (specified)\n", best_mask); } - } - - high_score = mask_score[0]; - best_mask = 0; - - for (i = 1; i < 4; i++) { - if (mask_score[i] >= high_score) { - high_score = mask_score[i]; - best_mask = i; - } - } - - /* Re-evaluate using forced corners if needed */ - if (best_mask <= (height * width) / 2) { + } else { + /* Evaluate data mask options */ for (i = 0; i < 4; i++) { apply_mask(i, data_length, masked_codeword_array, codeword_array, ecc_length); @@ -1511,25 +1496,62 @@ INTERNAL int dotcode(struct zint_symbol *symbol, unsigned char source[], int len fold_dotstream(dot_stream, width, height, dot_array); - force_corners(width, height, dot_array); - - mask_score[i + 4] = score_array(dot_array, height, width); + mask_score[i] = score_array(dot_array, height, width); if (debug & ZINT_DEBUG_PRINT) { - printf("Mask %d score is %d\n", i + 4, mask_score[i + 4]); + printf("Mask %d score is %d\n", i, mask_score[i]); } } - for (i = 4; i < 8; 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 (debug & ZINT_DEBUG_PRINT) { - printf("Applying mask %d, high_score %d\n", best_mask, high_score); + /* Re-evaluate using forced corners if needed */ + if (high_score <= (height * width) / 2) { + if (debug & ZINT_DEBUG_PRINT) { + printf("High score %d <= %d (height * width) / 2\n", high_score, (height * width) / 2); + } + + for (i = 0; i < 4; i++) { + + apply_mask(i, data_length, masked_codeword_array, codeword_array, ecc_length); + + dot_stream_length = make_dotstream(masked_codeword_array, (data_length + ecc_length + 1), dot_stream); + + /* Add pad bits */ + for (jc = dot_stream_length; jc < n_dots; jc++) { + strcat(dot_stream, "1"); + } + + fold_dotstream(dot_stream, width, height, dot_array); + + force_corners(width, height, dot_array); + + mask_score[i + 4] = score_array(dot_array, height, width); + + if (debug & ZINT_DEBUG_PRINT) { + printf("Mask %d score is %d\n", i + 4, mask_score[i + 4]); + } + } + + for (i = 4; i < 8; i++) { + if (mask_score[i] >= high_score) { + high_score = mask_score[i]; + best_mask = i; + } + } + } + + if (debug & ZINT_DEBUG_PRINT) { + printf("Applying mask %d, high_score %d\n", best_mask, high_score); + } } /* Apply best mask */ diff --git a/backend/library.c b/backend/library.c index 18bc5855..aa419c4f 100644 --- a/backend/library.c +++ b/backend/library.c @@ -677,6 +677,7 @@ unsigned int ZBarcode_Cap(int symbol_id, unsigned int cap_flag) { case BARCODE_QRCODE: case BARCODE_MICROQR: case BARCODE_HANXIN: + case BARCODE_DOTCODE: result |= ZINT_CAP_MASK; break; } diff --git a/backend/tests/test_dotcode.c b/backend/tests/test_dotcode.c index 637e3883..bebe5a9f 100644 --- a/backend/tests/test_dotcode.c +++ b/backend/tests/test_dotcode.c @@ -83,7 +83,7 @@ static void test_input(int index, int generate, int debug) { /* 34*/ { DATA_MODE, -1, "\001\200\201\202\203\204\200\201\202\203\204", -1, 0, "65 41 70 31 5A 35 21 5A 5F 31 5A 35 21 5A 5F", "LatchA (0x65) SOH BinaryLatch (0x70) 0x80 0x81 0x82 0x83 0x80 0x81 0x82 0x83" }, /* 35*/ { UNICODE_MODE, -1, "\001abc\011\015\012\036", -1, 0, "65 41 65 41 42 43 61 60 64", "LatchA (0x65) SOH 6xShiftB (0x65) a b c HT CR/LF RS" }, }; - int data_size = sizeof(data) / sizeof(struct item); + int data_size = ARRAY_SIZE(data); char escaped[1024]; @@ -94,15 +94,9 @@ static void test_input(int index, int generate, int debug) { struct zint_symbol *symbol = ZBarcode_Create(); assert_nonnull(symbol, "Symbol not created\n"); - symbol->symbology = BARCODE_DOTCODE; - symbol->input_mode = data[i].input_mode; - if (data[i].eci != -1) { - symbol->eci = data[i].eci; - } - symbol->debug = ZINT_DEBUG_TEST; // Needed to get codeword dump in errtxt - symbol->debug |= debug; + debug |= ZINT_DEBUG_TEST; // Needed to get codeword dump in errtxt - int length = data[i].length == -1 ? (int) strlen(data[i].data) : data[i].length; + int length = testUtilSetSymbol(symbol, BARCODE_DOTCODE, data[i].input_mode, data[i].eci, -1 /*option_1*/, -1, -1, -1 /*output_options*/, data[i].data, data[i].length, debug); ret = ZBarcode_Encode(symbol, (unsigned char *) 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); @@ -112,7 +106,7 @@ static void test_input(int index, int generate, int debug) { i, testUtilInputModeName(data[i].input_mode), data[i].eci, testUtilEscape(data[i].data, length, escaped, sizeof(escaped)), data[i].length, testUtilErrorName(data[i].ret), symbol->errtxt, data[i].comment); } else { - if (ret < 5) { + if (ret < ZINT_ERROR) { assert_zero(strcmp((char *) symbol->errtxt, data[i].expected), "i:%d strcmp(%s, %s) != 0\n", i, symbol->errtxt, data[i].expected); } } @@ -127,33 +121,26 @@ static void test_encode(int index, int generate, int debug) { testStart(""); + int do_bwipp = (debug & ZINT_DEBUG_TEST_BWIPP) && testUtilHaveGhostscript(); // Only do BWIPP test if asked, too slow otherwise + int ret; struct item { int input_mode; int option_2; + int option_3; char *data; int length; int ret; int expected_rows; int expected_width; + int bwipp_cmp; char *comment; char *expected; }; + // ISS DotCode, Rev 4.0, DRAFT 0.15, TSC Pre-PR #5, MAY 28, 2019 struct item data[] = { - /* 0*/ { UNICODE_MODE, -1, "2741", -1, 0, 10, 13, "ISS DotCode Rev 4.0 Figure 7B Mask = 2 prime (5)", - "1010001010101" - "0001000000000" - "1000100010101" - "0100000101000" - "0000101000100" - "0100010000010" - "1000101010001" - "0101010001000" - "1000100010101" - "0101000100010" - }, - /* 1*/ { GS1_MODE, 64, "[01]00012345678905[17]201231[10]ABC123456", -1, 0, 9, 64, "ISS DotCode Rev 4.0 Figure 1 (left)", + /* 0*/ { GS1_MODE, 64, -1, "[01]00012345678905[17]201231[10]ABC123456", -1, 0, 9, 64, 1, "ISS DotCode Rev 4.0 Figure 1 (left), same", "1010000000101000101010000010000010001010100010101000101000001010" "0100010001010001010001000001010100010100010001000100010101000001" "1010001010000000101010100010001010000010101000000010100010100000" @@ -163,8 +150,8 @@ static void test_encode(int index, int generate, int debug) { "1000000010100010001000001000100010101000100010000010101000100000" "0001010100010001010100010001010000010001010000000101010001010101" "1000100010001000100010100010001010001000101000101000100010000010" - }, - /* 2*/ { GS1_MODE, -1, "[01]00012345678905[17]201231[10]ABC123456", -1, 0, 20, 29, "ISS DotCode Rev 4.0 Figure 1 (right)", + }, + /* 1*/ { GS1_MODE, -1, -1, "[01]00012345678905[17]201231[10]ABC123456", -1, 0, 20, 29, 1, "ISS DotCode Rev 4.0 Figure 1 (right) (and Figure 10), same", "10101000101010100010101000101" "00010100010100010100000001010" "00001010100010000000101010000" @@ -185,8 +172,26 @@ static void test_encode(int index, int generate, int debug) { "00010101000001010100010100010" "10000010101000100000001000001" "01000100010101010000000101010" - }, - /* 3*/ { GS1_MODE, -1, "[17]070620[10]ABC123456", -1, 0, 16, 23, "ISS DotCode Rev 4.0 Figure 6, Mask = 1", + }, + /* 2*/ { GS1_MODE, -1, 1 << 8, "[17]070620[10]ABC123456", -1, 0, 16, 23, 1, "ISS DotCode Rev 4.0 Figure 5 (and Figure 6 top-left) when Mask = 0, same", + "10101000100010000000001" + "01000101010001010000000" + "00100010001000101000100" + "01010001010000000101010" + "00001000100010100010101" + "00000101010101010000010" + "00100010101000000010001" + "00010100000101000100010" + "00001000001000001010101" + "01010101010001000001010" + "10100000100010001000101" + "01000100000100010101000" + "10000010000010100010001" + "00010000010100010101010" + "10101000001000101010001" + "01000001010101010000010" + }, + /* 3*/ { GS1_MODE, -1, 2 << 8, "[17]070620[10]ABC123456", -1, 0, 16, 23, 1, "ISS DotCode Rev 4.0 Figure 6 top-right Mask = 1, same", "10000000001010001000101" "01010101000100000101000" "00100010000000100000001" @@ -203,17 +208,179 @@ static void test_encode(int index, int generate, int debug) { "01010001010001000001010" "10000010101010100010101" "01000101000101010101010" - }, - /* 4*/ { GS1_MODE, 40, "[01]00012345678905", -1, 0, 7, 40, "ISS DotCode Rev 4.0 Figure 8, 7x40 **NOT SAME** but same if force mask 1 instead of mask 6", - "1010000010001010000010100000001010100010" - "0101010001000100010100000101010000010101" - "0000001010100010001010001000100000100010" - "0001000001000101010100000100010100010001" - "1000101010100000100000101000000010101010" - "0100010000010001000101000101010001000001" - "1010000010001000100010101000101000100010" - }, - /* 5*/ { GS1_MODE, 18, "[01]00012345678905", -1, 0, 17, 18, "ISS DotCode Rev 4.0 Figure 8, 17x18 **NOT SAME** no matter what mask; but verified manually against bwipp and tec-it", + }, + /* 4*/ { GS1_MODE, -1, 3 << 8, "[17]070620[10]ABC123456", -1, 0, 16, 23, 1, "ISS DotCode Rev 4.0 Figure 6 bottom-left Mask = 2, same", + "10100000101010100010001" + "01000101000100000000010" + "10101010001010000010000" + "01010100010000010101010" + "00001000101000001000101" + "00000000000001010000010" + "00100010101010101000001" + "00010101010100010000000" + "00001000100010101010001" + "01000000010101010101000" + "10100010100000101000101" + "00000000000101000001010" + "10000010000010100010101" + "01010100010100010001010" + "10101010000000001010001" + "01010101000001000101010" + }, + /* 5*/ { GS1_MODE, -1, 4 << 8, "[17]070620[10]ABC123456", -1, 0, 16, 23, 1, "ISS DotCode Rev 4.0 Figure 6 bottom-right Mask = 3, same", + "10000000100000001010101" + "01010001010100010001000" + "10001000001010101010100" + "01010101000101010000010" + "10101010001000000010101" + "00000100000100010101000" + "00001000101010101000101" + "00000001010000000101010" + "00100010000000000000001" + "01010100010101010101010" + "10000000101010100010001" + "01010101000001010000010" + "10101010100000001000001" + "01000001010001000001010" + "10001000001010001000001" + "01010100000101000100010" + }, + /* 6*/ { GS1_MODE, -1, -1, "[17]070620[10]ABC123456", -1, 0, 16, 23, 1, "ISS DotCode Rev 4.0 Figure 6 top-right, auto Mask = 1, same", + "10000000001010001000101" + "01010101000100000101000" + "00100010000000100000001" + "01010001000001000001000" + "10101010100000001010101" + "00000100010100000100010" + "00000000001010101010001" + "00010001010001000001000" + "00101010101000001010001" + "01000100000001010000000" + "10101000101000101000001" + "00010101000100010101010" + "10001000001010100000101" + "01010001010001000001010" + "10000010101010100010101" + "01000101000101010101010" + }, + /* 7*/ { UNICODE_MODE, -1, 1 << 8, "2741", -1, 0, 10, 13, 0, "ISS DotCode Rev 4.0 Figure 7A top-left Mask = 0, same; BWIPP automatically primes mask", + "1010101010100" + "0000010001010" + "0000101000101" + "0101000000000" + "0000101010100" + "0100010101000" + "1000001000001" + "0101000101010" + "1000100010001" + "0000000000000" + }, + /* 8*/ { UNICODE_MODE, -1, 2 << 8, "2741", -1, 0, 10, 13, 0, "ISS DotCode Rev 4.0 Figure 7A top-right Mask = 1, same; BWIPP automatically primes mask", + "1010001000101" + "0000000100010" + "0000100000001" + "0101010001000" + "1000101000000" + "0101010101010" + "1000101000101" + "0100010101010" + "0000000010001" + "0001000001000" + }, + /* 9*/ { UNICODE_MODE, -1, 3 << 8, "2741", -1, 0, 10, 13, 0, "ISS DotCode Rev 4.0 Figure 7A bottom-left Mask = 2, same; BWIPP automatically primes mask", + "1010001010100" + "0001000000000" + "1000100010101" + "0100000101000" + "0000101000100" + "0100010000010" + "1000101010001" + "0101010001000" + "1000100010101" + "0001000100000" + }, + /* 10*/ { UNICODE_MODE, -1, 4 << 8, "2741", -1, 0, 10, 13, 0, "ISS DotCode Rev 4.0 Figure 7A bottom-right Mask = 3, same; BWIPP automatically primes mask", + "1010001000100" + "0001000001010" + "1000001000000" + "0101000100010" + "1000101010100" + "0101010000010" + "1000100000000" + "0100000101000" + "1000001010001" + "0101010101010" + }, + /* 11*/ { UNICODE_MODE, -1, 5 << 8, "2741", -1, 0, 10, 13, 1, "ISS DotCode Rev 4.0 Figure 7B top-left Mask = 0' (4), same", + "1010101010101" + "0000010001010" + "0000101000101" + "0101000000000" + "0000101010100" + "0100010101000" + "1000001000001" + "0101000101010" + "1000100010001" + "0100000000010" + }, + /* 12*/ { UNICODE_MODE, -1, 6 << 8, "2741", -1, 0, 10, 13, 1, "ISS DotCode Rev 4.0 Figure 7B top-right Mask = 1' (5), same", + "1010001000101" + "0000000100010" + "0000100000001" + "0101010001000" + "1000101000000" + "0101010101010" + "1000101000101" + "0100010101010" + "1000000010001" + "0101000001010" + }, + /* 13*/ { UNICODE_MODE, -1, 7 << 8, "2741", -1, 0, 10, 13, 1, "ISS DotCode Rev 4.0 Figure 7B bottom-left Mask = 2' (6), same", + "1010001010101" + "0001000000000" + "1000100010101" + "0100000101000" + "0000101000100" + "0100010000010" + "1000101010001" + "0101010001000" + "1000100010101" + "0101000100010" + }, + /* 14*/ { UNICODE_MODE, -1, 8 << 8, "2741", -1, 0, 10, 13, 1, "ISS DotCode Rev 4.0 Figure 7B bottom-right Mask = 3' (7), same", + "1010001000101" + "0001000001010" + "1000001000000" + "0101000100010" + "1000101010100" + "0101010000010" + "1000100000000" + "0100000101000" + "1000001010001" + "0101010101010" + }, + /* 15*/ { UNICODE_MODE, -1, -1, "2741", -1, 0, 10, 13, 1, "ISS DotCode Rev 4.0 Figure 7B bottom-left auto Mask = 2' (6), same", + "1010001010101" + "0001000000000" + "1000100010101" + "0100000101000" + "0000101000100" + "0100010000010" + "1000101010001" + "0101010001000" + "1000100010101" + "0101000100010" + }, + /* 16*/ { GS1_MODE, 40, -1, "[01]00012345678905", -1, 0, 7, 40, 1, "ISS DotCode Rev 4.0 Figure 8 top-left 7x40, Mask = 1, same", + "1010101010001000100010100010101000001000" + "0000010101000100010100010000010001000001" + "1010001000001000001000101010001000101000" + "0001010101000000010100010001000001010001" + "1010100010001010000010001010000000101010" + "0001010001010001000100000001010100010001" + "1000100010001000100010100010001010001000" + }, + /* 17*/ { GS1_MODE, 18, -1, "[01]00012345678905", -1, 0, 17, 18, 1, "ISS DotCode Rev 4.0 Figure 8 top-right 17x18 **NOT SAME** no matter what mask; but same as BWIPP and verified manually against tec-it", "101000001000101010" "010100000101010001" "000000101000001010" @@ -231,8 +398,8 @@ static void test_encode(int index, int generate, int debug) { "100010001010001010" "010001010001000101" "100010001000100010" - }, - /* 6*/ { GS1_MODE, 35, "[01]00012345678905", -1, 0, 8, 35, "ISS DotCode Rev 4.0 Figure 8, 8x35; **NOT SAME** using mask 3 prime (7) not 3 so extra dot in bottom right corner compared to figure", + }, + /* 18*/ { GS1_MODE, 35, -1, "[01]00012345678905", -1, 0, 8, 35, 1, "ISS DotCode Rev 4.0 Figure 8 bottom-left 8x35, Mask = 3, same", "10100010000000000010100000100010101" "00010101010001000000010100010100000" "10001000101010101010001010000010101" @@ -240,9 +407,9 @@ static void test_encode(int index, int generate, int debug) { "10101000100000101000100010001000001" "00010100010000010001010001010000000" "10000010101010101010000010000010001" - "01000001000101000100010100010001010" - }, - /* 7*/ { GS1_MODE, 17, "[01]00012345678905", -1, 0, 18, 17, "ISS DotCode Rev 4.0 Figure 8, 18x17 **NOT SAME** no matter what mask; verified manually against bwipp and tec-it", + "01000001000101000100010100010001000" + }, + /* 19*/ { GS1_MODE, 17, -1, "[01]00012345678905", -1, 0, 18, 17, 1, "ISS DotCode Rev 4.0 Figure 8 bottom-right 18x17 **NOT SAME** no matter what mask; same as BWIPP; verified manually against tec-it", "10101000001000001" "01000001010100010" "00000000100010001" @@ -261,8 +428,8 @@ static void test_encode(int index, int generate, int debug) { "01010100010101000" "10101010101010101" "01010101000101010" - }, - /* 8*/ { UNICODE_MODE, 35, "Dots can be Square!", -1, 0, 18, 35, "ISS DotCode Rev 4.0 Figure 11 **NOT SAME**; verified manually against bwipp and tec-it", + }, + /* 20*/ { UNICODE_MODE, 35, -1, "Dots can be Square!", -1, 0, 18, 35, 1, "ISS DotCode Rev 4.0 Figure 11 **NOT SAME**; same as BWIPP; verified manually against tec-it", "10000010101000000000000000101010101" "01010101000101000100010100000001000" "00001000000010101000101010101010000" @@ -281,8 +448,56 @@ static void test_encode(int index, int generate, int debug) { "01000101010101000100000100010101000" "10101000101000001000100010101000101" "01000001000001000101010001000000010" - }, - /* 9*/ { GS1_MODE, -1, "[99]8766", -1, 0, 10, 13, "ISS DotCode Rev 4.0 Table G.1 Mask 0 prime (4); all mask scores match Table G.1", + }, + /* 21*/ { GS1_MODE, -1, 1 << 8, "[99]8766", -1, 0, 10, 13, 0, "ISS DotCode Rev 4.0 Table G.1 Mask 0, same; BWIPP automatically primes mask", + "0000001010000" + "0001010000010" + "0000000010001" + "0100010101000" + "0010101000101" + "0100010101010" + "0010000010000" + "0101010000010" + "0010000000101" + "0101000101010" + }, + /* 22*/ { GS1_MODE, -1, 2 << 8, "[99]8766", -1, 0, 10, 13, 0, "ISS DotCode Rev 4.0 Table G.1 Mask 1, same; BWIPP automatically primes mask", + "0000100000001" + "0001010000000" + "0000000000001" + "0101010000010" + "1010101010101" + "0100000101010" + "0010000010100" + "0100010101000" + "0010101000101" + "0100010101000" + }, + /* 23*/ { GS1_MODE, -1, 3 << 8, "[99]8766", -1, 0, 10, 13, 0, "ISS DotCode Rev 4.0 Table G.1 Mask 2, same; BWIPP automatically primes mask", + "0000100010100" + "0000000000000" + "1000101010101" + "0100010101010" + "0010101000101" + "0101010101010" + "0010100000000" + "0101010100000" + "0000000010001" + "0100000001010" + }, + /* 24*/ { GS1_MODE, -1, 4 << 8, "[99]8766", -1, 0, 10, 13, 0, "ISS DotCode Rev 4.0 Table G.1 Mask 3, same; BWIPP automatically primes mask", + "0000000000000" + "0001010001000" + "1000001010000" + "0101010100010" + "1010101000101" + "0101010101010" + "0010001000101" + "0101010101010" + "1000000010000" + "0100000000010" + }, + /* 25*/ { GS1_MODE, -1, 5 << 8, "[99]8766", -1, 0, 10, 13, 1, "ISS DotCode Rev 4.0 Table G.1 Mask 0' (4), same", "1000001010001" "0001010000010" "0000000010001" @@ -293,9 +508,57 @@ static void test_encode(int index, int generate, int debug) { "0101010000010" "1010000000101" "0101000101010" - }, - /* 10*/ { UNICODE_MODE, 6, "A", -1, 0, 19, 6, "ISS DotCode Rev 4.0 5.2.1.4 2) Table 4, 1 padding dot available; same as bwipp and tec-it except corners lit", - "101010" + }, + /* 26*/ { GS1_MODE, -1, 6 << 8, "[99]8766", -1, 0, 10, 13, 1, "ISS DotCode Rev 4.0 Table G.1 Mask 1' (5), same", + "1000100000001" + "0001010000000" + "0000000000001" + "0101010000010" + "1010101010101" + "0100000101010" + "0010000010100" + "0100010101000" + "1010101000101" + "0100010101010" + }, + /* 27*/ { GS1_MODE, -1, 7 << 8, "[99]8766", -1, 0, 10, 13, 1, "ISS DotCode Rev 4.0 Table G.1 Mask 2' (6), same", + "1000100010101" + "0000000000000" + "1000101010101" + "0100010101010" + "0010101000101" + "0101010101010" + "0010100000000" + "0101010100000" + "1000000010001" + "0100000001010" + }, + /* 28*/ { GS1_MODE, -1, 8 << 8, "[99]8766", -1, 0, 10, 13, 1, "ISS DotCode Rev 4.0 Table G.1 Mask 3' (7), same", + "1000000000001" + "0001010001000" + "1000001010000" + "0101010100010" + "1010101000101" + "0101010101010" + "0010001000101" + "0101010101010" + "1000000010001" + "0100000000010" + }, + /* 29*/ { GS1_MODE, -1, -1, "[99]8766", -1, 0, 10, 13, 1, "ISS DotCode Rev 4.0 Table G.1 auto Mask 0' (4); all mask scores match Table G.1", + "1000001010001" + "0001010000010" + "0000000010001" + "0100010101000" + "0010101000101" + "0100010101010" + "0010000010000" + "0101010000010" + "1010000000101" + "0101000101010" + }, + /* 30*/ { UNICODE_MODE, 6, -1, "A", -1, 0, 19, 6, 1, "ISS DotCode Rev 4.0 5.2.1.4 2) Table 4, 1 padding dot available; verified manually against tec-it", + "101000" "000101" "101010" "000001" @@ -312,12 +575,12 @@ static void test_encode(int index, int generate, int debug) { "101010" "000001" "101000" - "010001" - "101010" - }, - /* 11*/ { UNICODE_MODE, 94, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS", -1, 0, 37, 94, "Interleaved R-S; same as bwipp and tec-it except corners lit", + "010000" + "101000" + }, + /* 31*/ { UNICODE_MODE, 94, -1, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS", -1, 0, 37, 94, 1, "Interleaved R-S; verified manually against tec-it", "1000001010000000100010000010101010101000001000100000001010101000001000001000101010001000101010" - "0101010000000101000001010001010001010100010001000001000000010101010000000101010100010001010101" + "0101010000000101000001010001010001010100010001000001000000010101010000000101010100010001010100" "0010101000100010000010101010000000101010000010101000001000100010100000100010100010001000101000" "0000000100010101000001010000010000010101010100010100000100000101000100010001000001010001010001" "0010100000100010101000001000101010000010001000001010100000101000101010000010001000101010100010" @@ -352,12 +615,23 @@ static void test_encode(int index, int generate, int debug) { "0101010000010100010000010100000101010100000101000001000000010101000101000101010000000101010101" "1000001010001010001000101000001000101010000010101000100010100000101000100000001000101010100000" "0001000101010100000001010101000001010000010001010001000100010000010001000101010001010001000001" - "1010001000001010101000000010101000101000001000001010100000101010001000000010100000001010101010" - }, + "0010001000001010101000000010101000101000001000001010100000101010001000000010100000001010101000" + }, + /* 32*/ { GS1_MODE, 50, -1, "[17]070620[10]ABC123456", -1, 0, 7, 50, 1, "GS1 Gen Spec Figure 5.1-8.", + "10000010101000100010101010001000000010100000100000" + "01000101000101010100000100010000010001000001010101" + "00001010001000101000101000100010001010100000000010" + "01000001000100000101010101010001000001000101000001" + "10001000001010100010001010100000100010100010000010" + "00010001010000000100010101000100010001010001000101" + "10001000001010101000001000100010100010100000101010" + }, }; - int data_size = sizeof(data) / sizeof(struct item); + int data_size = ARRAY_SIZE(data); char escaped[1024]; + char bwipp_buf[8192]; + char bwipp_msg[1024]; for (int i = 0; i < data_size; i++) { @@ -366,33 +640,38 @@ static void test_encode(int index, int generate, int debug) { struct zint_symbol *symbol = ZBarcode_Create(); assert_nonnull(symbol, "Symbol not created\n"); - symbol->symbology = BARCODE_DOTCODE; - symbol->input_mode = data[i].input_mode; - if (data[i].option_2 != -1) { - symbol->option_2 = data[i].option_2; - } - symbol->debug |= debug; - - int length = data[i].length == -1 ? (int) strlen(data[i].data) : data[i].length; + int length = testUtilSetSymbol(symbol, BARCODE_DOTCODE, data[i].input_mode, -1 /*eci*/, -1, data[i].option_2, data[i].option_3, -1 /*output_options*/, data[i].data, data[i].length, debug); ret = ZBarcode_Encode(symbol, (unsigned char *) 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); if (generate) { - printf(" /*%3d*/ { %s, %d, \"%s\", %d, %s, %d, %d, \"%s\",\n", - i, testUtilInputModeName(data[i].input_mode), data[i].option_2, testUtilEscape(data[i].data, length, escaped, sizeof(escaped)), data[i].length, - testUtilErrorName(data[i].ret), symbol->rows, symbol->width, data[i].comment); + printf(" /*%3d*/ { %s, %d, %s, \"%s\", %d, %s, %d, %d, %d, \"%s\",\n", + i, testUtilInputModeName(data[i].input_mode), data[i].option_2, testUtilOption3Name(data[i].option_3), + testUtilEscape(data[i].data, length, escaped, sizeof(escaped)), data[i].length, + testUtilErrorName(data[i].ret), symbol->rows, symbol->width, data[i].bwipp_cmp, data[i].comment); testUtilModulesDump(symbol, " ", "\n"); - printf(" },\n"); + printf(" },\n"); } else { - if (ret < 5) { + if (ret < ZINT_ERROR) { 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); + 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); + + if (do_bwipp && testUtilCanBwipp(i, symbol, -1, data[i].option_2, data[i].option_3, debug)) { + if (!data[i].bwipp_cmp) { + if (debug & ZINT_DEBUG_TEST_PRINT) printf("i:%d %s not BWIPP compatible (%s)\n", i, testUtilBarcodeName(symbol->symbology), data[i].comment); + } else { + ret = testUtilBwipp(i, symbol, -1, data[i].option_2, data[i].option_3, data[i].data, length, NULL, bwipp_buf, sizeof(bwipp_buf)); + assert_zero(ret, "i:%d %s testUtilBwipp ret %d != 0\n", i, testUtilBarcodeName(symbol->symbology), ret); + + ret = testUtilBwippCmp(symbol, bwipp_msg, bwipp_buf, data[i].expected); + assert_zero(ret, "i:%d %s testUtilBwippCmp %d != 0 %s\n actual: %s\nexpected: %s\n", + i, testUtilBarcodeName(symbol->symbology), ret, bwipp_msg, bwipp_buf, data[i].expected); + } } } } @@ -440,7 +719,7 @@ static void test_fuzz(int index, int debug) { /* 5*/ { "\237\032", -1, DATA_MODE, 0 }, // As above L904 /* 6*/ { "\237", -1, DATA_MODE, 0 }, // As above L1090 }; - int data_size = sizeof(data) / sizeof(struct item); + int data_size = ARRAY_SIZE(data); for (int i = 0; i < data_size; i++) { @@ -449,13 +728,7 @@ static void test_fuzz(int index, int debug) { struct zint_symbol *symbol = ZBarcode_Create(); assert_nonnull(symbol, "Symbol not created\n"); - symbol->symbology = BARCODE_DOTCODE; - if (data[i].input_mode != -1) { - symbol->input_mode = data[i].input_mode; - } - symbol->debug |= debug; - - int length = data[i].length == -1 ? (int) strlen(data[i].data) : data[i].length; + int length = testUtilSetSymbol(symbol, BARCODE_DOTCODE, data[i].input_mode, -1 /*eci*/, -1 /*option_1*/, -1, -1, -1 /*output_options*/, data[i].data, data[i].length, debug); ret = ZBarcode_Encode(symbol, (unsigned char *) 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); @@ -484,7 +757,7 @@ static void test_large(int index, int debug) { /* 2*/ { 200, '9', 200, 0 }, // Changes a number of mask scores re pre-Rev. 4 version, but best score still the same (7) /* 3*/ { 30, '\001', 71, 0 }, // Codeword length 72, ECC length 39, for ND + 1 == 112 }; - int data_size = sizeof(data) / sizeof(struct item); + int data_size = ARRAY_SIZE(data); char data_buf[4096]; @@ -495,14 +768,9 @@ static void test_large(int index, int debug) { struct zint_symbol *symbol = ZBarcode_Create(); assert_nonnull(symbol, "Symbol not created\n"); - symbol->symbology = BARCODE_DOTCODE; - symbol->input_mode = DATA_MODE; - symbol->option_2 = data[i].option_2; - symbol->debug |= debug; + memset(data_buf, data[i].datum, data[i].length); - int length = data[i].length; - - memset(data_buf, data[i].datum, length); + int length = testUtilSetSymbol(symbol, BARCODE_DOTCODE, -1 /*input_mode*/, -1 /*eci*/, -1 /*option_1*/, data[i].option_2, -1, -1 /*output_options*/, data_buf, data[i].length, debug); ret = ZBarcode_Encode(symbol, (unsigned char *) data_buf, length); assert_equal(ret, data[i].ret, "i:%d ZBarcode_Encode ret %d != %d (%s)\n", i, ret, data[i].ret, symbol->errtxt); diff --git a/backend/tests/testcommon.c b/backend/tests/testcommon.c index c8d5a722..07ab955f 100644 --- a/backend/tests/testcommon.c +++ b/backend/tests/testcommon.c @@ -1,6 +1,6 @@ /* libzint - the open source barcode library - Copyright (C) 2019 - 2020 Robin Stuart + Copyright (C) 2019 - 2021 Robin Stuart Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -1800,7 +1800,7 @@ static const char *testUtilBwippName(int index, const struct zint_symbol *symbol { "hibcazteccode", BARCODE_HIBC_AZTEC, 112, 1, 0, 1, 0, 0, }, { "", -1, 113, 0, 0, 0, 0, 0, }, { "", -1, 114, 0, 0, 0, 0, 0, }, - { "dotcode", BARCODE_DOTCODE, 115, 0, 0, 0, 0, 0, }, + { "dotcode", BARCODE_DOTCODE, 115, 0, 1, 1, 0, 0, }, { "hanxin", BARCODE_HANXIN, 116, 0, 0, 0, 0, 0, }, { "", -1, 117, 0, 0, 0, 0, 0, }, { "", -1, 118, 0, 0, 0, 0, 0, }, @@ -1898,6 +1898,11 @@ static const char *testUtilBwippName(int index, const struct zint_symbol *symbol printf("i:%d %s not BWIPP compatible, GS1_MODE not supported\n", index, testUtilBarcodeName(symbology)); } return NULL; + } else if (symbology == BARCODE_DOTCODE) { + if (gs1_cvt) { + *gs1_cvt = 1; + } + return "gs1dotcode"; } } @@ -2034,6 +2039,7 @@ int testUtilBwipp(int index, const struct zint_symbol *symbol, int option_1, int int bwipp_row_height[symbol->rows]; int linear_row_height; int gs1_cvt; + int user_mask; FILE *fp = NULL; int cnt; @@ -2312,6 +2318,18 @@ int testUtilBwipp(int index, const struct zint_symbol *symbol, int option_1, int if (option_3 != -1) { bwipp_opts = bwipp_opts_buf; } + } else if (symbology == BARCODE_DOTCODE) { + if (option_2 > 0) { + sprintf(bwipp_opts_buf + (int) strlen(bwipp_opts_buf), "%scolumns=%d", strlen(bwipp_opts_buf) ? " " : "", symbol->option_2); + bwipp_opts = bwipp_opts_buf; + } + if (option_3 != -1) { + user_mask = (option_3 >> 8) & 0x0F; /* User mask is pattern + 1, so >= 1 and <= 8 */ + if (user_mask >= 1 && user_mask <= 8) { + sprintf(bwipp_opts_buf + (int) strlen(bwipp_opts_buf), "%smask=%d", strlen(bwipp_opts_buf) ? " " : "", (user_mask - 1) % 4); + bwipp_opts = bwipp_opts_buf; + } + } } } diff --git a/backend/tests/tools/run_bwipp_tests.sh b/backend/tests/tools/run_bwipp_tests.sh index b94f288f..80b099ed 100755 --- a/backend/tests/tools/run_bwipp_tests.sh +++ b/backend/tests/tools/run_bwipp_tests.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (C) 2020 Robin Stuart +# Copyright (C) 2020 - 2021 Robin Stuart # vim: set ts=4 sw=4 et : set -e @@ -25,6 +25,7 @@ run_bwipp_test "test_code16k" "encode" run_bwipp_test "test_code49" "encode" run_bwipp_test "test_composite" run_bwipp_test "test_dmatrix" "encode" +run_bwipp_test "test_dotcode" "encode" run_bwipp_test "test_gs1" "gs1_reduce" run_bwipp_test "test_imail" "encode" run_bwipp_test "test_medical" "encode" diff --git a/backend_tcl/zint.c b/backend_tcl/zint.c index bd09ce18..cf88ea34 100644 --- a/backend_tcl/zint.c +++ b/backend_tcl/zint.c @@ -109,6 +109,8 @@ 2021-01-14 HaO - Added detection of presence of the Tk package and late initialization. This is a preparation to add a TCL only mode to the DLL. +2021-01-22 GL +- -cols maximum changed from 67 to 108 (DotCode) */ #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) @@ -431,7 +433,7 @@ static char help_message[] = "zint tcl(stub,obj) dll\n" " -border integer: width of a border around the symbol. Use with -bind/-box 1\n" " -box bool: box around bar code, size set be -border\n" /* cli option --cmyk not supported as no corresponding output */ - " -cols integer: PDF417, Codablock F: number of columns\n" + " -cols integer: PDF417, Codablock F, DotCode: number of columns\n" /* cli option --data is standard parameter */ " -dmre bool: Allow Data Matrix Rectangular Extended\n" " -dotsize number: radius ratio of dots from 0.01 to 1.0\n" @@ -449,9 +451,9 @@ static char help_message[] = "zint tcl(stub,obj) dll\n" " -height integer: Symbol height in modules\n" /* cli option --input not supported */ " -init bool: Create reader initialisation symbol (Code 128, Data Matrix)\n" - " number: Set encoding mode (MaxiCode/Composite)\n" + " -mask number: set masking pattern to use (QR/MicroQR/HanXin/DotCode)\n" /* cli option --mirror not supported */ - " -mode: Structured primary data mode (MaxiCode, Composite)\n" + " -mode number: set encoding mode (MaxiCode, Composite)\n" " -nobackground bool: set background transparent\n" " -notext bool: no interpretation line\n" /* cli option --output not supported */ @@ -977,7 +979,7 @@ static int Encode(Tcl_Interp *interp, int objc, case iVers: /* >> Int in Option 2 */ if (intValue < 1 - || (optionIndex==iCols && intValue > 67) + || (optionIndex==iCols && intValue > 108) || (optionIndex==iVers && intValue > 47)) { Tcl_SetObjResult(interp, diff --git a/docs/manual.txt b/docs/manual.txt index 9e620e5a..fd310db7 100644 --- a/docs/manual.txt +++ b/docs/manual.txt @@ -2561,11 +2561,17 @@ DotCode uses a grid of dots in a rectangular formation to encode characters up to a maximum of approximately 450 characters (or 900 numeric digits). The symbology supports ECI encoding and GS1 data encoding. By default Zint will produce a symbol which is approximately square, however the width of the symbol -can be adjusted by using the --cols= option or by setting option_2. Outputting -DotCode to raster images (PNG, GIF, BMP, PCX) will require setting the scale of -the image to a larger value than the default (e.g. approx 10) for the dots to -be plotted correctly. Approximately 33% of the resulting symbol is comprised of -error correction codewords. +can be adjusted by using the --cols= option or by setting option_2 (maximum +108). Outputting DotCode to raster images (PNG, GIF, BMP, PCX) will require +setting the scale of the image to a larger value than the default (e.g. approx +10) for the dots to be plotted correctly. Approximately 33% of the resulting +symbol is comprised of error correction codewords. + +DotCode has two sets of 4 masks, designated 0-3 and 0'-3', the second "prime" +set being the same as the first with corners lit. The best mask to use is +selected automatically by Zint but may be manually specified by using the +--mask= switch with values 0-7, where 4-7 denote 0'-3', or by setting option_3 +to (N + 1) << 8 where N is 0-7. 6.6.12 Han Xin Code ------------------- diff --git a/frontend/main.c b/frontend/main.c index e5d9f7a3..1a5f129b 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -138,7 +138,7 @@ static void usage(void) { " --height=NUMBER Set height of symbol in multiples of X-dimension\n" " -i, --input=FILE Read input data from FILE\n" " --init Create reader initialisation/programming symbol\n" - " --mask=NUMBER Set masking pattern to use (QR/Han Xin)\n" + " --mask=NUMBER Set masking pattern to use (QR/Han Xin/DotCode)\n" " --mirror Use batch data to determine filename\n" " --mode=NUMBER Set encoding mode (MaxiCode/Composite)\n" " --nobackground Remove background (PNG/SVG/EPS only)\n" @@ -904,7 +904,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error 131: Invalid columns value\n"); return do_exit(1); } - if ((val >= 1) && (val <= 67)) { + if ((val >= 1) && (val <= 108)) { my_symbol->option_2 = val; } else { fprintf(stderr, "Warning 111: Number of columns out of range\n"); diff --git a/frontend/tests/test_args.c b/frontend/tests/test_args.c index ac65ed0f..d32d428a 100644 --- a/frontend/tests/test_args.c +++ b/frontend/tests/test_args.c @@ -554,7 +554,7 @@ static void test_checks(int index, int debug) { /* 4*/ { -1, 1001, -1, -1, -1, NULL, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, "Warning 108: Border width out of range" }, /* 5*/ { -1, -1, -1, 0.009, -1, NULL, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, "Warning 106: Invalid dot radius value" }, /* 6*/ { -1, -1, -2, -1, -1, NULL, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, "Error 131: Invalid columns value" }, - /* 7*/ { -1, -1, 68, -1, -1, NULL, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, "Warning 111: Number of columns out of range" }, + /* 7*/ { -1, -1, 109, -1, -1, NULL, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, "Warning 111: Number of columns out of range" }, /* 8*/ { -1, -1, -1, -1, -2, NULL, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, "Error 138: Invalid ECI value" }, /* 9*/ { -1, -1, -1, -1, 1000000, NULL, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, "Warning 118: Invalid ECI code" }, /* 10*/ { -1, -1, -1, -1, -1, "jpg", -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, "Warning 142: File type 'jpg' not supported, ignoring" }, diff --git a/frontend_qt/grpDotCode.ui b/frontend_qt/grpDotCode.ui index 209cb8e2..a2b10492 100644 --- a/frontend_qt/grpDotCode.ui +++ b/frontend_qt/grpDotCode.ui @@ -295,6 +295,358 @@ 50 + + + 51 + + + + + 52 + + + + + 53 + + + + + 54 + + + + + 55 + + + + + 56 + + + + + 57 + + + + + 58 + + + + + 59 + + + + + 60 + + + + + 61 + + + + + 62 + + + + + 63 + + + + + 64 + + + + + 65 + + + + + 66 + + + + + 67 + + + + + 68 + + + + + 69 + + + + + 70 + + + + + 71 + + + + + 72 + + + + + 73 + + + + + 74 + + + + + 75 + + + + + 76 + + + + + 77 + + + + + 78 + + + + + 79 + + + + + 80 + + + + + 81 + + + + + 82 + + + + + 83 + + + + + 84 + + + + + 85 + + + + + 86 + + + + + 87 + + + + + 88 + + + + + 89 + + + + + 90 + + + + + 91 + + + + + 92 + + + + + 93 + + + + + 94 + + + + + 95 + + + + + 96 + + + + + 97 + + + + + 98 + + + + + 99 + + + + + 100 + + + + + 101 + + + + + 102 + + + + + 103 + + + + + 104 + + + + + 105 + + + + + 106 + + + + + 107 + + + + + 108 + + + + + + + + &Mask: + + + cmbDotMask + + + + + + + 8 + + + + Automatic + + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 0' + + + + + 1' + + + + + 2' + + + + + 3' + + diff --git a/frontend_qt/mainwindow.cpp b/frontend_qt/mainwindow.cpp index d7a4cc00..63f65ba5 100644 --- a/frontend_qt/mainwindow.cpp +++ b/frontend_qt/mainwindow.cpp @@ -664,6 +664,7 @@ void MainWindow::change_options() file.close(); tabMain->insertTab(1,m_optionWidget,tr("DotCod&e")); connect(m_optionWidget->findChild("cmbDotCols"), SIGNAL(currentIndexChanged( int )), SLOT(update_preview())); + connect(m_optionWidget->findChild("cmbDotMask"), SIGNAL(currentIndexChanged( int )), SLOT(update_preview())); connect(m_optionWidget->findChild("radDotStand"), SIGNAL(clicked( bool )), SLOT(update_preview())); connect(m_optionWidget->findChild("radDotGS1"), SIGNAL(clicked( bool )), SLOT(update_preview())); } @@ -1297,6 +1298,10 @@ void MainWindow::update_preview() case BARCODE_DOTCODE: m_bc.bc.setSymbol(BARCODE_DOTCODE); m_bc.bc.setOption2(m_optionWidget->findChild("cmbDotCols")->currentIndex()); + item_val = m_optionWidget->findChild("cmbDotMask")->currentIndex(); + if (item_val) { + m_bc.bc.setOption3((item_val << 8) | m_bc.bc.option3()); + } set_gs1_mode(m_optionWidget->findChild("radDotGS1")->isChecked()); break; @@ -1941,6 +1946,7 @@ void MainWindow::save_sub_settings(QSettings &settings, int symbology) { case BARCODE_DOTCODE: settings.setValue("studio/bc/dotcode/cols", get_combobox_index("cmbDotCols")); + settings.setValue("studio/bc/dotcode/mask", get_combobox_index("cmbDotMask")); settings.setValue("studio/bc/dotcode/encoding_mode", get_button_group_index(QStringList() << "radDotStand" << "radDotGS1")); break; @@ -2169,6 +2175,7 @@ void MainWindow::load_sub_settings(QSettings &settings, int symbology) { case BARCODE_DOTCODE: set_combobox_from_setting(settings, "studio/bc/dotcode/cols", "cmbDotCols"); + set_combobox_from_setting(settings, "studio/bc/dotcode/mask", "cmbDotMask"); set_radiobutton_from_setting(settings, "studio/bc/dotcode/encoding_mode", QStringList() << "radDotStand" << "radDotGS1"); break;