- CODABLOCKF: fix misencodation of extended ASCII 0xB0-0xB9 when

followed by digit (ignore 2nd byte of FNC4 when categorizing
  Code C characters)
- New `ZBarcode_Cap()` flag `ZINT_CAP_BINDABLE`, differentiated
  from `ZINT_CAP_STACKABLE`, and new Qt Backend method
  `isBindable()`
- CLI: fix `separator` check to use new `ZINT_CAP_BINDABLE` instead
  of `ZINT_CAP_STACKABLE`
- ZBarcode_Cap: add missing symbologies to `ZINT_CAP_BINDABLE` (was
  `ZINT_CAP_STACKABLE`)
- DOTCODE: pad rows if given number of columns instead of failing
  if rows below min (5)
- DBAR/composites: ensure stacked symbologies and composites are
  not stacked (set `symbol->rows` to 0)
- test suite: move `test_perf` routines into single test
  "test_perf";
  new "test_random" (based on "test_bwipp") to test various
  symbologies with random binary - discovered CODABLOCKF bug;
  expand "test_bwipp"
manual: Feeback: mention AZTEC -1 meaning min & MICROPDF417:
  doc new `ZINT_CAP_BINDABLE`
general: various code fiddlings and re-formattings
This commit is contained in:
gitlost 2025-04-03 16:08:15 +01:00
parent 2370fbfbb7
commit a74871a7de
60 changed files with 3509 additions and 3267 deletions

View file

@ -72,10 +72,10 @@ typedef struct sCharacterSetTable {
* The result is an OR of CodeA, CodeB, CodeC, CodeFNC1, CodeFNC4 depending on the
* possible Code 128 character sets.
*/
static int GetPossibleCharacterSet(unsigned char C) {
static int GetPossibleCharacterSet(const unsigned char C, const int prevNotFNC4) {
if (C <= '\x1f') /* Control chars */
return CodeA;
if (z_isdigit(C))
if (z_isdigit(C) && prevNotFNC4)
return ZTNum; /* ZTNum = CodeA | CodeB | CodeC */
if (C == aFNC1) /* FNC1s (GS1) not used */
return ZTFNC1; /* ZTFNC1 = CodeA | CodeB | CodeC | CodeFNC1 */ /* Not reached */
@ -93,37 +93,37 @@ static int GetPossibleCharacterSet(unsigned char C) {
* int AFollowing, BFollowing The number of source characters you still may encode in this character set.
* int CFollowing The number of characters encodable in CodeC if we start here.
*/
static void CreateCharacterSetTable(CharacterSetTable T[], unsigned char *data, const int dataLength) {
static void CreateCharacterSetTable(CharacterSetTable T[], const unsigned char *data, const int dataLength) {
int charCur;
int runChar;
int prevNotFNC4;
/* Treat the Data backwards */
charCur = dataLength - 1;
T[charCur].CharacterSet = GetPossibleCharacterSet(data[charCur]);
T[charCur].AFollowing = ((T[charCur].CharacterSet & CodeA) == 0) ? 0 : 1;
T[charCur].BFollowing = ((T[charCur].CharacterSet & CodeB) == 0) ? 0 : 1;
T[charCur].CFollowing = 0;
prevNotFNC4 = charCur > 0 ? data[charCur - 1] != aFNC4 : 1;
T[charCur].CharacterSet = GetPossibleCharacterSet(data[charCur], prevNotFNC4);
T[charCur].AFollowing = T[charCur].CharacterSet & CodeA ? 1 : 0;
T[charCur].BFollowing = T[charCur].CharacterSet & CodeB ? 1 : 0;
for (charCur--; charCur >= 0; charCur--) {
T[charCur].CharacterSet = GetPossibleCharacterSet(data[charCur]);
T[charCur].AFollowing = ((T[charCur].CharacterSet & CodeA) == 0) ? 0 : T[charCur + 1].AFollowing + 1;
T[charCur].BFollowing = ((T[charCur].CharacterSet & CodeB) == 0) ? 0 : T[charCur + 1].BFollowing + 1;
T[charCur].CFollowing = 0;
prevNotFNC4 = charCur > 0 ? data[charCur - 1] != aFNC4 : 1;
T[charCur].CharacterSet = GetPossibleCharacterSet(data[charCur], prevNotFNC4);
T[charCur].AFollowing = T[charCur].CharacterSet & CodeA ? T[charCur + 1].AFollowing + 1 : 0;
T[charCur].BFollowing = T[charCur].CharacterSet & CodeB ? T[charCur + 1].BFollowing + 1 : 0;
}
/* Find the CodeC-chains */
for (charCur = 0; charCur < dataLength; charCur++) {
T[charCur].CFollowing = 0;
if ((T[charCur].CharacterSet & CodeC) != 0) {
if (T[charCur].CharacterSet & CodeC) {
/* CodeC possible */
runChar = charCur;
do {
/* Whether this is FNC1, whether next is */
/* numeric */
if (T[runChar].CharacterSet == ZTFNC1) /* FNC1s (GS1) not used */
++(T[charCur].CFollowing); /* Not reached */
T[charCur].CFollowing++; /* Not reached */
else {
++runChar;
if (runChar >= dataLength)
if (++runChar >= dataLength)
break;
/* Only a Number may follow */
if (T[runChar].CharacterSet == ZTNum)
@ -141,11 +141,9 @@ static void CreateCharacterSetTable(CharacterSetTable T[], unsigned char *data,
* one bundle into the line (up to here). This is calculated online because
* it depends on the space in the line.
*/
static int RemainingDigits(CharacterSetTable *T, int charCur, int emptyColumns) {
int digitCount; /* Numerical digits fitting in the line */
int runChar;
runChar = charCur;
digitCount = 0;
static int RemainingDigits(const CharacterSetTable T[], const int charCur, int emptyColumns) {
int digitCount = 0; /* Numerical digits fitting in the line */
int runChar = charCur;
while (emptyColumns > 0 && runChar < charCur + T[charCur].CFollowing) {
if (T[runChar].CharacterSet != ZTFNC1) {
/* NOT FNC1 */
@ -168,8 +166,8 @@ static int RemainingDigits(CharacterSetTable *T, int charCur, int emptyColumns)
* pSet Output of the character sets used, allocated by me.
* Return value Resulting row count
*/
static int Columns2Rows(struct zint_symbol *symbol, CharacterSetTable *T, const int dataLength, int *pRows,
int *pUseColumns, int *pSet, int *pFillings) {
static int Columns2Rows(const CharacterSetTable T[], const int dataLength, int *pRows, int *pUseColumns, int *pSet,
int *pFillings, const int debug) {
int useColumns; /* Usable Characters per line */
int fillings = 0; /* Number of filling characters */
int rowsCur;
@ -191,7 +189,7 @@ static int Columns2Rows(struct zint_symbol *symbol, CharacterSetTable *T, const
/* >>> Line Loop */
do {
/* >> Start Character */
emptyColumns = useColumns; /* Remained place in Line */
emptyColumns = useColumns; /* Remaining space in line */
/* >>Choose in Set A or B */
/* (C is changed as an option later on) */
@ -226,7 +224,7 @@ static int Columns2Rows(struct zint_symbol *symbol, CharacterSetTable *T, const
/* >> Following characters */
while (emptyColumns > 0 && charCur < dataLength) {
isFNC4 = (T[charCur].CharacterSet & CodeFNC4);
isFNC4 = T[charCur].CharacterSet & CodeFNC4;
switch (characterSetCur) {
case CodeA:
case CodeB:
@ -342,13 +340,18 @@ static int Columns2Rows(struct zint_symbol *symbol, CharacterSetTable *T, const
switch (emptyColumns) {
case 1:
pSet[charCur - 1] |= CFill;
/* fall through */
/* [[fallthrough]] */
case 0:
++rowsCur;
fillings = useColumns - 2 + emptyColumns;
break;
case 2: fillings = 0; break;
default: pSet[charCur - 1] |= CFill; fillings = emptyColumns - 2;
case 2:
fillings = 0;
break;
default:
pSet[charCur - 1] |= CFill;
fillings = emptyColumns - 2;
break;
}
if (rowsCur > 44) {
@ -361,7 +364,7 @@ static int Columns2Rows(struct zint_symbol *symbol, CharacterSetTable *T, const
fillings += useColumns;
}
} while (rowsCur > 44);
if (symbol->debug & ZINT_DEBUG_PRINT) {
if (debug) {
printf(" -> out: rowsCur <%d>, useColumns <%d>, fillings <%d>\n", rowsCur, useColumns, fillings);
}
*pUseColumns = useColumns;
@ -372,8 +375,8 @@ static int Columns2Rows(struct zint_symbol *symbol, CharacterSetTable *T, const
/* Find columns if row count is given.
*/
static int Rows2Columns(struct zint_symbol *symbol, CharacterSetTable *T, const int dataLength, int *pRows,
int *pUseColumns, int *pSet, int *pFillings) {
static int Rows2Columns(const CharacterSetTable T[], const int dataLength, int *pRows, int *pUseColumns, int *pSet,
int *pFillings, const int debug) {
int rowsCur;
int rowsRequested; /* Number of requested rows */
int columnsRequested; /* Number of requested columns (if any) */
@ -387,7 +390,7 @@ static int Rows2Columns(struct zint_symbol *symbol, CharacterSetTable *T, const
rowsRequested = *pRows;
columnsRequested = *pUseColumns >= 4 ? *pUseColumns : 0;
if (symbol->debug & ZINT_DEBUG_PRINT) {
if (debug) {
printf("Optimizer : Searching <%d> rows\n", rowsRequested);
}
@ -407,7 +410,7 @@ static int Rows2Columns(struct zint_symbol *symbol, CharacterSetTable *T, const
pTestList[testListSize] = testColumns;
testListSize++;
useColumns = testColumns; /* Make a copy because it may be modified */
errorCur = Columns2Rows(symbol, T, dataLength, &rowsCur, &useColumns, pSet, &fillings);
errorCur = Columns2Rows(T, dataLength, &rowsCur, &useColumns, pSet, &fillings, debug);
if (errorCur != 0)
return errorCur;
if (rowsCur <= rowsRequested) {
@ -448,7 +451,7 @@ static int Rows2Columns(struct zint_symbol *symbol, CharacterSetTable *T, const
/* Print a character in character set A
*/
static void A2C128_A(uchar **ppOutPos, uchar c) {
static void A2C128_A(uchar **ppOutPos, const uchar c) {
uchar *pOutPos = *ppOutPos;
switch (c) {
case aCodeB: *pOutPos = 100; break;
@ -471,7 +474,7 @@ static void A2C128_A(uchar **ppOutPos, uchar c) {
/* Output c in Set B
*/
static void A2C128_B(uchar **ppOutPos, uchar c) {
static void A2C128_B(uchar **ppOutPos, const uchar c) {
uchar *pOutPos = *ppOutPos;
switch (c) {
case aFNC1: *pOutPos = 102; break; /* FNC1s (GS1) not used */ /* Not reached */
@ -483,12 +486,12 @@ static void A2C128_B(uchar **ppOutPos, uchar c) {
case aShift: *pOutPos = 98; break;
default: *pOutPos = (uchar) (c - ' '); break;
}
++(*ppOutPos);
(*ppOutPos)++;
}
/* Output c1, c2 in Set C
*/
static void A2C128_C(uchar **ppOutPos, uchar c1, uchar c2) {
static void A2C128_C(uchar **ppOutPos, const uchar c1, const uchar c2) {
uchar *pOutPos = *ppOutPos;
switch (c1) {
case aFNC1: *pOutPos = 102; break; /* FNC1s (GS1) not used */ /* Not reached */
@ -501,7 +504,7 @@ static void A2C128_C(uchar **ppOutPos, uchar c1, uchar c2) {
/* Output a character in Characterset
*/
static void ASCIIZ128(uchar **ppOutPos, int CharacterSet, uchar c1, uchar c2) {
static void ASCIIZ128(uchar **ppOutPos, const int CharacterSet, const uchar c1, const uchar c2) {
if (CharacterSet == CodeA)
A2C128_A(ppOutPos, c1);
else if (CharacterSet == CodeB)
@ -512,7 +515,7 @@ static void ASCIIZ128(uchar **ppOutPos, int CharacterSet, uchar c1, uchar c2) {
/* XLate Tables D.2, D.3 and F.1 of Codablock-F Specification and call output
*/
static void SumASCII(uchar **ppOutPos, int Sum, int CharacterSet) {
static void SumASCII(uchar **ppOutPos, const int Sum, const int CharacterSet) {
switch (CharacterSet) {
case CodeA: /* Row # Indicators and Data Check Characters K1/K2 for CodeA and CodeB are the same */
case CodeB:
@ -548,6 +551,7 @@ INTERNAL int codablockf(struct zint_symbol *symbol, unsigned char source[], int
int *pSet;
uchar *pOutput;
const int raw_text = symbol->output_options & BARCODE_RAW_TEXT;
const int debug = symbol->debug & ZINT_DEBUG_PRINT;
/* Suppresses clang-analyzer-core.VLASize warning */
assert(length > 0);
@ -595,18 +599,15 @@ INTERNAL int codablockf(struct zint_symbol *symbol, unsigned char source[], int
dataLength = 0;
if (symbol->output_options & READER_INIT) {
data[dataLength] = aFNC3;
dataLength++;
data[dataLength++] = aFNC3;
}
/* Replace all Codes>127 with <fnc4>Code-128 */
for (charCur = 0; charCur < length; charCur++) {
if (source[charCur] > 127) {
data[dataLength] = aFNC4;
dataLength++;
data[dataLength] = (unsigned char) (source[charCur] & 127);
data[dataLength++] = aFNC4;
data[dataLength++] = (unsigned char) (source[charCur] & 127);
} else
data[dataLength] = source[charCur];
dataLength++;
data[dataLength++] = source[charCur];
}
/* Build character set table */
@ -625,7 +626,7 @@ INTERNAL int codablockf(struct zint_symbol *symbol, unsigned char source[], int
} else if (columns < 9) {
columns = 9;
}
if (symbol->debug & ZINT_DEBUG_PRINT) {
if (debug) {
printf("Auto column count for %d characters:%d\n", dataLength, columns);
}
}
@ -633,10 +634,10 @@ INTERNAL int codablockf(struct zint_symbol *symbol, unsigned char source[], int
useColumns = columns - 5;
if (rows > 0) {
/* Row count given */
error_number = Rows2Columns(symbol, T, dataLength, &rows, &useColumns, pSet, &fillings);
error_number = Rows2Columns(T, dataLength, &rows, &useColumns, pSet, &fillings, debug);
} else {
/* Column count given */
error_number = Columns2Rows(symbol, T, dataLength, &rows, &useColumns, pSet, &fillings);
error_number = Columns2Rows(T, dataLength, &rows, &useColumns, pSet, &fillings, debug);
}
if (error_number != 0) {
return errtxt(error_number, symbol, 413,
@ -652,7 +653,7 @@ INTERNAL int codablockf(struct zint_symbol *symbol, unsigned char source[], int
Sum2 = (Sum2 + charCur * source[charCur]) % 86;
}
if (symbol->debug & ZINT_DEBUG_PRINT) {
if (debug) {
int DPos;
fputs("\nData:", stdout);
for (DPos = 0; DPos < dataLength; DPos++)
@ -745,24 +746,24 @@ INTERNAL int codablockf(struct zint_symbol *symbol, unsigned char source[], int
while (emptyColumns > 0 && charCur < dataLength) {
/* ? Change character set */
if (emptyColumns < useColumns) {
if ((pSet[charCur] & CodeA) != 0) {
if (pSet[charCur] & CodeA) {
/* Change to A */
ASCIIZ128(&pOutPos, characterSetCur, aCodeA, '\0');
--emptyColumns;
characterSetCur = CodeA;
} else if ((pSet[charCur] & CodeB) != 0) {
} else if (pSet[charCur] & CodeB) {
/* Change to B */
ASCIIZ128(&pOutPos, characterSetCur, aCodeB, '\0');
--emptyColumns;
characterSetCur = CodeB;
} else if ((pSet[charCur] & CodeC) != 0) {
} else if (pSet[charCur] & CodeC) {
/* Change to C */
ASCIIZ128(&pOutPos, characterSetCur, aCodeC, '\0');
--emptyColumns;
characterSetCur = CodeC;
}
}
if ((pSet[charCur] & CShift) != 0) {
if (pSet[charCur] & CShift) {
/* >> Shift it and put out the shifted character */
ASCIIZ128(&pOutPos, characterSetCur, aShift, '\0');
emptyColumns -= 2;
@ -832,7 +833,7 @@ INTERNAL int codablockf(struct zint_symbol *symbol, unsigned char source[], int
*pOutPos++ = 106;
} /* End Lineloop */
if (symbol->debug & ZINT_DEBUG_PRINT) {
if (debug) {
int DPos, DPos2;
fputs("\nCode 128 Code Numbers:\n", stdout);
for (DPos = 0; DPos < rows; DPos++) {