1
0
mirror of https://git.code.sf.net/p/zint/code synced 2026-09-19 18:52:37 +00:00

GS1: new GS1RAW_MODE (CLI "--gs1raw") and GS1 Syntax Engine

"Unbracketed AI" (caret) options for specifying GS1 input
  (ticket #350, props Mario Verbruggen)
DBAR_EXP_CC/DBAR_EXPSTK_CC: fix separator over finder patterns
  when linear part is greater than 4 codeblocks
general: left-over raw_text -> content_segs in comments;
  update & expand some GS1 General Specs refs;
  some minor code fiddling
test suite: suppress some additional ZINT_SANITIZEM false positives
  (& add new ZINT_TESTUTIL_SANITIZEM_INIT helpers)
This commit is contained in:
gitlost
2026-02-26 15:05:45 +00:00
parent 3b24d129d7
commit 0a8a79fa6c
59 changed files with 7504 additions and 4206 deletions
+4 -4
View File
@@ -1,7 +1,7 @@
/* 2of5_based.c - Handles Code 2 of 5 Interleaved derivatives ITF-14, DP Leitcode and DP Identcode */
/*
libzint - the open source barcode library
Copyright (C) 2008-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2008-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -109,7 +109,7 @@ INTERNAL int zint_itf14(struct zint_symbol *symbol, unsigned char source[], int
z_hrt_cpy_nochk(symbol, local_source, 14);
/* Use `raw_text` set by `zint_c25_inter_common()` */
/* Use `content_segs` set by `zint_c25_inter_common()` */
return error_number;
}
@@ -161,7 +161,7 @@ INTERNAL int zint_dpleit(struct zint_symbol *symbol, unsigned char source[], int
z_hrt_printf_nochk(symbol, "%.5s.%.3s.%.3s.%.3s", local_source, local_source + 5, local_source + 8,
local_source + 11);
/* Use `raw_text` set by `zint_c25_inter_common()` */
/* Use `content_segs` set by `zint_c25_inter_common()` */
return error_number;
}
@@ -204,7 +204,7 @@ INTERNAL int zint_dpident(struct zint_symbol *symbol, unsigned char source[], in
z_hrt_printf_nochk(symbol, "%.2s.%.2s %c.%.3s.%.3s %c", local_source, local_source + 2, local_source[4],
local_source + 5, local_source + 8, local_source[11]);
/* Use `raw_text` set by `zint_c25_inter_common()` */
/* Use `content_segs` set by `zint_c25_inter_common()` */
return error_number;
}
+1 -1
View File
@@ -107,7 +107,7 @@ endif()
if(NOT ZINT_SANITIZE AND ZINT_SANITIZEM AND CMAKE_C_COMPILER_ID MATCHES "Clang")
add_compile_options(-fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -O2)
link_libraries(-fsanitize=memory)
zint_target_compile_definitions(PRIVATE ZINT_SANITIZEM)
zint_target_compile_definitions(PUBLIC ZINT_SANITIZEM)
endif()
if(ZINT_TEST)
+121
View File
@@ -0,0 +1,121 @@
/* aztec_trace.h - Trace routines for optimized AZTEC encodation algorithm */
/*
libzint - the open source barcode library
Copyright (C) 2026 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.
*/
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef Z_AZTEC_TRACE_H
#define Z_AZTEC_TRACE_H
static void AZ_TRACE_EdgeToString(char *buf, const unsigned char *source, const int length, const char initial_mode,
struct az_edge *edges, struct az_edge *edge) {
struct az_edge *previous_edge = AZ_PREVIOUS(edges, edge);
int previousMode = previous_edge ? previous_edge->mode : initial_mode;
int previous = previous_edge ? (int) (previous_edge - edges) : 0;
int current = (int) (edge - edges);
(void)length;
if (buf) {
sprintf(buf, "%d_%c_%d %c(%d,%d) %d -> %d_%c_%d",
edge->from, az_mode_char(previousMode), previous, az_mode_char(edge->mode), source[edge->from], edge->len,
edge->size, edge->from + 1, az_mode_char(edge->mode), current);
} else {
printf("%d_%c_%d %c(%d,%d) %d -> %d_%c_%d",
edge->from, az_mode_char(previousMode), previous, az_mode_char(edge->mode), source[edge->from], edge->len,
edge->size, edge->from + 1, az_mode_char(edge->mode), current);
}
}
static void AZ_TRACE_Path(const unsigned char *source, const int length, const char initial_mode,
struct az_edge *edges, struct az_edge *edge, char *result, const int result_size) {
struct az_edge *current;
AZ_TRACE_EdgeToString(result, source, length, initial_mode, edges, edge);
current = AZ_PREVIOUS(edges, edge);
while (current) {
char s[256];
char *pos;
int len;
AZ_TRACE_EdgeToString(s, source, length, initial_mode, edges, current);
pos = strrchr(s, ' ');
assert(pos);
len = strlen(result);
if ((pos - s) + 1 + len + 1 >= result_size) {
memcpy(result + result_size - 4, "...", 4); /* Include terminating NUL */
break;
}
memmove(result + (pos - s) + 1, result, len + 1);
memcpy(result, s, (pos - s) + 1);
current = AZ_PREVIOUS(edges, current);
}
puts(result);
}
static void AZ_TRACE_Edges(const char *prefix, const unsigned char *source, const int length, const char initial_mode,
struct az_edge *edges, const int vertexIndex) {
int i, j, e_i;
char result[1024 * 2];
if (vertexIndex) {
printf(prefix, vertexIndex);
} else {
fputs(prefix, stdout);
}
for (i = vertexIndex; i <= length; i++) {
e_i = i * AZ_NUM_MODES;
for (j = 0; j < AZ_NUM_MODES; j++) {
if (edges[e_i + j].mode) {
fputs(" **** ", stdout);
AZ_TRACE_Path(source, length, initial_mode, edges, edges + e_i + j, result, (int) ARRAY_SIZE(result));
}
}
}
}
static void AZ_TRACE_AddEdge(const unsigned char *source, const int length, struct az_edge *edges,
struct az_edge *previous, const int vertexIndex, struct az_edge *edge) {
const int v_ij = vertexIndex * AZ_NUM_MODES + AZ_MASK(edge->mode) - 1;
(void)source; (void)length;
printf("add mode %c_%d, previous %d, from %d, len %d, v_ij mode %d || v_ij.size %d >= edge->size %d\n",
az_mode_char(edge->mode), v_ij, previous ? (int) (previous - edges) : 0, edge->from, edge->len,
edges[v_ij].mode, edges[v_ij].size, edge->size);
}
static void AZ_TRACE_NotAddEdge(const unsigned char *source, const int length, struct az_edge *edges,
struct az_edge *previous, const int vertexIndex, const int e_ij, struct az_edge *edge) {
const int v_ij = vertexIndex * AZ_NUM_MODES + AZ_MASK(edge->mode) - 1;
(void)source; (void)length; (void)e_ij;
printf("NOT mode %c_%d, previous %d, from %d, len %d, v_ij.size %d < edge->size %d\n",
az_mode_char(edge->mode), v_ij, previous ? (int) (previous - edges) : 0, edge->from, edge->len,
edges[v_ij].size, edge->size);
}
/* vim: set ts=4 sw=4 et : */
#endif /* Z_AZTEC_TRACE_H */
+4 -9
View File
@@ -542,7 +542,7 @@ INTERNAL int zint_gs1_128_cc(struct zint_symbol *symbol, unsigned char source[],
symbol->row_height[separator_row] = 1;
}
error_number = zint_gs1_verify(symbol, source, length, reduced, &reduced_length);
error_number = zint_gs1_verify(symbol, source, length, reduced, &reduced_length, 1 /*set_hrt*/);
if (error_number >= ZINT_ERROR) {
return error_number;
}
@@ -612,7 +612,7 @@ INTERNAL int zint_gs1_128_cc(struct zint_symbol *symbol, unsigned char source[],
}
}
if (reduced_length > 48) { /* GS1 General Specifications 5.4.4.3 */
if (reduced_length > 48) { /* GS1 General Specifications Release 26.0 5.4.4.3 Maximum symbol length */
if (error_number == 0) { /* Don't overwrite any `zint_gs1_verify()` warning */
error_number = z_errtxtf(ZINT_WARN_NONCOMPLIANT, symbol, 843,
"Input too long, requires %d characters (maximum 48)", reduced_length);
@@ -620,7 +620,7 @@ INTERNAL int zint_gs1_128_cc(struct zint_symbol *symbol, unsigned char source[],
}
if (symbol->output_options & COMPLIANT_HEIGHT) {
/* GS1 General Specifications 21.0.1 5.12.3.2 table 2, including footnote (**):
/* GS1 General Specifications Release 26.0 5.12.3.2 Symbol specification table 2, including footnote (**):
same as ITF-14: "in case of further space constraints" height 5.8mm / 1.016mm (X max) ~ 5.7;
default 31.75mm / 0.495mm ~ 64.14 */
const float min_height = 5.70866156f; /* 5.8 / 1.016 */
@@ -650,12 +650,7 @@ INTERNAL int zint_gs1_128_cc(struct zint_symbol *symbol, unsigned char source[],
"Cannot use Reader Initialisation in GS1 mode, ignoring");
}
/* Note won't overflow `text` buffer due to symbol character maximum restricted to C128_SYMBOL_MAX */
if (symbol->input_mode & GS1PARENS_MODE) {
z_hrt_cpy_nochk(symbol, source, length);
} else {
z_hrt_conv_gs1_brackets_nochk(symbol, source, length);
}
/* HRT set by `zint_gs1_verify()` */
if (content_segs && z_ct_cpy(symbol, reduced, reduced_length)) {
return ZINT_ERROR_MEMORY; /* `z_ct_cpy()` only fails with OOM */
+4 -4
View File
@@ -1,7 +1,7 @@
/* code128_based.c - Handles Code 128 derivatives NVE-18, EAN-14, DPD and Universal Postal Union S10 */
/*
libzint - the open source barcode library
Copyright (C) 2008-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2008-2026 Robin Stuart <rstuart114@gmail.com>
Bugfixes thanks to Christian Sakowski and BogDan Vatra
Redistribution and use in source and binary forms, with or without
@@ -94,7 +94,7 @@ static int nve18_or_ean14(struct zint_symbol *symbol, const unsigned char source
error_number = zint_gs1_128(symbol, ean128_equiv, data_len + 5);
/* Use `raw_text` set by `zint_gs1_128()` */
/* Use `content_segs` set by `zint_gs1_128()` */
return error_number;
}
@@ -219,7 +219,7 @@ INTERNAL int zint_dpd(struct zint_symbol *symbol, unsigned char source[], int le
z_hrt_cpy_nochk(symbol, hrt, p + 1);
/* Use `raw_text` from `zint_code128()` */
/* Use `content_segs` from `zint_code128()` */
/* Some compliance checks */
if (z_not_sane(NEON_F, local_source + length - 16, 16)) {
@@ -323,7 +323,7 @@ INTERNAL int zint_upu_s10(struct zint_symbol *symbol, unsigned char source[], in
}
z_hrt_cpy_nochk(symbol, hrt, j);
/* Use `raw_text` set by `zint_code128()` */
/* Use `content_segs` set by `zint_code128()` */
if (symbol->output_options & COMPLIANT_HEIGHT) {
/* Universal Postal Union S10 Section 8, using max X 0.51mm & minimum height 12.5mm or 15% of width */
+13 -6
View File
@@ -117,7 +117,7 @@ static const unsigned short flags[256] = {
/* Whether a character `ch` matches `flag` */
INTERNAL int z_is_chr(const unsigned int flag, const unsigned int ch) {
return z_isascii(ch) && (flags[ch] & flag); /* As passed an int ch need to check it's ASCII */
return z_isascii(ch) && (flags[ch] & flag); /* As `ch` passed as an int need to check it's ASCII */
}
/* Verifies if a string only uses valid characters, returning 1-based position in `source` if not, 0 for success */
@@ -780,15 +780,16 @@ INTERNAL int z_utf8_to_unicode(struct zint_symbol *symbol, const unsigned char s
INTERNAL int z_hrt_cpy_iso8859_1(struct zint_symbol *symbol, const unsigned char source[], const int length) {
int i, j;
int warn_number = 0;
const int text_size = ARRAY_SIZE(symbol->text);
for (i = 0, j = 0; i < length && j < ARRAY_SIZE(symbol->text); i++) {
for (i = 0, j = 0; i < length && j < text_size; i++) {
if (z_isascii(source[i])) {
symbol->text[j++] = z_iscntrl(source[i]) ? ' ' : source[i];
} else if (source[i] < 0xC0) {
if (source[i] < 0xA0) { /* 0x80-0x9F not valid ISO/IEC 8859-1 */
symbol->text[j++] = ' ';
} else {
if (j + 2 >= ARRAY_SIZE(symbol->text)) {
if (j + 2 >= text_size) {
warn_number = ZINT_WARN_HRT_TRUNCATED;
break;
}
@@ -796,7 +797,7 @@ INTERNAL int z_hrt_cpy_iso8859_1(struct zint_symbol *symbol, const unsigned char
symbol->text[j++] = source[i];
}
} else {
if (j + 2 >= ARRAY_SIZE(symbol->text)) {
if (j + 2 >= text_size) {
warn_number = ZINT_WARN_HRT_TRUNCATED;
break;
}
@@ -804,7 +805,7 @@ INTERNAL int z_hrt_cpy_iso8859_1(struct zint_symbol *symbol, const unsigned char
symbol->text[j++] = source[i] - 0x40;
}
}
if (j == ARRAY_SIZE(symbol->text)) {
if (j == text_size) {
warn_number = ZINT_WARN_HRT_TRUNCATED;
j--;
}
@@ -955,7 +956,7 @@ static int ct_init_seg_source(struct zint_symbol *symbol, const int seg_idx, con
assert(length > 0);
if (!(symbol->content_segs[seg_idx].source = (unsigned char *) malloc((size_t) length))) {
return z_errtxt(ZINT_ERROR_MEMORY, symbol, 245, "Insufficient memory for content text source buffer");
return z_errtxt(ZINT_ERROR_MEMORY, symbol, 245, "Insufficient memory for content segs source buffer");
}
return 0;
}
@@ -1013,6 +1014,9 @@ INTERNAL int z_ct_cpy_cat(struct zint_symbol *symbol, const unsigned char source
if (z_ct_init_segs(symbol, 1 /*seg_count*/) || ct_init_seg_source(symbol, 0 /*seg_idx*/, total_length)) {
return ZINT_ERROR_MEMORY; /* `z_ct_init_segs()` & `ct_init_seg_source()` only fail with OOM */
}
#ifdef ZINT_SANITIZEM /* Suppress clang-22 -fsanitize=memory false positive (seems unable to track `*s++ =`) */
memset(symbol->content_segs[0].source, 0, total_length);
#endif
s = symbol->content_segs[0].source;
if (length > 0) {
memcpy(s, source, (size_t) length);
@@ -1044,6 +1048,9 @@ INTERNAL int z_ct_cpy_iso8859_1(struct zint_symbol *symbol, const unsigned char
if (z_ct_init_segs(symbol, 1 /*seg_count*/) || ct_init_seg_source(symbol, 0 /*seg_idx*/, length + iso_cnt)) {
return ZINT_ERROR_MEMORY; /* `z_ct_init_segs()` & `ct_init_seg_source()` only fail with OOM */
}
#ifdef ZINT_SANITIZEM /* Suppress clang-22 -fsanitize=memory false positive (seems unable to track `*s++ =`) */
memset(symbol->content_segs[0].source, 0, length + iso_cnt);
#endif
s = symbol->content_segs[0].source;
for (i = 0; i < length; i++) {
+492 -152
View File
@@ -95,7 +95,7 @@ static int gs1_numeric(const unsigned char *data, int data_len, int offset, int
return 1;
}
/* GS1 General Specifications 21.0.1 Figure 7.9.5-1. GS1 AI encodable character reference values.
/* GS1 General Specifications Release 26.0 Table 7-18 GS1 AI encodable character reference values.
Also used to determine if character in set 82 - a value of 82 means not in */
static const char gs1_c82[] = {
/* ! " # $ % & ' ( ) * */
@@ -118,7 +118,8 @@ static const char gs1_c82[] = {
72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
};
/* Validate of character set 82 (GS1 General Specifications Figure 7.11-1) */
/* Validate of character set 82
(GS1 General Specifications Release 26.0 Table 7-20 GS1 AI encodable character set 82) */
static int gs1_cset82(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50]) {
@@ -144,7 +145,8 @@ static int gs1_cset82(const unsigned char *data, int data_len, int offset, int m
return 1;
}
/* Validate of character set 39 (GS1 General Specifications Figure 7.11-2) */
/* Validate of character set 39
(GS1 General Specifications Release 26.0 Table 7-21 GS1 AI encodable character set 39) */
static int gs1_cset39(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50]) {
@@ -203,7 +205,8 @@ static int gs1_cset64(const unsigned char *data, int data_len, int offset, int m
return 1;
}
/* Check a check digit (GS1 General Specifications 7.9.1) */
/* Check a check digit
(GS1 General Specifications Release 26.0 7.9.1 Standard check digit calculations for GS1 data structures) */
static int gs1_csum(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) {
@@ -237,7 +240,8 @@ static int gs1_csum(const unsigned char *data, int data_len, int offset, int min
return 1;
}
/* Check alphanumeric check characters (GS1 General Specifications 7.9.5) */
/* Check alphanumeric check characters
(GS1 General Specifications Release 26.0 7.9.5 Check character calculation (for alphanumeric keys)) */
static int gs1_csumalpha(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) {
@@ -280,7 +284,7 @@ static int gs1_csumalpha(const unsigned char *data, int data_len, int offset, in
#define GS1_GCP_MIN_LENGTH 4 /* Minimum length of GS1 Company Prefix */
/* Check for a GS1 Prefix (GS1 General Specifications GS1 1.4.2) */
/* Check for a GS1 Prefix (GS1 General Specifications Release 26.0 1.2.3.3 GS1 Company Prefix) */
static int gs1_gcppos1(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) {
(void)max;
@@ -406,7 +410,8 @@ static int gs1_yymmd0(const unsigned char *data, int data_len, int offset, int m
if (!length_only && data_len) {
/* For leap year detection, only matters if 00 represents century divisible by 400 or not */
/* Following good until 2050 when 00 will mean 2100 (GS1 General Specifications 7.12) */
/* Following good until 2050 when 00 will mean 2100
(GS1 General Specifications Release 26.0 7.12 Determination of century in dates) */
unsigned char buf[8] = { '2', '0' };
memcpy(buf + 2, data + offset, 6);
@@ -697,7 +702,8 @@ static int gs1_yesno(const unsigned char *data, int data_len, int offset, int mi
return 1;
}
/* Check for importer index (GS1 General Specifications 3.8.17) */
/* Check for importer index
(GS1 General Specifications Release 26.0 3.8.18 GS1 UIC with Extension 1 and Importer index: AI (7040)) */
static int gs1_importeridx(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) {
(void)max;
@@ -745,7 +751,7 @@ static int gs1_nonzero(const unsigned char *data, int data_len, int offset, int
return 1;
}
/* Check winding direction (0/1/9) (GS1 General Specifications 3.9.1) */
/* Check winding direction (0/1/9) (GS1 General Specifications Release 26.0 3.9.1 Roll products) */
static int gs1_winding(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) {
(void)max;
@@ -789,7 +795,9 @@ static int gs1_zero(const unsigned char *data, int data_len, int offset, int min
return 1;
}
/* Check piece of a trade item (GS1 General Specifications 3.9.6 and 3.9.17) */
/* Check piece of a trade item
(GS1 General Specifications Release 26.0 3.9.6 Identification of an individual trade item piece
and 3.9.18 Identification of pieces of a trade item (ITIP) contained in a logistic unit) */
static int gs1_pieceoftotal(const unsigned char *data, int data_len, int offset, int min, int max, int *p_err_no,
int *p_err_posn, char err_msg[50], const int length_only) {
(void)max;
@@ -914,8 +922,8 @@ static int gs1_nozeroprefix(const unsigned char *data, int data_len, int offset,
}
if (!length_only && data_len) {
/* GS1 General Specifications 3.9.11 "The C/P serial number SHALL NOT begin with a "0" digit, unless the
entire serial number consists of the single digit '0'." */
/* GS1 General Specifications Release 26.0 3.9.11 "The C/P serial number SHALL NOT begin with a "0" digit,
unless the entire serial number consists of the single digit '0'." */
if (data[0] == '0' && data_len != 1) {
*p_err_no = 3;
*p_err_posn = offset + 1;
@@ -1609,34 +1617,72 @@ static int gs1_packagetype(const unsigned char *data, int data_len, int offset,
return 1;
}
/* Helper to say if AI pointed to by `source` has predefined length according to GS1 General Specifications 26.0
Figure 7-6 "Element strings with predefined length using GS1 Application Identifiers" */
static int gs1_predefined_len(const unsigned char source[]) {
const int ai2 = z_to_int(source, 2); /* First 2 digits */
/* NOTE: previously allowed legacy 23, removed 2026-02-26 */
return (ai2 >= 0 && ai2 <= 4) || (ai2 >= 11 && ai2 <= 20) || (ai2 >= 31 && ai2 <= 36) || ai2 == 41;
}
/* Generated by "php backend/tools/gen_gs1_linter.php > backend/gs1_lint.h" */
#include "gs1_lint.h"
#ifdef ZINT_TEST /* Wrapper for direct testing */
INTERNAL int zint_test_gs1_lint_parse_raw_caret(const unsigned char source[], const int length,
const int ai_max, int *ai_vals, int *ai_locs, int *data_locs, int *data_lens, int *p_ai_count,
int *p_err_no, int *p_err_posn) {
return gs1_lint_parse_raw_caret(source, length,
ai_max, ai_vals, ai_locs, data_locs, data_lens, p_ai_count, p_err_no, p_err_posn);
}
#endif
/* Whether starts with Digital Link URI */
static int gs1_is_digital_link(const unsigned char *source, const int length) {
return (length >= 8 && (memcmp(source, "https://", 8) == 0 || memcmp(source, "HTTPS://", 8) == 0))
|| (length >= 7 && (memcmp(source, "http://", 7) == 0 || memcmp(source, "HTTP://", 7) == 0));
return length >= 7 && (memcmp(source, "http://", 7) == 0 || memcmp(source, "HTTP://", 7) == 0
|| (length >= 8 && (memcmp(source, "https://", 8) == 0
|| memcmp(source, "HTTPS://", 8) == 0)));
}
/* If built with GS1 Syntax Engine library */
#ifdef ZINT_HAVE_GS1SE
/* Helper to set `errtxt` on internal error and exit, freeing `ctx` */
static int gs1se_internal_errtxt(struct zint_symbol *symbol, const int err_id, gs1_encoder *ctx) {
const char *errmsg = gs1_encoder_getErrMsg(ctx);
z_errtxtf(0, symbol, err_id, "Internal error using GS1 Syntax Engine: %.80s", *errmsg ? errmsg : "unknown");
gs1_encoder_free(ctx);
return ZINT_ERROR_ENCODING_PROBLEM;
}
/* Helper to replace GSs with carets */
static void gs1se_gs_caret_sub(const unsigned char *src, const int length, unsigned char *dst) {
int i;
for (i = 0; i < length; i++) {
dst[i] = src[i] == '\x1D' ? '^' : src[i];
}
dst[length] = '\0';
}
/* Use GS1 Syntax Engine to verify */
static int gs1se_verify(struct zint_symbol *symbol, const unsigned char source[], const int length,
unsigned char reduced[], int *p_reduced_length) {
int i, j;
const int is_composite = z_is_composite(symbol->symbology);
const int primary_len = is_composite ? (int) strlen(symbol->primary) : 0;
const int primary_len = z_is_composite(symbol->symbology) ? (int) strlen(symbol->primary) : 0;
const int gs1parens_mode = symbol->input_mode & GS1PARENS_MODE;
const char obracket = gs1parens_mode ? '(' : '[';
const int parens_cnt = !gs1parens_mode ? z_chr_cnt(source, length, '(') : 0;
const int gs1raw_mode = !!(symbol->input_mode & GS1RAW_MODE);
const int gs1_caret = source[0] == '^';
const int is_digital_link = !primary_len && !gs1_caret && gs1_is_digital_link(source, length);
const int parens_cnt = !gs1parens_mode && !gs1raw_mode && !gs1_caret ? z_chr_cnt(source, length, '(') : 0;
int local_length = length;
const unsigned char *local_source = source;
const unsigned char *local_source2 = source;
unsigned char *local_source_buf = (unsigned char *) z_alloca(ARRAY_SIZE(symbol->primary) + length + 1);
unsigned char *local_source_buf = (unsigned char *) z_alloca(ARRAY_SIZE(symbol->primary) + 4 + length + 1);
unsigned char *local_source2_buf = (unsigned char *) z_alloca(ARRAY_SIZE(symbol->primary) * 2 + length
+ parens_cnt + 1);
int is_digital_link = 0;
int linear_len = 0;
char msgBuf[120];
gs1_encoder_init_status_t status = GS1_ENCODERS_INIT_SUCCESS;
gs1_encoder_init_opts_t opts = {
@@ -1646,35 +1692,89 @@ static int gs1se_verify(struct zint_symbol *symbol, const unsigned char source[]
gs1_encoder *ctx;
int gs1se_ret;
if (length < 2 + gs1_caret) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 802, "Data does not start with an AI");
}
/* Only matrix symbols can encode Digital Link URIs */
if (is_digital_link && !z_is_fixed_ratio(symbol->symbology)) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 264, "Data does not start with an AI");
}
/* Need "linear|composite" format to check required AIs */
if (is_composite && primary_len) {
if (primary_len) {
if (symbol->symbology == BARCODE_GS1_128_CC || symbol->symbology == BARCODE_DBAR_EXP_CC
|| symbol->symbology == BARCODE_DBAR_EXPSTK_CC) {
memcpy(local_source_buf, symbol->primary, primary_len);
local_source_buf[primary_len] = '|';
memcpy(local_source_buf + 1 + primary_len, source, length + 1); /* Include terminating NUL */
local_length += 1 + primary_len;
/* These take GS1 data in the linear part */
if (gs1raw_mode) {
/* Need to add initial carets */
local_source_buf[0] = '^';
local_length = 1;
if (symbol->primary[0] == '\x1D') { /* Allow initial GS */
gs1se_gs_caret_sub(ZCUCP(symbol->primary + 1), primary_len - 1, local_source_buf + 1); /* Linear GSs */
local_length += primary_len - 1;
} else {
gs1se_gs_caret_sub(ZCUCP(symbol->primary), primary_len, local_source_buf + 1); /* Linear GSs */
local_length += primary_len;
}
local_source_buf[local_length++] = '|';
local_source_buf[local_length++] = '^';
if (source[0] == '\x1D') { /* Allow initial GS */
gs1se_gs_caret_sub(source + 1, length - 1, local_source_buf + primary_len + 3); /* CC GSs */
local_length += length - 1;
} else {
gs1se_gs_caret_sub(source, length, local_source_buf + primary_len + 3); /* CC GSs */
local_length += length;
}
linear_len = primary_len;
} else {
memcpy(local_source_buf, symbol->primary, primary_len);
local_source_buf[primary_len] = '|';
memcpy(local_source_buf + primary_len + 1, source, length + 1); /* Include terminating NUL */
local_length += primary_len + 1;
linear_len = primary_len - 1; /* Only actual linear data counts - exclude initial caret */
}
} else {
/* Just use dummy "01" linear */
memcpy(local_source_buf, gs1parens_mode ? "(01)12345678901231|" : "[01]12345678901231|", 19);
memcpy(local_source_buf + 19, source, length + 1); /* Include terminating NUL */
local_length += 19;
if (gs1_caret || gs1raw_mode) {
memcpy(local_source_buf, "^0112345678901231|^", 18 + gs1raw_mode);
if (gs1_caret) {
memcpy(local_source_buf + 18, source, length + 1); /* Include terminating NUL */
} else {
gs1se_gs_caret_sub(source, length, local_source_buf + 19);
}
local_length += 18 + gs1raw_mode;
} else {
memcpy(local_source_buf, gs1parens_mode ? "(01)12345678901231|" : "[01]12345678901231|", 19);
memcpy(local_source_buf + 19, source, length + 1); /* Include terminating NUL */
local_length += 19;
}
linear_len = 16; /* Only actual linear data "0112345678901231" counts */
}
local_source = local_source_buf;
local_source2 = local_source_buf;
} else if (gs1raw_mode && !is_digital_link) {
if (symbol->symbology == BARCODE_GS1_128 || symbol->symbology == BARCODE_DBAR_EXP
|| symbol->symbology == BARCODE_DBAR_EXPSTK || z_is_fixed_ratio(symbol->symbology)
|| symbol->symbology == BARCODE_CODABLOCKF) { /* Codablock-F will be GS1-enabled in the future */
/* Prefix caret */
local_source_buf[0] = '^';
if (source[0] == '\x1D') { /* Allow initial GS */
gs1se_gs_caret_sub(source + 1, length - 1, local_source_buf + 1);
} else {
gs1se_gs_caret_sub(source, length, local_source_buf + 1);
local_length++;
}
} else {
/* Just copy over */
memcpy(local_source_buf, source, length + 1); /* Include terminating NULL */
}
local_source = local_source_buf;
local_source2 = local_source_buf;
}
if (local_source[0] != obracket) {
if (!z_is_fixed_ratio(symbol->symbology)) { /* Only matrix symbols can encode Digital Link URIs */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 264, "Data does not start with an AI");
}
if (!(is_digital_link = gs1_is_digital_link(local_source, local_length))) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 265,
"Data does not start with an AI or a Digital Link URI");
}
}
if (!is_digital_link) {
if (!is_digital_link && !gs1raw_mode && !gs1_caret) {
assert(local_length > 0);
/* Convert to GS1 Syntax Engine parenthesis mode */
if (!gs1parens_mode) {
/* Replace '[' and ']' with '(' and ')' & escape any data opening parentheses (not closing parentheses) */
@@ -1711,19 +1811,20 @@ static int gs1se_verify(struct zint_symbol *symbol, const unsigned char source[]
if (!(ctx = gs1_encoder_init_ex(NULL /*mem*/, &opts))) {
const int error_number = status == GS1_ENCODERS_INIT_FAILED_NO_MEM
? ZINT_ERROR_MEMORY : ZINT_ERROR_ENCODING_PROBLEM;
return z_errtxtf(error_number, symbol, 266, "GS1 Syntax Engine: %s", opts.msgBuf);
return z_errtxtf(error_number, symbol, 266, "Failed to initialize GS1 Syntax Engine: %.80s",
*opts.msgBuf ? opts.msgBuf : "unknown error");
}
/* Do not check for required checks for GS1-128 as may be spread across multiple barcodes - ticket #348
and https://github.com/gs1/gs1-syntax-dictionary/issues/24 */
if (symbol->symbology == BARCODE_GS1_128) {
if (!gs1_encoder_setValidationEnabled(ctx, gs1_encoder_vREQUISITE_AIS, false)) {
const char *errmsg = gs1_encoder_getErrMsg(ctx);
return z_errtxtf(ZINT_ERROR_ENCODING_PROBLEM, symbol, 0, "Internal error using GS1SE: %.80s",
errmsg ? errmsg : "unknown");
return gs1se_internal_errtxt(symbol, 0 /*err_id*/, ctx);
}
}
if (is_digital_link) {
if (is_digital_link || gs1raw_mode || gs1_caret) {
/* Required for `gs1_encoder_getScanData()` */
gs1_encoder_setSym(ctx, primary_len ? gs1_encoder_sGS1_128_CCA : gs1_encoder_sDM);
gs1se_ret = gs1_encoder_setDataStr(ctx, ZCCP(local_source2));
} else {
gs1se_ret = gs1_encoder_setAIdataStr(ctx, ZCCP(local_source2));
@@ -1733,81 +1834,285 @@ static int gs1se_verify(struct zint_symbol *symbol, const unsigned char source[]
const int errmsg_len = (int) strlen(errmsg);
const char *errmarkup = gs1_encoder_getErrMarkup(ctx);
int errmarkup_len = (int) strlen(errmarkup);
if (errmarkup_len && errmsg_len + 1 + errmarkup_len < ARRAY_SIZE(symbol->errtxt)) {
int errtxt_set = 0;
if (errmsg_len && errmarkup_len && errmsg_len + 1 + errmarkup_len < ARRAY_SIZE(symbol->errtxt)) {
char *local_errmarkup = (char *) z_alloca(errmarkup_len * 4 + 1);
z_debug_print_escape(ZCUCP(errmarkup), errmarkup_len, local_errmarkup);
errmarkup_len = (int) strlen(local_errmarkup);
if (errmsg_len + 1 + errmarkup_len < ARRAY_SIZE(symbol->errtxt)) {
ZEXT z_errtxtf(0, symbol, 267, "%1$s %2$s", errmsg, local_errmarkup);
} else {
z_errtxt(0, symbol, 268, errmsg);
errtxt_set = 1;
}
} else {
z_errtxt(0, symbol, 268, errmsg);
}
if (!errtxt_set) {
z_errtxt(0, symbol, 268, *errmsg ? errmsg : "Unknown error using GS1 Syntax Engine");
}
gs1_encoder_free(ctx);
return ZINT_ERROR_INVALID_DATA;
}
/* If Digital Link, set `reduced` */
if (is_digital_link) {
/* If Digital Link or raw/caret mode, set `reduced` */
if (is_digital_link || gs1raw_mode || gs1_caret) {
const char *scan_data;
gs1_encoder_setSym(ctx, gs1_encoder_sDM); /* Required for `gs1_encoder_getScanData()` */
int reduced_length;
if (!(scan_data = gs1_encoder_getScanData(ctx))) { /* Shouldn't happen */
const char *errmsg = gs1_encoder_getErrMsg(ctx);
z_errtxt(0, symbol, 269, strlen(errmsg) ? errmsg : "Internal error");
gs1_encoder_free(ctx);
return ZINT_ERROR_ENCODING_PROBLEM;
return gs1se_internal_errtxt(symbol, 269 /*err_id*/, ctx);
}
*p_reduced_length = (int) strlen(scan_data);
reduced_length = (int) strlen(scan_data);
/* Skip over Symbology Identifier */
if (*p_reduced_length >= 3 && scan_data[0] == ']') {
if (reduced_length >= 3 && scan_data[0] == ']') {
scan_data += 3;
*p_reduced_length -= 3;
reduced_length -= 3;
}
/* Skip over any linear stuff */
if (linear_len) {
scan_data += linear_len;
reduced_length -= linear_len;
/* Need to check if the last linear AI was non-predefined length, when a GS is added */
if (*scan_data == '\x1D') { /* Skip */
scan_data++;
reduced_length--;
}
}
assert(reduced_length <= length);
memcpy(reduced, scan_data, *p_reduced_length + 1); /* Include terminating NUL */
memcpy(reduced, scan_data, reduced_length + 1); /* Include terminating NUL */
reduced[reduced_length] = '\0';
*p_reduced_length = reduced_length;
} else {
*p_reduced_length = 0; /* Not Digital Link & `reduced` not set */
*p_reduced_length = 0; /* `reduced` not set */
}
gs1_encoder_free(ctx);
return 0;
}
#endif /* ZINT_HAVE_GS1SE */
/* Check for valid AI values and data lengths according to GS1 General Specifications Release 26, January 2026 */
static int gs1_run_lint(struct zint_symbol *symbol, const unsigned char source[], const int ai_count,
int *ai_vals, int *ai_locs, int *data_locs, int *data_lens) {
int i;
int error_number = 0;
for (i = 0; i < ai_count; i++) {
int err_no, err_posn;
char err_msg[50];
if (!gs1_lint(ai_vals[i], source + data_locs[i], data_lens[i], &err_no, &err_posn,
err_msg)) {
if (err_no == 1) {
ZEXT z_errtxtf(0, symbol, 260, "Invalid AI (%1$02d) at position %2$d",
ai_vals[i], ai_locs[i] + 1);
} else if (err_no == 2 || err_no == 4) { /* 4 is backward-incompatible bad length */
ZEXT z_errtxtf(0, symbol, 259, "Invalid data length for AI (%1$02d) at position %2$d",
ai_vals[i], ai_locs[i] + 1);
} else {
ZEXT z_errtxtf(0, symbol, 261, "AI (%1$02d) data position %2$d: %3$s", ai_vals[i], err_posn, err_msg);
}
/* For backward compatibility only error on unknown AI or bad length */
if (err_no == 1 || err_no == 2) {
return ZINT_ERROR_INVALID_DATA;
}
error_number = ZINT_WARN_NONCOMPLIANT;
}
}
return error_number;
}
/* Helper to convert parsed AIs to HRT */
static int gs1_hrt_conv_parsed(struct zint_symbol *symbol, const unsigned char source[], const int length,
const int ai_count, const int *ai_vals, const int *ai_locs, const int *data_locs, const int *data_lens,
const int no_errtxt) {
int i, j;
int warn_number = 0;
const int text_size = ARRAY_SIZE(symbol->text);
#ifdef NDEBUG
(void)length;
#endif
for (i = 0, j = 0; i < ai_count && j < text_size; i++) {
const int ai_len = 2 + (ai_vals[i] >= 100) + (ai_vals[i] >= 1000);
if (j + 1 + ai_len + 1 + data_lens[i] >= text_size) {
warn_number = ZINT_WARN_HRT_TRUNCATED;
break;
}
symbol->text[j++] = '(';
memcpy(symbol->text + j, source + ai_locs[i], ai_len);
j += ai_len;
symbol->text[j++] = ')';
assert(data_locs[i] + data_lens[i] <= length);
memcpy(symbol->text + j, source + data_locs[i], data_lens[i]);
j += data_lens[i];
}
if (j == text_size) {
warn_number = ZINT_WARN_HRT_TRUNCATED;
j--;
}
symbol->text_length = j;
symbol->text[j] = '\0';
if (warn_number && !no_errtxt) {
z_errtxt(0, symbol, 799, "Human Readable Text truncated");
}
return warn_number;
}
#ifdef ZINT_TEST /* Wrapper for direct testing */
INTERNAL int zint_test_gs1_hrt_conv_parsed(struct zint_symbol *symbol, const unsigned char source[], const int length,
const int ai_count, const int *ai_vals, const int *ai_locs, const int *data_locs, const int *data_lens,
const int no_errtxt) {
return gs1_hrt_conv_parsed(symbol, source, length, ai_count, ai_vals, ai_locs, data_locs, data_lens,
no_errtxt);
}
#endif
/* Helper to set HRT when have GS1PARENS_MODE - truncates if too long for HRT buffer */
static int gs1_hrt_cpy(struct zint_symbol *symbol, const unsigned char source[], const int length,
const int no_errtxt) {
int warn_number;
const int text_size = ARRAY_SIZE(symbol->text);
assert(symbol->input_mode & GS1PARENS_MODE);
assert(source[0] == '(');
if (length < text_size) {
memcpy(symbol->text, source, length + 1); /* Include terminating NUL */
symbol->text_length = length;
warn_number = 0;
} else {
/* Find last full AI - need to scan forward to track escaped opening parens */
int i, last_opening_bracket = 0;
for (i = 0; i < text_size; i++) {
if (source[i] == '\\' && i + 1 < length && (source[i + 1] == '\\' || source[i + 1] == '(')) {
i++;
} else if (source[i] == '(') {
last_opening_bracket = i;
}
}
if (text_size < length && source[text_size] == '(') {
/* Finished at end of AI data */
memcpy(symbol->text, source, text_size - 1);
symbol->text_length = text_size - 1;
symbol->text[text_size - 1] = '\0';
} else {
/* Use last full AI + data */
memcpy(symbol->text, source, last_opening_bracket);
symbol->text_length = last_opening_bracket;
symbol->text[last_opening_bracket] = '\0';
}
warn_number = ZINT_WARN_HRT_TRUNCATED;
if (!no_errtxt) {
z_errtxt(0, symbol, 801, "Human Readable Text truncated");
}
}
return warn_number;
}
#ifdef ZINT_TEST /* Wrapper for direct testing */
INTERNAL int zint_test_gs1_hrt_cpy(struct zint_symbol *symbol, const unsigned char source[], const int length,
const int no_errtxt) {
return gs1_hrt_cpy(symbol, source, length, no_errtxt);
}
#endif
/* Helper to set HRT when don't have GS1PARENS_MODE - substitutes square brackets with parentheses
& truncates if too long for HRT buffer */
static int gs1_hrt_conv_brackets(struct zint_symbol *symbol, const unsigned char source[], const int length,
const int no_errtxt) {
int i;
int warn_number = 0;
int bracket_level = 0; /* Non-compliant closing square brackets may be in text */
int last_opening_bracket = 0;
const int text_size = ARRAY_SIZE(symbol->text);
const int max_len = length > text_size ? text_size : length;
assert((symbol->input_mode & GS1PARENS_MODE) == 0);
assert(source[0] == '[');
for (i = 0; i < max_len; i++) {
if (source[i] == '[') {
symbol->text[i] = '(';
bracket_level++;
last_opening_bracket = i;
} else if (source[i] == ']' && bracket_level) {
symbol->text[i] = ')';
bracket_level--;
} else {
symbol->text[i] = source[i];
}
}
if (i == text_size) {
i--;
if (source[i] != '[') {
i = last_opening_bracket;
}
warn_number = ZINT_WARN_HRT_TRUNCATED;
}
symbol->text_length = i;
symbol->text[i] = '\0';
if (warn_number && !no_errtxt) {
z_errtxt(0, symbol, 798, "Human Readable Text truncated");
}
return warn_number;
}
#ifdef ZINT_TEST /* Wrapper for direct testing */
INTERNAL int zint_test_gs1_hrt_conv_brackets(struct zint_symbol *symbol, const unsigned char source[],
const int length, const int no_errtxt) {
return gs1_hrt_conv_brackets(symbol, source, length, no_errtxt);
}
#endif
#define GS1_PARENS_PLACEHOLDER_MASK 0x20 /* Mask `(` or `)` by this if escaped (get BS (\x08) or HT (\x09)) */
/* Helper to re-instate escaped parentheses */
static void gs1_reinstate_escaped_parens(unsigned char source[], const int length) {
int i;
for (i = 0; i < length; i++) {
if (source[i] < '\x1D') {
source[i] |= GS1_PARENS_PLACEHOLDER_MASK;
}
}
}
/* Verify a GS1 input string */
INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, const unsigned char source[], const int length,
unsigned char reduced[], int *p_reduced_length) {
unsigned char reduced[], int *p_reduced_length, const int set_hrt) {
int i, j;
int error_number = 0;
int error_number = 0, warn_number = 0;
int bracket_level = 0;
int ai_latch;
int local_length = length;
const unsigned char *local_source = source;
unsigned char *local_source_buf = (unsigned char *) z_alloca(length + 1);
int *ai_vals, *ai_locs, *data_locs, *data_lens;
const int gs1parens_mode = symbol->input_mode & GS1PARENS_MODE;
const char obracket = gs1parens_mode ? '(' : '[';
const char cbracket = gs1parens_mode ? ')' : ']';
int done_gs1se = 0;
int is_digital_link = 0; /* Is Digital Link? */
const int gs1raw_mode = symbol->input_mode & GS1RAW_MODE;
const int gs1nocheck_mode = symbol->input_mode & GS1NOCHECK_MODE;
/* Note: Digital Link URIs only validated if have GS1 Syntax Engine and GS1SYNTAXENGINE_MODE set */
*p_reduced_length = 0;
#ifdef ZINT_HAVE_GS1SE
if ((symbol->input_mode & GS1SYNTAXENGINE_MODE) && !(symbol->input_mode & GS1NOCHECK_MODE)) {
if ((symbol->input_mode & GS1SYNTAXENGINE_MODE) && !gs1nocheck_mode) {
/* Strict verification */
if ((error_number = gs1se_verify(symbol, source, length, reduced, p_reduced_length))) {
return error_number;
}
done_gs1se = 1;
is_digital_link = *p_reduced_length != 0; /* `p_reduced_length` will be set if Digital Link */
/* `p_reduced_length` will be set if Digital Link or raw/caret */
}
#endif
@@ -1821,7 +2126,7 @@ INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, const unsigned char sou
if (source[i] == '\0') {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 262, "NUL characters not permitted in GS1 mode");
}
if (source[i] < 32) {
if (source[i] < 32 && (!gs1raw_mode || source[i] != '\x1D')) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 251, "Control characters are not supported by GS1");
}
if (source[i] == 127) {
@@ -1829,24 +2134,90 @@ INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, const unsigned char sou
}
}
is_digital_link = gs1_is_digital_link(source, length);
if (source[0] != obracket && !is_digital_link && (gs1raw_mode || source[0] == '^')) {
int ai_count;
int err_no, err_posn;
const int ai_max = (length + 2) / 3 + 1; /* Min AI 2, min data 1 */
ai_vals = (int *) z_alloca(sizeof(int) * ai_max);
ai_locs = (int *) z_alloca(sizeof(int) * ai_max);
data_locs = (int *) z_alloca(sizeof(int) * ai_max);
data_lens = (int *) z_alloca(sizeof(int) * ai_max);
if (!gs1_lint_parse_raw_caret(source, length, ai_max, ai_vals, ai_locs, data_locs, data_lens, &ai_count,
&err_no, &err_posn)) {
/* Both raw & caret modes require valid AIs and underlong data lengths to work, so check regardless of
GS1NOCHECK_MODE */
if (err_no == 1) {
if (ai_vals[ai_count] != -1) {
ZEXT z_errtxtf(0, symbol, 856, "Invalid AI (%1$02d) at position %2$d", ai_vals[ai_count],
err_posn);
} else {
z_errtxtf(0, symbol, 857, "Invalid AI at position %d", err_posn);
}
} else {
assert(err_no == 2);
if (data_lens[ai_count] == 0) {
ZEXT z_errtxtf(0, symbol, 858, "Empty data field for AI (%1$02d) at position %2$d",
ai_vals[ai_count], err_posn);
} else {
ZEXT z_errtxtf(0, symbol, 859, "Invalid data length for AI (%1$02d) at position %2$d",
ai_vals[ai_count], err_posn);
}
}
return ZINT_ERROR_INVALID_DATA;
}
assert(ai_count < ai_max); /* Suppress clang-tidy-22 clang-analyzer-security.ArrayBound */
if (!gs1nocheck_mode) {
/* Do lint to check that data values are ok */
error_number = gs1_run_lint(symbol, source, ai_count, ai_vals, ai_locs, data_locs, data_lens);
if (error_number >= ZINT_ERROR) {
return error_number;
}
}
if (set_hrt) {
warn_number = gs1_hrt_conv_parsed(symbol, source, length, ai_count, ai_vals, ai_locs, data_locs,
data_lens, error_number != 0 /*no_errtxt*/);
}
/* Set reduced - need to set it via `ai_vals` etc. so as to lose any superfluous carets/GSs */
for (i = 0, j = 0; i < ai_count; i++) {
const int ai_len = 2 + (ai_vals[i] >= 100) + (ai_vals[i] >= 1000);
memcpy(reduced + j, source + ai_locs[i], ai_len);
j += ai_len;
memcpy(reduced + j, source + data_locs[i], data_lens[i]);
j += data_lens[i];
if (i + 1 != ai_count && !gs1_predefined_len(source + ai_locs[i])) {
reduced[j++] = '\x1D';
}
}
reduced[j] = '\0';
*p_reduced_length = j;
return error_number ? error_number : warn_number;
}
if (source[0] != obracket) {
if (!z_is_fixed_ratio(symbol->symbology)) { /* Only matrix symbols can encode Digital Link URIs */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 252, "Data does not start with an AI");
}
if (!(is_digital_link = gs1_is_digital_link(source, length))) {
if (!is_digital_link) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 855,
"Data does not start with an AI or Digital Link URI");
}
}
}
if (is_digital_link) {
if (is_digital_link || (done_gs1se && *p_reduced_length)) {
if (!done_gs1se) {
/* Just copy over Digital Link URI - no verification */
memcpy(reduced, source, length + 1); /* Include terminating NUL */
*p_reduced_length = length;
if (set_hrt) {
error_number = z_hrt_cpy_iso8859_1(symbol, source, length);
}
}
return 0;
return error_number;
}
if (gs1parens_mode) {
@@ -1881,10 +2252,11 @@ INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, const unsigned char sou
int ai_zero_len_no_data = 0, ai_single_digit = 0, ai_nonnumeric = 0;
int ai_nonnumeric_pos = 0; /* Suppress gcc 14 "-Wmaybe-uninitialized" false positive */
const int ai_max = z_chr_cnt(local_source, local_length, obracket) + 1; /* Plus 1 so non-zero */
int *ai_value = (int *) z_alloca(sizeof(int) * ai_max);
int *ai_location = (int *) z_alloca(sizeof(int) * ai_max);
int *data_location = (int *) z_alloca(sizeof(int) * ai_max);
int *data_length = (int *) z_alloca(sizeof(int) * ai_max);
ai_vals = (int *) z_alloca(sizeof(int) * ai_max);
ai_locs = (int *) z_alloca(sizeof(int) * ai_max);
data_locs = (int *) z_alloca(sizeof(int) * ai_max);
data_lens = (int *) z_alloca(sizeof(int) * ai_max);
/* Check the balance of the brackets & AI lengths */
ai_latch = 0;
@@ -1941,7 +2313,7 @@ INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, const unsigned char sou
if (min_ai_length <= 1) {
/* Allow too short AI if GS1NOCHECK_MODE and no single-digit AIs and all zero-length AIs have some data
- permits dummy "[]" workaround for ticket #204 data with no valid AI */
if (!(symbol->input_mode & GS1NOCHECK_MODE) || ai_single_digit || ai_zero_len_no_data) {
if (!gs1nocheck_mode || ai_single_digit || ai_zero_len_no_data) {
/* AI is too short */
return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 256,
"Invalid AI at position %d in input (AI too short)", min_ai_pos);
@@ -1954,36 +2326,32 @@ INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, const unsigned char sou
"Invalid AI at position %d in input (non-numeric characters in AI)", ai_nonnumeric_pos);
}
if (!(symbol->input_mode & GS1NOCHECK_MODE)) {
if (!gs1nocheck_mode) {
const unsigned char *local_source2 = local_source;
unsigned char *local_source2_buf = (unsigned char *) z_alloca(local_length + 1);
int ai_count = 0;
for (i = 1; i < local_length; i++) {
if (local_source[i - 1] == obracket) {
ai_location[ai_count] = i;
ai_locs[ai_count] = i - 1;
for (j = 1; local_source[i + j] != cbracket; j++);
ai_value[ai_count] = z_to_int(local_source + i, j);
ai_vals[ai_count] = z_to_int(local_source + i, j);
ai_count++;
i += j;
}
}
for (i = 0; i < ai_count; i++) {
if (ai_value[i] >= 1000) {
data_location[i] = ai_location[i] + 5;
} else if (ai_value[i] >= 100) {
data_location[i] = ai_location[i] + 4;
} else {
data_location[i] = ai_location[i] + 3;
data_locs[i] = ai_locs[i] + 2 /*brackets*/ + 2 + (ai_vals[i] >= 100) + (ai_vals[i] >= 1000);
data_lens[i] = 0;
while (data_locs[i] + data_lens[i] < local_length
&& local_source[data_locs[i] + data_lens[i]] != obracket) {
data_lens[i]++;
}
data_length[i] = 0;
while (data_location[i] + data_length[i] < local_length
&& local_source[data_location[i] + data_length[i]] != obracket) {
data_length[i]++;
}
if (data_length[i] == 0) {
if (data_lens[i] == 0) {
/* No data for given AI */
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 258, "Empty data field in input");
return ZEXT z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 258,
"Empty data field for AI (%1$02d) at position %2$d",
ai_vals[i], ai_locs[i] + 1);
}
}
@@ -1991,82 +2359,54 @@ INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, const unsigned char sou
/* Temporarily re-instate escaped parentheses before linting */
local_source2 = local_source2_buf;
memcpy(local_source2_buf, local_source, local_length + 1); /* Include terminating NUL */
for (i = 0; i < local_length; i++) {
if (local_source2_buf[i] < '\x1D') {
local_source2_buf[i] |= GS1_PARENS_PLACEHOLDER_MASK;
}
}
gs1_reinstate_escaped_parens(local_source2_buf, local_length);
}
/* Check for valid AI values and data lengths according to GS1 General
Specifications Release 25, January 2025 */
for (i = 0; i < ai_count; i++) {
int err_no, err_posn;
char err_msg[50];
if (!gs1_lint(ai_value[i], local_source2 + data_location[i], data_length[i], &err_no, &err_posn,
err_msg)) {
if (err_no == 1) {
z_errtxtf(0, symbol, 260, "Invalid AI (%02d)", ai_value[i]);
} else if (err_no == 2 || err_no == 4) { /* 4 is backward-incompatible bad length */
z_errtxtf(0, symbol, 259, "Invalid data length for AI (%02d)", ai_value[i]);
} else {
ZEXT z_errtxtf(0, symbol, 261, "AI (%1$02d) position %2$d: %3$s", ai_value[i], err_posn,
err_msg);
}
/* For backward compatibility only error on unknown AI or bad length */
if (err_no == 1 || err_no == 2) {
return ZINT_ERROR_INVALID_DATA;
}
error_number = ZINT_WARN_NONCOMPLIANT;
}
/* Check AI/data */
error_number = gs1_run_lint(symbol, local_source2, ai_count, ai_vals, ai_locs, data_locs, data_lens);
if (error_number >= ZINT_ERROR) {
return error_number;
}
}
}
/* Resolve AI data - put resulting string in 'reduced' */
j = 0;
ai_latch = 1;
for (i = 0; i < local_length; i++) {
if (local_source[i] == obracket) {
bracket_level++;
/* Start of an AI string */
if (ai_latch == 0) {
reduced[j++] = '\x1D';
}
if (i + 1 != local_length) {
int last_ai = z_to_int(local_source + i + 1, 2);
ai_latch = 0;
/* The following values from GS1 General Specifications Release 25.0
Figure 7.8.5-2 "Element strings with predefined length using GS1 Application Identifiers" */
if ((last_ai >= 0 && last_ai <= 4) || (last_ai >= 11 && last_ai <= 20)
/* NOTE: as noted by Terry Burton the following complies with ISO/IEC 24724:2011 Table
D.1, but clashes with TPX AI [235], introduced May 2019; awaiting feedback from GS1 */
|| last_ai == 23 /* legacy support */ /* TODO: probably remove */
|| (last_ai >= 31 && last_ai <= 36) || last_ai == 41) {
ai_latch = 1;
if (!*p_reduced_length) {
/* Resolve AI data - put resulting string in `reduced` */
j = 0;
ai_latch = 1;
for (i = 0; i < local_length; i++) {
if (local_source[i] == obracket) {
bracket_level++;
/* Start of an AI string */
if (ai_latch == 0) {
reduced[j++] = '\x1D';
}
if (i + 1 != local_length) {
ai_latch = gs1_predefined_len(local_source + i + 1);
}
} else if (local_source[i] == cbracket && bracket_level) {
bracket_level--;
} else {
reduced[j++] = local_source[i];
}
} else if (local_source[i] == cbracket && bracket_level) {
/* The closing bracket is simply dropped from the input */
bracket_level--;
} else {
reduced[j++] = local_source[i];
}
}
reduced[j] = '\0';
*p_reduced_length = j;
reduced[j] = '\0';
*p_reduced_length = j;
if (local_length != length) {
/* Re-instate escaped parentheses */
for (i = 0; i < *p_reduced_length; i++) {
if (reduced[i] < '\x1D') {
reduced[i] |= GS1_PARENS_PLACEHOLDER_MASK;
if (local_length != length) {
gs1_reinstate_escaped_parens(reduced, *p_reduced_length);
}
if (set_hrt) {
if (gs1parens_mode) {
warn_number = gs1_hrt_cpy(symbol, source, length, error_number != 0 /*no_errtxt*/);
} else {
warn_number = gs1_hrt_conv_brackets(symbol, source, length, error_number != 0 /*no_errtxt*/);
}
}
}
/* The character '\x1D' (GS) in the reduced string refers to the FNC1 character */
return error_number;
return error_number ? error_number : warn_number;
}
/* Helper to return standard GS1 check digit (GS1 General Specifications 7.9.1) */
+2 -2
View File
@@ -1,7 +1,7 @@
/* gs1.h - Verifies GS1 data */
/*
libzint - the open source barcode library
Copyright (C) 2009-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2009-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -38,7 +38,7 @@ extern "C" {
#endif /* __cplusplus */
INTERNAL int zint_gs1_verify(struct zint_symbol *symbol, const unsigned char source[], const int length,
unsigned char reduced[], int *p_reduced_length);
unsigned char reduced[], int *p_reduced_length, const int set_hrt);
INTERNAL char zint_gs1_check_digit(const unsigned char source[], const int length);
INTERNAL int zint_gs1_iso3166_alpha2(const unsigned char *cc);
+384 -1
View File
@@ -4,7 +4,7 @@
*/
/*
libzint - the open source barcode library
Copyright (C) 2021-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2021-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -713,6 +713,7 @@ static int gs1_lint(const int ai, const unsigned char *data, const int data_len,
/* Assume data length failure */
*p_err_no = 2;
*p_err_posn = 1;
if (ai < 100) {
@@ -1072,4 +1073,386 @@ static int gs1_lint(const int ai, const unsigned char *data, const int data_len,
return 0;
}
/* Helper to parse non-bracketed input */
static int gs1_lint_parse_ai(const unsigned char source[], const int length, const int position,
int *p_ai, int *p_min, int *p_max) {
int ai, ai2, ai3 = -1, ai4 = -1, min, max = 0;
*p_ai = -1;
if (position + 1 >= length || !z_isdigit(source[position]) || !z_isdigit(source[position + 1])) {
return 0;
}
ai2 = z_to_int(source + position, 2);
if (position + 3 < length && z_isdigit(source[position + 2])) {
ai3 = z_to_int(source + position, 3);
if (position + 4 < length && z_isdigit(source[position + 3])) {
ai4 = z_to_int(source + position, 4);
}
}
ai = ai2;
if (ai == 0) {
min = 18, max = 18;
} else if (ai >= 1 && ai <= 3) {
min = 14, max = 14;
} else if (ai == 10 || ai == 21 || ai == 22) {
min = 1, max = 20;
} else if ((ai >= 11 && ai <= 13) || (ai >= 15 && ai <= 17)) {
min = 6, max = 6;
} else if (ai == 20) {
min = 2, max = 2;
} else if (ai == 30 || ai == 37) {
min = 1, max = 8;
} else if (ai == 90) {
min = 1, max = 30;
} else if (ai >= 91) {
min = 1, max = 90;
}
if (max == 0 && ai3 != -1) {
if (ai3 < 300) {
ai = ai3;
if (ai == 235) {
min = 1, max = 28;
} else if (ai == 240 || ai == 241 || ai == 250 || ai == 251) {
min = 1, max = 30;
} else if (ai == 242) {
min = 1, max = 6;
} else if (ai == 243 || ai == 254) {
min = 1, max = 20;
} else if (ai == 253) {
min = 13, max = 30;
} else if (ai == 255) {
min = 13, max = 25;
}
} else if (ai3 < 500) {
ai = ai3;
if (ai == 400 || ai == 403) {
min = 1, max = 30;
} else if (ai == 401) {
min = 1, max = 30;
} else if (ai == 402) {
min = 17, max = 17;
} else if (ai >= 410 && ai <= 417) {
min = 13, max = 13;
} else if (ai == 420) {
min = 1, max = 20;
} else if (ai == 421) {
min = 4, max = 12;
} else if (ai == 422 || ai == 424 || ai == 426) {
min = 3, max = 3;
} else if (ai == 423 || ai == 425) {
min = 3, max = 15;
} else if (ai == 427) {
min = 1, max = 3;
}
} else if (ai3 < 800) {
ai = ai3;
if (ai >= 710 && ai <= 717) {
min = 1, max = 20;
}
}
}
if (max == 0 && ai4 != -1) {
if (ai4 < 3200) {
ai = ai4;
if ((ai >= 3100 && ai <= 3105) || (ai >= 3110 && ai <= 3115) || (ai >= 3120 && ai <= 3125)
|| (ai >= 3130 && ai <= 3135) || (ai >= 3140 && ai <= 3145) || (ai >= 3150 && ai <= 3155)
|| (ai >= 3160 && ai <= 3165)) {
min = 6, max = 6;
}
} else if (ai4 < 3300) {
ai = ai4;
if (ai <= 3205 || (ai >= 3210 && ai <= 3215) || (ai >= 3220 && ai <= 3225) || (ai >= 3230 && ai <= 3235)
|| (ai >= 3240 && ai <= 3245) || (ai >= 3250 && ai <= 3255) || (ai >= 3260 && ai <= 3265)
|| (ai >= 3270 && ai <= 3275) || (ai >= 3280 && ai <= 3285) || (ai >= 3290 && ai <= 3295)) {
min = 6, max = 6;
}
} else if (ai4 < 3400) {
ai = ai4;
if (ai <= 3305 || (ai >= 3310 && ai <= 3315) || (ai >= 3320 && ai <= 3325) || (ai >= 3330 && ai <= 3335)
|| (ai >= 3340 && ai <= 3345) || (ai >= 3350 && ai <= 3355) || (ai >= 3360 && ai <= 3365)
|| (ai >= 3370 && ai <= 3375)) {
min = 6, max = 6;
}
} else if (ai4 < 3500) {
ai = ai4;
if (ai <= 3405 || (ai >= 3410 && ai <= 3415) || (ai >= 3420 && ai <= 3425) || (ai >= 3430 && ai <= 3435)
|| (ai >= 3440 && ai <= 3445) || (ai >= 3450 && ai <= 3455) || (ai >= 3460 && ai <= 3465)
|| (ai >= 3470 && ai <= 3475) || (ai >= 3480 && ai <= 3485) || (ai >= 3490 && ai <= 3495)) {
min = 6, max = 6;
}
} else if (ai4 < 3600) {
ai = ai4;
if (ai <= 3505 || (ai >= 3510 && ai <= 3515) || (ai >= 3520 && ai <= 3525) || (ai >= 3530 && ai <= 3535)
|| (ai >= 3540 && ai <= 3545) || (ai >= 3550 && ai <= 3555) || (ai >= 3560 && ai <= 3565)
|| (ai >= 3570 && ai <= 3575)) {
min = 6, max = 6;
}
} else if (ai4 < 3700) {
ai = ai4;
if (ai <= 3605 || (ai >= 3610 && ai <= 3615) || (ai >= 3620 && ai <= 3625) || (ai >= 3630 && ai <= 3635)
|| (ai >= 3640 && ai <= 3645) || (ai >= 3650 && ai <= 3655) || (ai >= 3660 && ai <= 3665)
|| (ai >= 3670 && ai <= 3675) || (ai >= 3680 && ai <= 3685) || (ai >= 3690 && ai <= 3695)) {
min = 6, max = 6;
}
} else if (ai4 < 4000) {
ai = ai4;
if ((ai >= 3900 && ai <= 3909) || (ai >= 3920 && ai <= 3929)) {
min = 1, max = 15;
} else if ((ai >= 3910 && ai <= 3919) || (ai >= 3930 && ai <= 3939)) {
min = 4, max = 18;
} else if (ai >= 3940 && ai <= 3943) {
min = 4, max = 4;
} else if (ai >= 3950 && ai <= 3955) {
min = 6, max = 6;
}
} else if (ai4 < 4400) {
ai = ai4;
if (ai == 4300 || ai == 4301 || ai == 4310 || ai == 4311 || ai == 4320) {
min = 1, max = 35;
} else if ((ai >= 4302 && ai <= 4306) || (ai >= 4312 && ai <= 4316)) {
min = 1, max = 70;
} else if (ai == 4307 || ai == 4317) {
min = 2, max = 2;
} else if (ai == 4308 || ai == 4319) {
min = 1, max = 30;
} else if (ai == 4309) {
min = 20, max = 20;
} else if (ai == 4318) {
min = 1, max = 20;
} else if (ai >= 4321 && ai <= 4323) {
min = 1, max = 1;
} else if (ai == 4324 || ai == 4325) {
min = 10, max = 10;
} else if (ai == 4326) {
min = 6, max = 6;
} else if (ai >= 4330 && ai <= 4333) {
min = 6, max = 7;
}
} else if (ai4 < 7100) {
ai = ai4;
if (ai == 7001) {
min = 13, max = 13;
} else if (ai == 7002) {
min = 1, max = 30;
} else if (ai == 7003) {
min = 10, max = 10;
} else if (ai == 7004) {
min = 1, max = 4;
} else if (ai == 7005) {
min = 1, max = 12;
} else if (ai == 7006) {
min = 6, max = 6;
} else if (ai == 7007) {
min = 6, max = 12;
} else if (ai == 7008) {
min = 1, max = 3;
} else if (ai == 7009) {
min = 1, max = 10;
} else if (ai == 7010) {
min = 1, max = 2;
} else if (ai == 7011) {
min = 6, max = 10;
} else if (ai >= 7020 && ai <= 7022) {
min = 1, max = 20;
} else if (ai == 7023) {
min = 1, max = 30;
} else if (ai >= 7030 && ai <= 7039) {
min = 4, max = 30;
} else if (ai == 7040) {
min = 4, max = 4;
} else if (ai == 7041) {
min = 1, max = 4;
}
} else if (ai4 < 7300) {
ai = ai4;
if (ai >= 7230 && ai <= 7239) {
min = 3, max = 30;
} else if (ai == 7240) {
min = 1, max = 20;
} else if (ai == 7241) {
min = 2, max = 2;
} else if (ai == 7242) {
min = 1, max = 25;
} else if (ai == 7250) {
min = 8, max = 8;
} else if (ai == 7251) {
min = 12, max = 12;
} else if (ai == 7252) {
min = 1, max = 1;
} else if (ai == 7253 || ai == 7254 || ai == 7259) {
min = 1, max = 40;
} else if (ai == 7255) {
min = 1, max = 10;
} else if (ai == 7256) {
min = 1, max = 90;
} else if (ai == 7257) {
min = 1, max = 70;
} else if (ai == 7258) {
min = 3, max = 3;
}
} else if (ai4 < 8100) {
ai = ai4;
if (ai == 8001) {
min = 14, max = 14;
} else if (ai == 8002 || ai == 8012) {
min = 1, max = 20;
} else if (ai == 8003) {
min = 14, max = 30;
} else if (ai == 8004) {
min = 1, max = 30;
} else if (ai == 8005) {
min = 6, max = 6;
} else if (ai == 8006 || ai == 8026) {
min = 18, max = 18;
} else if (ai == 8007) {
min = 1, max = 34;
} else if (ai == 8008) {
min = 8, max = 12;
} else if (ai == 8009) {
min = 1, max = 50;
} else if (ai == 8010) {
min = 1, max = 30;
} else if (ai == 8011) {
min = 1, max = 12;
} else if (ai == 8013) {
min = 1, max = 25;
} else if (ai == 8014) {
min = 1, max = 25;
} else if (ai == 8017 || ai == 8018) {
min = 18, max = 18;
} else if (ai == 8019) {
min = 1, max = 10;
} else if (ai == 8020) {
min = 1, max = 25;
} else if (ai == 8030) {
min = 1, max = 90;
} else if (ai == 8040 || ai == 8041) {
min = 15, max = 15;
} else if (ai == 8042) {
min = 32, max = 32;
} else if (ai == 8043) {
min = 18, max = 20;
}
} else if (ai4 < 8200) {
ai = ai4;
if (ai == 8110) {
min = 1, max = 70;
} else if (ai == 8111) {
min = 4, max = 4;
} else if (ai == 8112) {
min = 1, max = 70;
}
} else if (ai4 < 8300) {
ai = ai4;
if (ai == 8200) {
min = 1, max = 70;
}
}
}
if (max == 0) {
*p_ai = ai2; /* Use 2-digit as feedback */
return 0;
}
assert(ai >= 0 && ai <= 9999);
assert(min >= 1);
assert(max >= min);
*p_ai = ai;
if (p_min) {
*p_min = min;
}
if (p_max) {
*p_max = max;
}
return 1;
}
/* Parse non-bracketed input */
static int gs1_lint_parse_raw_caret(const unsigned char source[], const int length,
const int ai_max, int *ai_vals, int *ai_locs, int *data_locs, int *data_lens, int *p_ai_count,
int *p_err_no, int *p_err_posn) {
int i, j;
const int gs1_caret = source[0] == '^';
const unsigned char separator = gs1_caret ? '^' : '\x1D';
int ai, max;
int ai_count = 0, ai_len;
#ifdef NDEBUG
(void)ai_max;
#endif
i = gs1_caret || source[0] == '\x1D'; /* Allow GS at start also */
if (i >= length) {
*p_ai_count = 0; /* For feedback */
ai_vals[0] = -1;
ai_locs[0] = i - 1;
*p_err_no = 1;
*p_err_posn = i;
return 0;
}
while (i < length) {
int data_start, data_max, on_separator;
assert(ai_count < ai_max);
ai_locs[ai_count] = i;
if (!gs1_lint_parse_ai(source, length, i, &ai, NULL /*min*/, &max)) {
*p_ai_count = ai_count; /* For feedback */
ai_vals[ai_count] = ai; /* May be -1 */
*p_err_no = 1;
*p_err_posn = i + 1; /* Position 1-base */
return 0;
}
ai_vals[ai_count] = ai;
ai_len = ai < 100 ? 2 : ai < 1000 ? 3 : 4;
/* Following GS1 Syntax Engine tolerating superfluous FNC1s at end of AI data
(for both final AI and AIs with predefined length) */
data_start = i + ai_len;
data_max = data_start + max;
for (j = data_start; j < length && j < data_max; j++) {
if (source[j] == separator) {
break;
}
}
data_locs[ai_count] = data_start;
/* Only checking that have at least one char, and haven't exceeded max */
on_separator = j < length && source[j] == separator;
if (j == data_start || (j + 1 == length && length > data_max && !on_separator)) {
*p_ai_count = ai_count; /* For feedback */
data_lens[ai_count] = j - data_start;
*p_err_no = 2;
*p_err_posn = i + 1; /* Position 1-base */
return 0;
}
data_lens[ai_count] = j - data_locs[ai_count];
ai_count++;
i = j + on_separator;
}
*p_ai_count = ai_count;
return 1;
}
#endif /* Z_GS1_LINT_H */
+2 -2
View File
@@ -1218,7 +1218,7 @@ int ZBarcode_Encode_Segs(struct zint_symbol *symbol, const struct zint_seg segs[
if (is_composite || !check_force_gs1(symbol->symbology)) {
unsigned char *reduced = (unsigned char *) z_alloca(local_segs[0].length + 1);
error_number = zint_gs1_verify(symbol, local_segs[0].source, local_segs[0].length, reduced,
&local_segs[0].length);
&local_segs[0].length, 0 /*set_hrt*/);
if (error_number) {
#ifdef ZINT_HAVE_GS1SE
if (is_composite && !(symbol->input_mode & GS1SYNTAXENGINE_MODE)) {
@@ -1387,7 +1387,7 @@ static int txt_hex_plot(struct zint_symbol *symbol) {
}
if (!zint_fm_close(fmp, symbol)) {
return ZEXT z_errtxtf(ZINT_ERROR_FILE_WRITE, symbol, 792, "Failure on closing TXT output file (%1$d: %2$s)",
return ZEXT z_errtxtf(ZINT_ERROR_FILE_WRITE, symbol, 796, "Failure on closing TXT output file (%1$d: %2$s)",
fmp->err, strerror(fmp->err));
}
+10 -19
View File
@@ -1,7 +1,7 @@
/* rss.c - GS1 DataBar (formerly Reduced Space Symbology) */
/*
libzint - the open source barcode library
Copyright (C) 2008-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2008-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -1188,17 +1188,6 @@ static void dbar_exp_separator(struct zint_symbol *symbol, int width, const int
}
}
/* Set HRT for DataBar Expanded */
static void dbar_exp_hrt(struct zint_symbol *symbol, unsigned char source[], const int length) {
/* Max possible length is 77 digits so will fit */
if (symbol->input_mode & GS1PARENS_MODE) {
z_hrt_cpy_nochk(symbol, source, length);
} else {
z_hrt_conv_gs1_brackets_nochk(symbol, source, length);
}
}
/* Return DataBar Expanded group (-1) */
static int dbar_exp_group(const int val) {
int i;
@@ -1225,10 +1214,11 @@ INTERNAL int zint_dbar_exp_cc(struct zint_symbol *symbol, unsigned char source[]
int reduced_length;
/* Allow for 8 bits + 5-bit latch per char + 200 bits overhead/padding */
char *binary_string = (char *) z_alloca(13 * length + 200 + 1);
const int set_hrt = symbol->symbology == BARCODE_DBAR_EXP || symbol->symbology == BARCODE_DBAR_EXP_CC;
const int content_segs = symbol->output_options & BARCODE_CONTENT_SEGS;
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
error_number = zint_gs1_verify(symbol, source, length, reduced, &reduced_length);
error_number = zint_gs1_verify(symbol, source, length, reduced, &reduced_length, set_hrt);
if (error_number >= ZINT_ERROR) {
return error_number;
}
@@ -1318,10 +1308,6 @@ INTERNAL int zint_dbar_exp_cc(struct zint_symbol *symbol, unsigned char source[]
check_char = 211 * (symbol_chars - 4) + checksum % 211;
if (debug_print) {
printf("Data chars: %d, Check char: %d\n", data_chars, check_char);
}
group = dbar_exp_group(check_char);
odd = (check_char - dbar_exp_g_sum[group]) / dbar_exp_t_even[group];
@@ -1335,6 +1321,11 @@ INTERNAL int zint_dbar_exp_cc(struct zint_symbol *symbol, unsigned char source[]
pattern_width = codeblocks * 5 + symbol_chars * 8 + 4;
memset(elements, 0, sizeof(int) * pattern_width);
if (debug_print) {
printf("Data chars: %d, Check char: %d, Codeblocks: %d, Symbol chars: %d\n",
data_chars, check_char, codeblocks, symbol_chars);
}
/* Put finder patterns in element array */
p = (symbol_chars - 1) / 2 - 1;
for (i = 0; i < codeblocks; i++) {
@@ -1380,7 +1371,7 @@ INTERNAL int zint_dbar_exp_cc(struct zint_symbol *symbol, unsigned char source[]
}
symbol->rows++;
dbar_exp_hrt(symbol, source, length);
/* HRT set by `zint_gs1_verify()` */
if (content_segs && z_ct_cpy(symbol, reduced, reduced_length)) {
return ZINT_ERROR_MEMORY; /* `z_ct_cpy()` only fails with OOM */
@@ -1498,7 +1489,7 @@ INTERNAL int zint_dbar_exp_cc(struct zint_symbol *symbol, unsigned char source[]
if (symbol->symbology == BARCODE_DBAR_EXP_CC || symbol->symbology == BARCODE_DBAR_EXPSTK_CC) {
/* Composite separator */
dbar_exp_separator(symbol, symbol->width, 4, separator_row, 1 /*above*/, 0 /*special_case_row*/,
dbar_exp_separator(symbol, symbol->width, codeblocks, separator_row, 1 /*above*/, 0 /*special_case_row*/,
1 /*left_to_right*/, 0 /*odd_last_row*/, NULL);
}
+1 -1
View File
@@ -4934,7 +4934,7 @@ static void test_encode(const testCtx *const p_ctx) {
struct zint_symbol *symbol = NULL;
char escaped[8192];
char cmp_buf[32768];
char cmp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do BWIPP/ZXing-C++ tests if asked, too slow otherwise */
+4 -4
View File
@@ -1,6 +1,6 @@
/*
libzint - the open source barcode library
Copyright (C) 2020-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2020-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -2897,7 +2897,7 @@ static void test_encode(const testCtx *const p_ctx) {
struct zint_symbol *symbol = NULL;
char escaped[8192];
char bwipp_buf[32768];
char bwipp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char bwipp_msg[1024];
int do_bwipp = (debug & ZINT_DEBUG_TEST_BWIPP) && testUtilHaveGhostscript(); /* Only do BWIPP test if asked, too slow otherwise */
@@ -3286,7 +3286,7 @@ static void test_encode_segs(const testCtx *const p_ctx) {
struct zint_symbol *symbol = NULL;
char escaped[8192];
char bwipp_buf[32768];
char bwipp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char bwipp_msg[1024];
int do_bwipp = (debug & ZINT_DEBUG_TEST_BWIPP) && testUtilHaveGhostscript(); /* Only do BWIPP test if asked, too slow otherwise */
@@ -3574,7 +3574,7 @@ static void test_fuzz(const testCtx *const p_ctx) {
int i, length, ret;
struct zint_symbol *symbol = NULL;
char bwipp_buf[32768];
char bwipp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char bwipp_msg[1024];
int do_bwipp = (debug & ZINT_DEBUG_TEST_BWIPP) && testUtilHaveGhostscript(); /* Only do BWIPP test if asked, too slow otherwise */
+2 -2
View File
@@ -104,7 +104,7 @@ static void test_large(const testCtx *const p_ctx) {
char escaped2[1024];
char cmp_buf[8192];
char cmp_msg[1024];
char ret_buf[8192] = {0}; /* Suppress clang -fsanitize=memory false positive */
char ret_buf[8192] ZINT_TESTUTIL_SANITIZEM_INIT;
/* Only do BWIPP/zxing-cpp tests if asked, too slow otherwise */
int do_bwipp = (debug & ZINT_DEBUG_TEST_BWIPP) && testUtilHaveGhostscript();
@@ -937,7 +937,7 @@ static void test_hibc_input(const testCtx *const p_ctx) {
char escaped[1024];
char escaped2[1024];
char cmp_buf[8192];
char cmp_buf[8192] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
char ret_buf[8192];
File diff suppressed because it is too large Load Diff
+13 -11
View File
@@ -1,6 +1,6 @@
/*
libzint - the open source barcode library
Copyright (C) 2019-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2019-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -360,7 +360,7 @@ static void test_large(const testCtx *const p_ctx) {
char data_buf[ZINT_MAX_DATA_LEN];
char escaped[8192];
char escaped[8192] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_buf[32768];
char cmp_msg[1024];
@@ -1304,7 +1304,7 @@ static void test_input(const testCtx *const p_ctx) {
&& data[i].option_3 == data[i - 1].option_3
&& data[i].output_options == data[i - 1].output_options
&& strcmp(data[i].data, data[i - 1].data) == 0) {
unsigned char binary[2][2200];
unsigned char binary[2][2200] ZINT_TESTUTIL_SANITIZEM_INIT_2D;
int gs1;
int binlen;
int binlens[2] = {0};
@@ -1319,7 +1319,7 @@ static void test_input(const testCtx *const p_ctx) {
i, expected_rows_width, prev_expected_rows_width);
if ((data[i].input_mode & 0x07) == GS1_MODE) {
ret = zint_gs1_verify(symbol, ZUCP(data[i].data), length, reduced, &length);
ret = zint_gs1_verify(symbol, ZUCP(data[i].data), length, reduced, &length, 0 /*set_hrt*/);
assert_zero(ret, "i:%d zint_gs1_verify() ret %d != 0 (%s)\n", i, ret, symbol->errtxt);
text = reduced;
} else {
@@ -6395,7 +6395,7 @@ static void test_encode_segs(const testCtx *const p_ctx) {
testFinish();
}
static void test_rt(const testCtx *const p_ctx) {
static void test_ct(const testCtx *const p_ctx) {
int debug = p_ctx->debug;
struct item {
@@ -6427,9 +6427,11 @@ static void test_rt(const testCtx *const p_ctx) {
/* 11*/ { BARCODE_DATAMATRIX, UNICODE_MODE, 899, BARCODE_CONTENT_SEGS, "é", -1, 0, 899, "é", -1, 899 },
/* 12*/ { BARCODE_DATAMATRIX, GS1_MODE, -1, -1, "[01]04912345123459[15]970331[30]128[10]ABC123", -1, 0, 0, "", -1, 0 },
/* 13*/ { BARCODE_DATAMATRIX, GS1_MODE, -1, BARCODE_CONTENT_SEGS, "[01]04912345123459[15]970331[30]128[10]ABC123", -1, 0, 0, "01049123451234591597033130128\03510ABC123", -1, 3 },
/* 14*/ { BARCODE_DATAMATRIX, GS1_MODE, 28, BARCODE_CONTENT_SEGS, "[01]04912345123459[15]970331[30]128[10]ABC123", -1, 0, 28, "01049123451234591597033130128\03510ABC123", -1, 3 }, /* Note: content seg ECI rremains at default 3 */
/* 15*/ { BARCODE_HIBC_DM, UNICODE_MODE, -1, -1, "H123ABC01234567890", -1, 0, 0, "", -1, 0 },
/* 16*/ { BARCODE_HIBC_DM, UNICODE_MODE, -1, BARCODE_CONTENT_SEGS, "H123ABC01234567890", -1, 0, 0, "+H123ABC01234567890D", -1, 3 },
/* 14*/ { BARCODE_DATAMATRIX, GS1_MODE, 28, BARCODE_CONTENT_SEGS, "[01]04912345123459[15]970331[30]128[10]ABC123", -1, 0, 28, "01049123451234591597033130128\03510ABC123", -1, 3 }, /* Note: content seg ECI remains at default 3 */
/* 15*/ { BARCODE_DATAMATRIX, GS1_MODE, -1, -1, "https://example.com/01/09506000134369", -1, 0, 0, "", -1, 0 },
/* 16*/ { BARCODE_DATAMATRIX, GS1_MODE, -1, BARCODE_CONTENT_SEGS, "https://example.com/01/09506000134369", -1, 0, 0, "https://example.com/01/09506000134369", -1, 3 },
/* 17*/ { BARCODE_HIBC_DM, UNICODE_MODE, -1, -1, "H123ABC01234567890", -1, 0, 0, "", -1, 0 },
/* 18*/ { BARCODE_HIBC_DM, UNICODE_MODE, -1, BARCODE_CONTENT_SEGS, "H123ABC01234567890", -1, 0, 0, "+H123ABC01234567890D", -1, 3 },
};
const int data_size = ARRAY_SIZE(data);
int i, length, ret;
@@ -6487,7 +6489,7 @@ static void test_rt(const testCtx *const p_ctx) {
testFinish();
}
static void test_rt_segs(const testCtx *const p_ctx) {
static void test_ct_segs(const testCtx *const p_ctx) {
int debug = p_ctx->debug;
struct item {
@@ -7686,8 +7688,8 @@ int main(int argc, char *argv[]) {
{ "test_input", test_input },
{ "test_encode", test_encode },
{ "test_encode_segs", test_encode_segs },
{ "test_rt", test_rt },
{ "test_rt_segs", test_rt_segs },
{ "test_ct", test_ct },
{ "test_ct_segs", test_ct_segs },
#ifdef ZINT_TEST_ENCODING
{ "test_minimalenc", test_minimalenc },
#endif
+2 -2
View File
@@ -71,7 +71,7 @@ static void test_large(const testCtx *const p_ctx) {
char data_buf[4096];
char escaped[8192];
char cmp_buf[50000];
char cmp_buf[50000] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do BWIPP/zxing-cpp tests if asked, too slow otherwise */
@@ -294,7 +294,7 @@ static void test_input(const testCtx *const p_ctx) {
struct zint_symbol *symbol = NULL;
char escaped[1024];
char cmp_buf[32768];
char cmp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do BWIPP/ZXing-C++ tests if asked, too slow otherwise */
+2949 -2247
View File
File diff suppressed because it is too large Load Diff
+854 -416
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -195,15 +195,15 @@ static void test_checks(const testCtx *const p_ctx) {
/*131*/ { BARCODE_CODE128, -1, "\\o200", -1, UNICODE_MODE | ESCAPE_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, -1, ZINT_ERROR_INVALID_DATA, "Error 245: Invalid UTF-8 in input", -1 },
/*132*/ { BARCODE_MAXICODE, -1, "\200", -1, UNICODE_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, -1, ZINT_ERROR_INVALID_DATA, "Error 245: Invalid UTF-8 in input", -1 },
/*133*/ { BARCODE_MAXICODE, -1, "\\o200", -1, UNICODE_MODE | ESCAPE_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, -1, ZINT_ERROR_INVALID_DATA, "Error 245: Invalid UTF-8 in input", -1 },
/*134*/ { BARCODE_GS1_128, -1, "[01]12345678901234", -1, GS1_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, -1, ZINT_WARN_NONCOMPLIANT, "Warning 261: AI (01) position 14: Bad checksum '4', expected '1'", -1 },
/*135*/ { BARCODE_GS1_128, -1, "[01]12345678901234", -1, GS1_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, WARN_FAIL_ALL, ZINT_ERROR_NONCOMPLIANT, "Error 261: AI (01) position 14: Bad checksum '4', expected '1'", -1 },
/*134*/ { BARCODE_GS1_128, -1, "[01]12345678901234", -1, GS1_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, -1, ZINT_WARN_NONCOMPLIANT, "Warning 261: AI (01) data position 14: Bad checksum '4', expected '1'", -1 },
/*135*/ { BARCODE_GS1_128, -1, "[01]12345678901234", -1, GS1_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, WARN_FAIL_ALL, ZINT_ERROR_NONCOMPLIANT, "Error 261: AI (01) data position 14: Bad checksum '4', expected '1'", -1 },
/*136*/ { BARCODE_QRCODE, -1, "", -1, UNICODE_MODE, 13, 0, 0, 0, 0, -1, -1, 0, -1, -1, 0, "", -1 },
/*137*/ { BARCODE_QRCODE, -1, "", -1, UNICODE_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, -1, ZINT_WARN_USES_ECI, "Warning 222: Encoded data includes ECI 13", -1 },
/*138*/ { BARCODE_QRCODE, -1, "", -1, UNICODE_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, WARN_FAIL_ALL, ZINT_ERROR_USES_ECI, "Error 222: Encoded data includes ECI 13", -1 },
/*139*/ { BARCODE_CODEONE, -1, "[01]12345678901231", -1, GS1_MODE, 3, 0, 0, 0, 0, -1, -1, 0, -1, -1, ZINT_WARN_INVALID_OPTION, "Warning 512: ECI ignored for GS1 mode", -1 },
/*140*/ { BARCODE_CODEONE, -1, "[01]12345678901231", -1, GS1_MODE, 3, 0, 0, 0, 0, -1, -1, 0, -1, WARN_FAIL_ALL, ZINT_ERROR_INVALID_OPTION, "Error 512: ECI ignored for GS1 mode", -1 },
/*141*/ { BARCODE_CODEONE, -1, "[01]12345678901234", -1, GS1_MODE, 3, 0, 0, 0, 0, -1, -1, 0, -1, -1, ZINT_WARN_INVALID_OPTION, "Warning 512: ECI ignored for GS1 mode", -1 }, /* Warning in encoder overrides library warnings */
/*142*/ { BARCODE_CODEONE, -1, "[01]12345678901234", -1, GS1_MODE, 3, 0, 0, 0, 0, -1, -1, 0, -1, WARN_FAIL_ALL, ZINT_ERROR_NONCOMPLIANT, "Error 261: AI (01) position 14: Bad checksum '4', expected '1'", -1 }, /* But not errors */
/*142*/ { BARCODE_CODEONE, -1, "[01]12345678901234", -1, GS1_MODE, 3, 0, 0, 0, 0, -1, -1, 0, -1, WARN_FAIL_ALL, ZINT_ERROR_NONCOMPLIANT, "Error 261: AI (01) data position 14: Bad checksum '4', expected '1'", -1 }, /* But not errors */
/*143*/ { BARCODE_AZTEC, -1, "", -1, UNICODE_MODE, 13, 0, 0, 0, 0, -1, -1, 0, -1, -1, 0, "", -1 },
/*144*/ { BARCODE_AZTEC, -1, "", -1, UNICODE_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, -1, ZINT_WARN_USES_ECI, "Warning 222: Encoded data includes ECI 13", -1 },
/*145*/ { BARCODE_AZTEC, -1, "", -1, UNICODE_MODE, -1, 0, 0, 0, 0, -1, -1, 0, -1, WARN_FAIL_ALL, ZINT_ERROR_USES_ECI, "Error 222: Encoded data includes ECI 13", -1 },
@@ -2105,7 +2105,7 @@ static int test_prev_ZBarcode_BarcodeName(int symbol_id, char name[32]) {
static void test_barcode_name(const testCtx *const p_ctx) {
int ret;
char name[32] = {0}; /* Suppress clang -fsanitize=memory false positive */
char name[32] ZINT_TESTUTIL_SANITIZEM_INIT;
int symbol_id;
(void)p_ctx;
@@ -2810,8 +2810,8 @@ static void test_utf8_to_eci(const testCtx *const p_ctx) {
for (i = 0; i < data_size; i++) {
int ret_dest;
unsigned char dest[1024];
int dest_length;
unsigned char dest[1024] ZINT_TESTUTIL_SANITIZEM_INIT;
int dest_length ZINT_TESTUTIL_SANITIZEM_INIT;
if (testContinue(p_ctx, i)) continue;
+2 -2
View File
@@ -1,6 +1,6 @@
/*
libzint - the open source barcode library
Copyright (C) 2019-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2019-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -648,7 +648,7 @@ static void test_2d_encode(const testCtx *const p_ctx) {
struct zint_symbol *symbol = NULL;
char escaped[8192];
char cmp_buf[32768];
char cmp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do BWIPP/ZXing-C++ tests if asked, too slow otherwise */
+10 -10
View File
@@ -1,6 +1,6 @@
/*
libzint - the open source barcode library
Copyright (C) 2019-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2019-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -70,10 +70,10 @@ static void test_qr_large(const testCtx *const p_ctx) {
int i, length, ret;
struct zint_symbol *symbol = NULL;
char data_buf[ZINT_MAX_DATA_LEN];
char data_buf[ZINT_MAX_DATA_LEN] ZINT_TESTUTIL_SANITIZEM_INIT;
char escaped[ZINT_MAX_DATA_LEN] = {0}; /* Suppress clang -fsanitize=memory false positive */
char cmp_buf[177 * 177 + 1] = {0}; /* Suppress clang -fsanitize=memory false positive */
char escaped[ZINT_MAX_DATA_LEN] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_buf[177 * 177 + 1] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do BWIPP/ZXing-C++ tests if asked, too slow otherwise */
@@ -489,7 +489,7 @@ static void test_qr_input(const testCtx *const p_ctx) {
char escaped[4096];
char escaped2[4096];
char ret_buf[4096];
char cmp_buf[32768] = {0}; /* Suppress clang -fsanitize=memory false positive */
char cmp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do BWIPP/ZXing-C++ tests if asked, too slow otherwise */
@@ -856,7 +856,7 @@ static void test_qr_optimize(const testCtx *const p_ctx) {
struct zint_symbol *symbol = NULL;
char escaped[4096];
char cmp_buf[32768] = {0}; /* Suppress clang -fsanitize=memory false positive */
char cmp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do BWIPP/ZXing-C++ tests if asked, too slow otherwise */
@@ -4740,7 +4740,7 @@ static void test_qr_encode_segs(const testCtx *const p_ctx) {
struct zint_symbol *symbol = NULL;
char escaped[4096];
char cmp_buf[32768];
char cmp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do BWIPP/ZXing-C++ tests if asked, too slow otherwise */
@@ -7369,7 +7369,7 @@ static void test_upnqr_encode(const testCtx *const p_ctx) {
struct zint_symbol *symbol = NULL;
char escaped[4096];
char cmp_buf[32768];
char cmp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
#if 0 /* Need to add "force binary mode" to BWIPP for this to work */
@@ -8078,7 +8078,7 @@ static void test_rmqr_large(const testCtx *const p_ctx) {
char data_buf[ZINT_MAX_DATA_LEN];
char escaped[4096];
char cmp_buf[32768];
char cmp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do ZXing-C++ test if asked, too slow otherwise */
@@ -9397,7 +9397,7 @@ static void test_rmqr_encode_segs(const testCtx *const p_ctx) {
struct zint_symbol *symbol = NULL;
char escaped[4096];
char cmp_buf[32768] = {0}; /* Suppress clang -fsanitize=memory false positive */
char cmp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do BWIPP/ZXing-C++ tests if asked, too slow otherwise */
+21 -19
View File
@@ -1,6 +1,6 @@
/*
libzint - the open source barcode library
Copyright (C) 2019-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2019-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -790,7 +790,7 @@ static void test_examples(const testCtx *const p_ctx) {
"00000100010110000110100000001010101010111100111101110010110001110011101000000000100010000011010010000100011110111011001000101010101010011101111111010000000000000000000000000000000000000000000000000000"
"00100011101001111001001111110000010101000011000010001101001110001100010111111111001101111100101101111011100001000100110011000000000101100010000000101010000000000000000000000000000000000000000000000000"
},
/* 90*/ { BARCODE_DBAR_EXPSTK, -1, -1, 2, "[8110]106141416543213500110000310123196000", 0, 5, 200, 1, "NACAG Figure 1 (& Appendix C: Example 6) specifying max rows",
/* 90*/ { BARCODE_DBAR_EXPSTK, -1, -1, 2, "[8110]106141416543213500110000310123196000", 0, 5, 200, 0, "NACAG Figure 1 (& Appendix C: Example 6) specifying max rows; BWIPP need segments arg, see above",
"01001100101110001110111111110000101000001101000001010111100110111000101111100000011011010001000000110000111100001010100011111100001010000010101111000111010000011001101011111111001110000001101101000101"
"00000011010001110001000000001010010111110010111110101000011001000111010000010101000100101110111111001111000011110101010100000010100101111101010000111000101111100110010100000000100001111110010010110000"
"00000101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010000"
@@ -804,7 +804,7 @@ static void test_examples(const testCtx *const p_ctx) {
"0000001000100001011010000000010000111001101101100001100111100100100010100001010100100001101011111000000000000000000000000000000000000000000000000000000"
"1011110111011110100101111111100111000110010010011110011000011011011100011110000001011110010100000100100000000000000000000000000000000000000000000000000"
},
/* 92*/ { BARCODE_DBAR_EXPSTK, -1, -1, 2, "[8110]10614141011111275110111", 0, 5, 151, 1, "NACAG Figure 3 specifying max rows",
/* 92*/ { BARCODE_DBAR_EXPSTK, -1, -1, 2, "[8110]10614141011111275110111", 0, 5, 151, 0, "NACAG Figure 3 specifying max rows; BWIPP need segments arg, see above",
"0101011110011100001011111111000010100000110100000101011110011011100010111110000001101101000100000011000011100001101010001111110000101010000011000111010"
"0000100001100011110100000000101001011111001011111010100001100100011101000001010100010010111011111100111100011110010101010000001010010101111100111000000"
"0000010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010100000"
@@ -818,7 +818,7 @@ static void test_examples(const testCtx *const p_ctx) {
"0000011010011011100010100101010100100111000100000101010000111000110010100000101010001111000101110100011001110001101000100000001010101011011110000111010111110001101011101000000000100011110100100010000110101100111000001000101010101000000000000000000000000000000000000000000000000000000000000000000000"
"0010000101100100011100011000000001011000111011111010101111000111001101011111000000110000111010001011100110001110010111001111110000010100100001111000101000001110010100010111111111001100001011011101111001010011000111110011000000000101000000000000000000000000000000000000000000000000000000000000000000"
},
/* 94*/ { BARCODE_DBAR_EXPSTK, -1, -1, 2, "[8110]106141410222223100110222111101231023456721104561045678991201", 0, 5, 298, 1, "NACAG Figure 4, specifying max rows",
/* 94*/ { BARCODE_DBAR_EXPSTK, -1, -1, 2, "[8110]106141410222223100110222111101231023456721104561045678991201", 0, 5, 298, 0, "NACAG Figure 4, specifying max rows; BWIPP need segments arg, see above",
"0100101111101001111011111111000010110010000011001101011110011011100010111100000000101101000100000011000011100001101010001111110000101011000011100001010111000011011110101111000000111010011111000101110001110110011001100011110000001011100110000101000110101111110111001011111111001110011010011100001101"
"0000010000010110000100000000101001001101111100110010100001100100011101000010101010010010111011111100111100011110010101010000001010010100111100011110101000111100100001010000101010000101100000111010001110001001100110010100001010100100011001111010111001010000001000110100000000100001100101100011110000"
"0000010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010000"
@@ -832,7 +832,7 @@ static void test_examples(const testCtx *const p_ctx) {
"000001110000100101101000000101000011110100100011100111100001001101001010010101010010011111110101101100010010110011111010000010101000111101010000010001110001001100110010001010101010100011000111001011000010011010000010100000000010000000000000000000000"
"101110001111011010010111111000011100001011011100011000011110110010110001100000000101100000001010010011101101001100000101111100000011000010101111101110001110110011001100110000000001011100111000110100111101100101111101011111111100110100000000000000000"
},
/* 96*/ { BARCODE_DBAR_EXPSTK, -1, -1, 2, "[8110]1061414165432131501101201211014092110256100126663101231", 0, 5, 249, 1, "NACAG Appendix C: Example 1, specifying max row",
/* 96*/ { BARCODE_DBAR_EXPSTK, -1, -1, 2, "[8110]1061414165432131501101201211014092110256100126663101231", 0, 5, 249, 0, "NACAG Appendix C: Example 1, specifying max row; BWIPP need segments arg, see above",
"010111101100011011101111111100001011001000001100110101111001101110001011110000000010110100010000001100001111000010101000111111000010100000101011110001110100000110011010111100000011101001111000101111000111011001001110001111000000101011001000001110010"
"000000010011100100010000000010100100110111110011001010000110010001110100001010101001001011101111110011110000111101010101000000101001011111010100001110001011111001100101000010101000010110000111010000111000100110110001010000101010010100110111110000000"
"000001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010100000"
@@ -872,8 +872,8 @@ static void test_examples(const testCtx *const p_ctx) {
int i, length, ret;
struct zint_symbol *symbol = NULL;
char escaped[4096] = {0}; /* Suppress clang -fsanitize=memory false positive */
char cmp_buf[16384];
char escaped[4096] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_buf[16384] ZINT_TESTUTIL_SANITIZEM_INIT;
char cmp_msg[1024];
/* Only do BWIPP/ZXing-C++ tests if asked, too slow otherwise */
@@ -1332,12 +1332,14 @@ static void test_hrt(const testCtx *const p_ctx) {
/* 9*/ { BARCODE_DBAR_LTD, BARCODE_CONTENT_SEGS, "13410567901384", 0, "(01)13410567901384", "0113410567901384" },
/* 10*/ { BARCODE_DBAR_EXP, -1, "[01]12345678901231", 0, "(01)12345678901231", "" }, /* See test_hrt() in "test_gs1.c" for full HRT tests */
/* 11*/ { BARCODE_DBAR_EXP, BARCODE_CONTENT_SEGS, "[01]12345678901231", 0, "(01)12345678901231", "0112345678901231" },
/* 12*/ { BARCODE_DBAR_STK, -1, "12345678901231", 0, "", "" }, /* No HRT for stacked */
/* 13*/ { BARCODE_DBAR_STK, BARCODE_CONTENT_SEGS, "12345678901231", 0, "", "0112345678901231" }, /* But have content segs */
/* 14*/ { BARCODE_DBAR_OMNSTK, -1, "10000000000090", 0, "", "" },
/* 15*/ { BARCODE_DBAR_OMNSTK, BARCODE_CONTENT_SEGS, "10000000000090", 0, "", "0110000000000090" },
/* 16*/ { BARCODE_DBAR_EXPSTK, -1, "[01]12345678901231", 0, "", "" },
/* 17*/ { BARCODE_DBAR_EXPSTK, BARCODE_CONTENT_SEGS, "[01]12345678901231", 0, "", "0112345678901231" },
/* 12*/ { BARCODE_DBAR_EXP, -1, "^0112345678901231", 0, "(01)12345678901231", "" },
/* 13*/ { BARCODE_DBAR_EXP, BARCODE_CONTENT_SEGS, "^0112345678901231", 0, "(01)12345678901231", "0112345678901231" },
/* 14*/ { BARCODE_DBAR_STK, -1, "12345678901231", 0, "", "" }, /* No HRT for stacked */
/* 15*/ { BARCODE_DBAR_STK, BARCODE_CONTENT_SEGS, "12345678901231", 0, "", "0112345678901231" }, /* But have content segs */
/* 16*/ { BARCODE_DBAR_OMNSTK, -1, "10000000000090", 0, "", "" },
/* 17*/ { BARCODE_DBAR_OMNSTK, BARCODE_CONTENT_SEGS, "10000000000090", 0, "", "0110000000000090" },
/* 18*/ { BARCODE_DBAR_EXPSTK, -1, "[01]12345678901231", 0, "", "" },
/* 19*/ { BARCODE_DBAR_EXPSTK, BARCODE_CONTENT_SEGS, "[01]12345678901231", 0, "", "0112345678901231" },
};
const int data_size = ARRAY_SIZE(data);
int i, length, ret;
@@ -1441,16 +1443,16 @@ static void test_input(const testCtx *const p_ctx) {
/* 34*/ { BARCODE_DBAR_LTD, -1, -1, -1, "[01)12345678901231", ZINT_ERROR_TOO_LONG, -1, -1, "Error 382: Input length 18 too long (maximum 14)", 0, 0 },
/* 35*/ { BARCODE_DBAR_LTD, -1, -1, -1, "[10]12345678901231", ZINT_ERROR_TOO_LONG, -1, -1, "Error 382: Input length 18 too long (maximum 14)", 0, 0 },
/* 36*/ { BARCODE_DBAR_LTD, GS1NOCHECK_MODE, -1, -1, "22345678901238", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 384: Input value out of range (0 to 1999999999999)", 0, 0 },
/* 37*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]12345678901234", ZINT_WARN_NONCOMPLIANT, 1, 134, "Warning 261: AI (01) position 14: Bad checksum '4', expected '1'", 0, 0 },
/* 37*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]12345678901234", ZINT_WARN_NONCOMPLIANT, 1, 134, "Warning 261: AI (01) data position 14: Bad checksum '4', expected '1'", 0, 0 },
/* 38*/ { BARCODE_DBAR_EXP, GS1NOCHECK_MODE, -1, -1, "[01]12345678901234", 0, 1, 134, "", 0, 0 },
/* 39*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]12345678901231", 0, 1, 134, "", 0, 0 },
/* 40*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]1234567890123A", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 385: Invalid character in Compressed Field data (digits only)", 0, 0 },
/* 41*/ { BARCODE_DBAR_EXP, GS1NOCHECK_MODE, -1, -1, "[01]1234567890123A", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 385: Invalid character in Compressed Field data (digits only)", 0, 0 },
/* 42*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]123456789012315", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 259: Invalid data length for AI (01)", 0, 0 },
/* 42*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]123456789012315", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 259: Invalid data length for AI (01) at position 1", 0, 0 },
/* 43*/ { BARCODE_DBAR_EXP, GS1NOCHECK_MODE, -1, -1, "[01]123456789012315", 0, 1, 151, "", 0, 0 },
/* 44*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]12345678901234", ZINT_WARN_NONCOMPLIANT, 1, 134, "Warning 261: AI (01) position 14: Bad checksum '4', expected '1'", 0, 0 },
/* 44*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]12345678901234", ZINT_WARN_NONCOMPLIANT, 1, 134, "Warning 261: AI (01) data position 14: Bad checksum '4', expected '1'", 0, 0 },
/* 45*/ { BARCODE_DBAR_EXP, GS1NOCHECK_MODE, -1, -1, "[01]12345678901234", 0, 1, 134, "", 0, 0 },
/* 46*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]12345678901231[91]!\"%&'()*+,-./:;<=>?_ ", ZINT_WARN_NONCOMPLIANT, 1, 526, "Warning 261: AI (91) position 21: Invalid CSET 82 character ' '", 0, 0 }, /* ISOIEC punc */
/* 46*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]12345678901231[91]!\"%&'()*+,-./:;<=>?_ ", ZINT_WARN_NONCOMPLIANT, 1, 526, "Warning 261: AI (91) data position 21: Invalid CSET 82 character ' '", 0, 0 }, /* ISOIEC punc */
/* 47*/ { BARCODE_DBAR_EXP, GS1NOCHECK_MODE, -1, -1, "[01]12345678901231[91]!\"%&'()*+,-./:;<=>?_ ", 0, 1, 526, "", 0, 0 },
/* 48*/ { BARCODE_DBAR_EXP, -1, -1, -1, "[01]12345678901231[91]!\"%&'()*+,-./:;<=>?_", 0, 1, 494, "", 0, 0 }, /* ISOIEC punc less space */
/* 49*/ { BARCODE_DBAR_EXP, GS1NOCHECK_MODE, -1, -1, "[01]12345678901231[91]!\"%&'()*+,-./:;<=>?_", 0, 1, 494, "", 0, 0 },
@@ -1486,12 +1488,12 @@ static void test_input(const testCtx *const p_ctx) {
/* 79*/ { BARCODE_DBAR_OMNSTK, -1, -1, -1, "(01)12345678901231", 0, 5, 50, "", 0, 0 }, /* Allow '(01)' prefix if check digit given */
/* 80*/ { BARCODE_DBAR_OMNSTK, -1, -1, -1, "(01)1234567890123", 0, 5, 50, "", 0, 0 }, /* Or not */
/* 81*/ { BARCODE_DBAR_OMNSTK, -1, -1, -1, "(00)12345678901231", ZINT_ERROR_TOO_LONG, -1, -1, "Error 380: Input length 18 too long (maximum 14)", 0, 0 },
/* 82*/ { BARCODE_DBAR_EXPSTK, -1, -1, -1, "[01]12345678901234", ZINT_WARN_NONCOMPLIANT, 5, 102, "Warning 261: AI (01) position 14: Bad checksum '4', expected '1'", 2, 0 },
/* 82*/ { BARCODE_DBAR_EXPSTK, -1, -1, -1, "[01]12345678901234", ZINT_WARN_NONCOMPLIANT, 5, 102, "Warning 261: AI (01) data position 14: Bad checksum '4', expected '1'", 2, 0 },
/* 83*/ { BARCODE_DBAR_EXPSTK, GS1NOCHECK_MODE, -1, -1, "[01]12345678901234", 0, 5, 102, "", 2, 0 },
/* 84*/ { BARCODE_DBAR_EXPSTK, -1, -1, -1, "[01]12345678901231", 0, 5, 102, "", 2, 0 },
/* 85*/ { BARCODE_DBAR_EXPSTK, -1, -1, -1, "[01]1234567890123A", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 385: Invalid character in Compressed Field data (digits only)", 0, 0 },
/* 86*/ { BARCODE_DBAR_EXPSTK, GS1NOCHECK_MODE, -1, -1, "[01]1234567890123A", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 385: Invalid character in Compressed Field data (digits only)", 0, 0 },
/* 87*/ { BARCODE_DBAR_EXPSTK, -1, -1, -1, "[01]123456789012315", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 259: Invalid data length for AI (01)", 0, 0 },
/* 87*/ { BARCODE_DBAR_EXPSTK, -1, -1, -1, "[01]123456789012315", ZINT_ERROR_INVALID_DATA, -1, -1, "Error 259: Invalid data length for AI (01) at position 1", 0, 0 },
/* 88*/ { BARCODE_DBAR_EXPSTK, GS1NOCHECK_MODE, -1, -1, "[01]123456789012315", 0, 5, 102, "", 2, 0 },
/* 89*/ { BARCODE_DBAR_EXPSTK, -1, 12, -1, "[01]12345678901231", 0, 5, 102, "", 2, 0 }, /* Cols > 11 ignored */
/* 90*/ { BARCODE_DBAR_EXPSTK, -1, -1, 12, "[01]12345678901231", 0, 5, 102, "", 2, 0 }, /* Rows > 11 ignored */
+2 -2
View File
@@ -1,6 +1,6 @@
/*
libzint - the open source barcode library
Copyright (C) 2020-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2020-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -810,7 +810,7 @@ static void test_encode(const testCtx *const p_ctx) {
struct zint_symbol *symbol = NULL;
char escaped[1024];
char bwipp_buf[32768];
char bwipp_buf[32768] ZINT_TESTUTIL_SANITIZEM_INIT;
char bwipp_msg[1024];
int do_bwipp = (debug & ZINT_DEBUG_TEST_BWIPP) && testUtilHaveGhostscript(); /* Only do BWIPP test if asked, too slow otherwise */
+54 -34
View File
@@ -570,6 +570,7 @@ const char *testUtilInputModeName(int input_mode) {
{ "FAST_MODE", FAST_MODE, 0x0080 },
{ "EXTRA_ESCAPE_MODE", EXTRA_ESCAPE_MODE, 0x0100 },
{ "GS1SYNTAXENGINE_MODE", GS1SYNTAXENGINE_MODE, 0x0200 },
{ "GS1RAW_MODE", GS1RAW_MODE, 0x0400 },
};
const int data_size = ARRAY_SIZE(data);
int set, i;
@@ -2296,7 +2297,7 @@ int testUtilVerifyTiffInfo(const char *filename, int debug) {
/* Map Zint symbology to BWIPP routine */
static const char *testUtilBwippName(int index, const struct zint_symbol *symbol, int option_1, int option_2,
int option_3, int debug, int *linear_row_height, int *gs1_cvt) {
int option_3, int debug, int *p_linear_row_height, int *p_gs1_cvt) {
struct item {
const char *name;
int define;
@@ -2324,7 +2325,7 @@ static const char *testUtilBwippName(int index, const struct zint_symbol *symbol
{ "ean13", BARCODE_EANX, 13, 0, 1, 0, 0, 1 /*gs1_cvt*/, },
{ "ean13", BARCODE_EANX_CHK, 14, 0, 1, 0, 0, 1, },
{ "ean13", BARCODE_EAN13, 15, 0, 1, 0, 0, 1, },
{ "gs1-128", BARCODE_GS1_128, 16, 0, 0, 0, 0, 1 /*gs1_cvt*/, },
{ "gs1-128", BARCODE_GS1_128, 16, 0, 0, 1 /*GS1CARET*/, 0, 1 /*gs1_cvt*/, },
{ "", -1, 17, 0, 0, 0, 0, 0, },
{ "rationalizedCodabar", BARCODE_CODABAR, 18, 0, 1, 0, 0, 0, },
{ "", -1, 19, 0, 0, 0, 0, 0, },
@@ -2339,7 +2340,7 @@ static const char *testUtilBwippName(int index, const struct zint_symbol *symbol
{ "flattermarken", BARCODE_FLAT, 28, 0, 0, 0, 0, 0, },
{ "databaromni", BARCODE_DBAR_OMN, 29, 0, 0, 0, 0, 1 /*gs1_cvt*/, },
{ "databarlimited", BARCODE_DBAR_LTD, 30, 0, 0, 0, 0, 1, },
{ "databarexpanded", BARCODE_DBAR_EXP, 31, 0, 1, 0, 1 /*linear_row_height*/, 1, },
{ "databarexpanded", BARCODE_DBAR_EXP, 31, 0, 1, 1 /*GS1CARET*/, 1 /*linear_row_height*/, 1, },
{ "telepen", BARCODE_TELEPEN, 32, 0, 0, 0, 0, 0, },
{ "", -1, 33, 0, 0, 0, 0, 0, },
{ "upca", BARCODE_UPCA, 34, 0, 1, 0, 0, 1 /*gs1_cvt*/, },
@@ -2380,7 +2381,7 @@ static const char *testUtilBwippName(int index, const struct zint_symbol *symbol
{ "isbn", BARCODE_ISBNX, 69, 0, 1, 0, 0, 1 /*gs1_cvt*/, },
{ "royalmail", BARCODE_RM4SCC, 70, 0, 0, 0, 0, 0, },
{ "datamatrix", BARCODE_DATAMATRIX, 71, 0, 1, 1, 1, 0, },
{ "ean14", BARCODE_EAN14, 72, 0, 0, 0, 0, 1 /*gs1_cvt*/, },
{ "ean14", BARCODE_EAN14, 72, 0, 0, 1 /*GS1CARET*/, 0, 1 /*gs1_cvt*/, },
{ "code39", BARCODE_VIN, 73, 0, 0, 0, 0, 0, },
{ "codablockf", BARCODE_CODABLOCKF, 74, 1, 1, 0, 10 /*linear_row_height*/, 0, },
{ "sscc18", BARCODE_NVE18, 75, 0, 0, 0, 0, 1 /*gs1_cvt*/, },
@@ -2389,7 +2390,7 @@ static const char *testUtilBwippName(int index, const struct zint_symbol *symbol
{ "", -1, 78, 0, 0, 0, 0, 0, },
{ "databarstacked", BARCODE_DBAR_STK, 79, 0, 0, 0, 0, 1 /*gs1_cvt*/, },
{ "databarstackedomni", BARCODE_DBAR_OMNSTK, 80, 0, 0, 0, 33 /*linear_row_height*/, 1, },
{ "databarexpandedstacked", BARCODE_DBAR_EXPSTK, 81, 0, 1, 0, 34 /*linear_row_height*/, 1, },
{ "databarexpandedstacked", BARCODE_DBAR_EXPSTK, 81, 0, 1, 1 /*GS1CARET*/, 34 /*linear_row_height*/, 1, },
{ "planet", BARCODE_PLANET, 82, 0, 0, 0, 0, 0, },
{ "", -1, 83, 0, 0, 0, 0, 0, },
{ "micropdf417", BARCODE_MICROPDF417, 84, 0, 1, 0, 0, 0, },
@@ -2439,15 +2440,15 @@ static const char *testUtilBwippName(int index, const struct zint_symbol *symbol
{ "aztecrune", BARCODE_AZRUNE, 128, 0, 0, 0, 0, 0, },
{ "code32", BARCODE_CODE32, 129, 0, 0, 0, 0, 0, },
{ "ean13composite", BARCODE_EANX_CC, 130, 1, 1, 0, 72 /*linear_row_height*/, 1 /*gs1_cvt*/, },
{ "gs1-128composite", BARCODE_GS1_128_CC, 131, 1, 0, 0, 36, 1, },
{ "gs1-128composite", BARCODE_GS1_128_CC, 131, 1, 0, 1 /*GS1CARET*/, 36, 1, },
{ "databaromnicomposite", BARCODE_DBAR_OMN_CC, 132, 1, 0, 0, 33, 1, },
{ "databarlimitedcomposite", BARCODE_DBAR_LTD_CC, 133, 1, 0, 0, 10 /*linear_row_height*/, 1, },
{ "databarexpandedcomposite", BARCODE_DBAR_EXP_CC, 134, 1, 1, 0, 34 /*linear_row_height*/, 1, },
{ "databarexpandedcomposite", BARCODE_DBAR_EXP_CC, 134, 1, 1, 1 /*GS1CARET*/, 34 /*linear_row_height*/, 1, },
{ "upcacomposite", BARCODE_UPCA_CC, 135, 1, 1, 0, 72, 1, },
{ "upcecomposite", BARCODE_UPCE_CC, 136, 1, 1, 0, 72, 1, },
{ "databarstackedcomposite", BARCODE_DBAR_STK_CC, 137, 1, 0, 0, 0, 1, },
{ "databarstackedomnicomposite", BARCODE_DBAR_OMNSTK_CC, 138, 1, 0, 0, 33 /*linear_row_height*/, 1, },
{ "databarexpandedstackedcomposite", BARCODE_DBAR_EXPSTK_CC, 139, 1, 1, 0, 34 /*linear_row_height*/, 1, },
{ "databarexpandedstackedcomposite", BARCODE_DBAR_EXPSTK_CC, 139, 1, 1, 1 /*GS1CARET*/, 34 /*lin_r_h*/, 1, },
{ "channelcode", BARCODE_CHANNEL, 140, 0, 0, 0, 0, 0, },
{ "codeone", BARCODE_CODEONE, 141, 0, 1, 0, 0, 0, },
{ "", BARCODE_GRIDMATRIX, 142, 0, 0, 0, 0, 0, },
@@ -2462,7 +2463,9 @@ static const char *testUtilBwippName(int index, const struct zint_symbol *symbol
const int data_size = ARRAY_SIZE(data);
const int symbology = symbol->symbology;
const int gs1 = (symbol->input_mode & 0x07) == GS1_MODE;
const int gs1raw_mode = !!(symbol->input_mode & GS1RAW_MODE);
const int gs1_caret = option_3 != -1 && !!(option_3 & ZINT_TESTUTIL_GS1CARET); /* Hack indicator */
const int gs1 = (symbol->input_mode & 0x07) == GS1_MODE || gs1raw_mode || gs1_caret;
if (symbology < 0 || symbology >= data_size) {
fprintf(stderr, "testUtilBwippName: unknown symbology (%d)\n", symbology);
@@ -2544,16 +2547,30 @@ static const char *testUtilBwippName(int index, const struct zint_symbol *symbol
}
}
if (linear_row_height) {
*linear_row_height = data[symbology].linear_row_height;
if (p_linear_row_height) {
*p_linear_row_height = data[symbology].linear_row_height;
}
if (gs1_cvt) {
*gs1_cvt = data[symbology].gs1_cvt;
if (p_gs1_cvt) {
*p_gs1_cvt = data[symbology].gs1_cvt;
}
if (gs1) {
if (gs1raw_mode) {
if (debug & ZINT_DEBUG_TEST_PRINT) {
printf("i:%d %s not BWIPP compatible, GS1RAW_MODE not supported\n",
index, testUtilBarcodeName(symbology));
}
return NULL;
}
if (gs1_caret) {
if (debug & ZINT_DEBUG_TEST_PRINT) {
printf("i:%d %s not BWIPP compatible, GS1 Syntax Engine caret input not supported\n",
index, testUtilBarcodeName(symbology));
}
return NULL;
}
if (symbology == BARCODE_DATAMATRIX) {
if (gs1_cvt) {
*gs1_cvt = 1;
if (p_gs1_cvt) {
*p_gs1_cvt = 1;
}
return "gs1datamatrix";
} else if (symbology == BARCODE_AZTEC || symbology == BARCODE_CODE16K || symbology == BARCODE_ULTRA
@@ -2564,13 +2581,13 @@ static const char *testUtilBwippName(int index, const struct zint_symbol *symbol
}
return NULL;
} else if (symbology == BARCODE_QRCODE) {
if (gs1_cvt) {
*gs1_cvt = 1;
if (p_gs1_cvt) {
*p_gs1_cvt = 1;
}
return "gs1qrcode";
} else if (symbology == BARCODE_DOTCODE) {
if (gs1_cvt) {
*gs1_cvt = 1;
if (p_gs1_cvt) {
*p_gs1_cvt = 1;
}
return "gs1dotcode";
}
@@ -3876,8 +3893,8 @@ static const char *testUtilZXingCPPName(int index, const struct zint_symbol *sym
{ "", -1, 5, },
{ "", BARCODE_C25LOGIC, 6, },
{ "", BARCODE_C25IND, 7, },
{ "Code39", BARCODE_CODE39, 8, },
{ "Code39", BARCODE_EXCODE39, 9, }, /* TODO: Code39 with specially encoded chars */
{ "Code39Std", BARCODE_CODE39, 8, },
{ "Code39", BARCODE_EXCODE39, 9, },
{ "EAN8", BARCODE_EAN8, 10, },
{ "", BARCODE_EAN_2ADDON, 11, },
{ "", BARCODE_EAN_5ADDON, 12, },
@@ -3918,9 +3935,9 @@ static const char *testUtilZXingCPPName(int index, const struct zint_symbol *sym
{ "", BARCODE_MSI_PLESSEY, 47, },
{ "", -1, 48, },
{ "", BARCODE_FIM, 49, },
{ "Code39", BARCODE_LOGMARS, 50, },
{ "Code39Std", BARCODE_LOGMARS, 50, },
{ "", BARCODE_PHARMA, 51, },
{ "Code39", BARCODE_PZN, 52, },
{ "Code39Std", BARCODE_PZN, 52, },
{ "", BARCODE_PHARMA_TWO, 53, },
{ "", -1, 54, },
{ "PDF417", BARCODE_PDF417, 55, },
@@ -3941,7 +3958,7 @@ static const char *testUtilZXingCPPName(int index, const struct zint_symbol *sym
{ "", BARCODE_RM4SCC, 70, },
{ "DataMatrix", BARCODE_DATAMATRIX, 71, },
{ "Code128", BARCODE_EAN14, 72, },
{ "Code39", BARCODE_VIN, 73, },
{ "Code39Std", BARCODE_VIN, 73, },
{ "CodablockF", BARCODE_CODABLOCKF, 74, },
{ "Code128", BARCODE_NVE18, 75, },
{ "", BARCODE_JAPANPOST, 76, },
@@ -3967,7 +3984,7 @@ static const char *testUtilZXingCPPName(int index, const struct zint_symbol *sym
{ "Code128", BARCODE_DPD, 96, },
{ "MicroQRCode", BARCODE_MICROQR, 97, },
{ "Code128", BARCODE_HIBC_128, 98, },
{ "Code39", BARCODE_HIBC_39, 99, },
{ "Code39Std", BARCODE_HIBC_39, 99, },
{ "", -1, 100, },
{ "", -1, 101, },
{ "DataMatrix", BARCODE_HIBC_DM, 102, },
@@ -4127,12 +4144,12 @@ int testUtilZXingCPP(int index, struct zint_symbol *symbol, const char *source,
if (symbology == BARCODE_EXCODE39) {
if (symbol->option_2 == 1) {
opts = "tryCode39ExtendedMode,validateCode39CheckSum";
opts = "tryCode39ExtendedMode,validateOptionalChecksum";
} else {
opts = "tryCode39ExtendedMode";
}
} else if ((symbology == BARCODE_CODE39 || symbology == BARCODE_LOGMARS) && symbol->option_2 == 1) {
opts = "validateCode39CheckSum";
opts = "validateOptionalChecksum";
}
if (zxingcpp_cmp > 1 && symbol->eci == 0) {
@@ -4283,8 +4300,8 @@ int testUtilZXingCPPCmp(struct zint_symbol *symbol, char *msg, char *cmp_buf, in
const int is_extra_escaped = (symbol->input_mode & EXTRA_ESCAPE_MODE) && symbol->symbology == BARCODE_CODE128;
const int is_escaped = (symbol->input_mode & ESCAPE_MODE) || is_extra_escaped;
const int is_hibc = symbology >= BARCODE_HIBC_128 && symbology <= BARCODE_HIBC_AZTEC;
const int have_c25checkdigit = symbol->option_2 == 1 || symbol->option_2 == 2;
const int have_c25inter = (symbology == BARCODE_C25INTER && ((expected_len & 1) || have_c25checkdigit))
const int have_ccheckdigit = symbol->option_2 == 1 || symbol->option_2 == 2; /* Good for C25, CODE39, CODABAR */
const int have_c25inter = (symbology == BARCODE_C25INTER && ((expected_len & 1) || have_ccheckdigit))
|| symbology == BARCODE_ITF14 || symbology == BARCODE_DPLEIT
|| symbology == BARCODE_DPIDENT;
const int is_upcean = (ZBarcode_Cap(symbology, ZINT_CAP_EANUPC) & ZINT_CAP_EANUPC) == ZINT_CAP_EANUPC;
@@ -4362,7 +4379,7 @@ int testUtilZXingCPPCmp(struct zint_symbol *symbol, char *msg, char *cmp_buf, in
expected = escaped;
}
if (gs1 && symbology != BARCODE_EAN14 && symbology != BARCODE_NVE18) {
ret = zint_gs1_verify(symbol, ZUCP(expected), expected_len, ZUCP(reduced), &expected_len);
ret = zint_gs1_verify(symbol, ZUCP(expected), expected_len, ZUCP(reduced), &expected_len, 0 /*set_hrt*/);
if (ret >= ZINT_ERROR) {
sprintf(msg, "zint_gs1_verify %d != 0", ret);
return 3;
@@ -4441,7 +4458,7 @@ int testUtilZXingCPPCmp(struct zint_symbol *symbol, char *msg, char *cmp_buf, in
cmp_len -= 2;
expected++;
expected_len -= 2;
if (symbol->option_2 == 1 || symbol->option_2 == 2) {
if (have_ccheckdigit) {
cmp_len--; /* Too messy to calc the check digit so ignore */
}
} else if (is_vin_international) {
@@ -4451,15 +4468,15 @@ int testUtilZXingCPPCmp(struct zint_symbol *symbol, char *msg, char *cmp_buf, in
expected = vin;
} else if (have_c25inter) {
if (symbology == BARCODE_C25INTER) {
if ((expected_len & 1) || have_c25checkdigit) {
if (((expected_len & 1) && !have_c25checkdigit) || (!(expected_len & 1) && have_c25checkdigit)) {
if ((expected_len & 1) || have_ccheckdigit) {
if (((expected_len & 1) && !have_ccheckdigit) || (!(expected_len & 1) && have_ccheckdigit)) {
c25inter[0] = '0';
memcpy(c25inter + 1, expected, expected_len);
expected_len++;
} else {
memcpy(c25inter, expected, expected_len);
}
if (have_c25checkdigit) {
if (have_ccheckdigit) {
c25inter[expected_len] = zint_gs1_check_digit((const unsigned char *) c25inter, expected_len);
expected_len++;
}
@@ -4652,6 +4669,9 @@ int testUtilZXingCPPCmp(struct zint_symbol *symbol, char *msg, char *cmp_buf, in
expected = azrune;
expected_len = 3;
}
} else if ((symbology == BARCODE_CODE39 || symbology == BARCODE_EXCODE39 || symbology == BARCODE_LOGMARS)
&& have_ccheckdigit) {
cmp_len--; /* Too tedious to calculate so ignore */
}
if (ret_buf) {
+12 -1
View File
@@ -1,6 +1,6 @@
/*
libzint - the open source barcode library
Copyright (C) 2019-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2019-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -49,6 +49,17 @@ extern "C" {
#define ZINT_DEBUG_TEST_ZXINGCPP 512
#define ZINT_DEBUG_TEST_BWIPP_ZXINGCPP 1024
#ifdef ZINT_SANITIZEM /* Suppress clang -fsanitize=memory false positives */
#define ZINT_TESTUTIL_SANITIZEM_INIT = {0}
#define ZINT_TESTUTIL_SANITIZEM_INIT_2D = {{0}}
#else
#define ZINT_TESTUTIL_SANITIZEM_INIT
#define ZINT_TESTUTIL_SANITIZEM_INIT_2D
#endif
/* Hack `option_3` flag to indicate GS1 Syntax Engine caret input */
#define ZINT_TESTUTIL_GS1CARET (1 << 24)
#include <errno.h>
#include <stdio.h>
#include "../common.h"
Binary file not shown.
+238 -6
View File
@@ -2,7 +2,7 @@
/* Generate GS1 verify include "backend/gs1_lint.h" for "backend/gs1.c" */
/*
libzint - the open source barcode library
Copyright (C) 2021-2025 <rstuart114@gmail.com>
Copyright (C) 2021-2026 <rstuart114@gmail.com>
*/
/* SPDX-License-Identifier: BSD-3-Clause */
@@ -76,8 +76,8 @@ foreach ($lines as $line) {
}
if (($hyphen = strpos($ai, '-')) !== false) {
if ($fixed !== '') {
$fixed_ais[substr($ai, 0, 2)] = true;
if ($fixed) {
$fixed_ais[substr($ai, 0, 2)] = strlen(substr($ai, 0, $hyphen));
}
$ai_s = (int) substr($ai, 0, $hyphen);
$ai_e = (int) substr($ai, $hyphen + 1);
@@ -98,8 +98,8 @@ foreach ($lines as $line) {
}
}
} else {
if ($fixed !== '') {
$fixed_ais[substr($ai, 0, 2)] = true;
if ($fixed) {
$fixed_ais[substr($ai, 0, 2)] = strlen($ai);
}
$ai = (int) $ai;
$ais[] = $ai;
@@ -243,7 +243,7 @@ if ($print_copyright) {
print <<<'EOD'
/*
libzint - the open source barcode library
Copyright (C) 2021-2025 Robin Stuart <rstuart114@gmail.com>
Copyright (C) 2021-2026 Robin Stuart <rstuart114@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -365,6 +365,7 @@ $tab$tab{$tab}char err_msg[50]) {
$tab/* Assume data length failure */
$tab*p_err_no = 2;
$tab*p_err_posn = 1;
EOD;
@@ -477,6 +478,237 @@ $tab}
EOD;
print <<<EOD
/* Helper to parse non-bracketed input */
static int gs1_lint_parse_ai(const unsigned char source[], const int length, const int position,
$tab$tab{$tab}int *p_ai, int *p_min, int *p_max) {
${tab}int ai, ai2, ai3 = -1, ai4 = -1, min, max = 0;
{$tab}*p_ai = -1;
{$tab}if (position + 1 >= length || !z_isdigit(source[position]) || !z_isdigit(source[position + 1])) {
$tab{$tab}return 0;
{$tab}}
{$tab}ai2 = z_to_int(source + position, 2);
{$tab}if (position + 3 < length && z_isdigit(source[position + 2])) {
$tab{$tab}ai3 = z_to_int(source + position, 3);
$tab{$tab}if (position + 4 < length && z_isdigit(source[position + 3])) {
$tab$tab{$tab}ai4 = z_to_int(source + position, 4);
$tab{$tab}}
{$tab}}
EOD;
$not_first_batch = false;
$last_batch_e = -1;
$big_batch = 2;
foreach ($batches as $batch => $batch_specs) {
if (empty($batch_specs)) {
continue;
}
$batch_s = $batch * 100;
$batch_e = $batch_s + 100;
$n = $batch_s < 100 ? 2 : ($batch_s < 1000 ? 3 : 4);
$first_tab = $n == 2 ? "" : $tab . $tab;
if ($n == 2) {
print "\n{$first_tab}{$tab}ai = ai2;\n";
} else {
if ($n != $big_batch) {
if ($n == 3) {
print "\n{$tab}if (max == 0 && ai3 != -1) {\n";
} else {
print "{$first_tab}}\n\n{$tab}}\n\n{$tab}if (max == 0 && ai4 != -1) {\n";
}
$not_first_batch = false;
}
if ($not_first_batch) {
print "\n$first_tab} else if (ai" . $n. " < $batch_e) {\n{$first_tab}{$tab}ai = ai" . $n . ";\n";
} else {
print "\n{$first_tab}if (ai" . $n . " < $batch_e) {\n{$first_tab}{$tab}ai = ai" . $n . ";\n";
$not_first_batch = true;
}
}
$not_first_spec = false;
foreach ($batch_specs as $spec) {
$total_min = $spec_ais[$spec][0];
$total_max = $spec_ais[$spec][1];
$ais = $spec_ais[$spec][2];
if ($not_first_spec) {
$str = "$first_tab{$tab}} else if (";
} else {
$str = "$first_tab{$tab}if (";
$not_first_spec = true;
}
print $str;
$width = strlen($str);
// Count the applicable AIs
$ais_cnt = 0;
foreach ($ais as $ai) {
if (is_array($ai)) {
if ($ai[1] < $batch_s || $ai[0] >= $batch_e) {
continue;
}
} else {
if ($ai < $batch_s || $ai >= $batch_e) {
continue;
}
}
$ais_cnt++;
}
// Output
$not_first_ai = false;
foreach ($ais as $ai) {
if (is_array($ai)) {
if ($ai[1] < $batch_s || $ai[0] >= $batch_e) {
continue;
}
} else {
if ($ai < $batch_s || $ai >= $batch_e) {
continue;
}
}
$str = '';
if ($not_first_ai) {
$str .= " || ";
} else {
$not_first_ai = true;
}
if (is_array($ai)) {
if ($ai[0] === $last_batch_e) { // Don't need 1st element of range if excluded by previous batch
$str .= "ai <= " . $ai[1];
} else if ($ai[1] + 1 == $batch_e) { // Don't need 2nd element of range if excluded by this batch
$str .= "ai >= " . $ai[0];
} else {
if ($ais_cnt > 1) {
$str .= "(ai >= " . $ai[0] . " && ai <= " . $ai[1] . ")";
} else {
$str .= "ai >= " . $ai[0] . " && ai <= " . $ai[1];
}
}
} else {
$str .= "ai == " . $ai;
}
if ($width + strlen($str) > 118) {
print "\n";
$str2 = "$first_tab$tab$tab ";
print $str2;
$width = strlen($str2);
}
print $str;
$width += strlen($str);
}
list($min, $max) = $spec_ais[$spec];
$str = "$first_tab$tab{$tab}min = $min, max = $max;";
print ") {\n$str\n";
print <<<EOD
EOD;
}
$last_batch_e = $batch_e;
if ($big_batch != $n) {
$big_batch = $n;
}
print <<<EOD
$first_tab{$tab}}
EOD;
}
print <<<EOD
$first_tab}
$tab}
{$tab}if (max == 0) {
$tab{$tab}*p_ai = ai2; /* Use 2-digit as feedback */
$tab{$tab}return 0;
$tab}
{$tab}assert(ai >= 0 && ai <= 9999);
{$tab}assert(min >= 1);
{$tab}assert(max >= min);
{$tab}*p_ai = ai;
{$tab}if (p_min) {
$tab{$tab}*p_min = min;
{$tab}}
{$tab}if (p_max) {
$tab{$tab}*p_max = max;
{$tab}}
{$tab}return 1;
}
/* Parse non-bracketed input */
static int gs1_lint_parse_raw_caret(const unsigned char source[], const int length,
$tab$tab{$tab}const int ai_max, int *ai_vals, int *ai_locs, int *data_locs, int *data_lens, int *p_ai_count,
$tab$tab{$tab}int *p_err_no, int *p_err_posn) {
{$tab}int i, j;
{$tab}const int gs1_caret = source[0] == '^';
{$tab}const unsigned char separator = gs1_caret ? '^' : '\\x1D';
{$tab}int ai, max;
{$tab}int ai_count = 0, ai_len;
{$tab}#ifdef NDEBUG
{$tab}(void)ai_max;
{$tab}#endif
{$tab}i = gs1_caret || source[0] == '\\x1D'; /* Allow GS at start also */
{$tab}if (i >= length) {
$tab{$tab}*p_ai_count = 0; /* For feedback */
$tab{$tab}ai_vals[0] = -1;
$tab{$tab}ai_locs[0] = i - 1;
$tab{$tab}*p_err_no = 1;
$tab{$tab}*p_err_posn = i;
$tab{$tab}return 0;
{$tab}}
{$tab}while (i < length) {
$tab{$tab}int data_start, data_max, on_separator;
$tab{$tab}assert(ai_count < ai_max);
$tab{$tab}ai_locs[ai_count] = i;
$tab{$tab}if (!gs1_lint_parse_ai(source, length, i, &ai, NULL /*min*/, &max)) {
$tab$tab{$tab}*p_ai_count = ai_count; /* For feedback */
$tab$tab{$tab}ai_vals[ai_count] = ai; /* May be -1 */
$tab$tab{$tab}*p_err_no = 1;
$tab$tab{$tab}*p_err_posn = i + 1; /* Position 1-base */
$tab$tab{$tab}return 0;
$tab{$tab}}
$tab{$tab}ai_vals[ai_count] = ai;
$tab{$tab}ai_len = ai < 100 ? 2 : ai < 1000 ? 3 : 4;
$tab{$tab}/* Following GS1 Syntax Engine tolerating superfluous FNC1s at end of AI data
$tab{$tab} (for both final AI and AIs with predefined length) */
$tab{$tab}data_start = i + ai_len;
$tab{$tab}data_max = data_start + max;
$tab{$tab}for (j = data_start; j < length && j < data_max; j++) {
$tab$tab{$tab}if (source[j] == separator) {
$tab$tab$tab{$tab}break;
$tab$tab{$tab}}
$tab{$tab}}
$tab{$tab}data_locs[ai_count] = data_start;
$tab{$tab}/* Only checking that have at least one char, and haven't exceeded max */
$tab{$tab}on_separator = j < length && source[j] == separator;
$tab{$tab}if (j == data_start || (j + 1 == length && length > data_max && !on_separator)) {
$tab$tab{$tab}*p_ai_count = ai_count; /* For feedback */
$tab$tab{$tab}data_lens[ai_count] = j - data_start;
$tab$tab{$tab}*p_err_no = 2;
$tab$tab{$tab}*p_err_posn = i + 1; /* Position 1-base */
$tab$tab{$tab}return 0;
$tab{$tab}}
$tab{$tab}data_lens[ai_count] = j - data_locs[ai_count];
$tab{$tab}ai_count++;
$tab{$tab}i = j + on_separator;
{$tab}}
{$tab}*p_ai_count = ai_count;
{$tab}return 1;
}
EOD;
if ($print_h_guard) {
print <<<'EOD'
+1
View File
@@ -326,6 +326,7 @@ extern "C" {
#define EXTRA_ESCAPE_MODE 0x0100 /* Process special symbology-specific escape sequences as well as others */
/* Note: currently Code 128 only */
#define GS1SYNTAXENGINE_MODE 0x0200 /* Use the GS1 Syntax Engine (if available) to strictly validate GS1 input */
#define GS1RAW_MODE 0x0400 /* Process GS1 data literally (no AI delimiters), parsing GSs as FNC1s */
/* Aztec Code specific options (`symbol->option_3`) */
#define ZINT_AZTEC_FULL 128 /* Only consider Full versions on automatic symbol size selection */