diff --git a/ChangeLog b/ChangeLog index 8a704e12..e50d38da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -Version 2.15.0.9 (dev) not released yet (2025-04-04) +Version 2.15.0.9 (dev) not released yet (2025-04-09) ==================================================== **Incompatible changes** @@ -42,6 +42,9 @@ Bugs `ZINT_CAP_STACKABLE` - ZBarcode_Cap: add missing symbologies to `ZINT_CAP_BINDABLE` (was `ZINT_CAP_STACKABLE`) +- MAILMARK_2D: fix postcode validation: no limited alphanumerics, spaced-out + DPS "outward"-only allowed, all-blank DPS allowed (ticket #334, props Milton + Neal) - manual/man page: fix DATAMATRIX Sizes tables "28 12x26" -> "27 12x26" diff --git a/backend/mailmark.c b/backend/mailmark.c index e7d1f37e..4e5af017 100644 --- a/backend/mailmark.c +++ b/backend/mailmark.c @@ -40,6 +40,7 @@ * */ +#include #include #include "common.h" #include "large.h" @@ -48,15 +49,15 @@ #define RUBIDIUM_F (IS_NUM_F | IS_UPR_F | IS_SPC_F) /* RUBIDIUM "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ " */ /* Allowed character values from Table 3 */ -#define SET_F "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define SET_A "ABCDEFGHIJKLMNOPQRSTUVWXYZ" #define SET_L "ABDEFGHJLNPQRSTUWXYZ" #define SET_N "0123456789" #define SET_S " " static const char mailmark_postcode_format[6][9] = { - {'F','N','F','N','L','L','N','L','S'}, {'F','F','N','N','L','L','N','L','S'}, - {'F','F','N','N','N','L','L','N','L'}, {'F','F','N','F','N','L','L','N','L'}, - {'F','N','N','L','L','N','L','S','S'}, {'F','N','N','N','L','L','N','L','S'} + {'A','N','A','N','L','L','N','L','S'}, {'A','A','N','N','L','L','N','L','S'}, + {'A','A','N','N','N','L','L','N','L'}, {'A','A','N','A','N','L','L','N','L'}, + {'A','N','N','L','L','N','L','S','S'}, {'A','N','N','N','L','L','N','L','S'} }; /* Data/Check Symbols from Table 5 */ @@ -80,15 +81,16 @@ static const unsigned char mailmark_extender_group_l[26] = { 2, 5, 7, 8, 13, 14, 15, 16, 21, 22, 23, 0, 1, 3, 4, 6, 9, 10, 11, 12, 17, 18, 19, 20, 24, 25 }; -static int mailmark_verify_character(char input, char type) { +static int mailmark_verify_character(const char input, const char type, const int is_2d) { int val = 0; switch (type) { - case 'F': - val = posn(SET_F, input); + case 'A': + val = posn(SET_A, input); break; case 'L': - val = posn(SET_L, input); + /* Limited only applies to 4-state (ticket #334, props Milton Neal) */ + val = posn(is_2d ? SET_A : SET_L, input); break; case 'N': val = posn(SET_N, input); @@ -98,35 +100,33 @@ static int mailmark_verify_character(char input, char type) { break; } - if (val == -1) { - return 0; - } else { - return 1; - } + return val != -1; } -static int mailmark_verify_postcode(const char postcode[10], int *p_postcode_type) { +static int mailmark_verify_postcode(const char postcode[10], const int length, int *p_postcode_type) { + const int is_2d = !p_postcode_type; int postcode_type; /* Detect postcode type */ /* postcode_type is used to select which format of postcode * - * 1 = FNFNLLNLS - * 2 = FFNNLLNLS - * 3 = FFNNNLLNL - * 4 = FFNFNLLNL - * 5 = FNNLLNLSS - * 6 = FNNNLLNLS + * 1 = ANANLLNLS + * 2 = AANNLLNLS + * 3 = AANNNLLNL + * 4 = AANANLLNL + * 5 = ANNLLNLSS + * 6 = ANNNLLNLS * 7 = International designation */ + assert(length == 9 || (length >= 2 && length <= 4)); - if (strcmp(postcode, "XY11 ") == 0) { + if (length >= 4 && memcmp(postcode, "XY11 ", length) == 0) { postcode_type = 7; } else { - if (postcode[7] == ' ') { + if (length == 2 || (length == 9 && postcode[7] == ' ')) { postcode_type = 5; } else { - if (postcode[8] == ' ') { + if (length == 3 || (length == 9 && postcode[8] == ' ')) { /* Types 1, 2 and 6 */ if (z_isdigit(postcode[1])) { if (z_isdigit(postcode[2])) { @@ -138,6 +138,7 @@ static int mailmark_verify_postcode(const char postcode[10], int *p_postcode_typ postcode_type = 2; } } else { + assert(length >= 4); /* Types 3 and 4 */ if (z_isdigit(postcode[3])) { postcode_type = 3; @@ -156,8 +157,8 @@ static int mailmark_verify_postcode(const char postcode[10], int *p_postcode_typ if (postcode_type != 7) { int i; const char *const pattern = mailmark_postcode_format[postcode_type - 1]; - for (i = 0; i < 9; i++) { - if (!(mailmark_verify_character(postcode[i], pattern[i]))) { + for (i = 0; i < length; i++) { + if (!mailmark_verify_character(postcode[i], pattern[i], is_2d)) { return 1; } } @@ -262,13 +263,12 @@ INTERNAL int mailmark_4s(struct zint_symbol *symbol, unsigned char source[], int } } - /* Separate Destination Post Code plus DPS field */ + /* Destination Post Code plus DPS field */ for (i = 0; i < 9; i++) { postcode[i] = local_source[(length - 9) + i]; } - postcode[9] = '\0'; - if (mailmark_verify_postcode(postcode, &postcode_type) != 0) { - return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 587, "Invalid postcode \"%s\"", postcode); + if (mailmark_verify_postcode(postcode, 9, &postcode_type) != 0) { + return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 587, "Invalid postcode \"%.9s\"", postcode); } /* Convert postcode to internal user field */ @@ -282,9 +282,9 @@ INTERNAL int mailmark_4s(struct zint_symbol *symbol, unsigned char source[], int for (i = 0; i < 9; i++) { switch (pattern[i]) { - case 'F': + case 'A': large_mul_u64(&b, 26); - large_add_u64(&b, posn(SET_F, postcode[i])); + large_add_u64(&b, posn(SET_A, postcode[i])); break; case 'L': large_mul_u64(&b, 20); @@ -511,7 +511,7 @@ INTERNAL int datamatrix(struct zint_symbol *symbol, struct zint_seg segs[], cons /* Royal Mail 2D Mailmark (CMDM) (Data Matrix) */ /* https://www.royalmailtechnical.com/rmt_docs/User_Guides_2021/Mailmark_Barcode_definition_document_20210215.pdf */ INTERNAL int mailmark_2d(struct zint_symbol *symbol, unsigned char source[], int length) { - + static const char spaces[9] = " "; unsigned char local_source[90 + 1]; char postcode[10]; int i; @@ -614,12 +614,16 @@ INTERNAL int mailmark_2d(struct zint_symbol *symbol, unsigned char source[], int } /* Destination Post Code plus DPS field */ - for (i = 0; i < 9; i++) { - postcode[i] = local_source[22 + i]; - } - postcode[9] = '\0'; - if (mailmark_verify_postcode(postcode, NULL) != 0) { - return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 872, "Invalid Destination Post Code plus DPS"); + if (memcmp(local_source + 22, spaces, 9) != 0) { /* If not blank (allowed) */ + for (i = 0; i < 9; i++) { + postcode[i] = local_source[22 + i]; + } + for (i = 8; i >= 0 && postcode[i] == ' '; i--); /* Find trailing spaces */ + i++; + /* If not 2 to 4 non-spaces left, check full post code */ + if (mailmark_verify_postcode(postcode, i >= 2 && i <= 4 ? i : 9, NULL /*p_postcode_type*/) != 0) { + return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 872, "Invalid Destination Post Code plus DPS"); + } } /* Service Type */ @@ -628,7 +632,7 @@ INTERNAL int mailmark_2d(struct zint_symbol *symbol, unsigned char source[], int } /* Return to Sender Post Code */ - if (memcmp(local_source + 32, " ", 7) != 0) { /* If not blank (allowed) */ + if (memcmp(local_source + 32, spaces, 7) != 0) { /* If not blank (allowed) */ for (i = 0; i < 7; i++) { postcode[i] = local_source[32 + i]; } @@ -640,14 +644,13 @@ INTERNAL int mailmark_2d(struct zint_symbol *symbol, unsigned char source[], int while (i != 9) { postcode[i++] = ' '; } - postcode[9] = '\0'; - if (mailmark_verify_postcode(postcode, NULL) != 0) { + if (mailmark_verify_postcode(postcode, 9, NULL /*p_postcode_type*/) != 0) { return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 874, "Invalid Return to Sender Post Code"); } } /* Reserved */ - if (memcmp(local_source + 39, " ", 6) != 0) { + if (memcmp(local_source + 39, spaces, 6) != 0) { return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 875, "Invalid Reserved field (must be spaces only)"); } diff --git a/backend/tests/test_mailmark.c b/backend/tests/test_mailmark.c index b24e896a..84716196 100644 --- a/backend/tests/test_mailmark.c +++ b/backend/tests/test_mailmark.c @@ -69,8 +69,8 @@ static void test_4s_hrt(const testCtx *const p_ctx) { assert_nonnull(symbol, "Symbol not created\n"); length = testUtilSetSymbol(symbol, BARCODE_MAILMARK_4S, -1 /*input_mode*/, -1 /*eci*/, - -1 /*option_1*/, -1 /*option_2*/, -1 /*option_3*/, data[i].output_options, - data[i].data, -1, debug); + -1 /*option_1*/, -1 /*option_2*/, -1 /*option_3*/, data[i].output_options, + data[i].data, -1, debug); expected_length = (int) strlen(data[i].expected); expected_raw_length = (int) strlen(data[i].expected_raw); @@ -167,7 +167,8 @@ static void test_4s_input(const testCtx *const p_ctx) { /* 53*/ { "01000000000000000C12JQ3U A", ZINT_ERROR_INVALID_DATA, -1, -1 }, /* F N N L L N L S S bad 2nd S */ /* 54*/ { "01000000000000000C123JQ4U ", 0, 3, 155, }, /* F N N N L L N L S */ /* 55*/ { "01000000000000000C 23JQ4U ", ZINT_ERROR_INVALID_DATA, -1, -1 }, /* F N N N L L N L S bad 1st N (non-alpha otherwise matches 2nd pattern) */ - /* 56*/ { "41038422416563762XY1", ZINT_ERROR_INVALID_DATA, -1, -1 }, + /* 56*/ { "01000000000000000 ", ZINT_ERROR_INVALID_DATA, -1, -1 }, /* All spaces */ + /* 57*/ { "41038422416563762XY1", ZINT_ERROR_INVALID_DATA, -1, -1 }, }; const int data_size = ARRAY_SIZE(data); int i, length, ret; @@ -182,14 +183,19 @@ static void test_4s_input(const testCtx *const p_ctx) { symbol = ZBarcode_Create(); assert_nonnull(symbol, "Symbol not created\n"); - length = testUtilSetSymbol(symbol, BARCODE_MAILMARK_4S, -1 /*input_mode*/, -1 /*eci*/, -1 /*option_1*/, -1, -1, -1 /*output_options*/, data[i].data, -1, debug); + length = testUtilSetSymbol(symbol, BARCODE_MAILMARK_4S, -1 /*input_mode*/, -1 /*eci*/, + -1 /*option_1*/, -1 /*option_2*/, -1 /*option_2*/, -1 /*output_options*/, + data[i].data, -1, debug); ret = ZBarcode_Encode(symbol, TCU(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); + assert_equal(ret, data[i].ret, "i:%d ZBarcode_Encode ret %d != %d (%s)\n", + i, ret, data[i].ret, symbol->errtxt); if (ret < ZINT_ERROR) { - assert_equal(symbol->rows, data[i].expected_rows, "i:%d symbol->rows %d != %d\n", i, symbol->rows, data[i].expected_rows); - assert_equal(symbol->width, data[i].expected_width, "i:%d symbol->width %d != %d\n", i, symbol->width, data[i].expected_width); + assert_equal(symbol->rows, data[i].expected_rows, "i:%d symbol->rows %d != %d\n", + i, symbol->rows, data[i].expected_rows); + assert_equal(symbol->width, data[i].expected_width, "i:%d symbol->width %d != %d\n", + i, symbol->width, data[i].expected_width); } ZBarcode_Delete(symbol); @@ -252,19 +258,24 @@ static void test_4s_encode_vector(const testCtx *const p_ctx) { symbol = ZBarcode_Create(); assert_nonnull(symbol, "Symbol not created\n"); - length = testUtilSetSymbol(symbol, BARCODE_MAILMARK_4S, -1 /*input_mode*/, -1 /*eci*/, -1 /*option_1*/, -1, -1, -1 /*output_options*/, data[i].data, -1, debug); + length = testUtilSetSymbol(symbol, BARCODE_MAILMARK_4S, -1 /*input_mode*/, -1 /*eci*/, + -1 /*option_1*/, -1 /*option_2*/, -1 /*option_3*/, -1 /*output_options*/, + data[i].data, -1, debug); ret = ZBarcode_Encode(symbol, TCU(data[i].data), length); - assert_equal(ret, data[i].ret_encode, "i:%d ZBarcode_Encode ret %d != %d (%s)\n", i, ret, data[i].ret_encode, symbol->errtxt); + assert_equal(ret, data[i].ret_encode, "i:%d ZBarcode_Encode ret %d != %d (%s)\n", + i, ret, data[i].ret_encode, symbol->errtxt); assert_equal(symbol->rows, 3, "i:%d symbol->rows %d != 3\n", i, symbol->rows); ret = testUtilDAFTConvert(symbol, actual_daft, sizeof(actual_daft)); assert_nonzero(ret, "i:%d testUtilDAFTConvert ret == 0", i); - assert_zero(strcmp(actual_daft, data[i].expected_daft), "i:%d\n actual %s\nexpected %s\n", i, actual_daft, data[i].expected_daft); + assert_zero(strcmp(actual_daft, data[i].expected_daft), "i:%d\n actual %s\nexpected %s\n", + i, actual_daft, data[i].expected_daft); ret = ZBarcode_Buffer_Vector(symbol, 0); - assert_equal(ret, data[i].ret_vector, "i:%d ZBarcode_Buffer_Vector ret %d != %d (%s)\n", i, ret, data[i].ret_vector, symbol->errtxt); + assert_equal(ret, data[i].ret_vector, "i:%d ZBarcode_Buffer_Vector ret %d != %d (%s)\n", + i, ret, data[i].ret_vector, symbol->errtxt); ZBarcode_Delete(symbol); } @@ -306,10 +317,13 @@ static void test_4s_encode(const testCtx *const p_ctx) { symbol = ZBarcode_Create(); assert_nonnull(symbol, "Symbol not created\n"); - length = testUtilSetSymbol(symbol, BARCODE_MAILMARK_4S, -1 /*input_mode*/, -1 /*eci*/, -1 /*option_1*/, -1, -1, -1 /*output_options*/, data[i].data, -1, debug); + length = testUtilSetSymbol(symbol, BARCODE_MAILMARK_4S, -1 /*input_mode*/, -1 /*eci*/, + -1 /*option_1*/, -1 /*option_2*/, -1 /*option_3*/, -1 /*output_options*/, + data[i].data, -1, debug); ret = ZBarcode_Encode(symbol, TCU(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); + assert_equal(ret, data[i].ret, "i:%d ZBarcode_Encode ret %d != %d (%s)\n", + i, ret, data[i].ret, symbol->errtxt); if (p_ctx->generate) { printf(" /*%3d*/ { \"%s\", %s, %d, %d, \"%s\",\n", @@ -321,11 +335,14 @@ static void test_4s_encode(const testCtx *const p_ctx) { if (ret < ZINT_ERROR) { int width, row; - 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); + 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); 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); + assert_zero(ret, "i:%d testUtilModulesCmp ret %d != 0 width %d row %d (%s)\n", + i, ret, width, row, data[i].data); } } @@ -348,6 +365,7 @@ static void test_2d_input(const testCtx *const p_ctx) { int expected_option_2; }; /* s/\/\*[ 0-9]*\*\//\=printf("\/\*%3d*\/", line(".") - line("'<")): */ + /* UUUUIVCSSSSSSSIIIIIIIIDDDDDDDDDSRRRRRRRSSSSSS */ static const struct item data[] = { /* 0*/ { -1, "012100123412345678AB19XY1A 0", 0, 24, 24, "", 8 }, /* 1*/ { -1, "012100123412345678ab19xy1a 0", 0, 24, 24, "", 8 }, /* Converts to upper */ @@ -371,18 +389,36 @@ static void test_2d_input(const testCtx *const p_ctx) { /* 19*/ { -1, "JGB 01 100123412345678AB19XY1A 0", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 869: Invalid Class (cannot be space)", 8 }, /* 20*/ { -1, "JGB 012100123A12345678AB19XY1A 0", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 870: Invalid Supply Chain ID (7 digits only)", 8 }, /* 21*/ { -1, "JGB 01210012341234567AAB19XY1A 0", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 871: Invalid Item ID (8 digits only)", 8 }, - /* 22*/ { -1, "JGB 012100123412345678AB19VY1A 0", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 872: Invalid Destination Post Code plus DPS", 8 }, + /* 22*/ { -1, "JGB 012100123412345678AB19VY1A 0", 0, 24, 24, "", 8 }, /* Limited ('V') allowed for 2D (ticket #334, props Milton Neal) */ /* 23*/ { -1, "JGB 012100123412345678AB19XY11 0", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 872: Invalid Destination Post Code plus DPS", 8 }, - /* 24*/ { -1, "JGB 012100123412345678AB19XY1A 7", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 873: Invalid Service Type (\"0\" to \"6\" only)", 8 }, - /* 25*/ { -1, "JGB 012100123412345678AB19XY1A 0AB18XY", 0, 24, 24, "", 8 }, - /* 26*/ { -1, "JGB 012100123412345678AB190XY1A0AB18XY", 0, 24, 24, "", 8 }, - /* 27*/ { -1, "JGB 012100123412345678AB19XY1A 0AB18XI", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 874: Invalid Return to Sender Post Code", 8 }, - /* 28*/ { -1, "JGB 012100123412345678AB19XY1A 0A18XY", 0, 24, 24, "", 8 }, - /* 29*/ { -1, "JGB 012100123412345678AB19XY1A 0A18XC", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 874: Invalid Return to Sender Post Code", 8 }, - /* 30*/ { -1, "JGB 012100123412345678AB19XY1A 0AB181XY", 0, 24, 24, "", 8 }, - /* 31*/ { -1, "JGB 012100123412345678AB19XY1A 0AB181VY", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 874: Invalid Return to Sender Post Code", 8 }, - /* 32*/ { -1, "JGB 012100123412345678AB19XY1A 0AB181XY ", 0, 24, 24, "", 8 }, - /* 33*/ { -1, "JGB 012100123412345678AB19XY1A 0AB181XYA ", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 875: Invalid Reserved field (must be spaces only)", 8 }, + /* 24*/ { -1, "JGB 012100123412345678ABC9XY1A 0", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 872: Invalid Destination Post Code plus DPS", 8 }, + /* 25*/ { -1, "JGB 012100123412345678 0AB181XY ", 0, 24, 24, "", 8 }, /* DPS all spaces */ + /* 25*/ { -1, "JGB 012100123412345678AB1 0AB181XY ", 0, 24, 24, "", 8 }, /* DPS 'AAN' + spaces */ + /* 26*/ { -1, "JGB 012100123412345678AB 0AB181XY ", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 872: Invalid Destination Post Code plus DPS", 8 }, + /* 27*/ { -1, "JGB 012100123412345678A1 0AB181XY ", 0, 24, 24, "", 8 }, /* DPS 'AN' + spaces */ + /* 28*/ { -1, "JGB 012100123412345678A 0AB181XY ", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 872: Invalid Destination Post Code plus DPS", 8 }, + /* 29*/ { -1, "JGB 012100123412345678A12 0AB181XY ", 0, 24, 24, "", 8 }, /* DPS 'ANN' + spaces */ + /* 30*/ { -1, "JGB 012100123412345678A123 0AB181XY ", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 872: Invalid Destination Post Code plus DPS", 8 }, + /* 31*/ { -1, "JGB 012100123412345678AB12 0AB181XY ", 0, 24, 24, "", 8 }, /* DPS 'AANN' + spaces */ + /* 32*/ { -1, "JGB 012100123412345678AB123 0AB181XY ", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 872: Invalid Destination Post Code plus DPS", 8 }, + /* 33*/ { -1, "JGB 012100123412345678A1B 0AB181XY ", 0, 24, 24, "", 8 }, /* DPS 'ANA' + spaces */ + /* 34*/ { -1, "JGB 012100123412345678A1B1 0AB181XY ", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 872: Invalid Destination Post Code plus DPS", 8 }, + /* 35*/ { -1, "JGB 012100123412345678AB1A 0AB181XY ", 0, 24, 24, "", 8 }, /* DPS 'AANA' + spaces */ + /* 36*/ { -1, "JGB 012100123412345678AB1A1 0AB181XY ", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 872: Invalid Destination Post Code plus DPS", 8 }, + /* 37*/ { -1, "JGB 012100123412345678AB19XY1A 7", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 873: Invalid Service Type (\"0\" to \"6\" only)", 8 }, + /* 38*/ { -1, "JGB 012100123412345678AB19XY1A 0AB18XY", 0, 24, 24, "", 8 }, + /* 39*/ { -1, "JGB 012100123412345678AB190XY1A0AB18XY", 0, 24, 24, "", 8 }, + /* 40*/ { -1, "JGB 012100123412345678AB19XY1A 0AB18XI", 0, 24, 24, "", 8 }, /* Limited ('I') allowed for 2D (ticket #334, props Milton Neal) */ + /* 41*/ { -1, "JGB 012100123412345678AB19XY1A 0A18XY", 0, 24, 24, "", 8 }, + /* 42*/ { -1, "JGB 012100123412345678AB19XY1A 0A18XC", 0, 24, 24, "", 8 }, /* Limited ('C') allowed for 2D (ticket #334, props Milton Neal) */ + /* 43*/ { -1, "JGB 012100123412345678AB19XY1A 0AB181XY", 0, 24, 24, "", 8 }, + /* 44*/ { -1, "JGB 012100123412345678AB19XY1A 0AB181VY", 0, 24, 24, "", 8 }, /* Limited ('C') allowed for 2D (ticket #334, props Milton Neal) */ + /* 45*/ { -1, "JGB 012100123412345678AB19XY1A 0ABC81XY", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 874: Invalid Return to Sender Post Code", 8 }, + /* 46*/ { -1, "JGB 012100123412345678AB19XY1A 01B181VY", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 874: Invalid Return to Sender Post Code", 8 }, + /* 47*/ { -1, "JGB 012100123412345678AB19XY1A 0AB1811Y", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 874: Invalid Return to Sender Post Code", 8 }, + /* 48*/ { -1, "JGB 012100123412345678AB19XY1A 0A1AB1XY", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 874: Invalid Return to Sender Post Code", 8 }, + /* 49*/ { -1, "JGB 012100123412345678AB19XY1A 0AB181XY ", 0, 24, 24, "", 8 }, + /* 50*/ { -1, "JGB 012100123412345678AB19XY1A 0AB181XYA ", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 875: Invalid Reserved field (must be spaces only)", 8 }, }; const int data_size = ARRAY_SIZE(data); int i, length, ret; @@ -402,11 +438,14 @@ static void test_2d_input(const testCtx *const p_ctx) { data[i].data, -1, debug); ret = ZBarcode_Encode(symbol, TCU(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); + assert_equal(ret, data[i].ret, "i:%d ZBarcode_Encode ret %d != %d (%s)\n", + i, ret, data[i].ret, symbol->errtxt); if (ret < ZINT_ERROR) { - assert_equal(symbol->rows, data[i].expected_rows, "i:%d symbol->rows %d != %d\n", i, symbol->rows, data[i].expected_rows); - assert_equal(symbol->width, data[i].expected_width, "i:%d symbol->width %d != %d\n", i, symbol->width, data[i].expected_width); + assert_equal(symbol->rows, data[i].expected_rows, "i:%d symbol->rows %d != %d\n", + i, symbol->rows, data[i].expected_rows); + assert_equal(symbol->width, data[i].expected_width, "i:%d symbol->width %d != %d\n", + i, symbol->width, data[i].expected_width); } assert_zero(strcmp(symbol->errtxt, data[i].expected_errtxt), "i:%d strcmp(%s, %s) != 0\n", i, symbol->errtxt, data[i].expected_errtxt); @@ -624,10 +663,13 @@ static void test_2d_encode(const testCtx *const p_ctx) { symbol = ZBarcode_Create(); assert_nonnull(symbol, "Symbol not created\n"); - length = testUtilSetSymbol(symbol, BARCODE_MAILMARK_2D, -1 /*input_mode*/, -1 /*eci*/, -1 /*option_1*/, data[i].option_2, -1, -1 /*output_options*/, data[i].data, -1, debug); + length = testUtilSetSymbol(symbol, BARCODE_MAILMARK_2D, -1 /*input_mode*/, -1 /*eci*/, + -1 /*option_1*/, data[i].option_2, -1 /*option_3*/, -1 /*output_options*/, + data[i].data, -1, debug); ret = ZBarcode_Encode(symbol, TCU(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); + assert_equal(ret, data[i].ret, "i:%d ZBarcode_Encode ret %d != %d (%s)\n", + i, ret, data[i].ret, symbol->errtxt); if (p_ctx->generate) { printf(" /*%3d*/ { %d, \"%s\", %s, %d, %d, %d, \"%s\",\n", @@ -639,30 +681,42 @@ static void test_2d_encode(const testCtx *const p_ctx) { if (ret < ZINT_ERROR) { int width, row; - 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); + 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); 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); + assert_zero(ret, "i:%d testUtilModulesCmp ret %d != 0 width %d row %d (%s)\n", + i, ret, width, row, data[i].data); if (do_bwipp && testUtilCanBwipp(i, symbol, -1, data[i].option_2, -1, debug)) { if (!data[i].bwipp_cmp) { - if (debug & ZINT_DEBUG_TEST_PRINT) printf("i:%d %s not BWIPP compatible (%s)\n", i, testUtilBarcodeName(symbol->symbology), data[i].comment); + if (debug & ZINT_DEBUG_TEST_PRINT) { + printf("i:%d %s not BWIPP compatible (%s)\n", + i, testUtilBarcodeName(symbol->symbology), data[i].comment); + } } else { - ret = testUtilBwipp(i, symbol, -1, data[i].option_2, -1, data[i].data, length, NULL, cmp_buf, sizeof(cmp_buf), NULL); - assert_zero(ret, "i:%d %s testUtilBwipp ret %d != 0\n", i, testUtilBarcodeName(symbol->symbology), ret); + ret = testUtilBwipp(i, symbol, -1, data[i].option_2, -1, data[i].data, length, NULL, cmp_buf, + sizeof(cmp_buf), NULL); + assert_zero(ret, "i:%d %s testUtilBwipp ret %d != 0\n", + i, testUtilBarcodeName(symbol->symbology), ret); ret = testUtilBwippCmp(symbol, cmp_msg, cmp_buf, data[i].expected); assert_zero(ret, "i:%d %s testUtilBwippCmp %d != 0 %s\n actual: %s\nexpected: %s\n", - i, testUtilBarcodeName(symbol->symbology), ret, cmp_msg, cmp_buf, data[i].expected); + i, testUtilBarcodeName(symbol->symbology), ret, cmp_msg, cmp_buf, + data[i].expected); } } if (do_zxingcpp && testUtilCanZXingCPP(i, symbol, data[i].data, length, debug)) { int cmp_len, ret_len; char modules_dump[144 * 144 + 1]; - assert_notequal(testUtilModulesDump(symbol, modules_dump, sizeof(modules_dump)), -1, "i:%d testUtilModulesDump == -1\n", i); - ret = testUtilZXingCPP(i, symbol, data[i].data, length, modules_dump, 1 /*zxingcpp_cmp*/, cmp_buf, sizeof(cmp_buf), &cmp_len); - assert_zero(ret, "i:%d %s testUtilZXingCPP ret %d != 0\n", i, testUtilBarcodeName(symbol->symbology), ret); + assert_notequal(testUtilModulesDump(symbol, modules_dump, sizeof(modules_dump)), -1, + "i:%d testUtilModulesDump == -1\n", i); + ret = testUtilZXingCPP(i, symbol, data[i].data, length, modules_dump, 1 /*zxingcpp_cmp*/, cmp_buf, + sizeof(cmp_buf), &cmp_len); + assert_zero(ret, "i:%d %s testUtilZXingCPP ret %d != 0\n", + i, testUtilBarcodeName(symbol->symbology), ret); ret = testUtilZXingCPPCmp(symbol, cmp_msg, cmp_buf, cmp_len, data[i].data, length, NULL /*primary*/, escaped, &ret_len); diff --git a/docs/manual.html b/docs/manual.html index df6537f0..524d33fc 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -5909,26 +5909,24 @@ data-tag=": Royal Mail 4-State Mailmark Input Fields">

The 6 Destination+DPS (Destination Post Code plus Delivery Point Suffix) patterns are:

-
- - - + @@ -6310,7 +6308,7 @@ data-tag=": Royal Mail 2D Mailmark Input Fields"> - + @@ -6325,12 +6323,43 @@ data-tag=": Royal Mail 2D Mailmark Input Fields">
Table : Royal Mail Mailmark Destination+DPS +
+ + - - - - - - - - + + + + + +
Table : Royal Mail 4-State Mailmark Destination+DPS Patterns:
FNFNLLNLSFFNNLLNLSFFNNNLLNL
FFNFNLLNLFNNLLNLSSFNNNLLNLSANNLLNLSSAANNLLNLSANNNLLNLSAANNNLLNLANANLLNLSAANANLLNL
-

where 'F' stands for full alphabetic (A-Z), +

where 'A' stands for full alphabetic (A-Z), 'L' for limited alphabetic (A-Z less 'CIKMOV'), 'N' for numeric (0-9), and 'S' for space.

@@ -6300,7 +6298,7 @@ data-tag=": Royal Mail 2D Mailmark Input Fields">
Destination+DPS 9Alphanumeric (1 of 6 patterns)Alphanumeric (1 of 13 patterns)
Service Type
RTS Post Code 7Alphanumeric (1 of 6 patterns)Alphanumeric (1 of 7 patterns)
Reserved
-

The 6 Destination+DPS (Destination Post Code plus Delivery Point -Suffix) patterns are the same as for the 4-state - see Table : Royal Mail Mailmark -Destination+DPS Patterns. The 6 RTS (Return to Sender) Post Code -patterns are the same also except without the additional DPS -'NL', i.e.

+

The 13 Destination+DPS (Destination Post Code plus Delivery Point +Suffix) patterns are similar to those for the 4-state except that the +alphabetic limitation ('L' versus 'A') does +not apply, only the initial “outward” part is required (the rest can be +blank), and the whole field can be blank:

+
+ + + + + + + + + + + + + + + + + + + + + + +
Table : Royal Mail 2D Mailmark Destination+DPS +Patterns:
ANNAANASSAANNAANASANNNAANASAANNNAANAANANAANASAANANAANASSSSSSSSS
ANSSSSSSSAANSSSSSSANNSSSSSSAANNSSSSSANASSSSSSAANASSSSS
+
+

where 'A' is alphabetic (A-Z), 'N' numeric +(0-9), and 'S' space.

+

The 7 RTS (Return to Sender) Post Code patterns are similar to above +except without the DPS ('NA'), and the trailing “inward” +part cannot be blank (although the whole field can be):

@@ -6338,21 +6367,17 @@ data-tag=": Royal Mail 2D Mailmark RTS Patterns"> - - - - - - - - + + + + + + +
FNFNLLSFFNNLLSFFNNNLL
FFNFNLLFNNLLSSFNNNLLSANNAASSAANNAASANNNAASAANNNAAANANAASAANANAASSSSSSS
-

where 'F' is full alphabetic (A-Z), 'L' -limited alphabetic (A-Z less 'CIKMOV'), 'N' -numeric (0-9), and 'S' space.

Three sizes are defined, one rectangular, with varying maximum amounts of optional customer data:

diff --git a/docs/manual.pmd b/docs/manual.pmd index 0458ec73..fb19b78c 100644 --- a/docs/manual.pmd +++ b/docs/manual.pmd @@ -3974,15 +3974,14 @@ tag=": Royal Mail 4-State Mailmark Input Fields"} The 6 Destination+DPS (Destination Post Code plus Delivery Point Suffix) patterns are: ------------ ----------- ----------- -`FNFNLLNLS` `FFNNLLNLS` `FFNNNLLNL` -`FFNFNLLNL` `FNNLLNLSS` `FNNNLLNLS` ------------ ----------- ----------- +----------- ----------- ----------- ----------- ----------- ----------- +`ANNLLNLSS` `AANNLLNLS` `ANNNLLNLS` `AANNNLLNL` `ANANLLNLS` `AANANLLNL` +----------- ----------- ----------- ----------- ----------- ----------- -Table: {#tbl:mailmark_destination_dps -tag=": Royal Mail Mailmark Destination+DPS Patterns"} +Table: {#tbl:mailmark_4s_destination_dps +tag=": Royal Mail 4-State Mailmark Destination+DPS Patterns"} -where `'F'` stands for full alphabetic (A-Z), `'L'` for limited alphabetic (A-Z +where `'A'` stands for full alphabetic (A-Z), `'L'` for limited alphabetic (A-Z less `'CIKMOV'`), `'N'` for numeric (0-9), and `'S'` for space. Four of the permitted patterns include a number of trailing space characters - @@ -4142,31 +4141,41 @@ Version ID 1 `"1"` Class 1 Alphanumeric Supply Chain ID 7 Numeric Item ID 8 Numeric -Destination+DPS 9 Alphanumeric (1 of 6 patterns) +Destination+DPS 9 Alphanumeric (1 of 13 patterns) Service Type 1 Numeric -RTS Post Code 7 Alphanumeric (1 of 6 patterns) +RTS Post Code 7 Alphanumeric (1 of 7 patterns) Reserved 6 Spaces Customer Data 6, 45 or 29 Anything (Latin-1) Table: {#tbl:mailmark_2d_input_fields tag=": Royal Mail 2D Mailmark Input Fields"} -The 6 Destination+DPS (Destination Post Code plus Delivery Point Suffix) -patterns are the same as for the 4-state - see Table -{@tbl:mailmark_destination_dps}. The 6 RTS (Return to Sender) Post Code patterns -are the same also except without the additional DPS `'NL'`, i.e. +The 13 Destination+DPS (Destination Post Code plus Delivery Point Suffix) +patterns are similar to those for the 4-state except that the alphabetic +limitation (`'L'` versus `'A'`) does not apply, only the initial "outward" part +is required (the rest can be blank), and the whole field can be blank: ---------- --------- --------- -`FNFNLLS` `FFNNLLS` `FFNNNLL` -`FFNFNLL` `FNNLLSS` `FNNNLLS` ---------- --------- --------- +----------- ----------- ----------- ----------- ----------- ----------- ----------- +`ANNAANASS` `AANNAANAS` `ANNNAANAS` `AANNNAANA` `ANANAANAS` `AANANAANA` `SSSSSSSSS` +`ANSSSSSSS` `AANSSSSSS` `ANNSSSSSS` `AANNSSSSS` `ANASSSSSS` `AANASSSSS` +----------- ----------- ----------- ----------- ----------- ----------- ----------- + +Table: {#tbl:mailmark_2d_destination_dps +tag=": Royal Mail 2D Mailmark Destination+DPS Patterns"} + +where `'A'` is alphabetic (A-Z), `'N'` numeric (0-9), and `'S'` space. + +The 7 RTS (Return to Sender) Post Code patterns are similar to above except +without the DPS (`'NA'`), and the trailing "inward" part cannot be blank +(although the whole field can be): + +--------- --------- --------- --------- --------- --------- --------- +`ANNAASS` `AANNAAS` `ANNNAAS` `AANNNAA` `ANANAAS` `AANANAA` `SSSSSSS` +--------- --------- --------- --------- --------- --------- --------- Table: {#tbl:mailmark_2d_rts tag=": Royal Mail 2D Mailmark RTS Patterns"} -where `'F'` is full alphabetic (A-Z), `'L'` limited alphabetic (A-Z less -`'CIKMOV'`), `'N'` numeric (0-9), and `'S'` space. - Three sizes are defined, one rectangular, with varying maximum amounts of optional customer data: diff --git a/docs/manual.txt b/docs/manual.txt index dae8603f..3a385f1e 100644 --- a/docs/manual.txt +++ b/docs/manual.txt @@ -3792,15 +3792,14 @@ the following table. The 6 Destination+DPS (Destination Post Code plus Delivery Point Suffix) patterns are: - ----------- ----------- ----------- - FNFNLLNLS FFNNLLNLS FFNNNLLNL - FFNFNLLNL FNNLLNLSS FNNNLLNLS - ----------- ----------- ----------- + ----------- ----------- ----------- ----------- ----------- ----------- + ANNLLNLSS AANNLLNLS ANNNLLNLS AANNNLLNL ANANLLNLS AANANLLNL + ----------- ----------- ----------- ----------- ----------- ----------- - Table : Royal Mail Mailmark Destination+DPS Patterns + Table : Royal Mail 4-State Mailmark Destination+DPS Patterns -where 'F' stands for full alphabetic (A-Z), 'L' for limited alphabetic (A-Z less +where 'A' stands for full alphabetic (A-Z), 'L' for limited alphabetic (A-Z less 'CIKMOV'), 'N' for numeric (0-9), and 'S' for space. Four of the permitted patterns include a number of trailing space characters - @@ -3943,37 +3942,49 @@ offers space for customer data following an initial pre-formatted 45 character section, as summarized below. Field Name Length Values - ------------------ ------------- -------------------------------- + ------------------ ------------- --------------------------------- UPU Country ID 4 "JGB " Information Type 1 Alphanumeric Version ID 1 "1" Class 1 Alphanumeric Supply Chain ID 7 Numeric Item ID 8 Numeric - Destination+DPS 9 Alphanumeric (1 of 6 patterns) + Destination+DPS 9 Alphanumeric (1 of 13 patterns) Service Type 1 Numeric - RTS Post Code 7 Alphanumeric (1 of 6 patterns) + RTS Post Code 7 Alphanumeric (1 of 7 patterns) Reserved 6 Spaces Customer Data 6, 45 or 29 Anything (Latin-1) Table : Royal Mail 2D Mailmark Input Fields -The 6 Destination+DPS (Destination Post Code plus Delivery Point Suffix) -patterns are the same as for the 4-state - see Table -: Royal Mail Mailmark Destination+DPS Patterns. The 6 RTS (Return to Sender) -Post Code patterns are the same also except without the additional DPS 'NL', -i.e. +The 13 Destination+DPS (Destination Post Code plus Delivery Point Suffix) +patterns are similar to those for the 4-state except that the alphabetic +limitation ('L' versus 'A') does not apply, only the initial “outward” part is +required (the rest can be blank), and the whole field can be blank: - --------- --------- --------- - FNFNLLS FFNNLLS FFNNNLL - FFNFNLL FNNLLSS FNNNLLS - --------- --------- --------- + ----------- ----------- ----------- ----------- ----------- ----------- ------ +----- + ANNAANASS AANNAANAS ANNNAANAS AANNNAANA ANANAANAS AANANAANA SSSSSS +SSS + ANSSSSSSS AANSSSSSS ANNSSSSSS AANNSSSSS ANASSSSSS AANASSSSS + ----------- ----------- ----------- ----------- ----------- ----------- ------ +----- + + Table : Royal Mail 2D Mailmark Destination+DPS Patterns + + +where 'A' is alphabetic (A-Z), 'N' numeric (0-9), and 'S' space. + +The 7 RTS (Return to Sender) Post Code patterns are similar to above except +without the DPS ('NA'), and the trailing “inward” part cannot be blank (although +the whole field can be): + + --------- --------- --------- --------- --------- --------- --------- + ANNAASS AANNAAS ANNNAAS AANNNAA ANANAAS AANANAA SSSSSSS + --------- --------- --------- --------- --------- --------- --------- Table : Royal Mail 2D Mailmark RTS Patterns -where 'F' is full alphabetic (A-Z), 'L' limited alphabetic (A-Z less 'CIKMOV'), -'N' numeric (0-9), and 'S' space. - Three sizes are defined, one rectangular, with varying maximum amounts of optional customer data: