zint-barcode-generator/backend/upcean.c
gitlost fab7435fac Performance improvements for linear encoding and raster output
- use fixed-length string tables (mostly) instead of (char *) pointer ones
   (saves ~40K)
 - re-use C128Table for CODABLOCKF and CODE16K
   (required removal of Stop character and extra CODE16K-only entry)
 - use pointer to destination and copy (memcpy/strcpy(), bin_append_posn())
   instead of concatenating (strcat()) (mostly)
 - replace last remaining bin_append()s with bin_append_posn();
   bin_append() removed
 - add length arg to toupper() and expand() (avoids strlen())
 - change is_sane() to use table-based flags (avoids an iteration)
 - rename lookup() to is_sane_lookup() and change to check and return posns
   and use in pointer to destination loops (avoids strcat()s)
 - remove special case PHARMA in expand() (dealt with in pharma())
 - make #define SILVER/CALCIUM/TECHNETIUM/KRSET etc static strings
 - replace strchr() -> posn()
 - CODE128: populate destination once in checksum loop; re-use and export
   some more routines (c128_set_a/b/c(), c128_put_in_set()) for sharing;
   prefix defines (SHIFTA -> C128_SHIFTA etc) and existing exported routines
 - use factor XOR toggle trick in checksum calcs (avoids branch)
 - raster.c: fill out single 1-pixel row and copy using new draw_bar_line(),
   copy_bar_line() routines; similarly in buffer_plot compare previous line &
   copy if same (same technique as used to improve non-half-integer scaling,
   significant performance increase, (c) codemonkey82);
   also done for PNG (BMP/GIF/PCX/TIFF not done)
 - raster/vector/output.c: shorten "output_" prefix -> "out_";
   sync vector to other raster changes to try to keep source files similar
 - 2of5.c: prefix "c25_"
JAPANPOST: return error if input data truncated (backward incompatible)
DAFT: max chars 50 -> 100
common.c: istwodigit() -> is_twodigit()
common.c/emf.c/output.c: use some further stripf()s (MSVC6 float variations)
library.c: new check_output_args() helper
zint.h: add BARCODE_LAST marker and use in library.c
QRCODE: remove a NOLINT (requires clang-tidy-13), one remaining
CMake: separate no-optimize from ZINT_DEBUG into new ZINT_NOOPT option
2021-10-20 23:05:30 +01:00

986 lines
35 KiB
C

/* upcean.c - Handles UPC, EAN and ISBN
libzint - the open source barcode library
Copyright (C) 2008 - 2021 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the project nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
*/
/* vim: set ts=4 sw=4 et : */
#define SODIUM_PLS_F (IS_NUM_F | IS_PLS_F) /* SODIUM "0123456789+" */
#define ISBNX_SANE_F (IS_NUM_F | IS_UX__F) /* ISBNX_SANE "0123456789X" */
#define ISBNX_ADDON_SANE_F (IS_NUM_F | IS_UX__F | IS_LX__F | IS_PLS_F) /* ISBNX_ADDON_SANE "0123456789Xx+" */
#include <stdio.h>
#include "common.h"
#include "gs1.h"
/* UPC and EAN tables checked against EN 797:1996 */
static const char UPCParity0[10][6] = {
/* Number set for UPC-E symbol (EN Table 4) */
{'B','B','B','A','A','A'}, {'B','B','A','B','A','A'}, {'B','B','A','A','B','A'}, {'B','B','A','A','A','B'},
{'B','A','B','B','A','A'}, {'B','A','A','B','B','A'}, {'B','A','A','A','B','B'}, {'B','A','B','A','B','A'},
{'B','A','B','A','A','B'}, {'B','A','A','B','A','B'}
};
static const char UPCParity1[10][6] = {
/* Not covered by BS EN 797:1995 */
{'A','A','A','B','B','B'}, {'A','A','B','A','B','B'}, {'A','A','B','B','A','B'}, {'A','A','B','B','B','A'},
{'A','B','A','A','B','B'}, {'A','B','B','A','A','B'}, {'A','B','B','B','A','A'}, {'A','B','A','B','A','B'},
{'A','B','A','B','B','A'}, {'A','B','B','A','B','A'}
};
static const char EAN2Parity[4][2] = {
/* Number sets for 2-digit add-on (EN Table 6) */
{'A','A'}, {'A','B'}, {'B','A'}, {'B','B'}
};
static const char EAN5Parity[10][5] = {
/* Number set for 5-digit add-on (EN Table 7) */
{'B','B','A','A','A'}, {'B','A','B','A','A'}, {'B','A','A','B','A'}, {'B','A','A','A','B'}, {'A','B','B','A','A'},
{'A','A','B','B','A'}, {'A','A','A','B','B'}, {'A','B','A','B','A'}, {'A','B','A','A','B'}, {'A','A','B','A','B'}
};
static const char EAN13Parity[10][5] = {
/* Left hand of the EAN-13 symbol (EN Table 3) */
{'A','A','A','A','A'}, {'A','B','A','B','B'}, {'A','B','B','A','B'}, {'A','B','B','B','A'}, {'B','A','A','B','B'},
{'B','B','A','A','B'}, {'B','B','B','A','A'}, {'B','A','B','A','B'}, {'B','A','B','B','A'}, {'B','B','A','B','A'}
};
static const char EANsetA[10][4] = {
/* Representation set A and C (EN Table 1) */
{'3','2','1','1'}, {'2','2','2','1'}, {'2','1','2','2'}, {'1','4','1','1'}, {'1','1','3','2'},
{'1','2','3','1'}, {'1','1','1','4'}, {'1','3','1','2'}, {'1','2','1','3'}, {'3','1','1','2'}
};
static const char EANsetB[10][4] = {
/* Representation set B (EN Table 1) */
{'1','1','2','3'}, {'1','2','2','2'}, {'2','2','1','2'}, {'1','1','4','1'}, {'2','3','1','1'},
{'1','3','2','1'}, {'4','1','1','1'}, {'2','1','3','1'}, {'3','1','2','1'}, {'2','1','1','3'}
};
/* UPC A is usually used for 12 digit numbers, but this function takes a source of any length */
static void upca_draw(const unsigned char source[], const int length, char *d) {
int i, half_way;
half_way = length / 2;
/* start character */
memcpy(d, "111", 3);
d += 3;
for (i = 0; i < length; i++, d += 4) {
if (i == half_way) {
/* middle character - separates manufacturer no. from product no. */
/* also inverts right hand characters */
memcpy(d, "11111", 5);
d += 5;
}
memcpy(d, EANsetA[source[i] - '0'], 4);
}
/* stop character */
strcpy(d, "111");
}
/* Make a UPC-A barcode, allowing for composite if `cc_rows` set */
static int upca_cc(struct zint_symbol *symbol, const unsigned char source[], int length, char dest[], int cc_rows) {
unsigned char *gtin = symbol->text;
int error_number = 0;
ustrcpy(gtin, source);
if (length == 11) {
gtin[length++] = gs1_check_digit(gtin, 11);
gtin[length] = '\0';
} else {
if (source[length - 1] != gs1_check_digit(gtin, 11)) {
sprintf(symbol->errtxt, "270: Invalid check digit '%c', expecting '%c'",
source[length - 1], gs1_check_digit(gtin, 11));
return ZINT_ERROR_INVALID_CHECK;
}
}
if (symbol->debug & ZINT_DEBUG_PRINT) {
printf("UPC-A: %s, gtin: %s, Check digit: %c\n", source, gtin,
length == 11 ? gtin[length] : gtin[length - 1]);
}
upca_draw(gtin, length, dest);
if (symbol->output_options & COMPLIANT_HEIGHT) {
/* BS EN 797:1996 4.5.1 Nominal dimensions 22.85mm / 0.33mm (X) ~ 69.24,
same as minimum GS1 General Specifications 21.0.1 5.12.3.1 */
const float height = stripf(22.85f / 0.33f);
if (symbol->symbology == BARCODE_UPCA_CC) {
symbol->height = height; /* Pass back min row == default height */
} else {
error_number = set_height(symbol, height, height, 0.0f, 0 /*no_errtxt*/);
}
} else {
const float height = 50.0f;
if (symbol->symbology == BARCODE_UPCA_CC) {
symbol->height = height - cc_rows * 2 - 6.0f;
} else {
(void) set_height(symbol, 0.0f, height, 0.0f, 1 /*no_errtxt*/);
}
}
return error_number;
}
/* UPC-A */
static int upca(struct zint_symbol *symbol, const unsigned char source[], int length, char dest[]) {
return upca_cc(symbol, source, length, dest, 0 /*cc_rows*/);
}
/* UPC-E, allowing for composite if `cc_rows` set */
static int upce_cc(struct zint_symbol *symbol, unsigned char source[], int length, char *d, int cc_rows) {
int i, num_system;
char emode, check_digit;
const char *parity;
char src_check_digit = '\0';
unsigned char equivalent[12];
unsigned char *hrt = symbol->text;
int error_number = 0;
if (length == 8 || symbol->symbology == BARCODE_UPCE_CHK) {
/* Will validate later */
src_check_digit = source[--length];
}
/* Two number systems can be used - system 0 and system 1 */
hrt[0] = '\0';
if (length == 7) {
switch (source[0]) {
case '0': num_system = 0;
ustrncat(hrt, source, length);
break;
case '1': num_system = 1;
ustrncat(hrt, source, length);
break;
default: num_system = 0;
/* First source char ignored */
ustrncat(hrt, source, length);
hrt[0] = '0'; /* Overwrite HRT first char with '0' to correct TODO: error/warn in future */
break;
}
for (i = 1; i <= length; i++) {
source[i - 1] = hrt[i];
}
length--;
} else {
/* Length 6, insert leading zero */
num_system = 0;
hrt[0] = '0';
hrt[1] = '\0';
ustrncat(hrt, source, length);
}
/* Expand the zero-compressed UPCE code to make a UPCA equivalent (EN Table 5) */
emode = source[5];
for (i = 0; i < 11; i++) {
equivalent[i] = '0';
}
if (num_system == 1) {
equivalent[0] = hrt[0];
}
equivalent[1] = source[0];
equivalent[2] = source[1];
equivalent[11] = '\0';
switch (emode) {
case '0':
case '1':
case '2':
equivalent[3] = emode;
equivalent[8] = source[2];
equivalent[9] = source[3];
equivalent[10] = source[4];
break;
case '3':
equivalent[3] = source[2];
equivalent[9] = source[3];
equivalent[10] = source[4];
if (((source[2] == '0') || (source[2] == '1')) || (source[2] == '2')) {
/* Note 1 - "X3 shall not be equal to 0, 1 or 2" */
strcpy(symbol->errtxt, "271: Invalid UPC-E data"); // TODO: Better error message
return ZINT_ERROR_INVALID_DATA;
}
break;
case '4':
equivalent[3] = source[2];
equivalent[4] = source[3];
equivalent[10] = source[4];
if (source[3] == '0') {
/* Note 2 - "X4 shall not be equal to 0" */
strcpy(symbol->errtxt, "272: Invalid UPC-E data"); // TODO: Better error message
return ZINT_ERROR_INVALID_DATA;
}
break;
case '5':
case '6':
case '7':
case '8':
case '9':
equivalent[3] = source[2];
equivalent[4] = source[3];
equivalent[5] = source[4];
equivalent[10] = emode;
if (source[4] == '0') {
/* Note 3 - "X5 shall not be equal to 0" */
strcpy(symbol->errtxt, "273: Invalid UPC-E data"); // TODO: Better error message
return ZINT_ERROR_INVALID_DATA;
}
break;
}
/* Get the check digit from the expanded UPCA code */
check_digit = gs1_check_digit(equivalent, 11);
if (src_check_digit && src_check_digit != check_digit) {
sprintf(symbol->errtxt, "274: Invalid check digit '%c', expecting '%c'", src_check_digit, check_digit);
return ZINT_ERROR_INVALID_CHECK;
}
/* Use the number system and check digit information to choose a parity scheme */
if (num_system == 1) {
parity = UPCParity1[ctoi(check_digit)];
} else {
parity = UPCParity0[ctoi(check_digit)];
}
/* Take all this information and make the barcode pattern */
/* start character */
memcpy(d, "111", 3);
d += 3;
for (i = 0; i < length; i++, d += 4) {
switch (parity[i]) {
case 'A': memcpy(d, EANsetA[source[i] - '0'], 4);
break;
case 'B': memcpy(d, EANsetB[source[i] - '0'], 4);
break;
}
}
/* stop character */
strcpy(d, "111111");
hrt[7] = check_digit;
hrt[8] = '\0';
if (symbol->debug & ZINT_DEBUG_PRINT) {
printf("UPC-E: %s, equivalent: %s, hrt: %s, Check digit: %c\n", source, equivalent, hrt, check_digit);
}
if (symbol->output_options & COMPLIANT_HEIGHT) {
/* BS EN 797:1996 4.5.1 Nominal dimensions 22.85mm / 0.33mm (X) ~ 69.24,
same as minimum GS1 General Specifications 21.0.1 5.12.3.1 */
const float height = stripf(22.85f / 0.33f);
if (symbol->symbology == BARCODE_UPCE_CC) {
symbol->height = height; /* Pass back min row == default height */
} else {
error_number = set_height(symbol, height, height, 0.0f, 0 /*no_errtxt*/);
}
} else {
const float height = 50.0f;
if (symbol->symbology == BARCODE_UPCE_CC) {
symbol->height = height - cc_rows * 2 - 6.0f;
} else {
(void) set_height(symbol, 0.0f, height, 0.0f, 1 /*no_errtxt*/);
}
}
return error_number;
}
/* UPC-E is a zero-compressed version of UPC-A */
static int upce(struct zint_symbol *symbol, unsigned char source[], int length, char dest[]) {
return upce_cc(symbol, source, length, dest, 0 /*cc_rows*/);
}
/* EAN-2 and EAN-5 add-on codes */
static void ean_add_on(const unsigned char source[], const int length, char dest[], const int addon_gap) {
const char *parity;
int i;
char *d = dest + strlen(dest);
/* If an add-on then append with space */
if (addon_gap != 0) {
*d++ = itoc(addon_gap);
}
/* Start character */
memcpy(d, "112", 3);
d += 3;
/* Calculate parity */
if (length == 2) { /* EAN-2 */
int code_value, parity_bit;
code_value = (10 * ctoi(source[0])) + ctoi(source[1]);
parity_bit = code_value % 4;
parity = EAN2Parity[parity_bit];
} else { /* EAN-5 */
int values[6], parity_sum, parity_bit;
for (i = 0; i < 6; i++) {
values[i] = ctoi(source[i]);
}
parity_sum = (3 * (values[0] + values[2] + values[4]));
parity_sum += (9 * (values[1] + values[3]));
parity_bit = parity_sum % 10;
parity = EAN5Parity[parity_bit];
}
for (i = 0; i < length; i++) {
switch (parity[i]) {
case 'A': memcpy(d, EANsetA[source[i] - '0'], 4);
d += 4;
break;
case 'B': memcpy(d, EANsetB[source[i] - '0'], 4);
d += 4;
break;
}
/* Glyph separator */
if (i != (length - 1)) {
memcpy(d, "11", 2);
d += 2;
}
}
*d = '\0';
}
/* ************************ EAN-13 ****************** */
static int ean13_cc(struct zint_symbol *symbol, const unsigned char source[], int length, char *d,
int cc_rows) {
int i, half_way;
const char *parity;
unsigned char *gtin = symbol->text;
int error_number = 0;
ustrcpy(gtin, source);
/* Add the appropriate check digit */
if (length == 12) {
gtin[length++] = gs1_check_digit(gtin, 12);
gtin[length] = '\0';
} else {
if (source[length - 1] != gs1_check_digit(gtin, 12)) {
sprintf(symbol->errtxt, "275: Invalid check digit '%c', expecting '%c'",
source[length - 1], gs1_check_digit(gtin, 12));
return ZINT_ERROR_INVALID_CHECK;
}
}
if (symbol->debug & ZINT_DEBUG_PRINT) {
printf("EAN-13: %s, gtin: %s, Check digit: %c\n", source, gtin, gtin[length - 1]);
}
/* Get parity for first half of the symbol */
parity = EAN13Parity[gtin[0] - '0'];
/* Now get on with the cipher */
half_way = 7;
/* start character */
memcpy(d, "111", 3);
d += 3;
for (i = 1; i < length; i++, d += 4) {
if (i == half_way) {
/* middle character - separates manufacturer no. from product no. */
/* also inverses right hand characters */
memcpy(d, "11111", 5);
d += 5;
}
if (((i > 1) && (i < 7)) && (parity[i - 2] == 'B')) {
memcpy(d, EANsetB[gtin[i] - '0'], 4);
} else {
memcpy(d, EANsetA[gtin[i] - '0'], 4);
}
}
/* stop character */
strcpy(d, "111");
if (symbol->output_options & COMPLIANT_HEIGHT) {
/* BS EN 797:1996 4.5.1 Nominal dimensions 22.85mm / 0.33mm (X) ~ 69.24,
same as minimum GS1 General Specifications 21.0.1 5.12.3.1 */
const float height = stripf(22.85f / 0.33f);
if (symbol->symbology == BARCODE_EANX_CC) {
symbol->height = height; /* Pass back min row == default height */
} else {
error_number = set_height(symbol, height, height, 0.0f, 0 /*no_errtxt*/);
}
} else {
const float height = 50.0f;
if (symbol->symbology == BARCODE_EANX_CC) {
symbol->height = height - cc_rows * 2 - 6.0f;
} else {
(void) set_height(symbol, 0.0f, height, 0.0f, 1 /*no_errtxt*/);
}
}
return error_number;
}
static int ean13(struct zint_symbol *symbol, const unsigned char source[], int length, char dest[]) {
return ean13_cc(symbol, source, length, dest, 0 /*cc_rows*/);
}
static int ean8_cc(struct zint_symbol *symbol, const unsigned char source[], int length, char dest[], int cc_rows) {
/* EAN-8 is basically the same as UPC-A but with fewer digits */
unsigned char *gtin = symbol->text;
int error_number = 0;
ustrcpy(gtin, source);
if (length == 7) {
gtin[length++] = gs1_check_digit(gtin, 7);
gtin[length] = '\0';
} else {
if (source[length - 1] != gs1_check_digit(gtin, 7)) {
sprintf(symbol->errtxt, "276: Invalid check digit '%c', expecting '%c'",
source[length - 1], gs1_check_digit(gtin, 7));
return ZINT_ERROR_INVALID_CHECK;
}
}
if (symbol->debug & ZINT_DEBUG_PRINT) {
printf("EAN-8: %s, gtin: %s, Check digit: %c\n", source, gtin,
length == 7 ? gtin[length] : gtin[length - 1]);
}
upca_draw(gtin, length, dest);
if (symbol->output_options & COMPLIANT_HEIGHT) {
/* BS EN 797:1996 4.5.1 Nominal dimensions 18.23mm / 0.33mm (X) ~ 55.24,
same as minimum GS1 General Specifications 21.0.1 5.12.3.1 */
const float height = stripf(18.23f / 0.33f);
if (symbol->symbology == BARCODE_EANX_CC) {
symbol->height = height; /* Pass back min row == default height */
} else {
error_number = set_height(symbol, height, height, 0.0f, 0 /*no_errtxt*/);
}
} else {
const float height = 50.0f;
if (symbol->symbology == BARCODE_EANX_CC) {
symbol->height = height - cc_rows * 2 - 6.0f;
} else {
(void) set_height(symbol, 0.0f, height, 0.0f, 1 /*no_errtxt*/);
}
}
return error_number;
}
/* Make an EAN-8 barcode when we haven't been given the check digit */
static int ean8(struct zint_symbol *symbol, const unsigned char source[], int length, char dest[]) {
return ean8_cc(symbol, source, length, dest, 0 /*cc_rows*/);
}
/* For ISBN(10) and SBN only */
static char isbnx_check(const unsigned char source[], const int length) {
int i, weight, sum, check;
char check_char;
sum = 0;
weight = 1;
for (i = 0; i < length; i++) { /* Length will always be 9 */
sum += ctoi(source[i]) * weight;
weight++;
}
check = sum % 11;
check_char = itoc(check);
if (check == 10) {
check_char = 'X';
}
return check_char;
}
/* Make an EAN-13 barcode from an SBN or ISBN */
static int isbnx(struct zint_symbol *symbol, unsigned char source[], const int src_len, char dest[]) {
int i;
char check_digit;
to_upper(source, src_len);
if (!is_sane(ISBNX_SANE_F, source, src_len)) {
strcpy(symbol->errtxt, "277: Invalid character in data (digits and \"X\" only)");
return ZINT_ERROR_INVALID_DATA;
}
/* Input must be 9, 10 or 13 characters */
if (src_len != 9 && src_len != 10 && src_len != 13) {
strcpy(symbol->errtxt, "278: Input wrong length (9, 10, or 13 characters only)");
return ZINT_ERROR_TOO_LONG;
}
if (src_len == 13) /* Using 13 character ISBN */ {
if (!(((source[0] == '9') && (source[1] == '7')) &&
((source[2] == '8') || (source[2] == '9')))) {
strcpy(symbol->errtxt, "279: Invalid ISBN (must begin with \"978\" or \"979\")");
return ZINT_ERROR_INVALID_DATA;
}
/* "X" cannot occur */
if (!is_sane(NEON_F, source, 13)) {
strcpy(symbol->errtxt, "282: Invalid character in data, \"X\" not allowed in ISBN-13");
return ZINT_ERROR_INVALID_DATA;
}
check_digit = gs1_check_digit(source, 12);
if (source[12] != check_digit) {
sprintf(symbol->errtxt, "280: Invalid ISBN check digit '%c', expecting '%c'", source[12], check_digit);
return ZINT_ERROR_INVALID_CHECK;
}
source[12] = '\0';
} else { /* Using 10 digit ISBN or 9 digit SBN padded with leading zero */
if (src_len == 9) /* Using 9 digit SBN */ {
/* Add leading zero */
for (i = 10; i > 0; i--) {
source[i] = source[i - 1];
}
source[0] = '0';
}
/* "X" can only occur in last position */
if (!is_sane(NEON_F, source, 9)) {
strcpy(symbol->errtxt, "296: Invalid character in data, \"X\" allowed in last position only");
return ZINT_ERROR_INVALID_DATA;
}
check_digit = isbnx_check(source, 9);
if (check_digit != source[9]) {
sprintf(symbol->errtxt, "281: Invalid %s check digit '%c', expecting '%c'", src_len == 9 ? "SBN" : "ISBN",
source[9], check_digit);
return ZINT_ERROR_INVALID_CHECK;
}
for (i = 11; i > 2; i--) { /* This drops the check digit */
source[i] = source[i - 3];
}
source[0] = '9';
source[1] = '7';
source[2] = '8';
source[12] = '\0';
}
return ean13(symbol, source, 12, dest);
}
/* Add leading zeroes to EAN and UPC strings */
INTERNAL int ean_leading_zeroes(struct zint_symbol *symbol, const unsigned char source[],
unsigned char local_source[], int *p_with_addon, unsigned char *zfirst_part,
unsigned char *zsecond_part) {
unsigned char first_part[14], second_part[6];
int with_addon = 0;
int first_len = 0, second_len = 0, zfirst_len = 0, zsecond_len = 0, i, h;
h = (int) ustrlen(source);
for (i = 0; i < h; i++) {
if (source[i] == '+') {
with_addon = 1;
} else {
if (with_addon == 0) {
first_len++;
} else {
second_len++;
}
}
}
if (first_len > 13 || second_len > 5) {
if (p_with_addon) {
*p_with_addon = second_len > 5 ? with_addon : 0;
}
return 0;
}
/* Split input into two strings */
for (i = 0; i < first_len; i++) {
first_part[i] = source[i];
}
first_part[first_len] = '\0';
for (i = 0; i < second_len; i++) {
second_part[i] = source[i + first_len + 1];
}
second_part[second_len] = '\0';
/* Calculate target lengths */
if (second_len == 0) {
zsecond_len = 0;
} else if (second_len <= 2) {
zsecond_len = 2;
} else {
zsecond_len = 5;
}
switch (symbol->symbology) {
case BARCODE_EANX:
case BARCODE_EANX_CC:
if (first_len <= 12) {
if (first_len <= 7) {
zfirst_len = 7;
} else {
zfirst_len = 12;
}
}
if (second_len == 0 && symbol->symbology == BARCODE_EANX) { /* No composite EAN-2/5 */
if (first_len <= 5) {
if (first_len <= 2) {
zfirst_len = 2;
} else {
zfirst_len = 5;
}
}
}
break;
case BARCODE_EANX_CHK:
if (first_len <= 13) {
if (first_len <= 8) {
zfirst_len = 8;
} else {
zfirst_len = 13;
}
}
if (second_len == 0) {
if (first_len <= 5) {
if (first_len <= 2) {
zfirst_len = 2;
} else {
zfirst_len = 5;
}
}
}
break;
case BARCODE_UPCA:
case BARCODE_UPCA_CC:
zfirst_len = 11;
break;
case BARCODE_UPCA_CHK:
zfirst_len = 12;
break;
case BARCODE_UPCE:
case BARCODE_UPCE_CC:
if (first_len == 7) {
zfirst_len = 7;
} else if (first_len <= 6) {
zfirst_len = 6;
}
break;
case BARCODE_UPCE_CHK:
if (first_len == 8) {
zfirst_len = 8;
} else if (first_len <= 7) {
zfirst_len = 7;
}
break;
case BARCODE_ISBNX:
if (first_len <= 9) {
zfirst_len = 9;
}
break;
}
/* Copy adjusted data back to local_source */
/* Add leading zeroes */
for (i = 0; i < (zfirst_len - first_len); i++) {
local_source[i] = '0';
}
ustrcpy(local_source + i, first_part);
if (zfirst_part) {
ustrcpy(zfirst_part, local_source);
}
if (with_addon) {
h = (int) ustrlen(local_source);
local_source[h++] = '+';
for (i = 0; i < (zsecond_len - second_len); i++) {
local_source[h + i] = '0';
}
ustrcpy(local_source + h + i, second_part);
if (zsecond_part) {
ustrcpy(zsecond_part, local_source + h);
}
} else if (zsecond_part) {
*zsecond_part = '\0';
}
if (p_with_addon) {
*p_with_addon = with_addon;
}
return 1; /* Success */
}
INTERNAL int eanx_cc(struct zint_symbol *symbol, unsigned char source[], int src_len, int cc_rows) {
unsigned char first_part[14], second_part[6];
unsigned char local_source[20]; /* Allow 13 + "+" + 5 + 1 */
char dest[1000] = {0};
int with_addon;
int error_number = 0, i, plus_count;
int addon_gap = 0;
int first_part_len, second_part_len;
if (src_len > 19) {
strcpy(symbol->errtxt, "283: Input too long (19 character maximum)");
return ZINT_ERROR_TOO_LONG;
}
if (symbol->symbology != BARCODE_ISBNX) {
/* ISBN has its own sanity routine */
if (!is_sane(SODIUM_PLS_F, source, src_len)) {
strcpy(symbol->errtxt, "284: Invalid character in data (digits and \"+\" only)");
return ZINT_ERROR_INVALID_DATA;
}
} else {
if (!is_sane(ISBNX_ADDON_SANE_F, source, src_len)) {
strcpy(symbol->errtxt, "285: Invalid character in data (digits, \"X\" and \"+\" only)");
return ZINT_ERROR_INVALID_DATA;
}
/* Add-on will be checked separately to be numeric only below */
}
/* Check for multiple '+' characters */
plus_count = 0;
for (i = 0; i < src_len; i++) {
if (source[i] == '+') {
plus_count++;
if (plus_count > 1) {
strcpy(symbol->errtxt, "293: Invalid add-on data (one \"+\" only)");
return ZINT_ERROR_INVALID_DATA;
}
}
}
/* Add leading zeroes, checking max lengths of parts */
if (!ean_leading_zeroes(symbol, source, local_source, &with_addon, first_part, second_part)) {
sprintf(symbol->errtxt, "294: Input too long (%s)",
with_addon ? "5 character maximum for add-on" : "13 character maximum");
return ZINT_ERROR_TOO_LONG;
}
if (with_addon) {
if (symbol->symbology == BARCODE_UPCA || symbol->symbology == BARCODE_UPCA_CHK
|| symbol->symbology == BARCODE_UPCA_CC) {
addon_gap = symbol->option_2 >= 9 && symbol->option_2 <= 12 ? symbol->option_2 : 9;
} else {
addon_gap = symbol->option_2 >= 7 && symbol->option_2 <= 12 ? symbol->option_2 : 7;
}
}
first_part_len = (int) ustrlen(first_part);
switch (symbol->symbology) {
case BARCODE_EANX:
case BARCODE_EANX_CHK:
switch (first_part_len) {
case 2: ean_add_on(first_part, first_part_len, dest, 0);
ustrcpy(symbol->text, first_part);
if (symbol->output_options & COMPLIANT_HEIGHT) {
/* 21.9mm from GS1 General Specifications 5.2.6.6, Figure 5.2.6.6-5 */
const float height = stripf(21.9f / 0.33f); /* 21.9mm / 0.33mm ~ 66.36 */
error_number = set_height(symbol, height, height, 0.0f, 0 /*no_errtxt*/);
} else {
(void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/);
}
break;
case 5: ean_add_on(first_part, first_part_len, dest, 0);
ustrcpy(symbol->text, first_part);
if (symbol->output_options & COMPLIANT_HEIGHT) {
/* 21.9mm from GS1 General Specifications 5.2.6.6, Figure 5.2.6.6-6 */
const float height = stripf(21.9f / 0.33f); /* 21.9mm / 0.33mm ~ 66.36 */
error_number = set_height(symbol, height, height, 0.0f, 0 /*no_errtxt*/);
} else {
(void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/);
}
break;
case 7:
case 8: error_number = ean8(symbol, first_part, first_part_len, dest);
break;
case 12:
case 13: error_number = ean13(symbol, first_part, first_part_len, dest);
break;
default: strcpy(symbol->errtxt, "286: Input wrong length (2, 5, 7, 8, 12 or 13 characters only)");
return ZINT_ERROR_TOO_LONG;
}
break;
case BARCODE_EANX_CC:
switch (first_part_len) { /* Adds vertical separator bars according to ISO/IEC 24723 section 11.4 */
case 7: set_module(symbol, symbol->rows, 1);
set_module(symbol, symbol->rows, 67);
set_module(symbol, symbol->rows + 1, 0);
set_module(symbol, symbol->rows + 1, 68);
set_module(symbol, symbol->rows + 2, 1);
set_module(symbol, symbol->rows + 2, 67);
symbol->row_height[symbol->rows] = 2;
symbol->row_height[symbol->rows + 1] = 2;
symbol->row_height[symbol->rows + 2] = 2;
symbol->rows += 3;
error_number = ean8_cc(symbol, first_part, first_part_len, dest, cc_rows);
break;
case 12:
case 13:set_module(symbol, symbol->rows, 1);
set_module(symbol, symbol->rows, 95);
set_module(symbol, symbol->rows + 1, 0);
set_module(symbol, symbol->rows + 1, 96);
set_module(symbol, symbol->rows + 2, 1);
set_module(symbol, symbol->rows + 2, 95);
symbol->row_height[symbol->rows] = 2;
symbol->row_height[symbol->rows + 1] = 2;
symbol->row_height[symbol->rows + 2] = 2;
symbol->rows += 3;
error_number = ean13_cc(symbol, first_part, first_part_len, dest, cc_rows);
break;
default: strcpy(symbol->errtxt, "287: Input wrong length (7, 12 or 13 characters only)");
return ZINT_ERROR_TOO_LONG;
}
break;
case BARCODE_UPCA:
case BARCODE_UPCA_CHK:
if ((first_part_len == 11) || (first_part_len == 12)) {
error_number = upca(symbol, first_part, first_part_len, dest);
} else {
strcpy(symbol->errtxt, "288: Input wrong length (12 character maximum)");
return ZINT_ERROR_TOO_LONG;
}
break;
case BARCODE_UPCA_CC:
if (first_part_len == 11 || first_part_len == 12) {
set_module(symbol, symbol->rows, 1);
set_module(symbol, symbol->rows, 95);
set_module(symbol, symbol->rows + 1, 0);
set_module(symbol, symbol->rows + 1, 96);
set_module(symbol, symbol->rows + 2, 1);
set_module(symbol, symbol->rows + 2, 95);
symbol->row_height[symbol->rows] = 2;
symbol->row_height[symbol->rows + 1] = 2;
symbol->row_height[symbol->rows + 2] = 2;
symbol->rows += 3;
error_number = upca_cc(symbol, first_part, first_part_len, dest, cc_rows);
} else {
strcpy(symbol->errtxt, "289: Input wrong length (12 character maximum)");
return ZINT_ERROR_TOO_LONG;
}
break;
case BARCODE_UPCE:
case BARCODE_UPCE_CHK:
if ((first_part_len >= 6) && (first_part_len <= 8)) {
error_number = upce(symbol, first_part, first_part_len, dest);
} else {
strcpy(symbol->errtxt, "290: Input wrong length (8 character maximum)");
return ZINT_ERROR_TOO_LONG;
}
break;
case BARCODE_UPCE_CC:
if ((first_part_len >= 6) && (first_part_len <= 8)) {
set_module(symbol, symbol->rows, 1);
set_module(symbol, symbol->rows, 51);
set_module(symbol, symbol->rows + 1, 0);
set_module(symbol, symbol->rows + 1, 52);
set_module(symbol, symbol->rows + 2, 1);
set_module(symbol, symbol->rows + 2, 51);
symbol->row_height[symbol->rows] = 2;
symbol->row_height[symbol->rows + 1] = 2;
symbol->row_height[symbol->rows + 2] = 2;
symbol->rows += 3;
error_number = upce_cc(symbol, first_part, first_part_len, dest, cc_rows);
} else {
strcpy(symbol->errtxt, "291: Input wrong length (8 character maximum)");
return ZINT_ERROR_TOO_LONG;
}
break;
case BARCODE_ISBNX:
error_number = isbnx(symbol, first_part, first_part_len, dest);
break;
}
if (error_number >= ZINT_ERROR) {
return error_number;
}
second_part_len = (int) ustrlen(second_part);
if (symbol->symbology == BARCODE_ISBNX) { /* Need to further check that add-on numeric only */
if (!is_sane(NEON_F, second_part, second_part_len)) {
strcpy(symbol->errtxt, "295: Invalid add-on data (digits only)");
return ZINT_ERROR_INVALID_DATA;
}
}
switch (second_part_len) {
case 0: break;
case 2:
ean_add_on(second_part, second_part_len, dest, addon_gap);
ustrcat(symbol->text, "+");
ustrcat(symbol->text, second_part);
break;
case 5:
ean_add_on(second_part, second_part_len, dest, addon_gap);
ustrcat(symbol->text, "+");
ustrcat(symbol->text, second_part);
break;
default:
strcpy(symbol->errtxt, "292: Add-on data wrong length (2 or 5 characters only)");
return ZINT_ERROR_TOO_LONG;
}
expand(symbol, dest, (int) strlen(dest));
switch (symbol->symbology) {
case BARCODE_EANX_CC:
case BARCODE_UPCA_CC:
case BARCODE_UPCE_CC:
/* shift the symbol to the right one space to allow for separator bars */
for (i = (symbol->width + 1); i >= 1; i--) {
if (module_is_set(symbol, symbol->rows - 1, i - 1)) {
set_module(symbol, symbol->rows - 1, i);
} else {
unset_module(symbol, symbol->rows - 1, i);
}
}
unset_module(symbol, symbol->rows - 1, 0);
symbol->width += 2;
break;
}
return error_number;
}
/* Handle UPC, EAN, ISBN */
INTERNAL int eanx(struct zint_symbol *symbol, unsigned char source[], int src_len) {
return eanx_cc(symbol, source, src_len, 0 /*cc_rows*/);
}