From 2b85585e69ea0dc5eb12a119f5a96be212e606c8 Mon Sep 17 00:00:00 2001 From: gitlost Date: Sat, 19 Dec 2020 17:13:35 +0000 Subject: [PATCH] MAXICODE: scmvv option #212; postcode needn't be space-filled --- backend/common.c | 96 ++-- backend/common.h | 17 +- backend/maxicode.c | 390 +++++++------- .../tests/data/print/bmp/maxicode_fig_2.bmp | Bin 0 -> 12062 bytes .../tests/data/print/emf/maxicode_fig_2.emf | Bin 0 -> 26928 bytes .../tests/data/print/eps/maxicode_fig_2.eps | 376 +++++++++++++ .../tests/data/print/gif/maxicode_fig_2.gif | Bin 0 -> 4676 bytes .../tests/data/print/pcx/maxicode_fig_2.pcx | Bin 0 -> 37178 bytes .../tests/data/print/png/maxicode_fig_2.png | Bin 0 -> 3845 bytes .../tests/data/print/svg/maxicode_fig_2.svg | 366 +++++++++++++ .../tests/data/print/tif/maxicode_fig_2.tif | Bin 0 -> 270476 bytes .../tests/data/print/txt/maxicode_fig_2.txt | 33 ++ backend/tests/test_maxicode.c | 497 ++++++++++++++++-- backend/tests/test_print.c | 1 + backend/zint.h | 2 +- backend_tcl/zint.c | 22 +- docs/manual.txt | 59 ++- frontend/main.c | 31 +- frontend_qt/grpMaxicode.ui | 97 +++- frontend_qt/mainwindow.cpp | 63 ++- frontend_qt/mainwindow.h | 2 + frontend_qt/qzint.cpp | 20 +- frontend_qt/qzint.h | 9 +- 23 files changed, 1703 insertions(+), 378 deletions(-) create mode 100644 backend/tests/data/print/bmp/maxicode_fig_2.bmp create mode 100644 backend/tests/data/print/emf/maxicode_fig_2.emf create mode 100644 backend/tests/data/print/eps/maxicode_fig_2.eps create mode 100644 backend/tests/data/print/gif/maxicode_fig_2.gif create mode 100644 backend/tests/data/print/pcx/maxicode_fig_2.pcx create mode 100644 backend/tests/data/print/png/maxicode_fig_2.png create mode 100644 backend/tests/data/print/svg/maxicode_fig_2.svg create mode 100644 backend/tests/data/print/tif/maxicode_fig_2.tif create mode 100644 backend/tests/data/print/txt/maxicode_fig_2.txt diff --git a/backend/common.c b/backend/common.c index 99e45888..d2befb5e 100644 --- a/backend/common.c +++ b/backend/common.c @@ -36,7 +36,7 @@ #endif #include "common.h" -/* Converts a character 0-9 to its equivalent integer value */ +/* Converts a character 0-9, A-F to its equivalent integer value */ INTERNAL int ctoi(const char source) { if ((source >= '0') && (source <= '9')) return (source - '0'); @@ -47,32 +47,6 @@ INTERNAL int ctoi(const char source) { return -1; } -/* Convert an integer value to a string representing its binary equivalent */ -INTERNAL void bin_append(const int arg, const int length, char *binary) { - int posn = (int) 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 */ -INTERNAL int bin_append_posn(const int arg, const int length, char *binary, int posn) { - int i; - int start; - - start = 0x01 << (length - 1); - - for (i = 0; i < length; i++) { - if (arg & (start >> i)) { - binary[posn + i] = '1'; - } else { - binary[posn + i] = '0'; - } - } - return posn + length; -} - /* Converts an integer value to its hexadecimal character */ INTERNAL char itoc(const int source) { if ((source >= 0) && (source <= 9)) { @@ -82,9 +56,25 @@ INTERNAL char itoc(const int source) { } } +/* Converts decimal string of length <= 9 to integer value. Returns -1 if not numeric */ +INTERNAL int to_int(const unsigned char source[], const int length) { + int val = 0; + int i; + + for (i = 0; i < length; i++) { + if (source[i] < '0' || source[i] > '9') { + return -1; + } + val *= 10; + val += source[i] - '0'; + } + + return val; +} + /* Converts lower case characters to upper case in a string source[] */ INTERNAL void to_upper(unsigned char source[]) { - size_t i, src_len = ustrlen(source); + int i, src_len = (int) ustrlen(source); for (i = 0; i < src_len; i++) { if ((source[i] >= 'a') && (source[i] <= 'z')) { @@ -94,9 +84,8 @@ INTERNAL void to_upper(unsigned char source[]) { } /* Verifies that a string only uses valid characters */ -INTERNAL int is_sane(const char test_string[], const unsigned char source[], const size_t length) { - unsigned int j; - size_t i, lt = strlen(test_string); +INTERNAL int is_sane(const char test_string[], const unsigned char source[], const int length) { + int i, j, lt = (int) strlen(test_string); for (i = 0; i < length; i++) { unsigned int latch = FALSE; @@ -126,6 +115,32 @@ INTERNAL void lookup(const char set_string[], const char *table[], const char da } } +/* Convert an integer value to a string representing its binary equivalent */ +INTERNAL void bin_append(const int arg, const int length, char *binary) { + int bin_posn = (int) strlen(binary); + + bin_append_posn(arg, length, binary, bin_posn); + + binary[bin_posn + length] = '\0'; +} + +/* Convert an integer value to a string representing its binary equivalent at a set position */ +INTERNAL int bin_append_posn(const int arg, const int length, char *binary, const int bin_posn) { + int i; + int start; + + start = 0x01 << (length - 1); + + for (i = 0; i < length; i++) { + if (arg & (start >> i)) { + binary[bin_posn + i] = '1'; + } else { + binary[bin_posn + i] = '0'; + } + } + return bin_posn + length; +} + /* Returns the position of data in set_string */ INTERNAL int posn(const char set_string[], const char data) { int i, n = (int) strlen(set_string); @@ -219,6 +234,7 @@ INTERNAL int is_stackable(const int symbology) { case BARCODE_CODABLOCKF: case BARCODE_HIBC_BLOCKF: return 1; + break; } return 0; @@ -239,6 +255,7 @@ INTERNAL int is_extendable(const int symbology) { case BARCODE_UPCA_CC: case BARCODE_UPCE_CC: return 1; + break; } return 0; @@ -249,6 +266,7 @@ INTERNAL int is_composite(const int symbology) { return symbology >= BARCODE_EANX_CC && symbology <= BARCODE_DBAR_EXPSTK_CC; } +/* Whether next two characters are digits */ INTERNAL int istwodigits(const unsigned char source[], const int length, const int position) { if ((position + 1 < length) && (source[position] >= '0') && (source[position] <= '9') && (source[position + 1] >= '0') && (source[position + 1] <= '9')) { @@ -306,7 +324,7 @@ INTERNAL unsigned int decode_utf8(unsigned int *state, unsigned int *codep, cons /* Convert UTF-8 to Unicode. If `disallow_4byte` unset, allow all values (UTF-32). If `disallow_4byte` set, * only allow codepoints <= U+FFFF (ie four-byte sequences not allowed) (UTF-16, no surrogates) */ INTERNAL int utf8_to_unicode(struct zint_symbol *symbol, const unsigned char source[], unsigned int vals[], - int *length, int disallow_4byte) { + int *length, const int disallow_4byte) { int bpos; int jpos; unsigned int codepoint, state = 0; @@ -362,7 +380,8 @@ INTERNAL void set_minimum_height(struct zint_symbol *symbol, const int min_heigh } } -INTERNAL int colour_to_red(int colour) { +/* Returns red component if any of ultra colour indexing "0CBMRYGKW" */ +INTERNAL int colour_to_red(const int colour) { int return_val = 0; switch(colour) { @@ -377,7 +396,8 @@ INTERNAL int colour_to_red(int colour) { return return_val; } -INTERNAL int colour_to_green(int colour) { +/* Returns green component if any of ultra colour indexing "0CBMRYGKW" */ +INTERNAL int colour_to_green(const int colour) { int return_val = 0; switch(colour) { @@ -392,7 +412,8 @@ INTERNAL int colour_to_green(int colour) { return return_val; } -INTERNAL int colour_to_blue(int colour) { +/* Returns blue component if any of ultra colour indexing "0CBMRYGKW" */ +INTERNAL int colour_to_blue(const int colour) { int return_val = 0; switch(colour) { @@ -409,7 +430,7 @@ INTERNAL int colour_to_blue(int colour) { #ifdef ZINT_TEST /* Dumps hex-formatted codewords in symbol->errtxt (for use in testing) */ -void debug_test_codeword_dump(struct zint_symbol *symbol, unsigned char *codewords, int length) { +void debug_test_codeword_dump(struct zint_symbol *symbol, const unsigned char *codewords, const int length) { int i, max = length, cnt_len = 0; if (length > 30) { /* 30*3 < errtxt 92 (100 - "Warning ") chars */ sprintf(symbol->errtxt, "(%d) ", length); /* Place the number of codewords at the front */ @@ -422,7 +443,8 @@ void debug_test_codeword_dump(struct zint_symbol *symbol, unsigned char *codewor symbol->errtxt[strlen(symbol->errtxt) - 1] = '\0'; /* Zap last space */ } -void debug_test_codeword_dump_int(struct zint_symbol *symbol, int *codewords, int length) { +/* Dumps decimal-formatted codewords in symbol->errtxt (for use in testing) */ +void debug_test_codeword_dump_int(struct zint_symbol *symbol, const int *codewords, const int length) { int i, max = 0, cnt_len, errtxt_len; char temp[20]; errtxt_len = sprintf(symbol->errtxt, "(%d) ", length); /* Place the number of codewords at the front */ diff --git a/backend/common.h b/backend/common.h index 714ce20c..68b244b7 100644 --- a/backend/common.h +++ b/backend/common.h @@ -90,11 +90,12 @@ extern "C" { INTERNAL int ctoi(const char source); INTERNAL char itoc(const int source); + INTERNAL int to_int(const unsigned char source[], const int length); INTERNAL void to_upper(unsigned char source[]); - INTERNAL int is_sane(const char test_string[], const unsigned char source[], const size_t length); + INTERNAL int is_sane(const char test_string[], const unsigned char source[], const int length); INTERNAL void lookup(const char set_string[], const char *table[], const char data, char dest[]); INTERNAL void bin_append(const int arg, const int length, char *binary); - INTERNAL int bin_append_posn(const int arg, const int length, char *binary, int posn); + INTERNAL int bin_append_posn(const int arg, const int length, char *binary, const int bin_posn); INTERNAL int posn(const char set_string[], const char data); #ifndef COMMON_INLINE INTERNAL int module_is_set(const struct zint_symbol *symbol, const int y_coord, const int x_coord); @@ -110,16 +111,16 @@ extern "C" { INTERNAL int istwodigits(const unsigned char source[], const int length, const int position); INTERNAL unsigned int decode_utf8(unsigned int *state, unsigned int *codep, const unsigned char byte); INTERNAL int utf8_to_unicode(struct zint_symbol *symbol, const unsigned char source[], unsigned int vals[], - int *length, int disallow_4byte); + int *length, const int disallow_4byte); INTERNAL void set_minimum_height(struct zint_symbol *symbol, const int min_height); - INTERNAL int colour_to_red(int colour); - INTERNAL int colour_to_green(int colour); - INTERNAL int colour_to_blue(int colour); + INTERNAL int colour_to_red(const int colour); + INTERNAL int colour_to_green(const int colour); + INTERNAL int colour_to_blue(const int colour); #ifdef ZINT_TEST - void debug_test_codeword_dump(struct zint_symbol *symbol, unsigned char *codewords, int length); - void debug_test_codeword_dump_int(struct zint_symbol *symbol, int *codewords, int length); + void debug_test_codeword_dump(struct zint_symbol *symbol, const unsigned char *codewords, const int length); + void debug_test_codeword_dump_int(struct zint_symbol *symbol, const int *codewords, const int length); #endif #ifdef __cplusplus diff --git a/backend/maxicode.c b/backend/maxicode.c index 415041fc..41f3d781 100644 --- a/backend/maxicode.c +++ b/backend/maxicode.c @@ -32,13 +32,16 @@ /* vim: set ts=4 sw=4 et : */ /* Includes corrections thanks to Monica Swanson @ Source Technologies */ +#include +#ifdef _MSC_VER +#include +#endif #include "common.h" #include "maxicode.h" #include "reedsol.h" /* Handles error correction of primary message */ -static void maxi_do_primary_check(int maxi_codeword[144]) { - unsigned char data[15]; +static void maxi_do_primary_check(unsigned char maxi_codeword[144]) { unsigned char results[15]; int j; int datalen = 10; @@ -48,17 +51,14 @@ static void maxi_do_primary_check(int maxi_codeword[144]) { rs_init_gf(&rs, 0x43); rs_init_code(&rs, ecclen, 1); - for (j = 0; j < datalen; j += 1) - data[j] = maxi_codeword[j]; - - rs_encode(&rs, datalen, data, results); + rs_encode(&rs, datalen, maxi_codeword, results); for (j = 0; j < ecclen; j += 1) maxi_codeword[ datalen + j] = results[ecclen - 1 - j]; } /* Handles error correction of odd characters in secondary */ -static void maxi_do_secondary_chk_odd(int maxi_codeword[144], int ecclen) { +static void maxi_do_secondary_chk_odd(unsigned char maxi_codeword[144], const int ecclen) { unsigned char data[100]; unsigned char results[30]; int j; @@ -71,9 +71,8 @@ static void maxi_do_secondary_chk_odd(int maxi_codeword[144], int ecclen) { if (ecclen == 20) datalen = 84; - for (j = 0; j < datalen; j += 1) - if (j & 1) // odd - data[(j - 1) / 2] = maxi_codeword[j + 20]; + for (j = 1; j < datalen; j += 2) + data[(j - 1) / 2] = maxi_codeword[j + 20]; rs_encode(&rs, datalen / 2, data, results); @@ -82,7 +81,7 @@ static void maxi_do_secondary_chk_odd(int maxi_codeword[144], int ecclen) { } /* Handles error correction of even characters in secondary */ -static void maxi_do_secondary_chk_even(int maxi_codeword[144], int ecclen) { +static void maxi_do_secondary_chk_even(unsigned char maxi_codeword[144], const int ecclen) { unsigned char data[100]; unsigned char results[30]; int j; @@ -95,9 +94,8 @@ static void maxi_do_secondary_chk_even(int maxi_codeword[144], int ecclen) { rs_init_gf(&rs, 0x43); rs_init_code(&rs, ecclen, 1); - for (j = 0; j < datalen + 1; j += 1) - if (!(j & 1)) // even - data[j / 2] = maxi_codeword[j + 20]; + for (j = 0; j < datalen + 1; j += 2) + data[j / 2] = maxi_codeword[j + 20]; rs_encode(&rs, datalen / 2, data, results); @@ -106,63 +104,75 @@ static void maxi_do_secondary_chk_even(int maxi_codeword[144], int ecclen) { } /* Moves everything up so that a shift or latch can be inserted */ -static void maxi_bump(int set[], int character[], int bump_posn) { - int i; +static void maxi_bump(unsigned char set[], unsigned char character[], const int bump_posn) { - for (i = 143; i > bump_posn; i--) { - set[i] = set[i - 1]; - character[i] = character[i - 1]; - } + memmove(set + bump_posn + 1, set + bump_posn, 143 - bump_posn); + memmove(character + bump_posn + 1, character + bump_posn, 143 - bump_posn); } /* If the value is present in array, return the value, else return badvalue */ -static int value_in_array(int val, int arr[], int badvalue, int arrLength) { +static int value_in_array(const unsigned char val, const unsigned char arr[], const int badvalue, const int arrLength) { int i; - for(i = 0; i < arrLength; i++){ - if(arr[i] == val) return val; + for (i = 0; i < arrLength; i++) { + if (arr[i] == val) return val; } return badvalue; } -/* Choose the best set from previous and next set in the range of the setval array, if no value can be found we return setval[0] */ -static int bestSurroundingSet(int index, int length, int set[], int setval[], int setLength) { +/* Choose the best set from previous and next set in the range of the setval array, if no value can be found we + * return setval[0] */ +static int bestSurroundingSet(const int index, const int length, const unsigned char set[], const unsigned char setval[], + const int setLength) { int badValue = -1; int option1 = value_in_array(set[index - 1], setval, badValue, setLength); if (index + 1 < length) { // we have two options to check (previous & next) int option2 = value_in_array(set[index + 1], setval, badValue, setLength); if (option2 != badValue && option1 > option2) { - return option2; + return option2; } } - // + if (option1 != badValue) { - return option1; + return option1; } return setval[0]; } /* Format text according to Appendix A */ -static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char source[], int length, int eci) { - /* This code doesn't make use of [Lock in C], [Lock in D] - and [Lock in E] and so is not always the most efficient at - compressing data, but should suffice for most applications */ +static int maxi_text_process(unsigned char maxi_codeword[144], const int mode, const unsigned char in_source[], int length, + const int eci, const int scm_vv) { - int set[144], character[144], i, j, done, count, current_set; + unsigned char set[144], character[144] = {0}; + int i, count, current_set, padding_set; - int set15[2] = { 1, 5 }; - int set12[2] = { 1, 2 }; - int set12345[5] = { 1, 2, 3, 4, 5 }; + static const unsigned char set15[2] = { 1, 5 }; + static const unsigned char set12[2] = { 1, 2 }; + static const unsigned char set12345[5] = { 1, 2, 3, 4, 5 }; - if (length > 138) { + const unsigned char *source = in_source; +#ifndef _MSC_VER + unsigned char source_buf[length + 9]; /* For prefixing 9-character SCM sequence */ +#else + unsigned char *source_buf = (unsigned char *) _alloca(length + 9); +#endif + + if (length > 144) { return ZINT_ERROR_TOO_LONG; } - for (i = 0; i < 144; i++) { - set[i] = -1; - character[i] = 0; + if (scm_vv != -1) { /* Add SCM prefix */ + if (length > 135) { + return ZINT_ERROR_TOO_LONG; + } + sprintf((char *) source_buf, "[)>\03601\035%02d", scm_vv); /* [)>\R01\Gvv */ + memcpy(source_buf + 9, in_source, length); + source = source_buf; + length += 9; } + memset(set, 255, 144); + for (i = 0; i < length; i++) { /* Look up characters in table from Appendix A - this gives value and code set for most characters */ @@ -181,7 +191,6 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou for (i = 1; i < length; i++) { if (set[i] == 0) { - done = 0; /* Special character */ if (character[i] == 13) { /* Carriage Return */ @@ -191,37 +200,29 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou } else { character[i] = 0; } - done = 1; - } - if ((done == 0) && (character[i] == 28)) { + } else if (character[i] == 28) { /* FS */ set[i] = bestSurroundingSet(i, length, set, set12345, 5); if (set[i] == 5) { character[i] = 32; } - done = 1; - } - if ((done == 0) && (character[i] == 29)) { + } else if (character[i] == 29) { /* GS */ set[i] = bestSurroundingSet(i, length, set, set12345, 5); if (set[i] == 5) { character[i] = 33; } - done = 1; - } - if ((done == 0) && (character[i] == 30)) { + } else if (character[i] == 30) { /* RS */ set[i] = bestSurroundingSet(i, length, set, set12345, 5); if (set[i] == 5) { character[i] = 34; } - done = 1; - } - if ((done == 0) && (character[i] == 32)) { + } else if (character[i] == 32) { /* Space */ set[i] = bestSurroundingSet(i, length, set, set12345, 5); if (set[i] == 1) { @@ -231,85 +232,61 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou } else { character[i] = 59; } - done = 1; - } - if ((done == 0) && (character[i] == 44)) { + } else if (character[i] == 44) { /* Comma */ set[i] = bestSurroundingSet(i, length, set, set12, 2); if (set[i] == 2) { character[i] = 48; } - done = 1; - } - if ((done == 0) && (character[i] == 46)) { + } else if (character[i] == 46) { /* Full Stop */ set[i] = bestSurroundingSet(i, length, set, set12, 2); if (set[i] == 2) { character[i] = 49; } - done = 1; - } - if ((done == 0) && (character[i] == 47)) { + } else if (character[i] == 47) { /* Slash */ set[i] = bestSurroundingSet(i, length, set, set12, 2); if (set[i] == 2) { character[i] = 50; } - done = 1; - } - if ((done == 0) && (character[i] == 58)) { + } else if (character[i] == 58) { /* Colon */ set[i] = bestSurroundingSet(i, length, set, set12, 2); if (set[i] == 2) { character[i] = 51; } - // done = 1 // As long as last branch not needed } } } + padding_set = set[length - 1] == 2 ? 2 : 1; for (i = length; i < 144; i++) { /* Add the padding */ - if (set[length - 1] == 2) { - set[i] = 2; - } else { - set[i] = 1; - } + set[i] = padding_set; character[i] = 33; } /* Find candidates for number compression */ - if ((mode == 2) || (mode == 3)) { - j = 0; - } else { - j = 9; - } - /* Number compression not allowed in primary message */ + /* Note the prohibition on number compression in the primary message in ISO/IEC 16023:2000 B.1 (1) + applies to modes 2 & 3 only */ count = 0; - for (i = j; i < 144; i++) { + for (i = 0; i < 144; i++) { if ((set[i] == 1) && ((character[i] >= 48) && (character[i] <= 57))) { /* Character is a number */ count++; + if (count == 9) { + /* Nine digits in a row can be compressed */ + memset(set + i - 8, 6, 9); + count = 0; + } } else { count = 0; } - if (count == 9) { - /* Nine digits in a row can be compressed */ - set[i] = 6; - set[i - 1] = 6; - set[i - 2] = 6; - set[i - 3] = 6; - set[i - 4] = 6; - set[i - 5] = 6; - set[i - 6] = 6; - set[i - 7] = 6; - set[i - 8] = 6; - count = 0; - } } /* Add shift and latch characters */ @@ -320,9 +297,9 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou if ((set[i] != current_set) && (set[i] != 6)) { switch (set[i]) { case 1: - if (i+1 < 144 && set[i + 1] == 1) { - if (i+2 < 144 && set[i + 2] == 1) { - if (i+3 < 144 && set[i + 3] == 1) { + if (i + 1 < 144 && set[i + 1] == 1) { + if (i + 2 < 144 && set[i + 2] == 1) { + if (i + 3 < 144 && set[i + 3] == 1) { /* Latch A */ maxi_bump(set, character, i); character[i] = 63; @@ -350,7 +327,7 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou } break; case 2: - if (i+1 < 144 && set[i + 1] == 2) { + if (i + 1 < 144 && set[i + 1] == 2) { /* Latch B */ maxi_bump(set, character, i); character[i] = 63; @@ -365,6 +342,7 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou break; case 3: if (i + 3 < 144 && set[i + 1] == 3 && set[i + 2] == 3 && set[i + 3] == 3) { + /* Lock in C */ maxi_bump(set, character, i); character[i] = 60; maxi_bump(set, character, i); @@ -380,8 +358,8 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou } break; case 4: - /* Shift D */ if (i + 3 < 144 && set[i + 1] == 4 && set[i + 2] == 4 && set[i + 3] == 4) { + /* Lock in D */ maxi_bump(set, character, i); character[i] = 61; maxi_bump(set, character, i); @@ -390,14 +368,15 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou length++; i += 3; } else { + /* Shift D */ maxi_bump(set, character, i); character[i] = 61; length++; } break; case 5: - /* Shift E */ if (i + 3 < 144 && set[i + 1] == 5 && set[i + 2] == 5 && set[i + 3] == 5) { + /* Lock in E */ maxi_bump(set, character, i); character[i] = 62; maxi_bump(set, character, i); @@ -406,10 +385,11 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou length++; i += 3; } else { + /* Shift E */ maxi_bump(set, character, i); character[i] = 62; length++; - } + } break; } i++; @@ -422,14 +402,7 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou do { if (set[i] == 6) { /* Number compression */ - char substring[10]; - int value; - - for (j = 0; j < 9; j++) { - substring[j] = character[i + j]; - } - substring[9] = '\0'; - value = atoi(substring); + int value = to_int(character + i, 9); character[i] = 31; /* NS */ character[i + 1] = (value & 0x3f000000) >> 24; @@ -439,10 +412,8 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou character[i + 5] = (value & 0x3f); i += 6; - for (j = i; j < 141; j++) { - set[j] = set[j + 3]; - character[j] = character[j + 3]; - } + memmove(set + i, set + i + 3, 141 - i); + memmove(character + i, character + i + 3, 141 - i); length -= 3; } else { i++; @@ -458,15 +429,13 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou maxi_bump(set, character, 1); character[1] = eci; length += 2; - } - if ((eci >= 32) && (eci <= 1023)) { + } else if (eci <= 1023) { maxi_bump(set, character, 1); maxi_bump(set, character, 1); character[1] = 0x20 + ((eci >> 6) & 0x0F); character[2] = eci & 0x3F; length += 3; - } - if ((eci >= 1024) && (eci <= 32767)) { + } else if (eci <= 32767) { maxi_bump(set, character, 1); maxi_bump(set, character, 1); maxi_bump(set, character, 1); @@ -474,8 +443,7 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou character[2] = (eci >> 6) & 0x3F; character[3] = eci & 0x3F; length += 4; - } - if (eci >= 32768) { + } else { maxi_bump(set, character, 1); maxi_bump(set, character, 1); maxi_bump(set, character, 1); @@ -490,34 +458,29 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou if (((mode == 2) || (mode == 3)) && (length > 84)) { return ZINT_ERROR_TOO_LONG; - } - if (((mode == 4) || (mode == 6)) && (length > 93)) { + } else if (((mode == 4) || (mode == 6)) && (length > 93)) { + return ZINT_ERROR_TOO_LONG; + + } else if ((mode == 5) && (length > 77)) { return ZINT_ERROR_TOO_LONG; } - if ((mode == 5) && (length > 77)) { - return ZINT_ERROR_TOO_LONG; - } - - /* Copy the encoded text into the codeword array */ if ((mode == 2) || (mode == 3)) { for (i = 0; i < 84; i++) { /* secondary only */ maxi_codeword[i + 20] = character[i]; } - } - if ((mode == 4) || (mode == 6)) { + } else if ((mode == 4) || (mode == 6)) { for (i = 0; i < 9; i++) { /* primary */ maxi_codeword[i + 1] = character[i]; } for (i = 0; i < 84; i++) { /* secondary */ maxi_codeword[i + 20] = character[i + 9]; } - } - if (mode == 5) { + } else { /* Mode 5 */ for (i = 0; i < 9; i++) { /* primary */ maxi_codeword[i + 1] = character[i]; } @@ -530,18 +493,11 @@ static int maxi_text_process(int maxi_codeword[144], int mode, unsigned char sou } /* Format structured primary for Mode 2 */ -static void maxi_do_primary_2(int maxi_codeword[144], char postcode[], int country, int service) { - size_t postcode_length; - int postcode_num, i; +static void maxi_do_primary_2(unsigned char maxi_codeword[144], const unsigned char postcode[], const int postcode_length, + const int country, const int service) { + int postcode_num; - for (i = 0; i < 10; i++) { - if ((postcode[i] < '0') || (postcode[i] > '9')) { - postcode[i] = '\0'; - } - } - - postcode_length = strlen(postcode); - postcode_num = atoi(postcode); + postcode_num = atoi((const char *) postcode); maxi_codeword[0] = ((postcode_num & 0x03) << 4) | 2; maxi_codeword[1] = ((postcode_num & 0xfc) >> 2); @@ -556,22 +512,13 @@ static void maxi_do_primary_2(int maxi_codeword[144], char postcode[], int count } /* Format structured primary for Mode 3 */ -static void maxi_do_primary_3(int maxi_codeword[144], char postcode[], int country, int service) { - int i, h; +static void maxi_do_primary_3(unsigned char maxi_codeword[144], unsigned char postcode[], const int country, + const int service) { + int i; - h = strlen(postcode); - to_upper((unsigned char*) postcode); - for (i = 0; i < h; i++) { - if ((postcode[i] >= 'A') && (postcode[i] <= 'Z')) { - /* (Capital) letters shifted to Code Set A values */ - postcode[i] -= 64; - } - if (((postcode[i] == 27) || (postcode[i] == 31)) || ((postcode[i] == 33) || (postcode[i] >= 59))) { - /* Not a valid postcode character */ - postcode[i] = ' '; - } - /* Input characters lower than 27 (NUL - SUB) in postcode are - interpreted as capital letters in Code Set A (e.g. LF becomes 'J') */ + /* Convert to Code Set A */ + for (i = 0; i < 6; i++) { + postcode[i] = maxiSymbolChar[postcode[i]]; } maxi_codeword[0] = ((postcode[5] & 0x03) << 4) | 3; @@ -586,25 +533,26 @@ static void maxi_do_primary_3(int maxi_codeword[144], char postcode[], int count maxi_codeword[9] = ((service & 0x3f0) >> 4); } -INTERNAL int maxicode(struct zint_symbol *symbol, unsigned char local_source[], const int length) { - int i, j, block, bit, mode, lp = 0; - int bit_pattern[7], error_number = 0, eclen; - int maxi_codeword[144] = {0}; - char postcode[12], countrystr[4], servicestr[4]; +INTERNAL int maxicode(struct zint_symbol *symbol, unsigned char source[], int length) { + int i, j, block, shift, mode, lp = 0; + int error_number = 0, eclen; + unsigned char maxi_codeword[144] = {0}; + int scm_vv = -1; mode = symbol->option_1; - strcpy(postcode, ""); - strcpy(countrystr, ""); - strcpy(servicestr, ""); - if (mode == -1) { /* If mode is unspecified */ - lp = strlen(symbol->primary); + if (mode <= 0) { /* If mode is unspecified (-1) or to be auto-determined (0) between 2 and 3 */ + lp = (int) strlen(symbol->primary); if (lp == 0) { + if (mode == 0) { /* Require primary message to auto-determine between 2 and 3 */ + strcpy(symbol->errtxt, "554: Primary Message empty"); + return ZINT_ERROR_INVALID_DATA; + } mode = 4; } else { mode = 2; - for (i = 0; i < 10 && i < lp; i++) { - if ((symbol->primary[i] < 48) || (symbol->primary[i] > 57)) { + for (i = 0; i < lp - 6; i++) { + if (((symbol->primary[i] < '0') || (symbol->primary[i] > '9')) && (symbol->primary[i] != ' ')) { mode = 3; break; } @@ -613,65 +561,85 @@ INTERNAL int maxicode(struct zint_symbol *symbol, unsigned char local_source[], } if ((mode < 2) || (mode > 6)) { /* Only codes 2 to 6 supported */ - strcpy(symbol->errtxt, "550: Invalid Maxicode Mode"); + strcpy(symbol->errtxt, "550: Invalid MaxiCode Mode"); return ZINT_ERROR_INVALID_OPTION; } if ((mode == 2) || (mode == 3)) { /* Modes 2 and 3 need data in symbol->primary */ + unsigned char postcode[10]; int countrycode; int service; + int postcode_len; if (lp == 0) { /* Mode set manually means lp doesn't get set */ - lp = strlen(symbol->primary); + lp = (int) strlen(symbol->primary); } - if (lp != 15) { - strcpy(symbol->errtxt, "551: Invalid Primary Message"); + if (lp < 7 || lp > 15) { /* 1 to 9 character postcode + 3 digit country code + 3 digit service class */ + strcpy(symbol->errtxt, "551: Invalid length for Primary Message"); + return ZINT_ERROR_INVALID_DATA; + } + postcode_len = lp - 6; + + countrycode = to_int((const unsigned char *) (symbol->primary + postcode_len), 3); + service = to_int((const unsigned char *) (symbol->primary + postcode_len + 3), 3); + + if (countrycode == -1 || service == -1) { /* check that country code and service are numeric */ + strcpy(symbol->errtxt, "552: Non-numeric country code or service class in Primary Message"); return ZINT_ERROR_INVALID_DATA; } - for (i = 9; i < 15; i++) { /* check that country code and service are numeric */ - if ((symbol->primary[i] < '0') || (symbol->primary[i] > '9')) { - strcpy(symbol->errtxt, "552: Invalid Primary Message"); - return ZINT_ERROR_INVALID_DATA; - } - } - - memcpy(postcode, symbol->primary, 9); - postcode[9] = '\0'; + memcpy(postcode, symbol->primary, postcode_len); + postcode[postcode_len] = '\0'; if (mode == 2) { - for (i = 0; i < 10; i++) { + for (i = 0; i < postcode_len; i++) { if (postcode[i] == ' ') { postcode[i] = '\0'; + postcode_len = i; + break; + } else if (postcode[i] < '0' || postcode[i] > '9') { + strcpy(symbol->errtxt, "555: Non-numeric postcode in Primary Message"); + return ZINT_ERROR_INVALID_DATA; } } - } else if (mode == 3) { + maxi_do_primary_2(maxi_codeword, postcode, postcode_len, countrycode, service); + } else { + /* Just truncate and space-pad */ postcode[6] = '\0'; - } - - countrystr[0] = symbol->primary[9]; - countrystr[1] = symbol->primary[10]; - countrystr[2] = symbol->primary[11]; - countrystr[3] = '\0'; - - servicestr[0] = symbol->primary[12]; - servicestr[1] = symbol->primary[13]; - servicestr[2] = symbol->primary[14]; - servicestr[3] = '\0'; - - countrycode = atoi(countrystr); - service = atoi(servicestr); - - if (mode == 2) { - maxi_do_primary_2(maxi_codeword, postcode, countrycode, service); - } - if (mode == 3) { + for (i = postcode_len; i < 6; i++) { + postcode[i] = ' '; + } + /* Upper-case and check for Code Set A characters only */ + to_upper(postcode); + for (i = 0; i < 6; i++) { + /* Don't allow Code Set A control characters CR, RS, GS and RS */ + if (postcode[i] < ' ' || maxiCodeSet[postcode[i]] > 1) { + strcpy(symbol->errtxt, "556: Invalid characters in postcode in Primary Message"); + return ZINT_ERROR_INVALID_DATA; + } + } maxi_do_primary_3(maxi_codeword, postcode, countrycode, service); } + + if (symbol->option_2) { /* Check for option_2 = vv + 1, where vv is version of SCM prefix "[)>\R01\Gvv" */ + if (symbol->option_2 < 0 || symbol->option_2 > 100) { + strcpy(symbol->errtxt, "557: Invalid SCM prefix version"); + return ZINT_ERROR_INVALID_OPTION; + } + scm_vv = symbol->option_2 - 1; + } + + if (symbol->debug & ZINT_DEBUG_PRINT) { + printf("Postcode: %s, Country Code: %d, Service Class: %d\n", postcode, countrycode, service); + } } else { maxi_codeword[0] = mode; } - i = maxi_text_process(maxi_codeword, mode, local_source, length, symbol->eci); + if (symbol->debug & ZINT_DEBUG_PRINT) { + printf("Mode: %d\n", mode); + } + + i = maxi_text_process(maxi_codeword, mode, source, length, symbol->eci, scm_vv); if (i == ZINT_ERROR_TOO_LONG) { strcpy(symbol->errtxt, "553: Input data too long"); return i; @@ -688,22 +656,26 @@ INTERNAL int maxicode(struct zint_symbol *symbol, unsigned char local_source[], maxi_do_secondary_chk_even(maxi_codeword, eclen / 2); // do error correction of even maxi_do_secondary_chk_odd(maxi_codeword, eclen / 2); // do error correction of odd + if (symbol->debug & ZINT_DEBUG_PRINT) { + printf("Codewords:"); + for (i = 0; i < 144; i++) printf(" %d", maxi_codeword[i]); + printf("\n"); + } +#ifdef ZINT_TEST + if (symbol->debug & ZINT_DEBUG_TEST) { + debug_test_codeword_dump(symbol, maxi_codeword, 144); + } +#endif + /* Copy data into symbol grid */ for (i = 0; i < 33; i++) { for (j = 0; j < 30; j++) { block = (MaxiGrid[(i * 30) + j] + 5) / 6; - bit = (MaxiGrid[(i * 30) + j] + 5) % 6; if (block != 0) { + shift = 5 - ((MaxiGrid[(i * 30) + j] + 5) % 6); - bit_pattern[0] = (maxi_codeword[block - 1] & 0x20) >> 5; - bit_pattern[1] = (maxi_codeword[block - 1] & 0x10) >> 4; - bit_pattern[2] = (maxi_codeword[block - 1] & 0x8) >> 3; - bit_pattern[3] = (maxi_codeword[block - 1] & 0x4) >> 2; - bit_pattern[4] = (maxi_codeword[block - 1] & 0x2) >> 1; - bit_pattern[5] = (maxi_codeword[block - 1] & 0x1); - - if (bit_pattern[bit] != 0) { + if ((maxi_codeword[block - 1] >> shift) & 0x1) { set_module(symbol, i, j); } } diff --git a/backend/tests/data/print/bmp/maxicode_fig_2.bmp b/backend/tests/data/print/bmp/maxicode_fig_2.bmp new file mode 100644 index 0000000000000000000000000000000000000000..73ac378352911b1002f99275df903083e2c0ee36 GIT binary patch literal 12062 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 U&E*B2jY;DfUgA2%-6He+4|GE^E&u=k literal 0 HcmV?d00001 diff --git a/backend/tests/data/print/emf/maxicode_fig_2.emf b/backend/tests/data/print/emf/maxicode_fig_2.emf new file mode 100644 index 0000000000000000000000000000000000000000..9cc448775e8d80dece2cbbc9fbeb2187fbce370f GIT binary patch literal 26928 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 G0029-^nuO* literal 0 HcmV?d00001 diff --git a/backend/tests/data/print/pcx/maxicode_fig_2.pcx b/backend/tests/data/print/pcx/maxicode_fig_2.pcx new file mode 100644 index 0000000000000000000000000000000000000000..fe4046ecfb0f9c2bdc6004689e3ee00a4d2e17c6 GIT binary patch literal 37178 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 BX%7GZ literal 0 HcmV?d00001 diff --git a/backend/tests/data/print/png/maxicode_fig_2.png b/backend/tests/data/print/png/maxicode_fig_2.png new file mode 100644 index 0000000000000000000000000000000000000000..2366bcdd6c2926b24ae27c671a9a562ea12ba9d9 GIT binary patch literal 3845 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 + + + Zint Generated Symbol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/tests/data/print/tif/maxicode_fig_2.tif b/backend/tests/data/print/tif/maxicode_fig_2.tif new file mode 100644 index 0000000000000000000000000000000000000000..56603e78ee0fb9230e92c07b6488a4116b7414af GIT binary patch literal 270476 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 VnEm$;e)M+xprimary, data[i].primary); ret = ZBarcode_Encode(symbol, (unsigned char *) data_buf, length); @@ -87,6 +112,97 @@ static void test_large(int index, int debug) { testFinish(); } +static void test_input(int index, int generate, int debug) { + + testStart(""); + + int ret; + struct item { + int input_mode; + int eci; + int option_1; + int option_2; + char *data; + int length; + char *primary; + int ret; + int expected_width; + char *expected; + char *comment; + }; + struct item data[] = { + /* 0*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "", 0, 30, "(144) 04 01 21 21 21 21 21 21 21 21 08 0E 19 2B 20 0C 24 06 32 1C 21 21 21 21 21 21 21 21", "" }, + /* 1*/ { UNICODE_MODE, -1, 2, -1, "A", -1, "", ZINT_ERROR_INVALID_DATA, 0, "Error 551: Invalid length for Primary Message", "" }, + /* 2*/ { UNICODE_MODE, -1, 2, -1, "A", -1, "A123456", ZINT_ERROR_INVALID_DATA, 0, "Error 555: Non-numeric postcode in Primary Message", "" }, + /* 3*/ { UNICODE_MODE, -1, 2, -1, "A", -1, "1123456", 0, 30, "(144) 12 00 00 00 00 10 30 1E 20 1C 1A 3D 1C 0D 1B 15 3C 17 3C 08 01 21 21 21 21 21 21 21", "1-digit postcode" }, + /* 4*/ { UNICODE_MODE, -1, 2, -1, "A", -1, "123456789123456", 0, 30, "(144) 12 05 0D 2F 35 11 32 1E 20 1C 0D 1D 3B 12 22 3F 30 14 23 1A 01 21 21 21 21 21 21 21", "9-digit postcode" }, + /* 5*/ { UNICODE_MODE, -1, 2, -1, "A", -1, "1234567890123456", ZINT_ERROR_INVALID_DATA, 0, "Error 551: Invalid length for Primary Message", "10-digit postcode" }, + /* 6*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "1123456", 0, 30, "(144) 12 00 00 00 00 10 30 1E 20 1C 1A 3D 1C 0D 1B 15 3C 17 3C 08 01 21 21 21 21 21 21 21", "1-digit postcode" }, + /* 7*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "123456789123456", 0, 30, "(144) 12 05 0D 2F 35 11 32 1E 20 1C 0D 1D 3B 12 22 3F 30 14 23 1A 01 21 21 21 21 21 21 21", "9-digit postcode" }, + /* 8*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "1234567890123456", ZINT_ERROR_INVALID_DATA, 0, "Error 551: Invalid length for Primary Message", "10-digit postcode" }, + /* 9*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "123456", ZINT_ERROR_INVALID_DATA, 0, "Error 551: Invalid length for Primary Message", "0-digit postcode" }, + /* 10*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "12345678123456", 0, 30, "(144) 22 13 21 31 0B 00 32 1E 20 1C 04 14 07 30 10 07 08 28 1D 09 01 21 21 21 21 21 21 21", "8-digit postcode" }, + /* 11*/ { UNICODE_MODE, -1, 3, -1, "A", -1, "", ZINT_ERROR_INVALID_DATA, 0, "Error 551: Invalid length for Primary Message", "" }, + /* 12*/ { UNICODE_MODE, -1, 3, -1, "A", -1, "A123456", 0, 30, "(144) 03 08 08 08 08 18 30 1E 20 1C 22 35 1C 0F 02 1A 26 04 10 31 01 21 21 21 21 21 21 21", "1-alphanumeric postcode" }, + /* 13*/ { UNICODE_MODE, -1, 3, -1, "A", -1, "1123456", 0, 30, "(144) 03 08 08 08 08 18 3C 1E 20 1C 13 37 07 2C 26 2D 18 29 3F 2C 01 21 21 21 21 21 21 21", "1-digit postcode" }, + /* 14*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "A123456", 0, 30, "(144) 03 08 08 08 08 18 30 1E 20 1C 22 35 1C 0F 02 1A 26 04 10 31 01 21 21 21 21 21 21 21", "1-alphanumeric postcode" }, + /* 15*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "ABCDEF123456", 0, 30, "(144) 23 11 01 31 20 10 30 1E 20 1C 3C 1D 22 03 19 15 0F 20 0F 2A 01 21 21 21 21 21 21 21", "6-alphanumeric postcode" }, + /* 16*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "ABCDEFG123456", 0, 30, "(144) 23 11 01 31 20 10 30 1E 20 1C 3C 1D 22 03 19 15 0F 20 0F 2A 01 21 21 21 21 21 21 21", "7-alphanumeric postcode truncated" }, + /* 17*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "ABCDE123456", 0, 30, "(144) 03 18 01 31 20 10 30 1E 20 1C 0F 38 38 1A 39 10 2F 37 22 12 01 21 21 21 21 21 21 21", "5-alphanumeric postcode" }, + /* 18*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "AAAAAA 840001", 0, 30, "(144) 13 10 10 10 10 10 00 12 07 00 17 36 2E 38 04 29 16 0D 27 16 01 21 21 21 21 21 21 21", "6-alphanumeric postcode with padding" }, + /* 19*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "AAAAA A840001", 0, 30, "(144) 03 18 10 10 10 10 00 12 07 00 19 07 29 31 26 01 23 2C 2E 07 01 21 21 21 21 21 21 21", "7-alphanumeric with embedded padding truncated" }, + /* 20*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "AA\015AAA840001", ZINT_ERROR_INVALID_DATA, 0, "Error 556: Invalid characters in postcode in Primary Message", "Alphanumeric postcode with CR" }, + /* 21*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "A#%-/A840001", 0, 30, "(144) 13 30 1B 1B 39 18 00 12 07 00 3F 1E 25 07 2A 1E 14 3C 28 2D 01 21 21 21 21 21 21 21", "Alphanumeric postcode with non-control Code A chars" }, + /* 22*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "1A23456", ZINT_ERROR_INVALID_DATA, 0, "Error 552: Non-numeric country code or service class in Primary Message", "Non-numeric country code" }, + /* 23*/ { UNICODE_MODE, -1, -1, -1, "A", -1, "12345678912345A", ZINT_ERROR_INVALID_DATA, 0, "Error 552: Non-numeric country code or service class in Primary Message", "Non-numeric service class" }, + /* 24*/ { UNICODE_MODE, -1, 0, -1, "A", -1, "123456789123456", 0, 30, "(144) 12 05 0D 2F 35 11 32 1E 20 1C 0D 1D 3B 12 22 3F 30 14 23 1A 01 21 21 21 21 21 21 21", "Auto-determine mode 2" }, + /* 25*/ { UNICODE_MODE, -1, 0, -1, "A", -1, "", ZINT_ERROR_INVALID_DATA, 0, "Error 554: Primary Message empty", "Auto-determine mode 2/3 requires primary message" }, + /* 26*/ { UNICODE_MODE, -1, 0, -1, "A", -1, "A23456123456", 0, 30, "(144) 23 1D 0D 3D 2C 1C 30 1E 20 1C 24 35 30 31 2A 0D 17 14 16 3D 01 21 21 21 21 21 21 21", "Auto-determine mode 3" }, + /* 27*/ { UNICODE_MODE, -1, -1, 100, "A", -1, "123456123456", 0, 30, "(144) 02 10 22 07 00 20 31 1E 20 1C 0E 29 13 1B 0D 26 36 25 3B 22 3B 2A 29 3B 28 1E 30 31", "SCM prefix version" }, + /* 28*/ { UNICODE_MODE, -1, -1, 101, "A", -1, "123456123456", ZINT_ERROR_INVALID_OPTION, 0, "Error 557: Invalid SCM prefix version", "SCM prefix version" }, + /* 29*/ { UNICODE_MODE, 3, -1, -1, "A", -1, "", 0, 30, "(144) 04 1B 03 01 21 21 21 21 21 21 2F 14 23 21 05 24 27 00 24 0C 21 21 21 21 21 21 21 21", "" }, + /* 30*/ { UNICODE_MODE, 32, -1, -1, "A", -1, "", 0, 30, "(144) 04 1B 20 20 01 21 21 21 21 21 3D 15 0F 30 0D 22 24 35 22 06 21 21 21 21 21 21 21 21", "" }, + /* 31*/ { UNICODE_MODE, 1024, -1, -1, "A", -1, "", 0, 30, "(144) 04 1B 30 10 00 01 21 21 21 21 11 2F 15 10 1D 29 06 35 14 2B 21 21 21 21 21 21 21 21", "" }, + /* 32*/ { UNICODE_MODE, 32768, -1, -1, "A", -1, "", 0, 30, "(144) 04 1B 38 08 00 00 01 21 21 21 10 30 3A 04 26 23 0E 21 3D 0F 21 21 21 21 21 21 21 21", "" }, + /* 33*/ { UNICODE_MODE, -1, 1, -1, "A", -1, "", ZINT_ERROR_INVALID_OPTION, 0, "Error 550: Invalid MaxiCode Mode", "" }, + }; + int data_size = sizeof(data) / sizeof(struct item); + + char escaped[1024]; + + for (int i = 0; i < data_size; i++) { + + if (index != -1 && i != index) continue; + + struct zint_symbol *symbol = ZBarcode_Create(); + assert_nonnull(symbol, "Symbol not created\n"); + + symbol->debug = ZINT_DEBUG_TEST; // Needed to get codeword dump in errtxt + + int length = testUtilSetSymbol(symbol, BARCODE_MAXICODE, data[i].input_mode, data[i].eci, data[i].option_1, data[i].option_2, -1, -1 /*output_options*/, data[i].data, data[i].length, debug); + strcpy(symbol->primary, data[i].primary); + + 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, %d, %d, \"%s\", %d, \"%s\", %s, %d, \"%s\", \"%s\" },\n", + i, testUtilInputModeName(data[i].input_mode), data[i].eci, data[i].option_1, data[i].option_2, + testUtilEscape(data[i].data, length, escaped, sizeof(escaped)), data[i].length, data[i].primary, + testUtilErrorName(data[i].ret), symbol->width, symbol->errtxt, data[i].comment); + } else { + if (ret < 5) { + 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); + } + assert_zero(strcmp(symbol->errtxt, data[i].expected), "i:%d strcmp(%s, %s) != 0\n", i, symbol->errtxt, data[i].expected); + } + + ZBarcode_Delete(symbol); + } + + testFinish(); +} + static void test_encode(int index, int generate, int debug) { testStart(""); @@ -95,7 +211,9 @@ static void test_encode(int index, int generate, int debug) { struct item { int input_mode; int option_1; + int option_2; char *data; + int length; char* primary; int ret; @@ -105,7 +223,7 @@ static void test_encode(int index, int generate, int debug) { char *expected; }; struct item data[] = { - /* 0*/ { -1, -1, "THIS IS A 93 CHARACTER CODE SET A MESSAGE THAT FILLS A MODE 4, UNAPPENDED, MAXICODE SYMBOL...", "", 0, 33, 30, "ISO/IEC 16023:2000 Figure 2", + /* 0*/ { -1, -1, -1, "THIS IS A 93 CHARACTER CODE SET A MESSAGE THAT FILLS A MODE 4, UNAPPENDED, MAXICODE SYMBOL...", -1, "", 0, 33, 30, "ISO/IEC 16023:2000 Figure 2, same", "011111010000001000001000100111" "000100000001000000001010000000" "001011001100100110110010010010" @@ -140,7 +258,7 @@ static void test_encode(int index, int generate, int debug) { "001001101111101101101010011100" "001011000000111101100100001000" }, - /* 1*/ { -1, 4, "MaxiCode (19 chars)", "", 0, 33, 30, "ISO/IEC 16023:2000 Figure H1 **NOT SAME** TODO: investigate", + /* 1*/ { -1, 4, -1, "MaxiCode (19 chars)", -1, "", 0, 33, 30, "ISO/IEC 16023:2000 Figure H1 **NOT SAME** different encodation (figure uses '3 Shift A' among other differences)", "001101011111011100000010101111" "101100010001001100010000001100" "101100001010001111001001111101" @@ -175,6 +293,251 @@ static void test_encode(int index, int generate, int debug) { "010110010110001110100000010100" "010011110011000001010111100111" }, + /* 2*/ { DATA_MODE | ESCAPE_MODE, 2, 96, "1Z00004951\\GUPSN\\G06X610\\G159\\G1234567\\G1/1\\G\\GY\\G634 ALPHA DR\\GPITTSBURGH\\GPA\\R\\E", -1, "152382802840001", 0, 33, 30, "ISO/IEC 16023:2000 Figure B2 **NOT SAME** uses different encodation (figure uses Latch B/Latch A instead of Shift B for '>\\R', and precedes PAD chars with Latch B)", + "110101110110111110111111101111" + "010101010111000011011000010010" + "110110110001001010101010010011" + "111000101010101111111111111100" + "001111000010110010011000000011" + "001001110010101010100000000000" + "111011111110111111101111111110" + "100110000011001001110000001010" + "010001100010101010101001110001" + "110111100011010000011011111100" + "001100110011110000001110101001" + "101110101000000001011111011000" + "101010000000000000010110111100" + "111101100000000000011011100010" + "101010010000000000000110011101" + "001000010000000000011100011110" + "010011001000000000001000001010" + "000000101000000000001010000010" + "000100111100000000001110101010" + "000010101100000000001000110010" + "100000111010000000011101100011" + "101000100000000000110110100000" + "001000001110100101100110100101" + "011001110010101001100000001000" + "000010100010110001010101011010" + "100111000011111000001001011000" + "110010001001010010101100011101" + "001001110101110100011001110010" + "011111010011101100111101010011" + "111111101111101010101101111000" + "101001110101110111010111000011" + "010110101101000001111000100110" + "110110100000010000001011110011" + }, + /* 3*/ { -1, 3, -1, "CEN", -1, "B1050056999", 0, 33, 30, "ISO/IEC 16023:2000 B.1 Example (primary only given, data arbitrary); verified manually against BWIP and tec-it", + "000000010101010101010101010111" + "001011000000000000000000000010" + "111001101010101010101010101000" + "010101010101010101010101010110" + "000000000000000000000000000001" + "101010101010101010101010101010" + "010101010101010101010101010100" + "000000000000000000000000000010" + "101010101010101010101010101000" + "010101010111100000100101010110" + "000000000001110000010100000010" + "101010101000000011010010101010" + "010101111100000000100001010110" + "000000001100000000011000000010" + "101010100100000000011110101001" + "010101011000000000000001010110" + "000000101000000000001000000001" + "101010110000000000001010101010" + "010101010000000000011101010101" + "000000101000000000011100000000" + "101010111100000000001010101001" + "010101011110000000011101010110" + "000000001110000001111000000011" + "101010101011011001000110101010" + "010101010101010101011100100010" + "000000000000000000000011011100" + "101010101010101010101001110100" + "111011101110000110101011010110" + "100001011111111101000011100111" + "110100001000001101100010100110" + "110110111111011110000011011111" + "010010001001110010000101000010" + "010001011010000011010010011100" + }, + /* 4*/ { UNICODE_MODE | ESCAPE_MODE, -1, -1, "Comité Européen de Normalisation\034rue de Stassart 36\034B-1050 BRUXELLES\034TEL +3225196811", -1, "", 0, 33, 30, "ISO/IEC 16023:2000 Example F.5 **NOT SAME** uses different encodation (2 Shift A among other things)", + "010010100010110000000100001111" + "001010001100110110111110100110" + "001010011100101010011100100000" + "000000100010000000001000000110" + "111101100000011100110011110001" + "011110001011100010100111010010" + "101010000100001101101000101001" + "110010101110100100001000000000" + "000110101100100000110010111110" + "111101111111111000110110100000" + "100000111001111010010010000011" + "011100111100000000101100011010" + "100001101110000000101111111101" + "110011000100000000111100001010" + "000110010000000000010110001010" + "101010010000000000001000011100" + "011000001000000000001000000010" + "001001010000000000001101000000" + "000000010000000000010100101000" + "101111110100000000011110001100" + "100000000010000000011010110011" + "101001010010000001011100001010" + "001101000010001011110111101010" + "111111001010000001100100100000" + "001000001101010101010010111001" + "111001000000000000001010100000" + "010001101010101010101010110001" + "011011000011100001011001101100" + "101100000001111010000001100011" + "110001001100011100110111011010" + "011110010010101101110100000100" + "001011110011100001001001101100" + "000010111011111010110011000011" + }, + /* 5*/ { -1, -1, -1, "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", -1, "", 0, 33, 30, "Numeric compaction, verified manually against BWIPP and tec-it", + "010111101101010111101101010111" + "111011110110111011110110111010" + "001111111101001111111101001100" + "101101010111101101010111101100" + "111110111001111110111001111100" + "111111000111111111000111111110" + "110101011110110101011110110110" + "011011101111011011101111011000" + "110100111111110100111111110101" + "010111101111111100110111010100" + "111001111011011110011111101100" + "000111111110000000111011110000" + "011110100110000000000111010101" + "101111001000000000110101101110" + "111111110100000000000011010001" + "010111111000000000001110110110" + "111001101000000000001011111001" + "000111010000000000001011111100" + "011110011000000000011011010101" + "101111000100000000010001101100" + "111111100110000000100111010010" + "010101110100000001010110110110" + "101110011010101111111011111011" + "110001110111110101111011111110" + "111011010101111111110000111011" + "111101101110110101010001001000" + "111111010011111010101111110011" + "000101011000111100010001000010" + "011110001101100001011010110010" + "101110000100010011000001001000" + "100000001010110100100110001100" + "111010101011001101111001011010" + "011110011111000011101011111011" + }, + /* 6*/ { -1, 5, -1, "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\037\237\240\242\243\244\245\246\247\251\255\256\266\225\226\227\230\231\232\233\234\235\236", 51, "", 0, 33, 30, "Mode 5 set E; single difference from BWIPP - Zint uses Latch B at end before padding instead of Latch A", + "000000000000000000101010101011" + "100101010111111111000000001000" + "110010011100100111001001110001" + "010101010101011010101010101000" + "010110101010001101010110101000" + "100011011000110101100011011000" + "010101010101111111111111111110" + "010111111111000000001010101010" + "011100100111001001110010011101" + "010101011011110000001011111100" + "000000001111110010010011010100" + "101010100110000010001011100000" + "010101110010000000001101010100" + "000000111000000000001000000010" + "101010110100000000001110101011" + "010101010000000000001001010100" + "000000001000000000001000000001" + "101010100000000000001010101010" + "010101100000000000001001010100" + "000000000000000000000000000000" + "101010100100000000011110101010" + "101100110100000001110101100110" + "011100010010110101110111010011" + "110011100010110001001000101010" + "110001101111100011111110101000" + "111110010101110100010110100000" + "110001110000111101111111000011" + "111001000100001011001011011110" + "101010110110100001110011010011" + "100100100001001100000001100000" + "010101001001000001111101011111" + "110110111000001000001101100100" + "100100010010000000010001010111" + }, + /* 7*/ { -1, 6, -1, "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\241\250\253\257\260\264\267\270\273\277\212\213\214\215\216\217\220\221\222\223\224", -1, "", 0, 33, 30, "Mode 6 set D; single difference from BWIPP as above - Zint uses Latch B at end before padding instead of Latch A", + "000000000000000000101010101011" + "100101010111111111000000001000" + "110010011100100111001001110001" + "010101010101010110101010101000" + "101010100000000001010110101000" + "110110001101100001100011011000" + "010101010101010111111111111111" + "010101011111111100000000101000" + "001001110010011100100111001001" + "010111110111110000000011111100" + "000011011011110010011101101000" + "101011101110000001000100110100" + "010101110000000000000001010100" + "000000101000000000000000000010" + "101010110100000000000110101010" + "010101010000000000001001010100" + "000000001000000000001000000011" + "101010100000000000001010101000" + "010101101100000000001001010110" + "000000101000000000001000000010" + "101010010110000000011010101001" + "010101011100000000111101010100" + "000000001110110111011000000011" + "101010100110110001100110101010" + "010101010101010101011101101100" + "000000000000000000001000010000" + "101010101010101010100110000001" + "101000111111101000101010110100" + "010100110011011001101100111001" + "100101010011111101011110000010" + "111101110110111101001001010101" + "011110000010110111001011110110" + "001110010110111101101011110010" + }, + /* 8*/ { -1, 6, -1, "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\252\254\261\262\263\265\271\272\274\275\276\200\201\202\203\204\205\206\207\210\211", -1, "", 0, 33, 30, "Mode 6 set C; single difference from BWIPP as above - Zint uses Latch B at end before padding instead of Latch A", + "000000000000000000101010101011" + "100101010111111111000000001000" + "110010011100100111001001110001" + "010101010101010110101010101000" + "101010100000000001010110101000" + "110110001101100001100011011000" + "010101010101010111111111111111" + "010101011111111100000000101000" + "001001110010011100100111001001" + "010111111111110000000111111100" + "000011010111110010010001101000" + "101011101010000001001100110100" + "010101100000000000000101010100" + "000000011000000000000100000010" + "101010110100000000000010101010" + "010101010000000000001001010100" + "000000001000000000001000000011" + "101010100000000000001010101000" + "010101110000000000001001010110" + "000000111100000000001100000010" + "101010110000000000010110101001" + "010101010100000000101101010100" + "000000001010110101011000000011" + "101010100010110001010110101010" + "010101010101010101011101101100" + "000000000000000000001000010000" + "101010101010101010100110000001" + "101000111111101000101010110100" + "010100110011011001101100111001" + "100101010011111101011110000010" + "111101110110111101001001010101" + "011110000010110111001011110110" + "001110010110111101101011110010" + }, }; int data_size = ARRAY_SIZE(data); @@ -187,16 +550,16 @@ static void test_encode(int index, int generate, int debug) { struct zint_symbol *symbol = ZBarcode_Create(); assert_nonnull(symbol, "Symbol not created\n"); - int length = testUtilSetSymbol(symbol, BARCODE_MAXICODE, data[i].input_mode, -1 /*eci*/, data[i].option_1, -1, -1, -1 /*output_options*/, data[i].data, -1, debug); + int length = testUtilSetSymbol(symbol, BARCODE_MAXICODE, data[i].input_mode, -1 /*eci*/, data[i].option_1, data[i].option_2, -1, -1 /*output_options*/, data[i].data, data[i].length, debug); strcpy(symbol->primary, data[i].primary); 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\", \"%s\", %s, %d, %d, \"%s\",\n", - i, testUtilInputModeName(data[i].input_mode), data[i].option_1, testUtilEscape(data[i].data, length, escaped, sizeof(escaped)), data[i].primary, - testUtilErrorName(data[i].ret), symbol->rows, symbol->width, data[i].comment); + printf(" /*%3d*/ { %s, %d, %d, \"%s\", %d, \"%s\", %s, %d, %d, \"%s\",\n", + i, testUtilInputModeName(data[i].input_mode), data[i].option_1, data[i].option_2, testUtilEscape(data[i].data, length, escaped, sizeof(escaped)), data[i].length, + data[i].primary, testUtilErrorName(data[i].ret), symbol->rows, symbol->width, data[i].comment); testUtilModulesDump(symbol, " ", "\n"); printf(" },\n"); } else { @@ -204,11 +567,9 @@ static void test_encode(int index, int generate, int debug) { 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); } } @@ -236,7 +597,7 @@ static void test_best_supported_set(int index, int generate, int debug) { char *expected; }; struct item data[] = { - /* 0*/ { "am.//ab,\034TA# z\015!", 0, 100, 100, 0, 33, 30, "TODO: Better data and verify expected", + /* 0*/ { "am.//ab,\034TA# z\015!", 0, 100, 100, 0, 33, 30, "Different encodation than BWIPP, same number of codewords", "111010000101111000111101010111" "111110000000010100111000000000" "110000101100110100111010101011" @@ -322,17 +683,18 @@ static void test_fuzz(int index, int debug) { int ret; struct item { + int eci; char *data; int length; int ret; }; // s/\/\*[ 0-9]*\*\//\=printf("\/*%2d*\/", line(".") - line("'<")) struct item data[] = { - /* 0*/ { "\223\223\223\223\223\200\000\060\060\020\122\104\060\343\000\000\040\104\104\104\104\177\377\040\000\324\336\000\000\000\000\104\060\060\060\060\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\060\104\104\000\000\000\040\104\104\104\104\177\377\377\377\324\336\000\000\000\000\104\377\104\001\104\104\104\104\104\104\233\233\060\060\060\060\060\060\060\060\060\325\074", 107, ZINT_ERROR_TOO_LONG }, // Original OSS-Fuzz triggering data - /* 1*/ { "AaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA123456789", -1, ZINT_ERROR_TOO_LONG }, // Add 6 lowercase a's so 6 SHIFTS inserted so 6 + 138 (max input len) = 144 and numbers come at end of buffer - /* 2*/ { "AaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA123456789A", -1, ZINT_ERROR_TOO_LONG }, - /* 3*/ { "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678", -1, ZINT_ERROR_TOO_LONG }, // First 6 chars ignored for number compaction so max numeric digits appears to be 135 not 138 (for mode 4 anyway) TODO: investigate further - /* 4*/ { "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345", -1, 0 }, + /* 0*/ { -1, "\223\223\223\223\223\200\000\060\060\020\122\104\060\343\000\000\040\104\104\104\104\177\377\040\000\324\336\000\000\000\000\104\060\060\060\060\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\104\060\104\104\000\000\000\040\104\104\104\104\177\377\377\377\324\336\000\000\000\000\104\377\104\001\104\104\104\104\104\104\233\233\060\060\060\060\060\060\060\060\060\325\074", 107, ZINT_ERROR_TOO_LONG }, // Original OSS-Fuzz triggering data + /* 1*/ { -1, "AaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA123456789", -1, ZINT_ERROR_TOO_LONG }, // Add 6 lowercase a's so 6 SHIFTS inserted so 6 + 138 (max input len) = 144 and numbers come at end of buffer + /* 2*/ { -1, "AaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA123456789A", -1, ZINT_ERROR_TOO_LONG }, + /* 3*/ { -1, "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", -1, ZINT_ERROR_TOO_LONG }, + /* 4*/ { 32768, "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678", -1, ZINT_ERROR_TOO_LONG }, }; int data_size = sizeof(data) / sizeof(struct item); @@ -343,13 +705,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_MAXICODE; - symbol->debug |= debug; - - int length = data[i].length; - if (length == -1) { - length = strlen(data[i].data); - } + int length = testUtilSetSymbol(symbol, BARCODE_MAXICODE, -1 /*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); @@ -360,13 +716,88 @@ static void test_fuzz(int index, int debug) { testFinish(); } +#include + +#define TEST_PERF_ITERATIONS 1000 + +// Not a real test, just performance indicator +static void test_perf(int index, int debug) { + + if (!(debug & ZINT_DEBUG_TEST_PERFORMANCE)) { /* -d 256 */ + return; + } + + int ret; + struct item { + int symbology; + int input_mode; + int option_1; + int option_2; + char *data; + char *primary; + int ret; + + int expected_rows; + int expected_width; + char *comment; + }; + struct item data[] = { + /* 0*/ { BARCODE_MAXICODE, UNICODE_MODE | ESCAPE_MODE, -1, -1, + "1Z34567890\\GUPSN\\G102562\\G034\\G\\G1/1\\G\\GY\\G2201 Second St\\GFt Myers\\GFL\\R\\E", + "339010000840001", 0, 33, 30, "Mode 2" }, + }; + int data_size = ARRAY_SIZE(data); + + clock_t start, total_encode = 0, total_buffer = 0, diff_encode, diff_buffer; + + for (int i = 0; i < data_size; i++) { + + if (index != -1 && i != index) continue; + + diff_encode = diff_buffer = 0; + + for (int j = 0; j < TEST_PERF_ITERATIONS; j++) { + struct zint_symbol *symbol = ZBarcode_Create(); + assert_nonnull(symbol, "Symbol not created\n"); + + int length = testUtilSetSymbol(symbol, data[i].symbology, data[i].input_mode, -1 /*eci*/, data[i].option_1, data[i].option_2, -1, -1 /*output_options*/, data[i].data, -1, debug); + strcpy(symbol->primary, data[i].primary); + + start = clock(); + ret = ZBarcode_Encode(symbol, (unsigned char *) data[i].data, length); + diff_encode += clock() - start; + assert_equal(ret, data[i].ret, "i:%d ZBarcode_Encode ret %d != %d (%s)\n", i, ret, data[i].ret, symbol->errtxt); + + 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); + + start = clock(); + ret = ZBarcode_Buffer(symbol, 0 /*rotate_angle*/); + diff_buffer += clock() - start; + assert_zero(ret, "i:%d ZBarcode_Buffer ret %d != 0 (%s)\n", i, ret, symbol->errtxt); + + ZBarcode_Delete(symbol); + } + + printf("%s: diff_encode %gms, diff_buffer %gms\n", data[i].comment, diff_encode * 1000.0 / CLOCKS_PER_SEC, diff_buffer * 1000.0 / CLOCKS_PER_SEC); + + total_encode += diff_encode; + total_buffer += diff_buffer; + } + if (index != -1) { + printf("totals: encode %gms, buffer %gms\n", total_encode * 1000.0 / CLOCKS_PER_SEC, total_buffer * 1000.0 / CLOCKS_PER_SEC); + } +} + int main(int argc, char *argv[]) { testFunction funcs[] = { /* name, func, has_index, has_generate, has_debug */ { "test_large", test_large, 1, 0, 1 }, + { "test_input", test_input, 1, 1, 1 }, { "test_encode", test_encode, 1, 1, 1 }, { "test_best_supported_set", test_best_supported_set, 1, 1, 1 }, { "test_fuzz", test_fuzz, 1, 0, 1 }, + { "test_perf", test_perf, 1, 0, 1 }, }; testRun(argc, argv, funcs, ARRAY_SIZE(funcs)); diff --git a/backend/tests/test_print.c b/backend/tests/test_print.c index a81721a8..c0d269d9 100644 --- a/backend/tests/test_print.c +++ b/backend/tests/test_print.c @@ -63,6 +63,7 @@ static void test_print(int index, int generate, int debug) { /* 1*/ { BARCODE_QRCODE, 2, 1, -1, "1234567890", "qr_v1_m" }, /* 2*/ { BARCODE_DOTCODE, -1, -1, 5, "2741", "dotcode_aim_fig7" }, /* 3*/ { BARCODE_ULTRA, -1, -1, -1, "A", "ultracode_a" }, + /* 4*/ { BARCODE_MAXICODE, -1, -1, -1, "THIS IS A 93 CHARACTER CODE SET A MESSAGE THAT FILLS A MODE 4, UNAPPENDED, MAXICODE SYMBOL...", "maxicode_fig_2" }, }; int data_size = sizeof(data) / sizeof(struct item); diff --git a/backend/zint.h b/backend/zint.h index acf676cb..7f2483fa 100644 --- a/backend/zint.h +++ b/backend/zint.h @@ -55,7 +55,7 @@ extern "C" { struct zint_vector_circle { float x, y, diameter; - int colour; + int colour; /* Non-zero for draw with background colour */ struct zint_vector_circle *next; /* Pointer to next circle */ }; diff --git a/backend_tcl/zint.c b/backend_tcl/zint.c index 64f91bc3..fda5ad74 100644 --- a/backend_tcl/zint.c +++ b/backend_tcl/zint.c @@ -436,6 +436,7 @@ static char help_message[] = "zint tcl(stub,obj) dll\n" " -secure integer: EC Level (PDF417, QR)\n" " -mode: Structured primary data mode (Maxicode, Composite)\n" " -primary text: Structured primary data (Maxicode, Composite)\n" + " -scmvv: Prefix secondary message with [)>\\R01\\Gvv (vv 00..99) (MaxiCode)\n" " -dotty bool: use dots instead of boxes for matrix codes\n" " -dotsize number: radius ratio of dots from 0.01 to 1.0\n" " -scale double: Scale the image to this factor\n" @@ -634,13 +635,13 @@ static int Encode(Tcl_Interp *interp, int objc, "-gssep", "-height", "-init", "-mode", "-nobackground", "-notext", "-primary", "-rotate", "-rows", "-scale", "-secure", "-smalltext", "-square", "-to", "-vers", "-whitesp", "-fullmultibyte", - "-separator", "-mask", NULL}; + "-separator", "-mask", "-scmvv", NULL}; enum iOption { iAddonGap, iBarcode, iBG, iBind, iBold, iBorder, iBox, iCols, iDMRE, iDotSize, iDotty, iECI, iFG, iFormat, iGSSep, iHeight, iInit, iMode, iNoBackground, iNoText, iPrimary, iRotate, iRows, iScale, iSecure, iSmallText, iSquare, iTo, iVers, - iWhiteSp, iFullMultiByte, iSeparator, iMask + iWhiteSp, iFullMultiByte, iSeparator, iMask, iSCMvv }; int optionIndex; int intValue; @@ -706,14 +707,8 @@ static int Encode(Tcl_Interp *interp, int objc, case iVers: case iWhiteSp: case iSeparator: - /* >> Int */ - if (TCL_OK != Tcl_GetIntFromObj(interp, objv[optionPos+1], - &intValue)) - { - fError = 1; - } - break; case iMask: + case iSCMvv: /* >> Int */ if (TCL_OK != Tcl_GetIntFromObj(interp, objv[optionPos+1], &intValue)) @@ -890,6 +885,15 @@ static int Encode(Tcl_Interp *interp, int objc, Mask = intValue + 1; } break; + case iSCMvv: + if (intValue < 0 || intValue > 99) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj("SCM version out of range", -1)); + fError = 1; + } else { + hSymbol->option_2 = intValue + 1; + } + break; case iCols: case iVers: /* >> Int in Option 2 */ diff --git a/docs/manual.txt b/docs/manual.txt index f0364ecb..af78b4b3 100644 --- a/docs/manual.txt +++ b/docs/manual.txt @@ -47,7 +47,7 @@ stacked: A stacked symbol consists of multiple linear symbols placed one above error correction data. Examples include PDF417. matrix: A matrix symbol is one based on a (usually square) grid of elements. - Examples include Data Matrix, but Maxicode and DotCode are also + Examples include Data Matrix, but MaxiCode and DotCode are also considered matrix symbologies. X-dimension: The X-dimension of a symbol is the size (usually the width) of the @@ -216,8 +216,8 @@ Escape Character | ASCII Equivalent | Interpretation \f | 0x0C | Form Feed \r | 0x0D | Carriage Return \e | 0x1B | Escape -\G | 0x1D | Group Selector -\R | 0x1E | Record Selector +\G | 0x1D | Group Separator +\R | 0x1E | Record Separator \\ | 0x5C | Backslash \xNN | 0xNN | Any other 8-bit character | | where NN is hexadecimal @@ -293,7 +293,7 @@ Numeric Value | Barcode Name 53 | Pharmacode Two-Track 55 | PDF417 56 | Compact PDF417 -57 | Maxicode +57 | MaxiCode 58 | QR Code 60 | Code 128 (Subset B) 63 | Australia Post Standard Customer @@ -425,7 +425,7 @@ vector files so that... zint --bg=ff0000 --fg=ffffff00 ... will give different results for PNG and SVG. Experimentation is advised! -Also note that these options don't work properly with Maxicode yet. +Also note that these options don't work properly with MaxiCode yet. In addition the --nobackground option will simply remove the background from PNG, GIF, SVG, EMF and EPS files. @@ -1090,7 +1090,7 @@ Value | 53 | BARCODE_PHARMA_TWO | Pharmacode Two-Track 55 | BARCODE_PDF417 | PDF417 56* | BARCODE_PDF417COMP | Compact PDF417 (Truncated PDF417) -57 | BARCODE_MAXICODE | Maxicode +57 | BARCODE_MAXICODE | MaxiCode 58 | BARCODE_QRCODE | QR Code 60 | BARCODE_CODE128B | Code 128 (Subset B) 63 | BARCODE_AUSPOST | Australia Post Standard Customer @@ -1286,7 +1286,7 @@ The version parts are separated by hundreds. For instance, version "2.9.1" is returned as "20901". [1] This value is ignored for Australia Post 4-State Barcodes, POSTNET, PLANET, -USPS Intelligent Mail, RM4SCC, PDF417, Data Matrix, Maxicode, QR Code, GS1 +USPS Intelligent Mail, RM4SCC, PDF417, Data Matrix, MaxiCode, QR Code, GS1 DataBar Stacked, PDF417 and MicroPDF417 - all of which have a fixed height. [2] This value is ignored for Code 16k and Codablock-F. Special considerations @@ -2288,12 +2288,12 @@ The following example creates a symbol from data saved as an ISO-8859-2 file: zint -o upnqr.png -b 143 --border=5 --scale=3 --binary -i ./upn.txt -6.6.6 Maxicode (ISO 16023) +6.6.6 MaxiCode (ISO 16023) -------------------------- -Developed by UPS the Maxicode symbology employs a grid of hexagons surrounding +Developed by UPS the MaxiCode symbology employs a grid of hexagons surrounding a 'bulls-eye' finder pattern. This symbology is designed for the identification -of parcels. Maxicode symbols can be encoded in one of five modes. In modes 2 -and 3 Maxicode symbols are composed of two parts named the primary and +of parcels. MaxiCode symbols can be encoded in one of five modes. In modes 2 +and 3 MaxiCode symbols are composed of two parts named the primary and secondary messages. The primary message consists of a structured data field which includes various data about the package being sent and the secondary message usually consists of address data in a data structure. The format of the @@ -2304,8 +2304,9 @@ Characters | Meaning ---------------------------------------------------------------------------- 1 - 9 | Postcode data which can consist of up to 9 digits (for mode 2) | or up to 6 alphanumeric characters (for mode 3). Remaining - | unused characters should be filled with the SPACE character - | (ASCII 32). + | unused characters can be filled with the SPACE character + | (ASCII 32) or omitted (if omitted adjust the following + | character positions). 10 - 12 | Three digit country code according to ISO 3166. 13 - 15 | Three digit service code. This depends on your parcel courier. ---------------------------------------------------------------------------- @@ -2313,36 +2314,48 @@ Characters | Meaning The primary message can be set at the command prompt using the --primary= switch. The secondary message uses the normal data entry method. For example: -zint -o test.eps -b 57 --primary='999999999840012' -d 'Secondary Message Here' +zint -o test.eps -b 57 --primary="999999999840012" -d "Secondary Message Here" When using the API the primary message must be placed in the symbol->primary string. The secondary is entered in the same way as described in section 5.2. When either of these modes is selected Zint will analyse the primary message and select either mode 2 or mode 3 as appropriate. -Modes 4 to 6 can be accessed using the --mode= switch or by setting option_1. -Modes 4 to 6 do not require a primary message. For example: +As a convenience the secondary message for modes 2 and 3 can be set to be +prefixed by the ISO 15434 Format "01" (transportation) sequence "[)>\R01\Gvv", +where "vv" is a 2-digit version, by using the --scmvv= switch or by setting +option_2 = vv + 1. For example to use the common version "96" (ASC MH10/SC 8): -zint -o test.eps -b 57 --mode=4 -d 'A MaxiCode Message in Mode 4' +zint -b 57 --primary="152382802840001" --scmvv=96 --esc \ + -d "1Z00004951\GUPSN\G06X610\G159\G1234567\G1/1\G\GY\G1 MAIN ST\GTOWN\GNY\R\E" + +will prefix "[)>\R01\G96" to the secondary message. ("\R", "\G" and "\E" are the +escape sequences for Record Separator, Group Separator and End of Transmission +respectively - see section 4.1.) + +Modes 4 to 6 can be accessed using the --mode= switch or by setting option_1. +Modes 4 to 6 do not have a primary message. For example: + +zint -o test.eps -b 57 --mode=4 -d "A MaxiCode Message in Mode 4" Mode 6 is reserved for the maintenance of scanner hardware and should not be used to encode user data. This symbology uses Latin-1 character encoding by default but also supports the ECI encoding mechanism. The maximum length of text which can be placed in a -Maxicode symbol depends on the type of characters used in the text. +MaxiCode symbol depends on the type of characters used in the text. Example maximum data lengths are given in the table below: ----------------------------------------------------------------------------- -Mode | Maximum Data Lenth | Maximum Data Length | Number of Error +Mode | Maximum Data Length | Maximum Data Length | Number of Error | for Capital Letters | for Numeric Digits | Correction Codewords ----------------------------------------------------------------------------- 2* | 84 | 126 | 50 3* | 84 | 126 | 50 -4 | 93 | 135 | 50 -5 | 77 | 110 | 66 -6 | 93 | 135 | 50 +4 | 93 | 138 | 50 +5 | 77 | 113 | 66 +6 | 93 | 138 | 50 ----------------------------------------------------------------------------- * - secondary only @@ -2999,7 +3012,7 @@ international standards: > ISO/IEC 16022:2006 Information technology - Automatic identification and data capture techniques - Data Matrix ECC200 bar code symbology specification > ISO/IEC 16023:2000 Information technology - International symbology - specification – Maxicode + specification – MaxiCode > ISO/IEC 16388:2007 Information technology - Automatic identification and data capture techniques - Code 39 bar code symbology specification > ISO/IEC 18004:2015 Information technology - Automatic identification and data diff --git a/frontend/main.c b/frontend/main.c index 799dd406..c7f1f552 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -44,7 +44,7 @@ static void types(void) { " 2: Standard 2of5 53: Pharma Two-Track 97: Micro QR Code\n" " 3: Interleaved 2of5 55: PDF417 98: HIBC Code 128\n" " 4: IATA 2of5 56: Compact PDF417 99: HIBC Code 39\n" - " 6: Data Logic 57: Maxicode 102: HIBC Data Matrix\n" + " 6: Data Logic 57: MaxiCode 102: HIBC Data Matrix\n" " 7: Industrial 2of5 58: QR Code 104: HIBC QR Code\n" " 8: Code 39 60: Code 128-B 106: HIBC PDF417\n" " 9: Extended Code 39 63: AP Standard Customer 108: HIBC MicroPDF417\n" @@ -126,15 +126,16 @@ static void usage(void) { " --init Create reader initialisation/programming symbol\n" " --mask=NUMBER Set masking pattern to use (QR/Han Xin)\n" " --mirror Use batch data to determine filename\n" - " --mode=NUMBER Set encoding mode (Maxicode/Composite)\n" + " --mode=NUMBER Set encoding mode (MaxiCode/Composite)\n" " --nobackground Remove background (PNG/SVG/EPS only)\n" " --notext Remove human readable text\n" " -o, --output=FILE Send output to FILE. Default is out.png\n" - " --primary=STRING Set structured primary message (Maxicode/Composite)\n" + " --primary=STRING Set structured primary message (MaxiCode/Composite)\n" " -r, --reverse Reverse colours (white on black)\n" " --rotate=NUMBER Rotate symbol by NUMBER degrees\n" " --rows=NUMBER Set number of rows (Codablock-F)\n" " --scale=NUMBER Adjust size of X-dimension\n" + " --scmvv=NUMBER Prefix SCM with [)>\\R01\\Gvv (vv is NUMBER) (MaxiCode)\n" " --secure=NUMBER Set error correction level (ECC)\n" " --separator=NUMBER Set height of row separator bars (stacked symbologies)\n" " --small Use small text\n" @@ -272,7 +273,7 @@ static void set_extension(char *file, char *filetype) { file[251] = '\0'; } strcat(file, "."); - strcat(file, filetype); + strncat(file, filetype, 3); } static char *raster_filetypes[] = { @@ -308,7 +309,7 @@ static int batch_process(struct zint_symbol *symbol, char *filename, int mirror_ if (symbol->outfile[0] == '\0') { strcpy(format_string, "~~~~~."); - strcat(format_string, filetype); + strncat(format_string, filetype, 3); } else { strcpy(format_string, symbol->outfile); set_extension(format_string, filetype); @@ -438,7 +439,7 @@ static int batch_process(struct zint_symbol *symbol, char *filename, int mirror_ output_file[o] = '.'; output_file[o + 1] = '\0'; - strcat(output_file, filetype); + strncat(output_file, filetype, 3); } strcpy(symbol->outfile, output_file); @@ -487,6 +488,7 @@ int main(int argc, char **argv) { int fullmultibyte = 0; int mask = 0; int separator = 0; + int scmvv = -1; int addon_gap = 0; char filetype[4] = {0}; int i; @@ -551,6 +553,7 @@ int main(int argc, char **argv) { {"rotate", 1, 0, 0}, {"rows", 1, 0, 0}, {"scale", 1, 0, 0}, + {"scmvv", 1, 0, 0}, {"secure", 1, 0, 0}, {"separator", 1, 0, 0}, {"small", 0, 0, 0}, @@ -646,6 +649,22 @@ int main(int argc, char **argv) { my_symbol->scale = 1.0f; } } + if (!strcmp(long_options[option_index].name, "scmvv")) { + error_number = validator(NESET, optarg); + if (error_number == ZINT_ERROR_INVALID_DATA) { + fprintf(stderr, "Error 149: Invalid Structured Carrier Message version value\n"); + exit(1); + } + scmvv = atoi(optarg); + if (scmvv < 0 || scmvv > 99) { + /* Version 00-99 only */ + fprintf(stderr, "Warning 150: Invalid version (vv) for Structured Carrier Message, ignoring\n"); + fflush(stderr); + scmvv = -1; + } else { + my_symbol->option_2 = scmvv + 1; + } + } if (!strcmp(long_options[option_index].name, "separator")) { error_number = validator(NESET, optarg); if (error_number == ZINT_ERROR_INVALID_DATA) { diff --git a/frontend_qt/grpMaxicode.ui b/frontend_qt/grpMaxicode.ui index 0fbe86ed..ae2d2d72 100644 --- a/frontend_qt/grpMaxicode.ui +++ b/frontend_qt/grpMaxicode.ui @@ -35,22 +35,6 @@ - - - - false - - - P&rimary Message: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - txtMaxiPrimary - - - @@ -84,6 +68,30 @@ + + + + false + + + P&rimary Message: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + txtMaxiPrimary + + + Format "PPPPPPPPPCCCSSS" where +P is postcode of up to 9 digits for Mode 2 or +6 alphanumerics for Mode 3 (will truncate) and +C is 3-digit country code +S is 3-digit service class +(ignored if disabled) + + + @@ -100,6 +108,63 @@ + + + + false + + + Enable S&CM Prefix + + + Prefix Secondary Carrier Message with "[)>\R01\Gvv" +where "vv" is version given below +(ignored if disabled) + + + + + + + false + + + SCM Pre&fix Version: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + spnMaxiSCMVV + + + Version "vv" to place at end of SCM prefix (default 96) +(ignored if disabled) + + + + + + + false + + + + + + + + + 0 + + + 99 + + + 96 + + + diff --git a/frontend_qt/mainwindow.cpp b/frontend_qt/mainwindow.cpp index ad905eb7..9e182d29 100644 --- a/frontend_qt/mainwindow.cpp +++ b/frontend_qt/mainwindow.cpp @@ -96,7 +96,7 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags fl) "Japanese Postal Barcode", "Korean Postal Barcode", "LOGMARS", - "Maxicode (ISO 16023)", + "MaxiCode (ISO 16023)", "MicroPDF417 (ISO 24728) (and HIBC)", "Micro QR Code", "MSI Plessey", @@ -881,10 +881,13 @@ void MainWindow::change_options() return; m_optionWidget=uiload.load(&file); file.close(); - tabMain->insertTab(1,m_optionWidget,tr("Maxicod&e")); + tabMain->insertTab(1,m_optionWidget,tr("MaxiCod&e")); connect(m_optionWidget->findChild("cmbMaxiMode"), SIGNAL(currentIndexChanged( int )), SLOT(update_preview())); connect(m_optionWidget->findChild("cmbMaxiMode"), SIGNAL(currentIndexChanged( int )), SLOT(maxi_primary())); connect(m_optionWidget->findChild("txtMaxiPrimary"), SIGNAL(textChanged( const QString& )), SLOT(update_preview())); + connect(m_optionWidget->findChild("chkMaxiSCMVV"), SIGNAL(stateChanged( int )), SLOT(update_preview())); + connect(m_optionWidget->findChild("chkMaxiSCMVV"), SIGNAL(stateChanged( int )), SLOT(maxi_primary())); + connect(m_optionWidget->findChild("spnMaxiSCMVV"), SIGNAL(valueChanged( int )), SLOT(update_preview())); } if (symbology == BARCODE_CHANNEL) @@ -1078,12 +1081,19 @@ void MainWindow::maxi_primary() { if (metaObject()->enumerator(0).value(bstyle->currentIndex())!=BARCODE_MAXICODE) return; + QCheckBox *chkMaxiSCMVV = m_optionWidget->findChild("chkMaxiSCMVV"); if(m_optionWidget->findChild("cmbMaxiMode")->currentIndex() == 0) { m_optionWidget->findChild("lblMaxiPrimary")->setEnabled(true); m_optionWidget->findChild("txtMaxiPrimary")->setEnabled(true); + chkMaxiSCMVV->setEnabled(true); + m_optionWidget->findChild("lblMaxiSCMVV")->setEnabled(chkMaxiSCMVV->isChecked()); + m_optionWidget->findChild("spnMaxiSCMVV")->setEnabled(chkMaxiSCMVV->isChecked()); } else { m_optionWidget->findChild("lblMaxiPrimary")->setEnabled(false); m_optionWidget->findChild("txtMaxiPrimary")->setEnabled(false); + chkMaxiSCMVV->setEnabled(false); + m_optionWidget->findChild("lblMaxiSCMVV")->setEnabled(false); + m_optionWidget->findChild("spnMaxiSCMVV")->setEnabled(false); } } @@ -1142,7 +1152,7 @@ void MainWindow::update_preview() } else { m_bc.bc.setText(txtData->text()); } - m_bc.bc.setSecurityLevel(0); + m_bc.bc.setOption1(-1); m_bc.bc.setOption2(0); m_bc.bc.setOption3(0); chkData->setEnabled(true); @@ -1254,7 +1264,7 @@ void MainWindow::update_preview() case BARCODE_PDF417: m_bc.bc.setOption2(m_optionWidget->findChild("cmbPDFCols")->currentIndex()); - m_bc.bc.setSecurityLevel(m_optionWidget->findChild("cmbPDFECC")->currentIndex()-1); + m_bc.bc.setOption1(m_optionWidget->findChild("cmbPDFECC")->currentIndex() - 1); if(m_optionWidget->findChild("radPDFStand")->isChecked()) m_bc.bc.setSymbol(BARCODE_PDF417); @@ -1286,7 +1296,7 @@ void MainWindow::update_preview() m_bc.bc.setOption2(m_optionWidget->findChild("cmbAztecSize")->currentIndex() + 1); if(m_optionWidget->findChild("radAztecECC")->isChecked()) - m_bc.bc.setSecurityLevel(m_optionWidget->findChild("cmbAztecECC")->currentIndex() + 1); + m_bc.bc.setOption1(m_optionWidget->findChild("cmbAztecECC")->currentIndex() + 1); set_gs1_mode(m_optionWidget->findChild("radAztecGS1")->isChecked()); if(m_optionWidget->findChild("radAztecHIBC")->isChecked()) @@ -1362,7 +1372,7 @@ void MainWindow::update_preview() // Height selection uses option 1 in zint_symbol item_val = m_optionWidget->findChild("cmbCbfHeight")->currentIndex(); if (item_val) { - m_bc.bc.setSecurityLevel(item_val); + m_bc.bc.setOption1(item_val); } // Row separator height selection uses option 3 in zint_symbol item_val = m_optionWidget->findChild("cmbCbfRowSepHeight")->currentIndex(); @@ -1372,7 +1382,6 @@ void MainWindow::update_preview() break; case BARCODE_DATAMATRIX: - m_bc.bc.setSecurityLevel(1); if(m_optionWidget->findChild("radDM200HIBC")->isChecked()) m_bc.bc.setSymbol(BARCODE_HIBC_DM); else @@ -1425,7 +1434,7 @@ void MainWindow::update_preview() } item_val = m_optionWidget->findChild("cmbQRECC")->currentIndex(); if (item_val) { - m_bc.bc.setSecurityLevel(item_val); + m_bc.bc.setOption1(item_val); } item_val = m_optionWidget->findChild("cmbQRMask")->currentIndex(); if (item_val) { @@ -1444,7 +1453,7 @@ void MainWindow::update_preview() } item_val = m_optionWidget->findChild("cmbMQRECC")->currentIndex(); if (item_val) { - m_bc.bc.setSecurityLevel(item_val); + m_bc.bc.setOption1(item_val); } item_val = m_optionWidget->findChild("cmbMQRMask")->currentIndex(); if (item_val) { @@ -1466,7 +1475,7 @@ void MainWindow::update_preview() } item_val = m_optionWidget->findChild("cmbRMQRECC")->currentIndex(); if (item_val) { - m_bc.bc.setSecurityLevel(item_val * 2); // Levels 2 (M) and 4 (H) only + m_bc.bc.setOption1(item_val * 2); // Levels 2 (M) and 4 (H) only } if (m_optionWidget->findChild("chkRMQRFullMultibyte")->isChecked()) { m_bc.bc.setOption3(ZINT_FULL_MULTIBYTE); @@ -1481,7 +1490,7 @@ void MainWindow::update_preview() } item_val = m_optionWidget->findChild("cmbGridECC")->currentIndex(); if (item_val) { - m_bc.bc.setSecurityLevel(item_val); + m_bc.bc.setOption1(item_val); } if (m_optionWidget->findChild("chkGridFullMultibyte")->isChecked()) { m_bc.bc.setOption3(ZINT_FULL_MULTIBYTE); @@ -1492,11 +1501,15 @@ void MainWindow::update_preview() m_bc.bc.setSymbol(BARCODE_MAXICODE); if(m_optionWidget->findChild("cmbMaxiMode")->currentIndex() == 0) { - m_bc.bc.setSecurityLevel(2); + m_bc.bc.setOption1(0); /* Auto-determine mode 2 or 3 from primary message (checks that it isn't empty) */ m_bc.bc.setPrimaryMessage(m_optionWidget->findChild("txtMaxiPrimary")->text()); + QCheckBox *chkMaxiSCMVV = m_optionWidget->findChild("chkMaxiSCMVV"); + if (chkMaxiSCMVV->isEnabled() && chkMaxiSCMVV->isChecked()) { + m_bc.bc.setOption2(m_optionWidget->findChild("spnMaxiSCMVV")->value() + 1); + } } else - m_bc.bc.setSecurityLevel(m_optionWidget->findChild("cmbMaxiMode")->currentIndex() + 3); + m_bc.bc.setOption1(m_optionWidget->findChild("cmbMaxiMode")->currentIndex() + 3); break; case BARCODE_CHANNEL: @@ -1531,7 +1544,7 @@ void MainWindow::update_preview() } item_val = m_optionWidget->findChild("cmbHXECC")->currentIndex(); if (item_val) { - m_bc.bc.setSecurityLevel(item_val); + m_bc.bc.setOption1(item_val); } item_val = m_optionWidget->findChild("cmbHXMask")->currentIndex(); if (item_val) { @@ -1545,7 +1558,7 @@ void MainWindow::update_preview() case BARCODE_ULTRA: m_bc.bc.setSymbol(BARCODE_ULTRA); if(m_optionWidget->findChild("radUltraEcc")->isChecked()) - m_bc.bc.setSecurityLevel(m_optionWidget->findChild("cmbUltraEcc")->currentIndex() + 1); + m_bc.bc.setOption1(m_optionWidget->findChild("cmbUltraEcc")->currentIndex() + 1); set_gs1_mode(m_optionWidget->findChild("radUltraGS1")->isChecked()); break; @@ -1568,7 +1581,7 @@ void MainWindow::update_preview() chkRInit->setEnabled(m_bc.bc.supportsReaderInit() && (m_bc.bc.inputMode() & 0x07) != GS1_MODE); if (!grpComposite->isHidden() && chkComposite->isChecked()) - m_bc.bc.setSecurityLevel(cmbCompType->currentIndex()); + m_bc.bc.setOption1(cmbCompType->currentIndex()); if (!chkAutoHeight->isEnabled() || chkAutoHeight->isChecked()) { m_bc.bc.setHeight(0); @@ -1832,6 +1845,20 @@ void MainWindow::set_lineedit_from_setting(QSettings &settings, const QString &s } } +/* Helper to return value of spin box, checking for NULL */ +int MainWindow::get_spinbox_val(const QString &child) { + QSpinBox *spinBox = m_optionWidget->findChild(child); + return spinBox ? spinBox->value() : 0; +} + +/* Helper to set spin box from settings, checking for NULL */ +void MainWindow::set_spinbox_from_setting(QSettings &settings, const QString &setting, const QString &child, int default_val) { + QSpinBox *spinBox = m_optionWidget->findChild(child); + if (spinBox) { + spinBox->setValue(settings.value(setting, default_val).toInt()); + } +} + /* Save settings for an individual symbol */ void MainWindow::save_sub_settings(QSettings &settings, int symbology) { @@ -2001,6 +2028,8 @@ void MainWindow::save_sub_settings(QSettings &settings, int symbology) { case BARCODE_MAXICODE: settings.setValue("studio/bc/maxicode/mode", get_combobox_index("cmbMaxiMode")); settings.setValue("studio/bc/maxicode/primary_message", get_lineedit_val("txtMaxiPrimary")); + settings.setValue("studio/bc/maxicode/chk_scm_vv", get_checkbox_val("chkMaxiSCMVV")); + settings.setValue("studio/bc/maxicode/spn_scm_vv", get_spinbox_val("spnMaxiSCMVV")); break; case BARCODE_CODEONE: @@ -2227,6 +2256,8 @@ void MainWindow::load_sub_settings(QSettings &settings, int symbology) { case BARCODE_MAXICODE: set_combobox_from_setting(settings, "studio/bc/maxicode/mode", "cmbMaxiMode", 1); set_lineedit_from_setting(settings, "studio/bc/maxicode/primary_message", "txtMaxiPrimary", "Primary Message Here!"); + set_checkbox_from_setting(settings, "studio/bc/maxicode/chk_scm_vv", "chkMaxiSCMVV"); + set_spinbox_from_setting(settings, "studio/bc/maxicode/spn_scm_vv", "spnMaxiSCMVV", 96); /* 96 is ASC MH10/SC 8 */ break; case BARCODE_CODEONE: diff --git a/frontend_qt/mainwindow.h b/frontend_qt/mainwindow.h index 93f4befd..92e880d2 100644 --- a/frontend_qt/mainwindow.h +++ b/frontend_qt/mainwindow.h @@ -146,6 +146,8 @@ protected: void set_doublespinbox_from_setting(QSettings &settings, const QString &setting, const QString &child, float default_val = 0); QString get_lineedit_val(const QString &child); void set_lineedit_from_setting(QSettings &settings, const QString &setting, const QString &child, const char *default_val = ""); + int get_spinbox_val(const QString &child); + void set_spinbox_from_setting(QSettings &settings, const QString &setting, const QString &child, int default_val = 0); void save_sub_settings(QSettings &settings, int symbology); void load_sub_settings(QSettings &settings, int symbology); diff --git a/frontend_qt/qzint.cpp b/frontend_qt/qzint.cpp index 6408d2b5..88e75b05 100644 --- a/frontend_qt/qzint.cpp +++ b/frontend_qt/qzint.cpp @@ -36,7 +36,7 @@ namespace Zint { m_borderType = 0; m_borderWidth = 0; m_fontSetting = 0; - m_securityLevel = -1; + m_option_1 = -1; m_fgColor = Qt::black; m_bgColor = Qt::white; m_cmyk = false; @@ -75,7 +75,7 @@ namespace Zint { m_zintSymbol->height = m_height; m_zintSymbol->whitespace_width = m_whitespace; m_zintSymbol->border_width = m_borderWidth; - m_zintSymbol->option_1 = m_securityLevel; + m_zintSymbol->option_1 = m_option_1; m_zintSymbol->input_mode = m_input_mode; m_zintSymbol->option_2 = m_option_2; if (m_dotty) { @@ -249,20 +249,12 @@ namespace Zint { m_whitespace = whitespace; } - int QZint::securityLevel() const { - return m_securityLevel; + int QZint::option1() const { + return m_option_1; } - void QZint::setSecurityLevel(int securityLevel) { - m_securityLevel = securityLevel; - } - - int QZint::mode() const { - return m_securityLevel; - } - - void QZint::setMode(int securityLevel) { - m_securityLevel = securityLevel; + void QZint::setOption1(int option_1) { + m_option_1 = option_1; } void QZint::setFontSetting(int fontSettingIndex) { diff --git a/frontend_qt/qzint.h b/frontend_qt/qzint.h index b04ec7ed..3051f8e8 100644 --- a/frontend_qt/qzint.h +++ b/frontend_qt/qzint.h @@ -78,11 +78,8 @@ public: int borderWidth() const; void setBorderWidth(int boderWidth); - int securityLevel() const; - void setSecurityLevel(int securityLevel); - - int mode() const; - void setMode(int securityLevel); + int option1() const; + void setOption1(int option_1); void setWhitespace(int whitespace); @@ -140,7 +137,7 @@ private: int m_borderWidth; int m_fontSetting; int m_option_2; - int m_securityLevel; + int m_option_1; int m_input_mode; QColor m_fgColor; QColor m_bgColor;