mirror of
https://github.com/zint/zint
synced 2024-11-16 20:57:25 +13:00
1399 lines
50 KiB
C
1399 lines
50 KiB
C
/* pdf417.c - Handles PDF417 stacked symbology */
|
|
/*
|
|
libzint - the open source barcode library
|
|
Copyright (C) 2008-2022 Robin Stuart <rstuart114@gmail.com>
|
|
Portions Copyright (C) 2004 Grandzebu
|
|
Bug Fixes thanks to KL Chin <klchin@users.sourceforge.net>
|
|
|
|
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 */
|
|
|
|
/* This code is adapted from "Code barre PDF 417 / PDF 417 barcode" v2.5.0
|
|
which is Copyright (C) 2004 (Grandzebu).
|
|
The original code can be downloaded from http://grandzebu.net/index.php */
|
|
|
|
/* NOTE: symbol->option_1 is used to specify the security level (i.e. control the
|
|
number of check codewords)
|
|
|
|
symbol->option_2 is used to adjust the width of the resulting symbol (i.e. the
|
|
number of codeword columns not including row start and end data)
|
|
|
|
symbol->option_3 is used to adjust the rows of the resulting symbol */
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include "common.h"
|
|
#include "pdf417.h"
|
|
#include "pdf417_tabs.h"
|
|
|
|
/* Modes */
|
|
#define TEX 900
|
|
#define BYT 901
|
|
#define NUM 902
|
|
|
|
/* TEX mode sub-modes */
|
|
#define T_ALPHA 1
|
|
#define T_LOWER 2
|
|
#define T_MIXED 4
|
|
#define T_PUNCT 8
|
|
|
|
/*
|
|
Three figure numbers in comments give the location of command equivalents in the
|
|
original Visual Basic source code file pdf417.frm
|
|
this code retains some original (French) procedure and variable names to ease conversion */
|
|
|
|
/* text mode processing tables */
|
|
|
|
/* TEX sub-mode assignments */
|
|
static const char pdf_asciix[127] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 8, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
7, 8, 8, 4, 12, 4, 4, 8, 8, 8, 12, 4, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
4, 4, 12, 8, 8, 4, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 8, 8, 8, 4, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 8, 8, 8, 8
|
|
};
|
|
|
|
/* TEX sub-mode values */
|
|
static const char pdf_asciiy[127] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 15, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
26, 10, 20, 15, 18, 21, 10, 28, 23, 24, 22, 20, 13, 16, 17, 19, 0, 1, 2, 3,
|
|
4, 5, 6, 7, 8, 9, 14, 0, 1, 23, 2, 25, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 4, 5, 6, 24, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 21, 27, 9
|
|
};
|
|
|
|
/* Automatic sizing table */
|
|
|
|
static const char pdf_MicroAutosize[56] = {
|
|
4, 6, 7, 8, 10, 12, 13, 14, 16, 18, 19, 20, 24, 29, 30, 33, 34, 37, 39, 46, 54, 58, 70, 72, 82, 90, 108, 126,
|
|
1, 14, 2, 7, 3, 25, 8, 16, 5, 17, 9, 6, 10, 11, 28, 12, 19, 13, 29, 20, 30, 21, 22, 31, 23, 32, 33, 34
|
|
};
|
|
|
|
/* ISO/IEC 15438:2015 5.1.1 c) 3) Max possible number of characters at error correction level 0
|
|
(Numeric Compaction mode) */
|
|
#define PDF_MAX_LEN 2710
|
|
|
|
/* ISO/IEC 24728:2006 5.1.1 c) 3) Max possible number of characters (Numeric Compaction mode) */
|
|
#define MICRO_PDF_MAX_LEN 366
|
|
|
|
/* 866 */
|
|
|
|
static int pdf_quelmode(const unsigned char codeascii) {
|
|
if (z_isdigit(codeascii)) {
|
|
return NUM;
|
|
}
|
|
if (codeascii < 127 && pdf_asciix[codeascii]) {
|
|
return TEX;
|
|
}
|
|
/* 876 */
|
|
|
|
return BYT;
|
|
}
|
|
|
|
/* Check consecutive segments for text/num and return the length */
|
|
static int pdf_text_num_length(const int liste[3][PDF_MAX_LEN], const int indexliste, const int start) {
|
|
int i, len = 0;
|
|
for (i = start; i < indexliste; i++) {
|
|
if (liste[1][i] == BYT)
|
|
break;
|
|
|
|
len += liste[0][i];
|
|
if (len >= 5) /* we don't care if it's longer than 5 */
|
|
break;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/* Calculate length of TEX allowing for sub-mode switches (no-output version of `pdf_textprocess()`) */
|
|
static int pdf_text_submode_length(const unsigned char chaine[], const int start, const int length, int *p_curtable) {
|
|
int j, indexlistet, curtable, listet[PDF_MAX_LEN], wnet;
|
|
|
|
wnet = 0;
|
|
curtable = *p_curtable;
|
|
|
|
for (indexlistet = 0; indexlistet < length; indexlistet++) {
|
|
listet[indexlistet] = pdf_asciix[chaine[start + indexlistet]];
|
|
}
|
|
|
|
for (j = 0; j < length; j++) {
|
|
if (listet[j] & curtable) {
|
|
/* The character is in the current table */
|
|
wnet++;
|
|
} else {
|
|
/* Obliged to change table */
|
|
int newtable;
|
|
if (j == (length - 1) || !(listet[j] & listet[j + 1])) {
|
|
/* we change only one character - look for temporary switch */
|
|
if ((listet[j] & T_ALPHA) && (curtable == T_LOWER)) {
|
|
wnet += 2; /* AS+char */
|
|
continue;
|
|
}
|
|
if (listet[j] & T_PUNCT) { /* (T_PUNCT and T_ALPHA not both possible) */
|
|
wnet += 2; /* PS+char */
|
|
continue;
|
|
}
|
|
/* No temporary switch available */
|
|
newtable = listet[j];
|
|
} else {
|
|
newtable = listet[j] & listet[j + 1];
|
|
}
|
|
|
|
/* 599 */
|
|
|
|
/* Maintain the first if several tables are possible */
|
|
if (newtable == 7) { /* (T_ALPHA | T_LOWER | T_MIXED) */
|
|
newtable = T_ALPHA;
|
|
} else if (newtable == 12) { /* (T_MIXED | T_PUNCT) */
|
|
newtable = T_MIXED;
|
|
}
|
|
|
|
/* 619 - select the switch */
|
|
switch (curtable) {
|
|
case T_ALPHA:
|
|
switch (newtable) {
|
|
case T_LOWER:
|
|
case T_MIXED: wnet++; /* LL or ML */
|
|
break;
|
|
case T_PUNCT: wnet += 2; /* ML+PL */
|
|
break;
|
|
}
|
|
break;
|
|
case T_LOWER:
|
|
switch (newtable) {
|
|
case T_ALPHA: wnet += 2; /* ML+AL */
|
|
break;
|
|
case T_MIXED: wnet++; /* ML */
|
|
break;
|
|
case T_PUNCT: wnet += 2; /* ML+PL */
|
|
break;
|
|
}
|
|
break;
|
|
case T_MIXED:
|
|
switch (newtable) {
|
|
case T_ALPHA:
|
|
case T_LOWER:
|
|
case T_PUNCT: wnet++; /* AL or LL or PL */
|
|
break;
|
|
}
|
|
break;
|
|
case T_PUNCT:
|
|
switch (newtable) {
|
|
case T_ALPHA: wnet++; /* AL */
|
|
break;
|
|
case T_LOWER:
|
|
case T_MIXED: wnet += 2; /* AL+LL or AL+ML */
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
curtable = newtable;
|
|
wnet++;
|
|
}
|
|
}
|
|
|
|
*p_curtable = curtable;
|
|
|
|
return wnet;
|
|
}
|
|
|
|
/* Whether to stay in numeric mode or not */
|
|
static int pdf_num_stay(const unsigned char *chaine, const int indexliste, const int liste[3][PDF_MAX_LEN],
|
|
const int i) {
|
|
int curtable, last_len, last_ml, next_len, num_cws, tex_cws;
|
|
|
|
if (liste[0][i] >= 13 || (indexliste == 1 && liste[0][i] > 5)) {
|
|
return 1;
|
|
}
|
|
if (liste[0][i] < 11) {
|
|
return 0;
|
|
}
|
|
|
|
curtable = T_ALPHA;
|
|
last_len = i == 0 ? 0 : pdf_text_submode_length(chaine, liste[2][i - 1], liste[0][i - 1], &curtable);
|
|
last_ml = curtable == T_MIXED;
|
|
|
|
curtable = T_ALPHA; /* Next len if after NUM, sub-mode will be alpha */
|
|
next_len = i == indexliste - 1 ? 0 : pdf_text_submode_length(chaine, liste[2][i + 1], liste[0][i + 1], &curtable);
|
|
num_cws = (last_len + 1) / 2 + 1 + 4 + (liste[0][i] > 11) + 1 + (next_len + 1) / 2;
|
|
|
|
curtable = T_MIXED; /* Next len if stay TEX, sub-mode will be mixed */
|
|
next_len = i == indexliste - 1 ? 0 : pdf_text_submode_length(chaine, liste[2][i + 1], liste[0][i + 1], &curtable);
|
|
tex_cws = (last_len + !last_ml + liste[0][i] + next_len + 1) / 2;
|
|
|
|
if (num_cws > tex_cws) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Pack segments using the method described in Appendix D of the AIM specification (ISO/IEC 15438:2015 Annex N) */
|
|
static void pdf_appendix_d_encode(const unsigned char *chaine, int liste[3][PDF_MAX_LEN], int *indexliste,
|
|
const int debug_print) {
|
|
int i = 0, next, last = 0, stayintext = 0;
|
|
|
|
while (i < *(indexliste)) {
|
|
if (debug_print) {
|
|
printf("Encoding block %d = %d (%d)\n", i, liste[1][i], liste[0][i]);
|
|
}
|
|
|
|
if ((liste[1][i] == NUM) && pdf_num_stay(chaine, *indexliste, liste, i)) {
|
|
/* leave as numeric */
|
|
liste[0][last] = liste[0][i];
|
|
liste[1][last] = NUM;
|
|
liste[2][last] = liste[2][i];
|
|
stayintext = 0;
|
|
last++;
|
|
} else if (((liste[1][i] == TEX) || (liste[1][i] == NUM))
|
|
&& (stayintext || i == *indexliste - 1 || (liste[0][i] >= 5)
|
|
|| (pdf_text_num_length(liste, *indexliste, i) >= 5))) {
|
|
/* set to text and combine additional text/short numeric segments */
|
|
liste[0][last] = liste[0][i];
|
|
liste[1][last] = TEX;
|
|
liste[2][last] = liste[2][i];
|
|
stayintext = 0;
|
|
|
|
next = i + 1;
|
|
while (next < *(indexliste)) {
|
|
if ((liste[1][next] == NUM) && pdf_num_stay(chaine, *indexliste, liste, next)) {
|
|
break;
|
|
} else if (liste[1][next] == BYT) {
|
|
break;
|
|
}
|
|
|
|
liste[0][last] += liste[0][next];
|
|
next++;
|
|
}
|
|
|
|
last++;
|
|
i = next;
|
|
continue;
|
|
} else {
|
|
/* build byte segment, including combining numeric/text segments that aren't long enough on their own */
|
|
liste[0][last] = liste[0][i];
|
|
liste[1][last] = BYT;
|
|
liste[2][last] = liste[2][i];
|
|
stayintext = 0;
|
|
|
|
next = i + 1;
|
|
while (next < *(indexliste)) {
|
|
if (liste[1][next] != BYT) {
|
|
/* check for case of a single byte shift from text mode */
|
|
if ((liste[0][last] == 1) && (last > 0) && (liste[1][last - 1] == TEX)) {
|
|
stayintext = 1;
|
|
break;
|
|
}
|
|
|
|
if ((liste[0][next] >= 5) || (pdf_text_num_length(liste, *indexliste, next) >= 5)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
liste[0][last] += liste[0][next];
|
|
next++;
|
|
}
|
|
|
|
last++;
|
|
i = next;
|
|
continue;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
/* set the size of the list based on the last consolidated segment */
|
|
*indexliste = last;
|
|
}
|
|
|
|
/* 547 */
|
|
static void pdf_textprocess(int *chainemc, int *mclength, const unsigned char chaine[], const int start,
|
|
const int length, const int lastmode, int *p_curtable, int *p_tex_padded) {
|
|
int j, indexlistet, curtable, listet[2][PDF_MAX_LEN] = {{0}}, chainet[PDF_MAX_LEN], wnet;
|
|
|
|
wnet = 0;
|
|
curtable = *p_curtable;
|
|
|
|
/* listet will contain the table numbers and the value of each characters */
|
|
for (indexlistet = 0; indexlistet < length; indexlistet++) {
|
|
const int codeascii = chaine[start + indexlistet];
|
|
listet[0][indexlistet] = pdf_asciix[codeascii];
|
|
listet[1][indexlistet] = pdf_asciiy[codeascii];
|
|
}
|
|
|
|
/* 570 */
|
|
if (lastmode != TEX) {
|
|
curtable = T_ALPHA; /* set default table upper alpha */
|
|
}
|
|
|
|
for (j = 0; j < length; j++) {
|
|
if (listet[0][j] & curtable) {
|
|
/* The character is in the current table */
|
|
chainet[wnet++] = listet[1][j];
|
|
} else {
|
|
/* Obliged to change table */
|
|
int newtable;
|
|
if (j == (length - 1) || !(listet[0][j] & listet[0][j + 1])) {
|
|
/* we change only one character - look for temporary switch */
|
|
if ((listet[0][j] & T_ALPHA) && (curtable == T_LOWER)) {
|
|
chainet[wnet++] = 27; /* AS */
|
|
chainet[wnet++] = listet[1][j];
|
|
continue;
|
|
}
|
|
if (listet[0][j] & T_PUNCT) { /* (T_PUNCT and T_ALPHA not both possible) */
|
|
chainet[wnet++] = 29; /* PS */
|
|
chainet[wnet++] = listet[1][j];
|
|
continue;
|
|
}
|
|
/* No temporary switch available */
|
|
newtable = listet[0][j];
|
|
} else {
|
|
newtable = listet[0][j] & listet[0][j + 1];
|
|
}
|
|
|
|
/* 599 */
|
|
|
|
/* Maintain the first if several tables are possible */
|
|
if (newtable == 7) { /* (T_ALPHA | T_LOWER | T_MIXED) */
|
|
newtable = T_ALPHA;
|
|
} else if (newtable == 12) { /* (T_MIXED | T_PUNCT) */
|
|
newtable = T_MIXED;
|
|
}
|
|
|
|
/* 619 - select the switch */
|
|
switch (curtable) {
|
|
case T_ALPHA:
|
|
switch (newtable) {
|
|
case T_LOWER: chainet[wnet++] = 27; /* LL */
|
|
break;
|
|
case T_MIXED: chainet[wnet++] = 28; /* ML */
|
|
break;
|
|
case T_PUNCT: chainet[wnet++] = 28; /* ML+PL */
|
|
chainet[wnet++] = 25;
|
|
break;
|
|
}
|
|
break;
|
|
case T_LOWER:
|
|
switch (newtable) {
|
|
case T_ALPHA: chainet[wnet++] = 28; /* ML+AL */
|
|
chainet[wnet++] = 28;
|
|
break;
|
|
case T_MIXED: chainet[wnet++] = 28; /* ML */
|
|
break;
|
|
case T_PUNCT: chainet[wnet++] = 28; /* ML+PL */
|
|
chainet[wnet++] = 25;
|
|
break;
|
|
}
|
|
break;
|
|
case T_MIXED:
|
|
switch (newtable) {
|
|
case T_ALPHA: chainet[wnet++] = 28; /* AL */
|
|
break;
|
|
case T_LOWER: chainet[wnet++] = 27; /* LL */
|
|
break;
|
|
case T_PUNCT: chainet[wnet++] = 25; /* PL */
|
|
break;
|
|
}
|
|
break;
|
|
case T_PUNCT:
|
|
switch (newtable) {
|
|
case T_ALPHA: chainet[wnet++] = 29; /* AL */
|
|
break;
|
|
case T_LOWER: chainet[wnet++] = 29; /* AL+LL */
|
|
chainet[wnet++] = 27;
|
|
break;
|
|
case T_MIXED: chainet[wnet++] = 29; /* AL+ML */
|
|
chainet[wnet++] = 28;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
curtable = newtable;
|
|
/* 659 - at last we add the character */
|
|
chainet[wnet++] = listet[1][j];
|
|
}
|
|
}
|
|
|
|
/* 663 */
|
|
*p_tex_padded = wnet & 1;
|
|
if (*p_tex_padded) {
|
|
chainet[wnet++] = 29;
|
|
}
|
|
/* Now translate the string chainet into codewords */
|
|
|
|
/* add mode indicator if needed */
|
|
if (lastmode != TEX) {
|
|
chainemc[(*mclength)++] = 900;
|
|
}
|
|
|
|
for (j = 0; j < wnet; j += 2) {
|
|
const int cw_number = (30 * chainet[j]) + chainet[j + 1];
|
|
chainemc[(*mclength)++] = cw_number;
|
|
}
|
|
|
|
*p_curtable = curtable;
|
|
}
|
|
|
|
/* 671 */
|
|
INTERNAL void pdf_byteprocess(int *chainemc, int *mclength, const unsigned char chaine[], int start, const int length,
|
|
const int lastmode, const int debug_print) {
|
|
|
|
if (debug_print) printf("\nEntering byte mode at position %d\n", start);
|
|
|
|
if (length == 1) {
|
|
/* shift or latch depending on previous mode */
|
|
chainemc[(*mclength)++] = (lastmode == TEX) ? 913 : 901;
|
|
chainemc[(*mclength)++] = chaine[start];
|
|
if (debug_print) {
|
|
printf("%s %d\n", (lastmode == TEX) ? "913" : "901", chaine[start]);
|
|
}
|
|
} else {
|
|
int len;
|
|
/* select the switch for multiple of 6 bytes */
|
|
if (length % 6 == 0) {
|
|
chainemc[(*mclength)++] = 924;
|
|
if (debug_print) printf("924 ");
|
|
} else {
|
|
/* Default mode for MICROPDF417 is Byte Compaction (ISO/IEC 24728:2006 5.4.3), but not emitting it
|
|
* depends on whether an ECI has been emitted previously (or not) it appears, so simpler and safer
|
|
* to always emit it. */
|
|
chainemc[(*mclength)++] = 901;
|
|
if (debug_print) printf("901 ");
|
|
}
|
|
|
|
len = 0;
|
|
|
|
while (len < length) {
|
|
uint64_t total;
|
|
unsigned int chunkLen = length - len;
|
|
if (6 <= chunkLen) { /* Take groups of 6 */
|
|
chunkLen = 6;
|
|
len += chunkLen;
|
|
total = 0;
|
|
|
|
while (chunkLen--) {
|
|
const uint64_t mantisa = chaine[start++];
|
|
total |= mantisa << (chunkLen * 8);
|
|
}
|
|
|
|
chunkLen = 5;
|
|
|
|
while (chunkLen--) {
|
|
chainemc[*mclength + chunkLen] = (int) (total % 900);
|
|
total /= 900;
|
|
}
|
|
*mclength += 5;
|
|
} else { /* If there remains a group of less than 6 bytes */
|
|
len += chunkLen;
|
|
while (chunkLen--) {
|
|
chainemc[(*mclength)++] = chaine[start++];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 712 */
|
|
static void pdf_numbprocess(int *chainemc, int *mclength, const unsigned char chaine[], const int start,
|
|
const int length) {
|
|
int j;
|
|
|
|
chainemc[(*mclength)++] = 902;
|
|
|
|
j = 0;
|
|
while (j < length) {
|
|
int dumlength = 0;
|
|
int p, len, loop, nombre, dummy[50];
|
|
char chainemod[45];
|
|
int longueur = length - j;
|
|
if (longueur > 44) {
|
|
longueur = 44;
|
|
}
|
|
len = longueur + 1;
|
|
chainemod[0] = 1;
|
|
for (loop = 1; loop < len; loop++) {
|
|
chainemod[loop] = ctoi(chaine[start + loop + j - 1]);
|
|
}
|
|
do {
|
|
/* 877 - gosub Modulo */
|
|
p = 0;
|
|
nombre = 0;
|
|
for (loop = 0; loop < len; loop++) {
|
|
nombre *= 10;
|
|
nombre += chainemod[loop];
|
|
if (nombre < 900) {
|
|
if (p) {
|
|
chainemod[p++] = 0;
|
|
}
|
|
} else {
|
|
chainemod[p] = (nombre / 900);
|
|
nombre -= chainemod[p++] * 900; /* nombre % 900 */
|
|
}
|
|
}
|
|
/* return to 723 */
|
|
|
|
dummy[dumlength++] = nombre;
|
|
len = p;
|
|
} while (p);
|
|
for (loop = dumlength - 1; loop >= 0; loop--) {
|
|
chainemc[(*mclength)++] = dummy[loop];
|
|
}
|
|
j += longueur;
|
|
}
|
|
}
|
|
|
|
#ifdef ZINT_TEST /* Wrapper for direct testing */
|
|
INTERNAL void pdf_numbprocess_test(int *chainemc, int *mclength, const unsigned char chaine[], const int start,
|
|
const int length) {
|
|
pdf_numbprocess(chainemc, mclength, chaine, start, length);
|
|
}
|
|
#endif
|
|
|
|
/* Return mode text */
|
|
static const char *pdf_mode_str(const int mode) {
|
|
static const char *modes[3] = { "Text", "Byte", "Number" };
|
|
return mode >= TEX && mode <= NUM ? modes[mode - TEX] : "ERROR";
|
|
}
|
|
|
|
/* Initial processing of data, shared by `pdf417()` and `micropdf417()` */
|
|
static int pdf_initial(struct zint_symbol *symbol, const unsigned char chaine[], const int length, const int eci,
|
|
const int is_micro, int *p_lastmode, int *p_curtable, int *p_tex_padded, int chainemc[PDF_MAX_LEN],
|
|
int *p_mclength) {
|
|
int i, indexchaine, indexliste, mode;
|
|
int liste[3][PDF_MAX_LEN] = {{0}};
|
|
int mclength;
|
|
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
|
|
|
|
/* 456 */
|
|
indexliste = 0;
|
|
indexchaine = 0;
|
|
|
|
mode = pdf_quelmode(chaine[indexchaine]);
|
|
|
|
/* 463 */
|
|
do {
|
|
liste[1][indexliste] = mode;
|
|
liste[2][indexliste] = indexchaine;
|
|
while ((liste[1][indexliste] == mode) && (indexchaine < length)) {
|
|
liste[0][indexliste]++;
|
|
indexchaine++;
|
|
mode = pdf_quelmode(chaine[indexchaine]);
|
|
}
|
|
indexliste++;
|
|
} while (indexchaine < length);
|
|
|
|
if (debug_print) {
|
|
printf("\nInitial block pattern:\n");
|
|
for (i = 0; i < indexliste; i++) {
|
|
printf("Start: %d Len: %d Type: %s\n", liste[2][i], liste[0][i], pdf_mode_str(liste[1][i]));
|
|
}
|
|
}
|
|
|
|
pdf_appendix_d_encode(chaine, liste, &indexliste, debug_print);
|
|
|
|
if (debug_print) {
|
|
printf("\nCompacted block pattern:\n");
|
|
for (i = 0; i < indexliste; i++) {
|
|
printf("Start: %d Len: %d Type: %s\n", liste[2][i], liste[0][i], pdf_mode_str(liste[1][i]));
|
|
}
|
|
}
|
|
|
|
/* 541 - now compress the data */
|
|
indexchaine = 0;
|
|
mclength = *p_mclength;
|
|
if (mclength == 0 && !is_micro) {
|
|
mclength++; /* Allow for length descriptor for full symbol */
|
|
}
|
|
|
|
if (*p_mclength == 0 && (symbol->output_options & READER_INIT)) {
|
|
chainemc[mclength++] = 921; /* Reader Initialisation */
|
|
}
|
|
|
|
if (eci != 0) {
|
|
if (eci > 811799) {
|
|
strcpy(symbol->errtxt, "472: Invalid ECI");
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
/* Encoding ECI assignment number, according to Table 8 */
|
|
if (eci <= 899) {
|
|
chainemc[mclength++] = 927; /* ECI */
|
|
chainemc[mclength++] = eci;
|
|
} else if (eci <= 810899) {
|
|
chainemc[mclength++] = 926; /* ECI */
|
|
chainemc[mclength++] = (eci / 900) - 1;
|
|
chainemc[mclength++] = eci % 900;
|
|
} else {
|
|
chainemc[mclength++] = 925; /* ECI */
|
|
chainemc[mclength++] = eci - 810900;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < indexliste; i++) {
|
|
switch (liste[1][i]) {
|
|
case TEX: /* 547 - text mode */
|
|
pdf_textprocess(chainemc, &mclength, chaine, indexchaine, liste[0][i], *p_lastmode, p_curtable,
|
|
p_tex_padded);
|
|
*p_lastmode = TEX;
|
|
break;
|
|
case BYT: /* 670 - octet stream mode */
|
|
pdf_byteprocess(chainemc, &mclength, chaine, indexchaine, liste[0][i], *p_lastmode, debug_print);
|
|
if (*p_lastmode != TEX || liste[0][i] != 1) { /* don't switch mode on single byte shift from text mode */
|
|
*p_lastmode = BYT;
|
|
} else if (*p_curtable == T_PUNCT && *p_tex_padded) { /* If T_PUNCT and padded with AL */
|
|
/* Then need to reset to alpha - ISO/IEC 15438:2015 5.4.2.4 b) 2) */
|
|
*p_curtable = T_ALPHA;
|
|
}
|
|
break;
|
|
case NUM: /* 712 - numeric mode */
|
|
pdf_numbprocess(chainemc, &mclength, chaine, indexchaine, liste[0][i]);
|
|
*p_lastmode = NUM;
|
|
break;
|
|
}
|
|
indexchaine = indexchaine + liste[0][i];
|
|
}
|
|
|
|
*p_mclength = mclength;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Call `pdf_initial()` for each segment, dealing with Structured Append beforehand */
|
|
static int pdf_initial_segs(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count,
|
|
const int is_micro, int chainemc[PDF_MAX_LEN], int *p_mclength, int structapp_cws[18],
|
|
int *p_structapp_cp) {
|
|
int i;
|
|
int error_number = 0;
|
|
int structapp_cp = 0;
|
|
int lastmode;
|
|
int curtable;
|
|
int tex_padded;
|
|
|
|
*p_mclength = 0;
|
|
|
|
if (symbol->structapp.count) {
|
|
int id_cnt = 0, ids[10];
|
|
|
|
if (symbol->structapp.count < 2 || symbol->structapp.count > 99999) {
|
|
strcpy(symbol->errtxt, "740: Structured Append count out of range (2-99999)");
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
if (symbol->structapp.index < 1 || symbol->structapp.index > symbol->structapp.count) {
|
|
sprintf(symbol->errtxt, "741: Structured Append index out of range (1-%d)", symbol->structapp.count);
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
if (symbol->structapp.id[0]) {
|
|
int id_len;
|
|
|
|
for (id_len = 0; id_len < 32 && symbol->structapp.id[id_len]; id_len++);
|
|
|
|
if (id_len > 30) { /* 10 triplets */
|
|
strcpy(symbol->errtxt, "742: Structured Append ID too long (30 digit maximum)");
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
|
|
for (i = 0; i < id_len; i += 3, id_cnt++) {
|
|
const int triplet_len = i + 3 < id_len ? 3 : id_len - i;
|
|
ids[id_cnt] = to_int((const unsigned char *) (symbol->structapp.id + i), triplet_len);
|
|
if (ids[id_cnt] == -1) {
|
|
strcpy(symbol->errtxt, "743: Invalid Structured Append ID (digits only)");
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
if (ids[id_cnt] > 899) {
|
|
sprintf(symbol->errtxt, "744: Structured Append ID triplet %d '%03d' out of range (000-899)",
|
|
id_cnt + 1, ids[id_cnt]);
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
}
|
|
}
|
|
structapp_cws[structapp_cp++] = 928; /* Macro marker */
|
|
structapp_cws[structapp_cp++] = (100000 + symbol->structapp.index - 1) / 900; /* Segment index 1 */
|
|
structapp_cws[structapp_cp++] = (100000 + symbol->structapp.index - 1) % 900; /* Segment index 2 */
|
|
for (i = 0; i < id_cnt; i++) {
|
|
structapp_cws[structapp_cp++] = ids[i];
|
|
}
|
|
structapp_cws[structapp_cp++] = 923; /* Optional field */
|
|
structapp_cws[structapp_cp++] = 1; /* Segment count tag */
|
|
structapp_cws[structapp_cp++] = (100000 + symbol->structapp.count) / 900; /* Segment count 1 */
|
|
structapp_cws[structapp_cp++] = (100000 + symbol->structapp.count) % 900; /* Segment count 2 */
|
|
if (symbol->structapp.index == symbol->structapp.count) {
|
|
structapp_cws[structapp_cp++] = 922; /* Special last segment terminator */
|
|
}
|
|
}
|
|
*p_structapp_cp = structapp_cp;
|
|
|
|
/* Default mode for PDF417 is Text Compaction Alpha (ISO/IEC 15438:2015 5.4.2.1), and for MICROPDF417 is Byte
|
|
* Compaction (ISO/IEC 24728:2006 5.4.3) */
|
|
lastmode = is_micro ? BYT : TEX;
|
|
/* Start in upper alpha - tracked across calls to `pdf_textprocess()` to allow for interleaving byte shifts */
|
|
curtable = T_ALPHA;
|
|
|
|
for (i = 0; i < seg_count; i++) {
|
|
error_number = pdf_initial(symbol, segs[i].source, segs[i].length, segs[i].eci, is_micro, &lastmode,
|
|
&curtable, &tex_padded, chainemc, p_mclength);
|
|
if (error_number) { /* Only errors return >= ZINT_ERROR */
|
|
return error_number;
|
|
}
|
|
}
|
|
|
|
return error_number;
|
|
}
|
|
|
|
/* 366 */
|
|
static int pdf_enc(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
|
|
int i, j, longueur, loop, mccorrection[520] = {0}, offset;
|
|
int total, chainemc[PDF_MAX_LEN], mclength, c1, c2, c3, dummy[35];
|
|
int rows, cols, ecc, ecc_cws, padding;
|
|
char pattern[580];
|
|
int bp = 0;
|
|
int structapp_cws[18] = {0}; /* 3 (Index) + 10 (ID) + 4 (Count) + 1 (Last) */
|
|
int structapp_cp = 0;
|
|
int error_number;
|
|
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
|
|
static const int ecc_num_cws[] = { 2, 4, 8, 16, 32, 64, 128, 256, 512 };
|
|
|
|
if (segs_length(segs, seg_count) > PDF_MAX_LEN) {
|
|
strcpy(symbol->errtxt, "463: Input string too long");
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
|
|
error_number = pdf_initial_segs(symbol, segs, seg_count, 0 /*is_micro*/, chainemc, &mclength, structapp_cws,
|
|
&structapp_cp);
|
|
if (error_number) { /* Only errors return >= ZINT_ERROR */
|
|
return error_number;
|
|
}
|
|
|
|
if (debug_print) {
|
|
printf("\nCompressed data stream (%d):\n", mclength - 1);
|
|
for (i = 1; i < mclength; i++) { /* Skip unset length descriptor */
|
|
printf("%d ", chainemc[i]);
|
|
}
|
|
printf("\n\n");
|
|
}
|
|
|
|
/* 752 - Now take care of the number of CWs per row */
|
|
|
|
/* ECC */
|
|
ecc = symbol->option_1;
|
|
if (ecc < 0) { /* If not specified, set ECC depending on no. of codewords */
|
|
const int data_cws = mclength - 1 + structapp_cp; /* -1 for length descriptor */
|
|
/* ISO/IEC 15438:2015 Annex E Table E.1 Recommended minima */
|
|
if (data_cws <= 40) {
|
|
ecc = 2;
|
|
} else if (data_cws <= 160) {
|
|
ecc = 3;
|
|
} else if (data_cws <= 320) {
|
|
ecc = 4;
|
|
} else if (data_cws <= 863) {
|
|
ecc = 5;
|
|
} else {
|
|
ecc = 6; /* Not mentioned in Table E.1 */
|
|
}
|
|
}
|
|
ecc_cws = ecc_num_cws[ecc];
|
|
|
|
longueur = mclength + structapp_cp + ecc_cws;
|
|
|
|
if (debug_print) printf("Total No. of Codewords: %d, ECC %d, No. of ECC Codewords: %d\n", longueur, ecc, ecc_cws);
|
|
|
|
if (longueur > 928) {
|
|
/* Enforce maximum codeword limit */
|
|
strcpy(symbol->errtxt, "464: Input string too long");
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
|
|
cols = symbol->option_2;
|
|
rows = symbol->option_3;
|
|
if (rows) { /* Rows given */
|
|
if (cols < 1) { /* Cols automatic */
|
|
cols = (longueur + rows - 1) / rows;
|
|
if (cols <= 1) {
|
|
cols = 1;
|
|
} else {
|
|
/* Increase rows if would need > 30 columns */
|
|
for (; cols > 30 && rows < 90; rows++, cols = (longueur + rows - 1) / rows);
|
|
assert(cols <= 30);
|
|
/* Increase rows if multiple too big */
|
|
for (; cols >= 1 && rows < 90 && rows * cols > 928; rows++, cols = (longueur + rows - 1) / rows);
|
|
if (rows * cols > 928) {
|
|
strcpy(symbol->errtxt, "465: Data too long for specified number of rows");
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
}
|
|
} else { /* Cols given */
|
|
/* Increase rows if multiple too big */
|
|
for (; rows <= 90 && rows * cols < longueur; rows++);
|
|
if (rows > 90 || rows * cols > 928) {
|
|
strcpy(symbol->errtxt, "745: Data too long for specified number of columns");
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
}
|
|
if (rows != symbol->option_3) {
|
|
sprintf(symbol->errtxt, "746: Rows increased from %d to %d", symbol->option_3, rows);
|
|
error_number = ZINT_WARN_INVALID_OPTION;
|
|
}
|
|
} else { /* Rows automatic, cols automatic or given */
|
|
if (cols < 1) { /* Cols automatic */
|
|
cols = (int) round(sqrt((longueur - 1) / 3.0)); /* -1 (length descriptor) for back-compatibility */
|
|
}
|
|
rows = (longueur + cols - 1) / cols;
|
|
if (rows <= 3) {
|
|
rows = 3;
|
|
} else {
|
|
/* Increase cols if would need > 90 rows - do this even if cols specified for better back-compatibility
|
|
(though previously only increased once) */
|
|
for (; rows > 90 && cols < 30; cols++, rows = (longueur + cols - 1) / cols);
|
|
assert(rows <= 90);
|
|
/* Increase cols if multiple too big */
|
|
for (; rows >= 3 && cols < 30 && rows * cols > 928; cols++, rows = (longueur + cols - 1) / cols);
|
|
if (rows * cols > 928) {
|
|
strcpy(symbol->errtxt, "747: Data too long for specified number of columns");
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
if (symbol->option_2 && cols != symbol->option_2) { /* Note previously did not warn if cols auto-upped */
|
|
sprintf(symbol->errtxt, "748: Columns increased from %d to %d", symbol->option_2, cols);
|
|
error_number = ZINT_WARN_INVALID_OPTION;
|
|
}
|
|
}
|
|
}
|
|
assert(rows * cols >= longueur);
|
|
|
|
/* 781 - Padding calculation */
|
|
padding = rows * cols - longueur;
|
|
|
|
/* We add the padding */
|
|
for (i = 0; i < padding; i++) {
|
|
chainemc[mclength++] = 900;
|
|
}
|
|
if (debug_print) printf("Padding: %d\n", padding);
|
|
|
|
/* We add the Structured Append Macro Control Block if any */
|
|
if (structapp_cp) {
|
|
for (i = 0; i < structapp_cp; i++) {
|
|
chainemc[mclength++] = structapp_cws[i];
|
|
}
|
|
}
|
|
|
|
/* Set the length descriptor */
|
|
chainemc[0] = mclength;
|
|
|
|
/* 796 - we now take care of the Reed Solomon codes */
|
|
switch (ecc) {
|
|
case 1: offset = 2;
|
|
break;
|
|
case 2: offset = 6;
|
|
break;
|
|
case 3: offset = 14;
|
|
break;
|
|
case 4: offset = 30;
|
|
break;
|
|
case 5: offset = 62;
|
|
break;
|
|
case 6: offset = 126;
|
|
break;
|
|
case 7: offset = 254;
|
|
break;
|
|
case 8: offset = 510;
|
|
break;
|
|
default: offset = 0;
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < mclength; i++) {
|
|
total = (chainemc[i] + mccorrection[ecc_cws - 1]) % 929;
|
|
for (j = ecc_cws - 1; j > 0; j--) {
|
|
mccorrection[j] = (mccorrection[j - 1] + 929 - (total * pdf_coefrs[offset + j]) % 929) % 929;
|
|
}
|
|
mccorrection[0] = (929 - (total * pdf_coefrs[offset]) % 929) % 929;
|
|
}
|
|
|
|
/* we add these codes to the string */
|
|
for (i = ecc_cws - 1; i >= 0; i--) {
|
|
chainemc[mclength++] = mccorrection[i] ? 929 - mccorrection[i] : 0;
|
|
}
|
|
|
|
if (debug_print) {
|
|
printf("Complete CW string (%d):\n", mclength);
|
|
for (i = 0; i < mclength; i++) {
|
|
printf("%d ", chainemc[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
#ifdef ZINT_TEST
|
|
if (symbol->debug & ZINT_DEBUG_TEST) {
|
|
debug_test_codeword_dump_int(symbol, chainemc, mclength);
|
|
}
|
|
#endif
|
|
|
|
if (debug_print) printf("\nSymbol size:\n%d columns x %d rows\n", cols, rows);
|
|
|
|
/* 818 - The CW string is finished */
|
|
c1 = (rows - 1) / 3;
|
|
c2 = ecc * 3 + (rows - 1) % 3;
|
|
c3 = cols - 1;
|
|
|
|
/* we now encode each row */
|
|
for (i = 0; i < rows; i++) {
|
|
const int k = (i / 3) * 30;
|
|
bp = 0;
|
|
for (j = 0; j < cols; j++) {
|
|
dummy[j + 1] = chainemc[i * cols + j];
|
|
}
|
|
switch (i % 3) {
|
|
case 0:
|
|
dummy[0] = k + c1;
|
|
dummy[cols + 1] = k + c3;
|
|
offset = 0; /* cluster(0) */
|
|
break;
|
|
case 1:
|
|
dummy[0] = k + c2;
|
|
dummy[cols + 1] = k + c1;
|
|
offset = 929; /* cluster(3) */
|
|
break;
|
|
case 2:
|
|
dummy[0] = k + c3;
|
|
dummy[cols + 1] = k + c2;
|
|
offset = 1858; /* cluster(6) */
|
|
break;
|
|
}
|
|
bp = bin_append_posn(0x1FEA8, 17, pattern, bp); /* Row start */
|
|
|
|
for (j = 0; j <= cols; j++) {
|
|
bp = bin_append_posn(pdf_bitpattern[offset + dummy[j]], 16, pattern, bp);
|
|
pattern[bp++] = '0';
|
|
}
|
|
|
|
if (symbol->symbology != BARCODE_PDF417COMP) {
|
|
bp = bin_append_posn(pdf_bitpattern[offset + dummy[j]], 16, pattern, bp);
|
|
pattern[bp++] = '0';
|
|
bp = bin_append_posn(0x3FA29, 18, pattern, bp); /* Row Stop */
|
|
} else {
|
|
pattern[bp++] = '1'; /* Compact PDF417 Stop pattern */
|
|
}
|
|
|
|
for (loop = 0; loop < bp; loop++) {
|
|
if (pattern[loop] == '1') {
|
|
set_module(symbol, i, loop);
|
|
}
|
|
}
|
|
}
|
|
symbol->width = bp;
|
|
symbol->rows = rows;
|
|
|
|
/* ISO/IEC 15438:2015 Section 5.8.2 3X minimum row height */
|
|
if (error_number) {
|
|
(void) set_height(symbol, 3.0f, 0.0f, 0.0f, 1 /*no_errtxt*/);
|
|
} else {
|
|
error_number = set_height(symbol, 3.0f, 0.0f, 0.0f, 0 /*no_errtxt*/);
|
|
}
|
|
|
|
/* 843 */
|
|
return error_number;
|
|
}
|
|
|
|
/* 345 */
|
|
INTERNAL int pdf417(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
|
|
int codeerr, error_number;
|
|
|
|
error_number = 0;
|
|
|
|
if ((symbol->option_1 < -1) || (symbol->option_1 > 8)) {
|
|
strcpy(symbol->errtxt, "460: Security value out of range");
|
|
if (symbol->warn_level == WARN_FAIL_ALL) {
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
symbol->option_1 = -1;
|
|
error_number = ZINT_WARN_INVALID_OPTION;
|
|
}
|
|
if ((symbol->option_2 < 0) || (symbol->option_2 > 30)) {
|
|
strcpy(symbol->errtxt, "461: Number of columns out of range (1 to 30)");
|
|
if (symbol->warn_level == WARN_FAIL_ALL) {
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
symbol->option_2 = 0;
|
|
error_number = ZINT_WARN_INVALID_OPTION;
|
|
}
|
|
if (symbol->option_3 && (symbol->option_3 < 3 || symbol->option_3 > 90)) {
|
|
strcpy(symbol->errtxt, "466: Number of rows out of range (3 to 90)");
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
if (symbol->option_2 && symbol->option_3 && symbol->option_2 * symbol->option_3 > 928) {
|
|
strcpy(symbol->errtxt, "475: Columns x rows out of range (1 to 928)");
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
|
|
/* 349 */
|
|
codeerr = pdf_enc(symbol, segs, seg_count);
|
|
|
|
/* 352 */
|
|
if (codeerr != 0) {
|
|
error_number = codeerr;
|
|
}
|
|
|
|
/* 364 */
|
|
return error_number;
|
|
}
|
|
|
|
/* like PDF417 only much smaller! */
|
|
INTERNAL int micropdf417(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
|
|
int i, k, j, longueur, mccorrection[50] = {0}, offset;
|
|
int total, chainemc[PDF_MAX_LEN], mclength, error_number = 0;
|
|
char pattern[580];
|
|
int bp = 0;
|
|
int structapp_cws[18] = {0}; /* 3 (Index) + 10 (ID) + 4 (Count) + 1 (Last) */
|
|
int structapp_cp = 0;
|
|
int variant, LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster;
|
|
int LeftRAP, CentreRAP, RightRAP, Cluster, loop;
|
|
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
|
|
|
|
if (segs_length(segs, seg_count) > MICRO_PDF_MAX_LEN) {
|
|
strcpy(symbol->errtxt, "474: Input data too long");
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
if (symbol->option_3) {
|
|
strcpy(symbol->errtxt, "476: Cannot specify rows for MicroPDF417");
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
|
|
/* Encoding starts out the same as PDF417, so use the same code */
|
|
|
|
error_number = pdf_initial_segs(symbol, segs, seg_count, 1 /*is_micro*/, chainemc, &mclength, structapp_cws,
|
|
&structapp_cp);
|
|
if (error_number) { /* Only errors return >= ZINT_ERROR */
|
|
return error_number;
|
|
}
|
|
|
|
/* This is where it all changes! */
|
|
|
|
if (mclength + structapp_cp > 126) {
|
|
strcpy(symbol->errtxt, "467: Input data too long");
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
if (symbol->option_2 > 4) {
|
|
strcpy(symbol->errtxt, "468: Specified width out of range");
|
|
if (symbol->warn_level == WARN_FAIL_ALL) {
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
symbol->option_2 = 0;
|
|
error_number = ZINT_WARN_INVALID_OPTION;
|
|
}
|
|
|
|
if (debug_print) {
|
|
printf("\nEncoded Data Stream (%d):\n", mclength);
|
|
for (i = 0; i < mclength; i++) {
|
|
printf("%3d ", chainemc[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/* Now figure out which variant of the symbol to use and load values accordingly */
|
|
|
|
variant = 0;
|
|
|
|
if ((symbol->option_2 == 1) && (mclength + structapp_cp > 20)) {
|
|
/* the user specified 1 column but the data doesn't fit - go to automatic */
|
|
strcpy(symbol->errtxt, "469: Specified symbol size too small for data");
|
|
if (symbol->warn_level == WARN_FAIL_ALL) {
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
symbol->option_2 = 0;
|
|
error_number = ZINT_WARN_INVALID_OPTION;
|
|
}
|
|
|
|
if ((symbol->option_2 == 2) && (mclength + structapp_cp > 37)) {
|
|
/* the user specified 2 columns but the data doesn't fit - go to automatic */
|
|
strcpy(symbol->errtxt, "470: Specified symbol size too small for data");
|
|
if (symbol->warn_level == WARN_FAIL_ALL) {
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
symbol->option_2 = 0;
|
|
error_number = ZINT_WARN_INVALID_OPTION;
|
|
}
|
|
|
|
if ((symbol->option_2 == 3) && (mclength + structapp_cp > 82)) {
|
|
/* the user specified 3 columns but the data doesn't fit - go to automatic */
|
|
strcpy(symbol->errtxt, "471: Specified symbol size too small for data");
|
|
if (symbol->warn_level == WARN_FAIL_ALL) {
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
}
|
|
symbol->option_2 = 0;
|
|
error_number = ZINT_WARN_INVALID_OPTION;
|
|
}
|
|
|
|
if (symbol->option_2 == 1) {
|
|
/* the user specified 1 column and the data does fit */
|
|
if (mclength + structapp_cp <= 4) {
|
|
variant = 1;
|
|
} else if (mclength + structapp_cp <= 7) {
|
|
variant = 2;
|
|
} else if (mclength + structapp_cp <= 10) {
|
|
variant = 3;
|
|
} else if (mclength + structapp_cp <= 12) {
|
|
variant = 4;
|
|
} else if (mclength + structapp_cp <= 16) {
|
|
variant = 5;
|
|
} else {
|
|
variant = 6;
|
|
}
|
|
} else if (symbol->option_2 == 2) {
|
|
/* the user specified 2 columns and the data does fit */
|
|
if (mclength + structapp_cp <= 8) {
|
|
variant = 7;
|
|
} else if (mclength + structapp_cp <= 13) {
|
|
variant = 8;
|
|
} else if (mclength + structapp_cp <= 19) {
|
|
variant = 9;
|
|
} else if (mclength + structapp_cp <= 24) {
|
|
variant = 10;
|
|
} else if (mclength + structapp_cp <= 29) {
|
|
variant = 11;
|
|
} else if (mclength + structapp_cp <= 33) {
|
|
variant = 12;
|
|
} else {
|
|
variant = 13;
|
|
}
|
|
} else if (symbol->option_2 == 3) {
|
|
/* the user specified 3 columns and the data does fit */
|
|
if (mclength + structapp_cp <= 6) {
|
|
variant = 14;
|
|
} else if (mclength + structapp_cp <= 10) {
|
|
variant = 15;
|
|
} else if (mclength + structapp_cp <= 14) {
|
|
variant = 16;
|
|
} else if (mclength + structapp_cp <= 18) {
|
|
variant = 17;
|
|
} else if (mclength + structapp_cp <= 24) {
|
|
variant = 18;
|
|
} else if (mclength + structapp_cp <= 34) {
|
|
variant = 19;
|
|
} else if (mclength + structapp_cp <= 46) {
|
|
variant = 20;
|
|
} else if (mclength + structapp_cp <= 58) {
|
|
variant = 21;
|
|
} else if (mclength + structapp_cp <= 70) {
|
|
variant = 22;
|
|
} else {
|
|
variant = 23;
|
|
}
|
|
} else if (symbol->option_2 == 4) {
|
|
/* the user specified 4 columns and the data does fit */
|
|
if (mclength + structapp_cp <= 8) {
|
|
variant = 24;
|
|
} else if (mclength + structapp_cp <= 12) {
|
|
variant = 25;
|
|
} else if (mclength + structapp_cp <= 18) {
|
|
variant = 26;
|
|
} else if (mclength + structapp_cp <= 24) {
|
|
variant = 27;
|
|
} else if (mclength + structapp_cp <= 30) {
|
|
variant = 28;
|
|
} else if (mclength + structapp_cp <= 39) {
|
|
variant = 29;
|
|
} else if (mclength + structapp_cp <= 54) {
|
|
variant = 30;
|
|
} else if (mclength + structapp_cp <= 72) {
|
|
variant = 31;
|
|
} else if (mclength + structapp_cp <= 90) {
|
|
variant = 32;
|
|
} else if (mclength + structapp_cp <= 108) {
|
|
variant = 33;
|
|
} else {
|
|
variant = 34;
|
|
}
|
|
} else {
|
|
/* Zint can choose automatically from all available variations */
|
|
for (i = 27; i >= 0; i--) {
|
|
/* Note mclength + structapp_cp <= 126 and pdf_MicroAutosize[27] == 126 so variant will be set */
|
|
if (pdf_MicroAutosize[i] >= mclength + structapp_cp) {
|
|
variant = pdf_MicroAutosize[i + 28];
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
assert(variant > 0); /* Suppress clang-tidy clang-analyzer-core.uninitialized.Assign */
|
|
|
|
/* Now we have the variant we can load the data */
|
|
variant--;
|
|
symbol->option_2 = pdf_MicroVariants[variant]; /* columns */
|
|
symbol->rows = pdf_MicroVariants[variant + 34]; /* rows */
|
|
k = pdf_MicroVariants[variant + 68]; /* number of EC CWs */
|
|
longueur = (symbol->option_2 * symbol->rows) - k; /* number of non-EC CWs */
|
|
i = longueur - (mclength + structapp_cp); /* amount of padding required */
|
|
offset = pdf_MicroVariants[variant + 102]; /* coefficient offset */
|
|
|
|
if (debug_print) {
|
|
printf("\nChoose symbol size:\n");
|
|
printf("%d columns x %d rows, variant %d\n", symbol->option_2, symbol->rows, variant + 1);
|
|
printf("%d data codewords (including %d pads), %d ecc codewords\n", longueur, i, k);
|
|
printf("\n");
|
|
}
|
|
|
|
/* We add the padding */
|
|
while (i > 0) {
|
|
chainemc[mclength++] = 900;
|
|
i--;
|
|
}
|
|
|
|
/* We add the Structured Append Macro Control Block if any */
|
|
if (structapp_cp) {
|
|
for (i = 0; i < structapp_cp; i++) {
|
|
chainemc[mclength++] = structapp_cws[i];
|
|
}
|
|
}
|
|
|
|
/* Reed-Solomon error correction */
|
|
longueur = mclength;
|
|
for (i = 0; i < longueur; i++) {
|
|
total = (chainemc[i] + mccorrection[k - 1]) % 929;
|
|
for (j = k - 1; j >= 0; j--) {
|
|
if (j == 0) {
|
|
mccorrection[j] = (929 - (total * pdf_Microcoeffs[offset + j]) % 929) % 929;
|
|
} else {
|
|
mccorrection[j] = (mccorrection[j - 1] + 929 - (total * pdf_Microcoeffs[offset + j]) % 929) % 929;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < k; j++) {
|
|
if (mccorrection[j] != 0) {
|
|
mccorrection[j] = 929 - mccorrection[j];
|
|
}
|
|
}
|
|
/* we add these codes to the string */
|
|
for (i = k - 1; i >= 0; i--) {
|
|
chainemc[mclength++] = mccorrection[i];
|
|
}
|
|
|
|
if (debug_print) {
|
|
printf("Encoded Data Stream with ECC (%d):\n", mclength);
|
|
for (i = 0; i < mclength; i++) {
|
|
printf("%3d ", chainemc[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
#ifdef ZINT_TEST
|
|
if (symbol->debug & ZINT_DEBUG_TEST) {
|
|
debug_test_codeword_dump_int(symbol, chainemc, mclength);
|
|
}
|
|
#endif
|
|
|
|
/* Now get the RAP (Row Address Pattern) start values */
|
|
LeftRAPStart = pdf_RAPTable[variant];
|
|
CentreRAPStart = pdf_RAPTable[variant + 34];
|
|
RightRAPStart = pdf_RAPTable[variant + 68];
|
|
StartCluster = pdf_RAPTable[variant + 102] / 3;
|
|
|
|
/* That's all values loaded, get on with the encoding */
|
|
|
|
LeftRAP = LeftRAPStart;
|
|
CentreRAP = CentreRAPStart;
|
|
RightRAP = RightRAPStart;
|
|
Cluster = StartCluster;
|
|
/* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */
|
|
|
|
if (debug_print) printf("\nInternal row representation:\n");
|
|
for (i = 0; i < symbol->rows; i++) {
|
|
if (debug_print) printf("row %d: ", i);
|
|
bp = 0;
|
|
offset = 929 * Cluster;
|
|
k = i * symbol->option_2;
|
|
|
|
/* Copy the data into codebarre */
|
|
bp = bin_append_posn(pdf_rap_side[LeftRAP - 1], 10, pattern, bp);
|
|
bp = bin_append_posn(pdf_bitpattern[offset + chainemc[k]], 16, pattern, bp);
|
|
pattern[bp++] = '0';
|
|
if (symbol->option_2 >= 2) {
|
|
if (symbol->option_2 == 3) {
|
|
bp = bin_append_posn(pdf_rap_centre[CentreRAP - 1], 10, pattern, bp);
|
|
}
|
|
bp = bin_append_posn(pdf_bitpattern[offset + chainemc[k + 1]], 16, pattern, bp);
|
|
pattern[bp++] = '0';
|
|
if (symbol->option_2 >= 3) {
|
|
if (symbol->option_2 == 4) {
|
|
bp = bin_append_posn(pdf_rap_centre[CentreRAP - 1], 10, pattern, bp);
|
|
}
|
|
bp = bin_append_posn(pdf_bitpattern[offset + chainemc[k + 2]], 16, pattern, bp);
|
|
pattern[bp++] = '0';
|
|
if (symbol->option_2 == 4) {
|
|
bp = bin_append_posn(pdf_bitpattern[offset + chainemc[k + 3]], 16, pattern, bp);
|
|
pattern[bp++] = '0';
|
|
}
|
|
}
|
|
}
|
|
bp = bin_append_posn(pdf_rap_side[RightRAP - 1], 10, pattern, bp);
|
|
pattern[bp++] = '1'; /* stop */
|
|
if (debug_print) printf("%.*s\n", bp, pattern);
|
|
|
|
/* so now pattern[] holds the string of '1's and '0's. - copy this to the symbol */
|
|
for (loop = 0; loop < bp; loop++) {
|
|
if (pattern[loop] == '1') {
|
|
set_module(symbol, i, loop);
|
|
}
|
|
}
|
|
|
|
/* Set up RAPs and Cluster for next row */
|
|
LeftRAP++;
|
|
CentreRAP++;
|
|
RightRAP++;
|
|
Cluster++;
|
|
|
|
if (LeftRAP == 53) {
|
|
LeftRAP = 1;
|
|
}
|
|
if (CentreRAP == 53) {
|
|
CentreRAP = 1;
|
|
}
|
|
if (RightRAP == 53) {
|
|
RightRAP = 1;
|
|
}
|
|
if (Cluster == 3) {
|
|
Cluster = 0;
|
|
}
|
|
}
|
|
symbol->width = bp;
|
|
|
|
/* ISO/IEC 24728:2006 Section 5.8.2 2X minimum row height */
|
|
if (error_number) {
|
|
(void) set_height(symbol, 2.0f, 0.0f, 0.0f, 1 /*no_errtxt*/);
|
|
} else {
|
|
error_number = set_height(symbol, 2.0f, 0.0f, 0.0f, 0 /*no_errtxt*/);
|
|
}
|
|
|
|
return error_number;
|
|
}
|
|
|
|
#undef TEX
|
|
#undef BYT
|
|
#undef NUM
|
|
|
|
#undef T_ALPHA
|
|
#undef T_LOWER
|
|
#undef T_MIXED
|
|
#undef T_PUNCT
|
|
|
|
/* vim: set ts=4 sw=4 et : */
|