From e737f992b5e9c980f9aaf53fd4fe792e2fc261f0 Mon Sep 17 00:00:00 2001 From: Robin Stuart Date: Sat, 3 Feb 2018 18:44:01 +0000 Subject: [PATCH] Add verification for North America VIN (Vehicle Identification Number) --- backend/code.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++- backend/library.c | 13 +++---- backend/zint.h | 1 + docs/manual.txt | 11 ++++++ frontend/main.c | 61 ++++++++++++++--------------- 5 files changed, 146 insertions(+), 38 deletions(-) diff --git a/backend/code.c b/backend/code.c index bdcb292e..3ad32996 100644 --- a/backend/code.c +++ b/backend/code.c @@ -39,6 +39,7 @@ #define SODIUM "0123456789-" #define SILVER "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd" +#define ARSENIC "0123456789ABCDEFGHJKLMNPRSTUVWXYZ" static const char *C11Table[11] = { "111121", "211121", "121121", "221111", "112121", "212111", "122111", @@ -461,7 +462,7 @@ int c93(struct zint_symbol *symbol, unsigned char source[], int length) { "It is the intent and understanding of AIM [t]hat the symbology presented in this specification is entirely in the public domain and free of all use restrictions, - licenses and fees. AIM USA, its memer companies, or individual officers + licenses and fees. AIM USA, its member companies, or individual officers assume no liability for the use of this document." */ void CheckCharacter() { @@ -591,3 +592,98 @@ int channel_code(struct zint_symbol *symbol, unsigned char source[], int length) } +/* Vehicle Identification Number (VIN) */ +int vin(struct zint_symbol *symbol, unsigned char source[], int length) { + + /* This code verifies the check digit present in North American VIN codes */ + + int zeros; + char local_source[18]; + char dest[200]; + char input_check; + char output_check; + int value[17]; + int weight[17] = {8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2}; + int sum; + int i; + + // Check length + if (length > 17) { + strcpy(symbol->errtxt, "336: Input too long"); + return ZINT_ERROR_TOO_LONG; + } + + // Check input characters, I, O and Q are not allowed + if (is_sane(ARSENIC, source, length) == ZINT_ERROR_INVALID_DATA) { + strcpy(symbol->errtxt, "337: Invalid characters in input data"); + return ZINT_ERROR_INVALID_DATA; + } + + // Pad with zeros + zeros = 17 - length; + + for (i = 0; i < 17; i++) { + local_source[i] = '0'; + } + local_source[17] = '\0'; + + for (i = 0; i < length; i++) { + local_source[zeros + i] = source[i]; + } + + input_check = local_source[8]; + + for (i = 0; i < 17; i++) { + if ((local_source[i] >= '0') && (local_source[i] <= '9')) { + value[i] = local_source[i] - '0'; + } + if ((local_source[i] >= 'A') && (local_source[i] <= 'I')) { + value[i] = (local_source[i] - 'A') + 1; + } + if ((local_source[i] >= 'J') && (local_source[i] <= 'R')) { + value[i] = (local_source[i] - 'J') + 1; + } + if ((local_source[i] >= 'S') && (local_source[i] <= 'Z')) { + value[i] = (local_source[i] - 'S') + 2; + } + } + + sum = 0; + for (i = 0; i < 17; i++) { + sum += value[i] * weight[i]; + } + + output_check = '0' + (sum % 11); + + if (output_check == ':') { + // Check digit was 10 + output_check = 'X'; + } + + if (symbol->debug) { + printf("Producing VIN code: %s\n", local_source); + printf("Input check was %c, calculated check is %c\n", input_check, output_check); + } + + if (input_check != output_check) { + strcpy(symbol->errtxt, "338: Invalid check digit in input data"); + return ZINT_ERROR_INVALID_DATA; + } + + /* Start character */ + strcpy(dest, "1211212111"); + + // Copy glyphs to symbol + for (i = 0; i < 17; i++) { + lookup(SILVER, C39Table, local_source[i], dest); + } + + /* Stop character */ + strcat(dest, "121121211"); + + ustrcpy(symbol->text, (unsigned char *) local_source); + expand(symbol, dest); + + return 0; +} + diff --git a/backend/library.c b/backend/library.c index 5c258fc1..f34388d2 100644 --- a/backend/library.c +++ b/backend/library.c @@ -171,8 +171,9 @@ extern int han_xin(struct zint_symbol * symbol, const unsigned char source[], si extern int dotcode(struct zint_symbol * symbol, const unsigned char source[], int length); /* DotCode */ extern int codablock(struct zint_symbol * symbol, const unsigned char source[], const size_t length); /* Codablock */ extern int upnqr(struct zint_symbol *symbol, const unsigned char source[], size_t length); /* UPNQR */ -extern int qr_code(struct zint_symbol *symbol, const unsigned char source[], size_t length); /* Data Matrix (IEC16022) */ -extern int dmatrix(struct zint_symbol *symbol, const unsigned char source[], const size_t in_length); /* QR Code */ +extern int qr_code(struct zint_symbol *symbol, const unsigned char source[], size_t length); /* QR Code */ +extern int dmatrix(struct zint_symbol *symbol, const unsigned char source[], const size_t in_length); /* Data Matrix (IEC16022) */ +extern int vin(struct zint_symbol *symbol, const unsigned char source[], const size_t in_length); /* VIN Code (Vehicle Identification Number) */ extern int plot_raster(struct zint_symbol *symbol, int rotate_angle, int file_type); /* Plot to PNG/BMP/PCX */ extern int render_plot(struct zint_symbol *symbol, float width, float height); /* Plot to gLabels */ @@ -554,6 +555,7 @@ int ZBarcode_ValidID(int symbol_id) { case BARCODE_DOTCODE: case BARCODE_CODABLOCKF: case BARCODE_UPNQR: + case BARCODE_VIN: result = 1; break; } @@ -773,6 +775,8 @@ static int reduced_charset(struct zint_symbol *symbol, const unsigned char *sour break; case BARCODE_CODABLOCKF: error_number = codablock(symbol, preprocessed, in_length); break; + case BARCODE_VIN: error_number = vin(symbol, preprocessed, in_length); + break; } return error_number; @@ -975,11 +979,6 @@ int ZBarcode_Encode(struct zint_symbol *symbol, const unsigned char *source, int if ((symbol->symbology == 64) || (symbol->symbology == 65)) { symbol->symbology = BARCODE_AUSPOST; } - if (symbol->symbology == 73) { - strcpy(symbol->errtxt, "211: Symbology out of range, using Code 128"); - symbol->symbology = BARCODE_CODE128; - error_number = ZINT_WARN_INVALID_OPTION; - } if (symbol->symbology == 78) { symbol->symbology = BARCODE_RSS14; } diff --git a/backend/zint.h b/backend/zint.h index 6a8fa1c7..473dfc40 100644 --- a/backend/zint.h +++ b/backend/zint.h @@ -151,6 +151,7 @@ extern "C" { #define BARCODE_RM4SCC 70 #define BARCODE_DATAMATRIX 71 #define BARCODE_EAN14 72 +#define BARCODE_VIN 73 #define BARCODE_CODABLOCKF 74 #define BARCODE_NVE18 75 #define BARCODE_JAPANPOST 76 diff --git a/docs/manual.txt b/docs/manual.txt index 051cd5d1..07a89809 100644 --- a/docs/manual.txt +++ b/docs/manual.txt @@ -280,6 +280,7 @@ Numeric Value | Barcode Name 70 | Royal Mail 4 State (RM4SCC) 71 | Data Matrix (ECC200) 72 | EAN-14 +73 | Vehicle Identification Number (America) 74 | Codablock-F 75 | NVE-18 76 | Japanese Postal Code @@ -526,6 +527,7 @@ GIF | Graphics Interchange Format PCX | ZSoft Paintbrush image PNG | Portable Network Graphic SVG | Scalable Vector Graphic +TIF | Tagged Image File Format TXT | Text file (see 4.16) -------------------------------------------------------------- @@ -952,6 +954,7 @@ Value | 70 | BARCODE_RM4SCC | Royal Mail 4 State (RM4SCC) 71 | BARCODE_DATAMATRIX | Data Matrix ECC200 72 | BARCODE_EAN14 | EAN-14 +73 | BARCODE_VIN | Vehicle Identification Number (America) 74 | BARCODE_CODABLOCKF | Codablock-F 75 | BARCODE_NVE18 | NVE-18 76 | BARCODE_JAPANPOST | Japanese Postal Code @@ -1309,6 +1312,12 @@ This option adds a leading '+' character and a trailing modulo-49 check digit to a standard Code 39 symbol as required by the Health Industry Barcode standards. +6.1.8.8 Vehicle Identification Number +------------------------------------- +This option includes a verification stage for vehicle identification numbers +used in North America which include a check digit. For European vehicle +identification numbers use Standard Code 39. + 6.1.9 Codabar (EN 798) ---------------------- Also known as NW-7, Monarch, ABC Codabar, USD-4, Ames Code and Code 27, this @@ -2519,6 +2528,8 @@ v2.6.2 - Further bugfixes in Aztec and DotCode. Expand escape sequence support to API and GUI. Handle UTF BOM. Bugfix raster images of Maxicode. 22/10/2017 +v2.6.3 - Includes VIN (US) verification. (Forthcoming) + 7.4 Sources of Information -------------------------- Below is a list of some of the sources used in rough chronological order: diff --git a/frontend/main.c b/frontend/main.c index a33d8719..be6a8b19 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -37,36 +37,37 @@ /* Print list of supported symbologies */ void types(void) { - printf( " 1: Code 11 50: Logmars 90: KIX Code\n" - " 2: Standard 2of5 51: Pharma One-Track 92: Aztec Code\n" - " 3: Interleaved 2of5 52: PZN 93: DAFT Code\n" - " 4: IATA 2of5 53: Pharma Two-Track 97: Micro QR Code\n" - " 6: Data Logic 55: PDF417 98: HIBC Code 128\n" - " 7: Industrial 2of5 56: PDF417 Trunc 99: HIBC Code 39\n" - " 8: Code 39 57: Maxicode 102: HIBC Data Matrix\n" - " 9: Extended Code 39 58: QR Code 104: HIBC QR Code\n" - "13: EAN 60: Code 128-B 106: HIBC PDF417\n" - "14: EAN + Check 63: AP Standard Customer 108: HIBC MicroPDF417\n" - "16: GS1-128 66: AP Reply Paid 110: HIBC Codablock-F\n" - "18: Codabar 67: AP Routing 112: HIBC Aztec Code\n" - "20: Code 128 68: AP Redirection 115: DotCode\n" - "21: Leitcode 69: ISBN 116: Han Xin Code\n" - "22: Identcode 70: RM4SCC 128: Aztec Runes\n" - "23: Code 16k 71: Data Matrix 129: Code 32\n" - "24: Code 49 72: EAN-14 130: Comp EAN\n" - "25: Code 93 74: Codablock-F 131: Comp GS1-128\n" - "28: Flattermarken 75: NVE-18 132: Comp DataBar Omni\n" - "29: GS1 DataBar Omni 76: Japanese Post 133: Comp DataBar Ltd\n" - "30: GS1 DataBar Ltd 77: Korea Post 134: Comp DataBar ExpOm\n" - "31: GS1 DataBar ExpOm 79: GS1 DataBar Stack 135: Comp UPC-A\n" - "32: Telepen Alpha 80: GS1 DataBar Stack Omni 136: Comp UPC-E\n" - "34: UPC-A 81: GS1 DataBar ESO 137: Comp DataBar Stack\n" - "35: UPC-A + Check 82: Planet 138: Comp DataBar Stack Omni\n" - "37: UPC-E 84: MicroPDF 139: Comp DataBar ESO\n" - "38: UPC-E + Check 85: USPS OneCode 140: Channel Code\n" - "40: Postnet 86: UK Plessey 141: Code One\n" - "47: MSI Plessey 87: Telepen Numeric 142: Grid Matrix\n" - "49: FIM 89: ITF-14 143: UPNQR\n" + printf( " 1: Code 11 51: Pharma One-Track 92: Aztec Code\n" + " 2: Standard 2of5 52: PZN 93: DAFT Code\n" + " 3: Interleaved 2of5 53: Pharma Two-Track 97: Micro QR Code\n" + " 4: IATA 2of5 55: PDF417 98: HIBC Code 128\n" + " 6: Data Logic 56: PDF417 Trunc 99: HIBC Code 39\n" + " 7: Industrial 2of5 57: Maxicode 102: HIBC Data Matrix\n" + " 8: Code 39 58: QR Code 104: HIBC QR Code\n" + " 9: Extended Code 39 60: Code 128-B 106: HIBC PDF417\n" + "13: EAN 63: AP Standard Customer 108: HIBC MicroPDF417\n" + "14: EAN + Check 66: AP Reply Paid 110: HIBC Codablock-F\n" + "16: GS1-128 67: AP Routing 112: HIBC Aztec Code\n" + "18: Codabar 68: AP Redirection 115: DotCode\n" + "20: Code 128 69: ISBN 116: Han Xin Code\n" + "21: Leitcode 70: RM4SCC 128: Aztec Runes\n" + "22: Identcode 71: Data Matrix 129: Code 32\n" + "23: Code 16k 72: EAN-14 130: Comp EAN\n" + "24: Code 49 73: VIN (North America) 131: Comp GS1-128\n" + "25: Code 93 74: Codablock-F 132: Comp DataBar Omni\n" + "28: Flattermarken 75: NVE-18 133: Comp DataBar Ltd\n" + "29: GS1 DataBar Omni 76: Japanese Post 134: Comp DataBar ExpOm\n" + "30: GS1 DataBar Ltd 77: Korea Post 135: Comp UPC-A\n" + "31: GS1 DataBar ExpOm 79: GS1 DataBar Stack 136: Comp UPC-E\n" + "32: Telepen Alpha 80: GS1 DataBar Stack Omni 137: Comp DataBar Stack\n" + "34: UPC-A 81: GS1 DataBar ESO 138: Comp DataBar Stack Omni\n" + "35: UPC-A + Check 82: Planet 139: Comp DataBar ESO\n" + "37: UPC-E 84: MicroPDF 140: Channel Code\n" + "38: UPC-E + Check 85: USPS OneCode 141: Code One\n" + "40: Postnet 86: UK Plessey 142: Grid Matrix\n" + "47: MSI Plessey 87: Telepen Numeric 143: UPNQR\n" + "49: FIM 89: ITF-14\n" + "50: Logmars 90: KIX Code\n" ); }