mirror of
https://github.com/zint/zint
synced 2024-11-16 20:57:25 +13:00
5e2044ff2e
encodation in certain cases (and no pessimizations found so far), props lyngklip (BWIPP); fix extended char latching when exactly 3 extended chars at end; count code set C (not digits) in loop deciding when to shift/latch to extended for better estimate AZTEC: return warning if ECC < 5% (due to bit-stuffing when version given); return error if > 22 layers (Zint 26) for Reader Initialisation symbol requested for better error message AZTEC/HANXIN/QRCODE: consolidate different ECC data size tables into one indexed by ECC DBAR_EXP: check for reduced length <= 77 up front for better error message HANXIN: use `malloc()` rather than `z_alloca()` for large binary array QRCODE: `ecc_level` now 0-based (not 1-based) MICROQR: consolidate different version end routines into one `microqr_end()` and use new `microqr_data` table to simplify code MICROPDF417: use table for max codewords per column library: centralize all error messages using new `errtxt()`, `errtxtf()`, `errtxt_adj()` funcs that protect `symbol->errtxt` from overflow, & try to make error messages more consistent thru-out, adding more feedback info to many, & use positional args "%n$" in prep for l10n (maybe); `is_sane/is_sane_lookup()` -> `not_sane/not_sane_lookup()`, returning 1-based position (zero on failure) instead of bool; `long` ints -> plain `int` (except those dealing with `ftell()`, `fread()` etc) as depend on int being 32-bits already GUI: in "grpDATF.ui" use "PlainText" rather than "RichText" for tracker ratio examples as height of text messing up sometimes manual: clarify Codablock-F length maximum & add examples docs: README: pandoc 3.5, Ubuntu 24.04 CMake: use "-Wpedantic" for Clang only as GNU complains about `errtxtf()` positional args "%n$"
892 lines
35 KiB
C
892 lines
35 KiB
C
/* codablock.c - Handles Codablock-F */
|
|
/*
|
|
libzint - the open source barcode library
|
|
Copyright (C) 2016-2024 Harald Oehlmann
|
|
|
|
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 */
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include "common.h"
|
|
#include "code128.h"
|
|
|
|
#define uchar unsigned char
|
|
|
|
/* FTab C128 flags - may be added */
|
|
#define CodeA 1
|
|
#define CodeB 2
|
|
#define CodeC 4
|
|
#define CEnd 8
|
|
#define CShift 16
|
|
#define CFill 32
|
|
#define CodeFNC1 64
|
|
#define CodeFNC4 128
|
|
#define ZTNum (CodeA + CodeB + CodeC)
|
|
#define ZTFNC1 (CodeA + CodeB + CodeC + CodeFNC1)
|
|
|
|
/* ASCII-Extension for Codablock-F */
|
|
#define aFNC1 ((uchar) 128)
|
|
#define aFNC2 ((uchar) 129)
|
|
#define aFNC3 ((uchar) 130)
|
|
#define aFNC4 ((uchar) 131)
|
|
#define aCodeA ((uchar) 132)
|
|
#define aCodeB ((uchar) 133)
|
|
#define aCodeC ((uchar) 134)
|
|
#define aShift ((uchar) 135)
|
|
|
|
/* Code F Analysing-Chart */
|
|
typedef struct sCharacterSetTable {
|
|
int CharacterSet; /* Still possible character sets for actual*/
|
|
int AFollowing; /* Still following Characters in Charset A */
|
|
int BFollowing; /* Still following Characters in Charset B */
|
|
int CFollowing; /* Still following Characters in Charset C */
|
|
} CharacterSetTable;
|
|
|
|
/* Find the possible Code-128 Character sets for a character
|
|
* 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) {
|
|
if (C <= '\x1f') /* Control chars */
|
|
return CodeA;
|
|
if (z_isdigit(C))
|
|
return ZTNum; /* ZTNum=CodeA+CodeB+CodeC */
|
|
if (C == aFNC1) /* FNC1s (GS1) not used */
|
|
return ZTFNC1; /* ZTFNC1=CodeA+CodeB+CodeC+CodeFNC1 */ /* Not reached */
|
|
if (C == aFNC4)
|
|
return (CodeA | CodeB | CodeFNC4);
|
|
if (C >= '\x60' && C <= '\x7f') /* 60 to 127 */
|
|
return CodeB;
|
|
return CodeA + CodeB;
|
|
}
|
|
|
|
/* Create a Table with the following information for each Data character:
|
|
* int CharacterSet is an or of CodeA, CodeB, CodeC, CodeFNC1, CodeFNC4,
|
|
* depending on which character set is applicable.
|
|
* (Result of GetPossibleCharacterSet)
|
|
* 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) {
|
|
int charCur;
|
|
int runChar;
|
|
|
|
/* 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;
|
|
|
|
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;
|
|
}
|
|
/* Find the CodeC-chains */
|
|
for (charCur = 0; charCur < dataLength; charCur++) {
|
|
T[charCur].CFollowing = 0;
|
|
if ((T[charCur].CharacterSet & CodeC) != 0) {
|
|
/* 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 */
|
|
else {
|
|
++runChar;
|
|
if (runChar >= dataLength)
|
|
break;
|
|
/* Only a Number may follow */
|
|
if (T[runChar].CharacterSet == ZTNum)
|
|
T[charCur].CFollowing += 2;
|
|
else
|
|
break;
|
|
}
|
|
++runChar;
|
|
} while (runChar < dataLength);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Find the amount of numerical characters in pairs which will fit in
|
|
* 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;
|
|
while (emptyColumns > 0 && runChar < charCur + T[charCur].CFollowing) {
|
|
if (T[runChar].CharacterSet != ZTFNC1) {
|
|
/* NOT FNC1 */
|
|
digitCount += 2;
|
|
runChar++;
|
|
}
|
|
runChar++;
|
|
emptyColumns--;
|
|
}
|
|
return digitCount;
|
|
}
|
|
|
|
/* Find the Character distribution at a given column count.
|
|
* If too many rows (>44) are requested the columns are extended.
|
|
* Parameters :
|
|
* T Pointer on the Characters which fit in the row
|
|
* If a different count is calculated it is corrected
|
|
* in the callers workspace.
|
|
* pFillings Output of filling characters
|
|
* 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) {
|
|
int useColumns; /* Usable Characters per line */
|
|
int fillings = 0; /* Number of filling characters */
|
|
int rowsCur;
|
|
int runChar;
|
|
int emptyColumns; /* Number of codes still empty in line. */
|
|
int emptyColumns2; /* Alternative emptyColumns to compare */
|
|
int CPaires; /* Number of digit pairs which may fit in the line */
|
|
int characterSetCur; /* Current Character Set */
|
|
int isFNC4; /* Set if current character FNC4 */
|
|
|
|
useColumns = *pUseColumns;
|
|
|
|
/* >>> Loop until rowsCur <= 44 */
|
|
do {
|
|
int charCur = 0;
|
|
memset(pSet, 0, sizeof(int) * dataLength);
|
|
rowsCur = 0;
|
|
|
|
/* >>> Line Loop */
|
|
do {
|
|
/* >> Start Character */
|
|
emptyColumns = useColumns; /* Remained place in Line */
|
|
|
|
/* >>Choose in Set A or B */
|
|
/* (C is changed as an option later on) */
|
|
|
|
pSet[charCur] = characterSetCur = (T[charCur].AFollowing > T[charCur].BFollowing) ? CodeA : CodeB;
|
|
|
|
/* >> Test on Numeric Mode C */
|
|
CPaires = RemainingDigits(T, charCur, emptyColumns);
|
|
if (CPaires >= 4) {
|
|
/* 4 Digits in Numeric compression ->OK */
|
|
/* > May an odd start find more ? */
|
|
/* Skip leading <FNC1>'s */
|
|
/* Typical structure : <FNC1><FNC1>12... */
|
|
/* Test if numeric after one isn't better.*/
|
|
runChar = charCur;
|
|
emptyColumns2 = emptyColumns;
|
|
while (T[runChar].CharacterSet == ZTFNC1) { /* FNC1s (GS1) not used */
|
|
++runChar; /* Not reached */
|
|
--emptyColumns2;
|
|
}
|
|
if (CPaires >= RemainingDigits(T, runChar + 1, emptyColumns2 - 1)) {
|
|
/* Start odd is not better */
|
|
/* We start in C */
|
|
pSet[charCur] = characterSetCur = CodeC;
|
|
/* Increment charCur */
|
|
if (T[charCur].CharacterSet != ZTFNC1)
|
|
++charCur; /* 2 Num.Digits */
|
|
}
|
|
}
|
|
++charCur;
|
|
--emptyColumns;
|
|
|
|
/* >> Following characters */
|
|
while (emptyColumns > 0 && charCur < dataLength) {
|
|
isFNC4 = (T[charCur].CharacterSet & CodeFNC4);
|
|
switch (characterSetCur) {
|
|
case CodeA:
|
|
case CodeB:
|
|
/* >> Check switching to CodeC */
|
|
/* Switch if :
|
|
* - Character not FNC1
|
|
* - 4 real Digits will fit in line
|
|
* - an odd Start will not be better
|
|
*/
|
|
if (T[charCur].CharacterSet == ZTNum
|
|
&& (CPaires = RemainingDigits(T, charCur, emptyColumns - 1)) >= 4
|
|
&& CPaires > RemainingDigits(T, charCur + 1, emptyColumns - 2)) {
|
|
/* > Change to C */
|
|
pSet[charCur] = characterSetCur = CodeC;
|
|
charCur += 2; /* 2 Digit */
|
|
emptyColumns -= 2; /* <SwitchC>12 */
|
|
} else if (characterSetCur == CodeA) {
|
|
if (T[charCur].AFollowing == 0 || (isFNC4 && T[charCur].AFollowing == 1)) {
|
|
/* Must change to B */
|
|
if (emptyColumns == 1 || (isFNC4 && emptyColumns == 2)) {
|
|
/* Can't switch: */
|
|
pSet[charCur - 1] |= CEnd + CFill;
|
|
emptyColumns = 0;
|
|
} else {
|
|
/* <Shift> or <switchB>? */
|
|
if (T[charCur].BFollowing == 1 || (isFNC4 && T[charCur].BFollowing == 2)) {
|
|
/* Note using order "FNC4 shift char" (same as CODE128) not "shift FNC4 char"
|
|
as given in Table B.1 and Table B.2 */
|
|
if (isFNC4) { /* So skip FNC4 and shift value instead */
|
|
--emptyColumns;
|
|
++charCur;
|
|
}
|
|
pSet[charCur] |= CShift;
|
|
} else {
|
|
pSet[charCur] |= CodeB;
|
|
characterSetCur = CodeB;
|
|
}
|
|
emptyColumns -= 2;
|
|
++charCur;
|
|
}
|
|
} else if (isFNC4 && emptyColumns == 1) {
|
|
/* Can't fit extended ASCII on same line */
|
|
pSet[charCur - 1] |= CEnd + CFill;
|
|
emptyColumns = 0;
|
|
} else {
|
|
--emptyColumns;
|
|
++charCur;
|
|
}
|
|
} else { /* Last possibility : CodeB */
|
|
if (T[charCur].BFollowing == 0 || (isFNC4 && T[charCur].BFollowing == 1)) {
|
|
/* Must change to A */
|
|
if (emptyColumns == 1 || (isFNC4 && emptyColumns == 2)) {
|
|
/* Can't switch: */
|
|
pSet[charCur - 1] |= CEnd + CFill;
|
|
emptyColumns = 0;
|
|
} else {
|
|
/* <Shift> or <switchA>? */
|
|
if (T[charCur].AFollowing == 1 || (isFNC4 && T[charCur].AFollowing == 2)) {
|
|
/* Note using order "FNC4 shift char" (same as CODE128) not "shift FNC4 char"
|
|
as given in Table B.1 and Table B.2 */
|
|
if (isFNC4) { /* So skip FNC4 and shift value instead */
|
|
--emptyColumns;
|
|
++charCur;
|
|
}
|
|
pSet[charCur] |= CShift;
|
|
} else {
|
|
pSet[charCur] |= CodeA;
|
|
characterSetCur = CodeA;
|
|
}
|
|
emptyColumns -= 2;
|
|
++charCur;
|
|
}
|
|
} else if (isFNC4 && emptyColumns == 1) {
|
|
/* Can't fit extended ASCII on same line */
|
|
pSet[charCur - 1] |= CEnd + CFill;
|
|
emptyColumns = 0;
|
|
} else {
|
|
--emptyColumns;
|
|
++charCur;
|
|
}
|
|
}
|
|
break;
|
|
case CodeC:
|
|
if (T[charCur].CFollowing > 0) {
|
|
charCur += (T[charCur].CharacterSet == ZTFNC1) ? 1 : 2;
|
|
emptyColumns--;
|
|
} else {
|
|
/* Must change to A or B */
|
|
if (emptyColumns == 1 || (isFNC4 && emptyColumns == 2)) {
|
|
/* Can't switch: */
|
|
pSet[charCur - 1] |= CEnd + CFill;
|
|
emptyColumns = 0;
|
|
} else {
|
|
/*<SwitchA> or <switchA>?*/
|
|
characterSetCur = pSet[charCur]
|
|
= (T[charCur].AFollowing > T[charCur].BFollowing) ? CodeA : CodeB;
|
|
emptyColumns -= 2;
|
|
++charCur;
|
|
}
|
|
}
|
|
break;
|
|
} /* switch */
|
|
} /* while */
|
|
|
|
/* > End of Codeline */
|
|
pSet[charCur - 1] |= CEnd;
|
|
++rowsCur;
|
|
} while (charCur < dataLength); /* <= Data.Len-1 */
|
|
|
|
/* Allow for check characters K1, K2 */
|
|
switch (emptyColumns) {
|
|
case 1:
|
|
pSet[charCur - 1] |= CFill;
|
|
/* fall through */
|
|
case 0:
|
|
++rowsCur;
|
|
fillings = useColumns - 2 + emptyColumns;
|
|
break;
|
|
case 2: fillings = 0; break;
|
|
default: pSet[charCur - 1] |= CFill; fillings = emptyColumns - 2;
|
|
}
|
|
|
|
if (rowsCur > 44) {
|
|
++useColumns;
|
|
if (useColumns > 62) {
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
} else if (rowsCur == 1) {
|
|
rowsCur = 2;
|
|
fillings += useColumns;
|
|
}
|
|
} while (rowsCur > 44);
|
|
if (symbol->debug & ZINT_DEBUG_PRINT) {
|
|
printf(" -> out: rowsCur <%d>, useColumns <%d>, fillings <%d>\n", rowsCur, useColumns, fillings);
|
|
}
|
|
*pUseColumns = useColumns;
|
|
*pRows = rowsCur;
|
|
*pFillings = fillings;
|
|
return 0;
|
|
}
|
|
|
|
/* 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) {
|
|
int rowsCur;
|
|
int rowsRequested; /* Number of requested rows */
|
|
int columnsRequested; /* Number of requested columns (if any) */
|
|
int fillings;
|
|
int useColumns;
|
|
int testColumns; /* To enter into Width2Rows */
|
|
int testListSize = 0;
|
|
int pTestList[62 + 1];
|
|
int *pBackupSet = (int *) z_alloca(sizeof(int) * dataLength);
|
|
|
|
rowsRequested = *pRows;
|
|
columnsRequested = *pUseColumns >= 4 ? *pUseColumns : 0;
|
|
|
|
if (symbol->debug & ZINT_DEBUG_PRINT) {
|
|
printf("Optimizer : Searching <%d> rows\n", rowsRequested);
|
|
}
|
|
|
|
if (columnsRequested) {
|
|
testColumns = columnsRequested;
|
|
} else {
|
|
/* First guess */
|
|
testColumns = dataLength / rowsRequested;
|
|
if (testColumns > 62)
|
|
testColumns = 62;
|
|
else if (testColumns < 4)
|
|
testColumns = 4;
|
|
}
|
|
|
|
for (;;) {
|
|
int errorCur;
|
|
pTestList[testListSize] = testColumns;
|
|
testListSize++;
|
|
useColumns = testColumns; /* Make a copy because it may be modified */
|
|
errorCur = Columns2Rows(symbol, T, dataLength, &rowsCur, &useColumns, pSet, &fillings);
|
|
if (errorCur != 0)
|
|
return errorCur;
|
|
if (rowsCur <= rowsRequested) {
|
|
/* Less or exactly line number found */
|
|
/* check if column count below already tested or at smallest/requested */
|
|
int fInTestList = (rowsCur == 2 || testColumns == 4 || testColumns == columnsRequested);
|
|
int posCur;
|
|
for (posCur = 0; posCur < testListSize && !fInTestList; posCur++) {
|
|
if (pTestList[posCur] == testColumns - 1)
|
|
fInTestList = 1;
|
|
}
|
|
if (fInTestList) {
|
|
/* >> Smaller Width already tested
|
|
*/
|
|
if (rowsCur < rowsRequested) {
|
|
fillings += useColumns * (rowsRequested - rowsCur);
|
|
rowsCur = rowsRequested;
|
|
}
|
|
/* Exit with actual */
|
|
*pFillings = fillings;
|
|
*pRows = rowsCur;
|
|
*pUseColumns = useColumns;
|
|
return 0;
|
|
}
|
|
/* > Test more rows (shorter CDB) */
|
|
memcpy(pBackupSet, pSet, sizeof(int) * dataLength);
|
|
--testColumns;
|
|
} else {
|
|
/* > Too many rows */
|
|
/* > Test less rows (longer code) */
|
|
memcpy(pBackupSet, pSet, sizeof(int) * dataLength);
|
|
if (++testColumns > 62) {
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Print a character in character set A
|
|
*/
|
|
static void A2C128_A(uchar **ppOutPos, uchar c) {
|
|
uchar *pOutPos = *ppOutPos;
|
|
switch (c) {
|
|
case aCodeB: *pOutPos = 100; break;
|
|
case aFNC4: *pOutPos = 101; break;
|
|
case aFNC1: *pOutPos = 102; break; /* FNC1s (GS1) not used */ /* Not reached */
|
|
case aFNC2: *pOutPos = 97; break; /* FNC2s (Message Append) not used */ /* Not reached */
|
|
case aFNC3: *pOutPos = 96; break;
|
|
case aCodeC: *pOutPos = 99; break;
|
|
case aShift: *pOutPos = 98; break;
|
|
default:
|
|
/* +++ HaO 13.11.98 c>' ' && c < '\x1F' corrected */
|
|
if (c >= ' ' && c <= '_')
|
|
*pOutPos = (uchar) (c - ' ');
|
|
else
|
|
*pOutPos = (uchar) (c + 64);
|
|
break;
|
|
}
|
|
(*ppOutPos)++;
|
|
}
|
|
|
|
/* Output c in Set B
|
|
*/
|
|
static void A2C128_B(uchar **ppOutPos, uchar c) {
|
|
uchar *pOutPos = *ppOutPos;
|
|
switch (c) {
|
|
case aFNC1: *pOutPos = 102; break; /* FNC1s (GS1) not used */ /* Not reached */
|
|
case aFNC2: *pOutPos = 97; break; /* FNC2s (Message Append) not used */ /* Not reached */
|
|
case aFNC3: *pOutPos = 96; break;
|
|
case aFNC4: *pOutPos = 100; break;
|
|
case aCodeA: *pOutPos = 101; break;
|
|
case aCodeC: *pOutPos = 99; break;
|
|
case aShift: *pOutPos = 98; break;
|
|
default: *pOutPos = (uchar) (c - ' '); break;
|
|
}
|
|
++(*ppOutPos);
|
|
}
|
|
|
|
/* Output c1, c2 in Set C
|
|
*/
|
|
static void A2C128_C(uchar **ppOutPos, uchar c1, uchar c2) {
|
|
uchar *pOutPos = *ppOutPos;
|
|
switch (c1) {
|
|
case aFNC1: *pOutPos = 102; break; /* FNC1s (GS1) not used */ /* Not reached */
|
|
case aCodeB: *pOutPos = 100; break;
|
|
case aCodeA: *pOutPos = 101; break;
|
|
default: *pOutPos = (uchar) (10 * (c1 - '0') + (c2 - '0')); break;
|
|
}
|
|
(*ppOutPos)++;
|
|
}
|
|
|
|
/* Output a character in Characterset
|
|
*/
|
|
static void ASCIIZ128(uchar **ppOutPos, int CharacterSet, uchar c1, uchar c2) {
|
|
if (CharacterSet == CodeA)
|
|
A2C128_A(ppOutPos, c1);
|
|
else if (CharacterSet == CodeB)
|
|
A2C128_B(ppOutPos, c1);
|
|
else
|
|
A2C128_C(ppOutPos, c1, 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) {
|
|
switch (CharacterSet) {
|
|
case CodeA: /* Row # Indicators and Data Check Characters K1/K2 for CodeA and CodeB are the same */
|
|
case CodeB:
|
|
if (Sum <= 31)
|
|
A2C128_B(ppOutPos, (uchar) (Sum + 96));
|
|
else if (Sum <= 47)
|
|
A2C128_B(ppOutPos, (uchar) Sum);
|
|
else
|
|
A2C128_B(ppOutPos, (uchar) (Sum + 10));
|
|
break;
|
|
case CodeC:
|
|
A2C128_C(ppOutPos, (uchar) (Sum / 10 + '0'), (uchar) (Sum % 10 + '0'));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Main function called by zint framework
|
|
*/
|
|
INTERNAL int codablockf(struct zint_symbol *symbol, unsigned char source[], int length) {
|
|
int charCur, dataLength;
|
|
int error_number;
|
|
int rows, columns, useColumns;
|
|
int fillings;
|
|
int Sum1, Sum2;
|
|
uchar *pOutPos;
|
|
int rowCur;
|
|
int characterSetCur;
|
|
int emptyColumns;
|
|
char dest[1000];
|
|
int r, c;
|
|
CharacterSetTable *T;
|
|
unsigned char *data;
|
|
int *pSet;
|
|
uchar *pOutput;
|
|
|
|
/* Suppresses clang-analyzer-core.VLASize warning */
|
|
assert(length > 0);
|
|
|
|
/* Parameter check */
|
|
/* option1: rows <= 0: automatic, 1..44 */
|
|
rows = symbol->option_1;
|
|
if (rows == 1) {
|
|
error_number = code128(symbol, source, length);
|
|
if (error_number < ZINT_ERROR) {
|
|
symbol->output_options |= BARCODE_BIND;
|
|
if (symbol->border_width == 0) { /* Allow override if non-zero */
|
|
symbol->border_width = 1; /* AIM ISS-X-24 Section 4.6.1 b) (note change from previous default 2) */
|
|
}
|
|
symbol->text[0] = '\0'; /* Disable HRT for compatibility with CODABLOCKF */
|
|
if (symbol->output_options & COMPLIANT_HEIGHT) {
|
|
/* AIM ISS-X-24 Section 4.6.1 minimum row height 8X (for compatibility with CODABLOCKF, not specced
|
|
for CODE128) */
|
|
if (error_number == 0) {
|
|
error_number = set_height(symbol, 8.0f, 10.0f, 0.0f, 0 /*no_errtxt*/);
|
|
} else {
|
|
(void) set_height(symbol, 8.0f, 10.0f, 0.0f, 1 /*no_errtxt*/);
|
|
}
|
|
} else {
|
|
(void) set_height(symbol, 0.0f, 5.0f, 0.0f, 1 /*no_errtxt*/);
|
|
}
|
|
}
|
|
return error_number;
|
|
}
|
|
if (rows > 44) {
|
|
return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 410, "Number of rows '%d' out of range (0 to 44)", rows);
|
|
}
|
|
/* option_2: (usable data) columns: <= 0: automatic, 9..67 (min 9 == 4 data, max 67 == 62 data) */
|
|
columns = symbol->option_2;
|
|
if (!(columns <= 0 || (columns >= 9 && columns <= 67))) {
|
|
return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 411, "Number of columns '%d' out of range (9 to 67)",
|
|
columns);
|
|
}
|
|
if (columns < 0) { /* Protect against negative overflow (ticket #300 (#9) Andre Maute) */
|
|
columns = 0;
|
|
}
|
|
|
|
data = (unsigned char *) z_alloca(length * 2 + 1);
|
|
|
|
dataLength = 0;
|
|
if (symbol->output_options & READER_INIT) {
|
|
data[dataLength] = aFNC3;
|
|
dataLength++;
|
|
}
|
|
/* 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);
|
|
} else
|
|
data[dataLength] = source[charCur];
|
|
dataLength++;
|
|
}
|
|
|
|
/* Build character set table */
|
|
T = (CharacterSetTable *) z_alloca(sizeof(CharacterSetTable) * dataLength);
|
|
pSet = (int *) z_alloca(sizeof(int) * dataLength);
|
|
CreateCharacterSetTable(T, data, dataLength);
|
|
|
|
/* Find final row and column count */
|
|
/* nor row nor column count given */
|
|
if (rows <= 0 && columns <= 0) {
|
|
/* use 1/1 aspect/ratio Codablock */
|
|
columns = (int) floor(sqrt(dataLength)) + 5;
|
|
if (columns > 67) {
|
|
columns = 67;
|
|
} else if (columns < 9) {
|
|
columns = 9;
|
|
}
|
|
if (symbol->debug & ZINT_DEBUG_PRINT) {
|
|
printf("Auto column count for %d characters:%d\n", dataLength, columns);
|
|
}
|
|
}
|
|
/* There are 5 Codewords for Organisation Start(2),row(1),CheckSum,Stop */
|
|
useColumns = columns - 5;
|
|
if (rows > 0) {
|
|
/* row count given */
|
|
error_number = Rows2Columns(symbol, T, dataLength, &rows, &useColumns, pSet, &fillings);
|
|
} else {
|
|
/* column count given */
|
|
error_number = Columns2Rows(symbol, T, dataLength, &rows, &useColumns, pSet, &fillings);
|
|
}
|
|
if (error_number != 0) {
|
|
return errtxt(error_number, symbol, 413,
|
|
"Input too long, requires too many symbol characters (maximum 2726)");
|
|
}
|
|
/* Suppresses clang-analyzer-core.VLASize warning */
|
|
assert(rows >= 2 && useColumns >= 4);
|
|
|
|
/* Data Check Characters K1 and K2, Annex F */
|
|
Sum1 = Sum2 = 0;
|
|
for (charCur = 0; charCur < length; charCur++) {
|
|
Sum1 = (Sum1 + (charCur + 1) * source[charCur]) % 86; /* Mod as we go along to avoid overflow */
|
|
Sum2 = (Sum2 + charCur * source[charCur]) % 86;
|
|
}
|
|
|
|
if (symbol->debug & ZINT_DEBUG_PRINT) { /* start a new level of local variables */
|
|
int DPos;
|
|
fputs("\nData:", stdout);
|
|
for (DPos = 0; DPos < dataLength; DPos++)
|
|
fputc(data[DPos], stdout);
|
|
fputs("\n Set:", stdout);
|
|
for (DPos = 0; DPos < dataLength; DPos++) {
|
|
switch (pSet[DPos] & (CodeA + CodeB + CodeC)) {
|
|
case CodeA: fputc('A', stdout); break;
|
|
case CodeB: fputc('B', stdout); break;
|
|
case CodeC: fputc('C', stdout); break;
|
|
default: fputc('.', stdout); break;
|
|
}
|
|
}
|
|
fputs("\nFNC1:", stdout);
|
|
for (DPos = 0; DPos < dataLength; DPos++)
|
|
fputc((pSet[DPos] & CodeFNC1) == 0 ? '.' : 'X', stdout);
|
|
fputs("\n END:", stdout);
|
|
for (DPos = 0; DPos < dataLength; DPos++)
|
|
fputc((pSet[DPos] & CEnd) == 0 ? '.' : 'X', stdout);
|
|
fputs("\nShif:", stdout);
|
|
for (DPos = 0; DPos < dataLength; DPos++)
|
|
fputc((pSet[DPos] & CShift) == 0 ? '.' : 'X', stdout);
|
|
fputs("\nFILL:", stdout);
|
|
for (DPos = 0; DPos < dataLength; DPos++)
|
|
fputc((pSet[DPos] & CFill) == 0 ? '.' : 'X', stdout);
|
|
fputc('\n', stdout);
|
|
printf("K1 %d, K2 %d\n", Sum1, Sum2);
|
|
}
|
|
|
|
columns = useColumns + 5;
|
|
|
|
/* >>> Build C128 code numbers */
|
|
/* The C128 column count contains Start (2CW), Row ID, Checksum, Stop */
|
|
pOutput = (unsigned char *) z_alloca(columns * rows);
|
|
pOutPos = pOutput;
|
|
charCur = 0;
|
|
/* >> Loop over rows */
|
|
for (rowCur = 0; rowCur < rows; rowCur++) {
|
|
if (charCur >= dataLength) {
|
|
/* >> Empty line with StartA, aCodeB, row #, and then filler aCodeC aCodeB etc */
|
|
*pOutPos = '\x67';
|
|
pOutPos++;
|
|
*pOutPos = 100; /* aCodeB */
|
|
pOutPos++;
|
|
characterSetCur = CodeB;
|
|
SumASCII(&pOutPos, rowCur + 42, characterSetCur); /* Row # */
|
|
emptyColumns = useColumns;
|
|
if (rowCur == rows - 1) {
|
|
emptyColumns -= 2;
|
|
}
|
|
while (emptyColumns > 0) {
|
|
if (characterSetCur == CodeC) {
|
|
A2C128_C(&pOutPos, aCodeB, '\0');
|
|
characterSetCur = CodeB;
|
|
} else {
|
|
A2C128_B(&pOutPos, aCodeC);
|
|
characterSetCur = CodeC;
|
|
}
|
|
--emptyColumns;
|
|
}
|
|
} else {
|
|
/* >> Normal Line */
|
|
/* > Startcode */
|
|
switch (pSet[charCur] & (CodeA + CodeB + CodeC)) {
|
|
case CodeA:
|
|
*pOutPos = '\x67';
|
|
pOutPos++;
|
|
*pOutPos = '\x62';
|
|
pOutPos++;
|
|
characterSetCur = CodeA;
|
|
break;
|
|
case CodeB:
|
|
*pOutPos = '\x67';
|
|
pOutPos++;
|
|
*pOutPos = '\x64';
|
|
pOutPos++;
|
|
characterSetCur = CodeB;
|
|
break;
|
|
case CodeC:
|
|
default:
|
|
*pOutPos = '\x67';
|
|
pOutPos++;
|
|
*pOutPos = '\x63';
|
|
pOutPos++;
|
|
characterSetCur = CodeC;
|
|
break;
|
|
}
|
|
/* > Set F1 */
|
|
/* In first line : # of rows */
|
|
SumASCII(&pOutPos, rowCur == 0 ? rows - 2 : rowCur + 42, characterSetCur);
|
|
/* >>> Data */
|
|
emptyColumns = useColumns;
|
|
/* >> Character loop */
|
|
while (emptyColumns > 0 && charCur < dataLength) {
|
|
/* ? Change character set */
|
|
if (emptyColumns < useColumns) {
|
|
if ((pSet[charCur] & CodeA) != 0) {
|
|
/* Change to A */
|
|
ASCIIZ128(&pOutPos, characterSetCur, aCodeA, '\0');
|
|
--emptyColumns;
|
|
characterSetCur = CodeA;
|
|
} else if ((pSet[charCur] & CodeB) != 0) {
|
|
/* Change to B */
|
|
ASCIIZ128(&pOutPos, characterSetCur, aCodeB, '\0');
|
|
--emptyColumns;
|
|
characterSetCur = CodeB;
|
|
} else if ((pSet[charCur] & CodeC) != 0) {
|
|
/* Change to C */
|
|
ASCIIZ128(&pOutPos, characterSetCur, aCodeC, '\0');
|
|
--emptyColumns;
|
|
characterSetCur = CodeC;
|
|
}
|
|
}
|
|
if ((pSet[charCur] & CShift) != 0) {
|
|
/* >> Shift it and put out the shifted character */
|
|
ASCIIZ128(&pOutPos, characterSetCur, aShift, '\0');
|
|
emptyColumns -= 2;
|
|
characterSetCur = (characterSetCur == CodeB) ? CodeA : CodeB;
|
|
ASCIIZ128(&pOutPos, characterSetCur, data[charCur], '\0');
|
|
characterSetCur = (characterSetCur == CodeB) ? CodeA : CodeB;
|
|
} else {
|
|
/* Normal Character */
|
|
if (characterSetCur == CodeC) {
|
|
if (data[charCur] == aFNC1) /* FNC1s (GS1) not used */
|
|
A2C128_C(&pOutPos, aFNC1, '\0'); /* Not reached */
|
|
else {
|
|
A2C128_C(&pOutPos, data[charCur],
|
|
(uchar) (charCur + 1 < dataLength ? data[charCur + 1] : 0));
|
|
++charCur;
|
|
/* We need this here to get the good index */
|
|
/* for the termination flags in Set. */
|
|
}
|
|
} else
|
|
ASCIIZ128(&pOutPos, characterSetCur, data[charCur], '\0');
|
|
--emptyColumns;
|
|
}
|
|
/* >> End Criteria */
|
|
if ((pSet[charCur] & CFill) || (pSet[charCur] & CEnd)) {
|
|
/* Fill Line but leave space for checks in last line */
|
|
if (rowCur == rows - 1) {
|
|
emptyColumns -= 2;
|
|
}
|
|
while (emptyColumns > 0) {
|
|
switch (characterSetCur) {
|
|
case CodeC:
|
|
A2C128_C(&pOutPos, aCodeB, '\0');
|
|
characterSetCur = CodeB;
|
|
break;
|
|
case CodeB:
|
|
A2C128_B(&pOutPos, aCodeC);
|
|
characterSetCur = CodeC;
|
|
break;
|
|
case CodeA:
|
|
A2C128_A(&pOutPos, aCodeC);
|
|
characterSetCur = CodeC;
|
|
break;
|
|
}
|
|
--emptyColumns;
|
|
}
|
|
}
|
|
++charCur;
|
|
} /* Loop over characters */
|
|
} /* if filling-Line / normal */
|
|
|
|
/* Add checksum in last line */
|
|
if (rowCur == rows - 1) {
|
|
SumASCII(&pOutPos, Sum1, characterSetCur);
|
|
SumASCII(&pOutPos, Sum2, characterSetCur);
|
|
}
|
|
/* Add Code 128 checksum */
|
|
{
|
|
int Sum = pOutput[columns * rowCur] % 103;
|
|
int Pos = 1;
|
|
for (; Pos < useColumns + 3; Pos++) {
|
|
Sum = (Sum + pOutput[columns * rowCur + Pos] * Pos) % 103;
|
|
}
|
|
*pOutPos = (uchar) Sum;
|
|
pOutPos++;
|
|
}
|
|
/* Add end character */
|
|
*pOutPos = 106;
|
|
pOutPos++;
|
|
} /* End Lineloop */
|
|
|
|
if (symbol->debug & ZINT_DEBUG_PRINT) {
|
|
/* Dump the output to the screen
|
|
*/
|
|
fputs("\nCode 128 Code Numbers:\n", stdout);
|
|
{ /* start a new level of local variables */
|
|
int DPos, DPos2;
|
|
for (DPos = 0; DPos < rows; DPos++) {
|
|
for (DPos2 = 0; DPos2 < columns; DPos2++) {
|
|
printf("%3d ", (int) (pOutput[DPos * columns + DPos2]));
|
|
}
|
|
fputc('\n', stdout);
|
|
}
|
|
}
|
|
printf("rows=%d columns=%d (%d data) fillings=%d\n", rows, columns, columns - 5, fillings);
|
|
}
|
|
#ifdef ZINT_TEST
|
|
if (symbol->debug & ZINT_DEBUG_TEST) {
|
|
debug_test_codeword_dump(symbol, pOutput, rows * columns);
|
|
}
|
|
#endif
|
|
|
|
/* Paint the C128 patterns */
|
|
for (r = 0; r < rows; r++) {
|
|
const int rc = r * columns;
|
|
char *d = dest;
|
|
for (c = 0; c < columns - 1; c++, d += 6) {
|
|
memcpy(d, C128Table[pOutput[rc + c]], 6);
|
|
}
|
|
memcpy(d, "2331112", 7); /* Stop character (106, not in C128Table) */
|
|
d += 7;
|
|
expand(symbol, dest, d - dest);
|
|
}
|
|
|
|
if (symbol->output_options & COMPLIANT_HEIGHT) {
|
|
/* AIM ISS-X-24 Section 4.6.1 minimum row height; use 10 * rows as default */
|
|
float min_row_height = stripf(0.55f * useColumns + 3.0f);
|
|
if (min_row_height < 8.0f) {
|
|
min_row_height = 8.0f;
|
|
}
|
|
error_number = set_height(symbol, min_row_height, (min_row_height > 10.0f ? min_row_height : 10.0f) * rows,
|
|
0.0f, 0 /*no_errtxt*/);
|
|
} else {
|
|
(void) set_height(symbol, 0.0f, 10.0f * rows, 0.0f, 1 /*no_errtxt*/);
|
|
}
|
|
|
|
symbol->output_options |= BARCODE_BIND;
|
|
|
|
if (symbol->border_width == 0) { /* Allow override if non-zero */
|
|
symbol->border_width = 1; /* AIM ISS-X-24 Section 4.6.1 b) (note change from previous default 2) */
|
|
}
|
|
|
|
return error_number;
|
|
}
|
|
|
|
/* vim: set ts=4 sw=4 et : */
|