Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/zint/backend/composite.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/zint/backend/composite.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,1590 @@ +/* composite.c - Handles GS1 Composite Symbols */ +/* + libzint - the open source barcode library + Copyright (C) 2008-2024 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 */ + +/* The functions "getBit", "init928" and "encode928" are copyright BSI and are + released with permission under the following terms: + + "Copyright subsists in all BSI publications. BSI also holds the copyright, in the + UK, of the international standardisation bodies. Except as + permitted under the Copyright, Designs and Patents Act 1988 no extract may be + reproduced, stored in a retrieval system or transmitted in any form or by any + means - electronic, photocopying, recording or otherwise - without prior written + permission from BSI. + + "This does not preclude the free use, in the course of implementing the standard, + of necessary details such as symbols, and size, type or grade designations. If these + details are to be used for any other purpose than implementation then the prior + written permission of BSI must be obtained." + + The date of publication for these functions is 31 May 2006 + */ + +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include "common.h" +#include "pdf417.h" +#include "gs1.h" +#include "general_field.h" + +#include "composite.h" + +INTERNAL int gs1_128_cc(struct zint_symbol *symbol, unsigned char source[], int length, const int cc_mode, + const int cc_rows); + +INTERNAL int eanx_cc(struct zint_symbol *symbol, unsigned char source[], int length, const int cc_rows); +INTERNAL int ean_leading_zeroes(struct zint_symbol *symbol, const unsigned char source[], + unsigned char local_source[], int *p_with_addon, unsigned char *zfirst_part, + unsigned char *zsecond_part); + +INTERNAL int dbar_omnstk_set_height(struct zint_symbol *symbol, const int first_row); +INTERNAL int dbar_omn_cc(struct zint_symbol *symbol, unsigned char source[], int length, const int cc_rows); +INTERNAL int dbar_ltd_cc(struct zint_symbol *symbol, unsigned char source[], int length, const int cc_rows); +INTERNAL int dbar_exp_cc(struct zint_symbol *symbol, unsigned char source[], int length, const int cc_rows); +INTERNAL int dbar_date(const unsigned char source[], const int length, const int src_posn); + +static int cc_min(const int first, const int second) { + + if (first <= second) + return first; + else + return second; +} + +/* gets bit in bitString at bitPos */ +static int cc_getBit(const unsigned short *bitStr, const int bitPos) { + return !!(bitStr[bitPos >> 4] & (0x8000 >> (bitPos & 15))); +} + +/* converts bit string to base 928 values, codeWords[0] is highest order */ +static int cc_encode928(const unsigned short bitString[], unsigned short codeWords[], const int bitLng) { + int i, j, b, cwNdx, cwLng; + for (cwNdx = cwLng = b = 0; b < bitLng; b += 69, cwNdx += 7) { + const int bitCnt = cc_min(bitLng - b, 69); + int cwCnt; + cwLng += cwCnt = bitCnt / 10 + 1; + for (i = 0; i < cwCnt; i++) + codeWords[cwNdx + i] = 0; /* init 0 */ + for (i = 0; i < bitCnt; i++) { + if (cc_getBit(bitString, b + bitCnt - i - 1)) { + for (j = 0; j < cwCnt; j++) + codeWords[cwNdx + j] += cc_pwr928[i][j + 7 - cwCnt]; + } + } + for (i = cwCnt - 1; i > 0; i--) { + /* add "carries" */ + codeWords[cwNdx + i - 1] += codeWords[cwNdx + i] / 928; + codeWords[cwNdx + i] %= 928; + } + } + return (cwLng); +} + +/* CC-A 2D component */ +static void cc_a(struct zint_symbol *symbol, const char source[], const int cc_width) { + int i, segment, bitlen, cwCnt, variant, rows; + int k, offset, j, total, rsCodeWords[8] = {0}; + int LeftRAPStart, RightRAPStart, CentreRAPStart, StartCluster; + int LeftRAP, RightRAP, CentreRAP, Cluster; + int loop; + unsigned short codeWords[28] = {0}; + unsigned short bitStr[13] = {0}; + char pattern[580]; + int bp = 0; + const int debug_print = symbol->debug & ZINT_DEBUG_PRINT; + + variant = 0; + + bitlen = (int) strlen(source); + + for (segment = 0; segment < 13; segment++) { + const int strpos = segment * 16; + if (strpos >= bitlen) { + break; + } + for (i = 0; i < 16 && strpos + i < bitlen; i++) { + if (source[strpos + i] == '1') { + bitStr[segment] |= (0x8000 >> i); + } + } + } + + /* encode codeWords from bitStr */ + cwCnt = cc_encode928(bitStr, codeWords, bitlen); + + switch (cc_width) { + case 2: + switch (cwCnt) { + case 6: variant = 0; + break; + case 8: variant = 1; + break; + case 9: variant = 2; + break; + case 11: variant = 3; + break; + case 12: variant = 4; + break; + case 14: variant = 5; + break; + case 17: variant = 6; + break; + } + break; + case 3: + switch (cwCnt) { + case 8: variant = 7; + break; + case 10: variant = 8; + break; + case 12: variant = 9; + break; + case 14: variant = 10; + break; + case 17: variant = 11; + break; + } + break; + case 4: + switch (cwCnt) { + case 8: variant = 12; + break; + case 11: variant = 13; + break; + case 14: variant = 14; + break; + case 17: variant = 15; + break; + case 20: variant = 16; + break; + } + break; + } + + rows = cc_aVariants[variant]; + k = cc_aVariants[17 + variant]; + offset = cc_aVariants[34 + variant]; + + /* Reed-Solomon error correction */ + + for (i = 0; i < cwCnt; i++) { + total = (codeWords[i] + rsCodeWords[k - 1]) % 929; + for (j = k - 1; j >= 0; j--) { + if (j == 0) { + rsCodeWords[j] = (929 - (total * cc_aCoeffs[offset + j]) % 929) % 929; + } else { + rsCodeWords[j] = (rsCodeWords[j - 1] + 929 - (total * cc_aCoeffs[offset + j]) % 929) % 929; + } + } + } + + for (j = 0; j < k; j++) { + if (rsCodeWords[j] != 0) { + rsCodeWords[j] = 929 - rsCodeWords[j]; + } + } + + for (i = k - 1; i >= 0; i--) { + codeWords[cwCnt] = rsCodeWords[i]; + cwCnt++; + } + + /* Place data into table */ + LeftRAPStart = cc_aRAPTable[variant]; + CentreRAPStart = cc_aRAPTable[variant + 17]; + RightRAPStart = cc_aRAPTable[variant + 34]; + StartCluster = cc_aRAPTable[variant + 51] / 3; + + LeftRAP = LeftRAPStart; + CentreRAP = CentreRAPStart; + RightRAP = RightRAPStart; + Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */ + + for (i = 0; i < rows; i++) { + bp = 0; + offset = 929 * Cluster; + k = i * cc_width; + /* Copy the data into codebarre */ + if (cc_width != 3) { + bp = bin_append_posn(pdf_rap_side[LeftRAP - 1], 10, pattern, bp); + } + bp = bin_append_posn(pdf_bitpattern[offset + codeWords[k]], 16, pattern, bp); + pattern[bp++] = '0'; + if (cc_width >= 2) { + if (cc_width == 3) { + bp = bin_append_posn(pdf_rap_centre[CentreRAP - 1], 10, pattern, bp); + } + bp = bin_append_posn(pdf_bitpattern[offset + codeWords[k + 1]], 16, pattern, bp); + pattern[bp++] = '0'; + if (cc_width >= 3) { + if (cc_width == 4) { + bp = bin_append_posn(pdf_rap_centre[CentreRAP - 1], 10, pattern, bp); + } + bp = bin_append_posn(pdf_bitpattern[offset + codeWords[k + 2]], 16, pattern, bp); + pattern[bp++] = '0'; + if (cc_width == 4) { + bp = bin_append_posn(pdf_bitpattern[offset + codeWords[k + 3]], 16, pattern, bp); + pattern[bp++] = '0'; + } + } + } + bp = bin_append_posn(pdf_rap_side[RightRAP - 1], 10, pattern, bp); + pattern[bp++] = '1'; /* stop */ + + /* 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); + } + } + symbol->row_height[i] = 2; + symbol->rows++; + + /* 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; + + if (debug_print) { + printf("CC-A Columns: %d, Rows: %d, Variant: %d, CodeWords: %d\n", cc_width, symbol->rows, variant, cwCnt); + } +} + +/* CC-B 2D component */ +static void cc_b(struct zint_symbol *symbol, const char source[], const int cc_width) { + const int length = (int) strlen(source) / 8; + int i; + unsigned char *data_string = (unsigned char *) z_alloca(length + 3); + short chainemc[180]; + int mclength = 0; + int k, j, p, longueur, mccorrection[50] = {0}, offset; + int total; + char pattern[580]; + int variant, LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster; + int LeftRAP, CentreRAP, RightRAP, Cluster, loop; + int columns; + int bp = 0; + const int debug_print = symbol->debug & ZINT_DEBUG_PRINT; + + for (i = 0; i < length; i++) { + const int binloc = i * 8; + + data_string[i] = 0; + for (p = 0; p < 8; p++) { + if (source[binloc + p] == '1') { + data_string[i] |= (0x80 >> p); + } + } + } + + /* "the CC-B component shall have codeword 920 in the first symbol character position" (section 9a) */ + chainemc[mclength++] = 920; + + pdf_byteprocess(chainemc, &mclength, data_string, 0, length, 0); + + /* Now figure out which variant of the symbol to use and load values accordingly */ + + variant = 0; + + if (cc_width == 2) { + if (mclength <= 8) { + variant = 7; + } else if (mclength <= 13) { + variant = 8; + } else if (mclength <= 19) { + variant = 9; + } else if (mclength <= 24) { + variant = 10; + } else if (mclength <= 29) { + variant = 11; + } else if (mclength <= 33) { + variant = 12; + } else { + variant = 13; + } + } else if (cc_width == 3) { + if (mclength <= 6) { + variant = 14; + } else if (mclength <= 10) { + variant = 15; + } else if (mclength <= 14) { + variant = 16; + } else if (mclength <= 18) { + variant = 17; + } else if (mclength <= 24) { + variant = 18; + } else if (mclength <= 34) { + variant = 19; + } else if (mclength <= 46) { + variant = 20; + } else if (mclength <= 58) { + variant = 21; + } else if (mclength <= 70) { + variant = 22; + } else { + variant = 23; + } + } else if (cc_width == 4) { + if (mclength <= 8) { + variant = 24; + } else if (mclength <= 12) { + variant = 25; + } else if (mclength <= 18) { + variant = 26; + } else if (mclength <= 24) { + variant = 27; + } else if (mclength <= 30) { + variant = 28; + } else if (mclength <= 39) { + variant = 29; + } else if (mclength <= 54) { + variant = 30; + } else if (mclength <= 72) { + variant = 31; + } else if (mclength <= 90) { + variant = 32; + } else if (mclength <= 108) { + variant = 33; + } else { + variant = 34; + } + } + + /* Now we have the variant we can load the data - from here on the same as MicroPDF417 code */ + variant--; + assert(variant >= 0); + columns = pdf_MicroVariants[variant]; /* columns */ + symbol->rows = pdf_MicroVariants[variant + 34]; /* rows */ + k = pdf_MicroVariants[variant + 68]; /* number of EC CWs */ + longueur = (columns * symbol->rows) - k; /* number of non-EC CWs */ + i = longueur - mclength; /* amount of padding required */ + offset = pdf_MicroVariants[variant + 102]; /* coefficient offset */ + + /* Binary input padded to target length so no padding should be necessary */ + while (i > 0) { + chainemc[mclength++] = 900; /* Not reached */ + 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]; + } + + /* 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) */ + + for (i = 0; i < symbol->rows; i++) { + bp = 0; + offset = 929 * Cluster; + k = i * columns; + /* 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 (cc_width >= 2) { + if (cc_width == 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 (cc_width >= 3) { + if (cc_width == 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 (cc_width == 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 */ + + /* 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); + } + } + symbol->row_height[i] = 2; + + /* 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; + + if (debug_print) { + printf("CC-B Columns: %d, Rows: %d, Variant: %d, CodeWords: %d\n", + cc_width, symbol->rows, variant + 1, mclength); + } +} + +/* CC-C 2D component - byte compressed PDF417 */ +static void cc_c(struct zint_symbol *symbol, const char source[], const int cc_width, const int ecc_level) { + const int length = (int) strlen(source) / 8; + int i, p; + unsigned char *data_string = (unsigned char *) z_alloca(length + 4); + short chainemc[1000]; + int mclength = 0, k; + int offset, longueur, loop, total, j, mccorrection[520] = {0}; + int c1, c2, c3, dummy[35]; + char pattern[580]; + int bp = 0; + const int debug_print = symbol->debug & ZINT_DEBUG_PRINT; + + for (i = 0; i < length; i++) { + const int binloc = i * 8; + + data_string[i] = 0; + for (p = 0; p < 8; p++) { + if (source[binloc + p] == '1') { + data_string[i] |= (0x80 >> p); + } + } + } + + chainemc[mclength++] = 0; /* space for length descriptor */ + chainemc[mclength++] = 920; /* CC-C identifier */ + + pdf_byteprocess(chainemc, &mclength, data_string, 0, length, 0); + + chainemc[0] = mclength; + + if (debug_print) { + printf("CC-C Codewords (%d):", mclength); + for (i = 0; i < mclength; i++) printf(" %d", chainemc[i]); + fputc('\n', stdout); + } + + k = 1; + for (i = 1; i <= (ecc_level + 1); i++) { + k *= 2; + } + + /* 796 - we now take care of the Reed Solomon codes */ + switch (ecc_level) { + case 1: offset = 2; /* Not reached */ + break; + case 2: offset = 6; /* Min ECC currently used is 2 */ + break; + case 3: offset = 14; + break; + case 4: offset = 30; + break; + case 5: offset = 62; /* Max ECC currently used is 5 */ + break; + case 6: offset = 126; /* Not reached */ + break; + case 7: offset = 254; /* Not reached */ + break; + case 8: offset = 510; /* Not reached */ + break; + default: offset = 0; /* Not reached */ + break; + } + + 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_coefrs[offset + j]) % 929) % 929; + } else { + mccorrection[j] = (mccorrection[j - 1] + 929 - (total * pdf_coefrs[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]; + } + + /* 818 - The CW string is finished */ + symbol->rows = mclength / cc_width; + c1 = (symbol->rows - 1) / 3; + c2 = ecc_level * 3 + (symbol->rows - 1) % 3; + c3 = cc_width - 1; + + /* we now encode each row */ + for (i = 0; i <= symbol->rows - 1; i++) { + for (j = 0; j < cc_width; j++) { + dummy[j + 1] = chainemc[i * cc_width + j]; + } + k = (i / 3) * 30; + switch (i % 3) { + case 0: + dummy[0] = k + c1; + dummy[cc_width + 1] = k + c3; + offset = 0; /* cluster(0) */ + break; + case 1: + dummy[0] = k + c2; + dummy[cc_width + 1] = k + c1; + offset = 929; /* cluster(3) */ + break; + case 2: + dummy[0] = k + c3; + dummy[cc_width + 1] = k + c2; + offset = 1858; /* cluster(6) */ + break; + } + bp = 0; + bp = bin_append_posn(0x1FEA8, 17, pattern, bp); /* Row start */ + + for (j = 0; j <= cc_width + 1; j++) { + 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 */ + + for (loop = 0; loop < bp; loop++) { + if (pattern[loop] == '1') { + set_module(symbol, i, loop); + } + } + symbol->row_height[i] = 3; + } + symbol->width = bp; + + if (debug_print) { + printf("CC-C Columns: %d, Rows: %d, CodeWords: %d, ECC Level: %d\n", + cc_width, symbol->rows, mclength, ecc_level); + } +} + +static int cc_a_calc_padding(const int binary_length, const int cc_width) { + int target_bitsize = 0; + + switch (cc_width) { + case 2: + if (binary_length <= 59) { + target_bitsize = 59; + } else if (binary_length <= 78) { + target_bitsize = 78; + } else if (binary_length <= 88) { + target_bitsize = 88; + } else if (binary_length <= 108) { + target_bitsize = 108; + } else if (binary_length <= 118) { + target_bitsize = 118; + } else if (binary_length <= 138) { + target_bitsize = 138; + } else if (binary_length <= 167) { + target_bitsize = 167; + } + break; + case 3: + if (binary_length <= 78) { + target_bitsize = 78; + } else if (binary_length <= 98) { + target_bitsize = 98; + } else if (binary_length <= 118) { + target_bitsize = 118; + } else if (binary_length <= 138) { + target_bitsize = 138; + } else if (binary_length <= 167) { + target_bitsize = 167; + } + break; + case 4: + if (binary_length <= 78) { + target_bitsize = 78; + } else if (binary_length <= 108) { + target_bitsize = 108; + } else if (binary_length <= 138) { + target_bitsize = 138; + } else if (binary_length <= 167) { + target_bitsize = 167; + } else if (binary_length <= 197) { + target_bitsize = 197; + } + break; + } + + return target_bitsize; +} + +static int cc_b_calc_padding(const int binary_length, const int cc_width) { + int target_bitsize = 0; + + switch (cc_width) { + case 2: + if (binary_length <= 56) { + target_bitsize = 56; + } else if (binary_length <= 104) { + target_bitsize = 104; + } else if (binary_length <= 160) { + target_bitsize = 160; + } else if (binary_length <= 208) { + target_bitsize = 208; + } else if (binary_length <= 256) { + target_bitsize = 256; + } else if (binary_length <= 296) { + target_bitsize = 296; + } else if (binary_length <= 336) { + target_bitsize = 336; + } + break; + case 3: + if (binary_length <= 32) { + target_bitsize = 32; + } else if (binary_length <= 72) { + target_bitsize = 72; + } else if (binary_length <= 112) { + target_bitsize = 112; + } else if (binary_length <= 152) { + target_bitsize = 152; + } else if (binary_length <= 208) { + target_bitsize = 208; + } else if (binary_length <= 304) { + target_bitsize = 304; + } else if (binary_length <= 416) { + target_bitsize = 416; + } else if (binary_length <= 536) { + target_bitsize = 536; + } else if (binary_length <= 648) { + target_bitsize = 648; + } else if (binary_length <= 768) { + target_bitsize = 768; + } + break; + case 4: + if (binary_length <= 56) { + target_bitsize = 56; + } else if (binary_length <= 96) { + target_bitsize = 96; + } else if (binary_length <= 152) { + target_bitsize = 152; + } else if (binary_length <= 208) { + target_bitsize = 208; + } else if (binary_length <= 264) { + target_bitsize = 264; + } else if (binary_length <= 352) { + target_bitsize = 352; + } else if (binary_length <= 496) { + target_bitsize = 496; + } else if (binary_length <= 672) { + target_bitsize = 672; + } else if (binary_length <= 840) { + target_bitsize = 840; + } else if (binary_length <= 1016) { + target_bitsize = 1016; + } else if (binary_length <= 1184) { + target_bitsize = 1184; + } + break; + } + + return target_bitsize; +} + +static int cc_c_calc_padding(const int binary_length, int *p_cc_width, const int linear_width, int *p_ecc_level) { + int target_bitsize = 0; + int byte_length, codewords_used, ecc_level, ecc_codewords, rows; + int codewords_total, target_codewords, target_bytesize; + + byte_length = binary_length / 8; + if (binary_length % 8 != 0) { + byte_length++; + } + + codewords_used = (byte_length / 6) * 5; + codewords_used += byte_length % 6; + + /* Recommended minimum ecc levels ISO/IEC 1543:2015 (PDF417) Annex E Table E.1, + restricted by CC-C codeword max 900 (30 cols * 30 rows), GS1 General Specifications 19.1 5.9.2.3 */ + if (codewords_used <= 40) { + ecc_level = 2; + } else if (codewords_used <= 160) { + ecc_level = 3; + } else if (codewords_used <= 320) { + ecc_level = 4; + } else if (codewords_used <= 833) { /* 900 - 3 - 64 */ + ecc_level = 5; + } else if (codewords_used <= 865) { /* 900 - 3 - 32 */ + ecc_level = 4; /* Not recommended but allow to meet advertised "up to 2361 digits" (allows max 2372) */ + } else { + return 0; + } + *p_ecc_level = ecc_level; + ecc_codewords = 1 << (ecc_level + 1); + + codewords_used += ecc_codewords; + codewords_used += 3; + + /* Minimum possible linear width (with GS1_NO_CHECK) is 11*5 (start, FNC1, linkage, data, check) + 13 stop */ + assert(linear_width >= 68); + /* -52 = 7 left shift (section 12.3 f) + 10 right quiet zone - 17 start + 2x17 row indicators + 18 stop */ + *p_cc_width = linear_width == 68 ? 1 : (linear_width - 52) / 17; /* Ensure > 0 */ + if (*p_cc_width > 30) { + *p_cc_width = 30; + } + assert(*p_cc_width > 0); + rows = (int) ceil((double) codewords_used / *p_cc_width); + /* stop the symbol from becoming too high */ + while (rows > 30 && *p_cc_width < 30) { + (*p_cc_width)++; + rows = (int) ceil((double) codewords_used / *p_cc_width); + } + + if (rows > 30) { /* Should never happen given `codewords_used` check above (865 / 30 ~ 28.83) */ + return 0; /* Not reached */ + } + if (rows < 3) { + rows = 3; + } + + codewords_total = *p_cc_width * rows; + + target_codewords = codewords_total - ecc_codewords; + target_codewords -= 3; + + target_bytesize = 6 * (target_codewords / 5); + target_bytesize += target_codewords % 5; + + target_bitsize = 8 * target_bytesize; + + return target_bitsize; +} + +/* Handles all data encodation from section 5 of ISO/IEC 24723 */ +static int cc_binary_string(struct zint_symbol *symbol, const unsigned char source[], const int length, + char binary_string[], const int cc_mode, int *p_cc_width, int *p_ecc_level, const int linear_width) { + int encoding_method, read_posn, alpha_pad; + int i, j, ai_crop, ai_crop_posn, fnc1_latch; + int ai90_mode, remainder; + char last_digit = '\0'; + int mode; + char *general_field = (char *) z_alloca(length + 1); + int target_bitsize; + int bp = 0; + const int debug_print = symbol->debug & ZINT_DEBUG_PRINT; + + encoding_method = 1; + read_posn = 0; + ai_crop = 0; + ai_crop_posn = -1; + fnc1_latch = 0; + alpha_pad = 0; + *p_ecc_level = 0; + target_bitsize = 0; + mode = NUMERIC; + + if (length > 1 && (source[0] == '1') && ((source[1] == '0') || (source[1] == '1') || (source[1] == '7'))) { + /* Source starts (10), (11) or (17) */ + if (source[1] == '0' || dbar_date(source, length, 2) >= 0) { /* Check date valid if (11) or (17) */ + encoding_method = 2; + } + } else if (length > 1 && (source[0] == '9') && (source[1] == '0')) { + /* Source starts (90) */ + encoding_method = 3; + } + + if (encoding_method == 1) { + binary_string[bp++] = '0'; + if (debug_print) printf("CC-%c Encodation Method: 0\n", 'A' + (cc_mode - 1)); + + } else if (encoding_method == 2) { + /* Encoding Method field "10" - date and lot number */ + + bp = bin_append_posn(2, 2, binary_string, bp); /* "10" */ + + if (source[1] == '0') { + /* No date data */ + bp = bin_append_posn(3, 2, binary_string, bp); /* "11" */ + read_posn = 2; + } else { + /* Production Date (11) or Expiration Date (17) */ + assert(length >= 8); /* Due to `dbar_date()` check above */ + + bp = bin_append_posn(dbar_date(source, length, 2), 16, binary_string, bp); + + if (source[1] == '1') { + /* Production Date AI 11 */ + binary_string[bp++] = '0'; + } else { + /* Expiration Date AI 17 */ + binary_string[bp++] = '1'; + } + read_posn = 8; + + if (read_posn + 1 < length && (source[read_posn] == '1') && (source[read_posn + 1] == '0')) { + /* Followed by AI 10 - strip this from general field */ + read_posn += 2; + } else if (source[read_posn]) { + /* ISO/IEC 24723:2010 5.3.1 "If a lot number does not directly follow the date element string, + a FNC1 is encoded following the date element string ..." */ + fnc1_latch = 1; + } else { + /* "... even if no more data follows the date element string" */ + /* So still need FNC1 character but can't do single FNC1 in numeric mode, so insert alphanumeric latch + "0000" and alphanumeric FNC1 "01111" (this implementation detail taken from BWIPP + https://github.com/bwipp/postscriptbarcode Copyright (c) 2004-2019 Terry Burton) */ + bp = bin_append_posn(15, 9, binary_string, bp); /* "000001111" */ + /* Note an alphanumeric FNC1 is also a numeric latch, so now in numeric mode */ + } + } + + if (debug_print) { + printf("CC-%c Encodation Method: 10, Compaction Field: %.*s\n", 'A' + (cc_mode - 1), read_posn, source); + } + + } else if (encoding_method == 3) { + /* Encodation Method field of "11" - AI 90 */ + unsigned char *ninety = (unsigned char *) z_alloca(length + 1); + int ninety_len, alpha, alphanum, numeric, alpha_posn; + + /* "This encodation method may be used if an element string with an AI + 90 occurs at the start of the data message, and if the data field + following the two-digit AI 90 starts with an alphanumeric string which + complies with a specific format." (para 5.3.2) */ + + i = 0; + if (length > 2) { + do { + ninety[i] = source[i + 2]; + i++; + } while (i + 2 < length && source[i + 2] != '\x1D'); + } + ninety[i] = '\0'; + ninety_len = i; + + /* Find out if the AI 90 data is alphabetic or numeric or both */ + + alpha = 0; + alphanum = 0; + numeric = 0; + + for (i = 0; i < ninety_len; i++) { + + if (z_isupper(ninety[i])) { + /* Character is alphabetic */ + alpha += 1; + } else if (z_isdigit(ninety[i])) { + /* Character is numeric */ + numeric += 1; + } else { + alphanum += 1; + } + } + + /* must start with 0, 1, 2 or 3 digits followed by an uppercase character */ + alpha_posn = -1; + if (ninety_len && ninety[0] != '0') { /* Leading zeros are not permitted */ + for (i = 0; i < ninety_len && i < 4; i++) { + if (z_isupper(ninety[i])) { + alpha_posn = i; + break; + } + if (!z_isdigit(ninety[i])) { + break; + } + } + } + + if (alpha_posn != -1) { + int next_ai_posn; + int numeric_value; + int table3_letter; + /* Encodation method "11" can be used */ + bp = bin_append_posn(3, 2, binary_string, bp); /* "11" */ + + numeric -= alpha_posn; + alpha--; + + /* Decide on numeric, alpha or alphanumeric mode */ + /* Alpha mode is a special mode for AI 90 */ + + if (alphanum == 0 && alpha > numeric) { + /* Alpha mode */ + bp = bin_append_posn(3, 2, binary_string, bp); /* "11" */ + ai90_mode = 2; + } else if (alphanum == 0 && alpha == 0) { + /* Numeric mode */ + bp = bin_append_posn(2, 2, binary_string, bp); /* "10" */ + ai90_mode = 3; + } else { + /* Note if first 4 are digits then it would be shorter to go into NUMERIC mode first; not + implemented */ + /* Alphanumeric mode */ + binary_string[bp++] = '0'; + ai90_mode = 1; + mode = ALPHANUMERIC; + } + + next_ai_posn = 2 + ninety_len; + + if (next_ai_posn < length && source[next_ai_posn] == '\x1D') { + /* There are more AIs afterwards */ + if (next_ai_posn + 2 < length + && (source[next_ai_posn + 1] == '2') && (source[next_ai_posn + 2] == '1')) { + /* AI 21 follows */ + ai_crop = 1; + } else if (next_ai_posn + 4 < length + && (source[next_ai_posn + 1] == '8') && (source[next_ai_posn + 2] == '0') + && (source[next_ai_posn + 3] == '0') && (source[next_ai_posn + 4] == '4')) { + /* AI 8004 follows */ + ai_crop = 3; + } + } + + switch (ai_crop) { + case 0: binary_string[bp++] = '0'; + break; + case 1: bp = bin_append_posn(2, 2, binary_string, bp); /* "10" */ + ai_crop_posn = next_ai_posn + 1; + break; + case 3: bp = bin_append_posn(3, 2, binary_string, bp); /* "11" */ + ai_crop_posn = next_ai_posn + 1; + break; + } + + numeric_value = alpha_posn ? to_int(ninety, alpha_posn) : 0; + + table3_letter = -1; + if (numeric_value < 31) { + table3_letter = posn("BDHIJKLNPQRSTVWZ", ninety[alpha_posn]); + } + + if (table3_letter != -1) { + /* Encoding can be done according to 5.3.2 c) 2) */ + /* five bit binary string representing value before letter */ + bp = bin_append_posn(numeric_value, 5, binary_string, bp); + + /* followed by four bit representation of letter from Table 3 */ + bp = bin_append_posn(table3_letter, 4, binary_string, bp); + } else { + /* Encoding is done according to 5.3.2 c) 3) */ + bp = bin_append_posn(31, 5, binary_string, bp); + /* ten bit representation of number */ + bp = bin_append_posn(numeric_value, 10, binary_string, bp); + + /* five bit representation of ASCII character */ + bp = bin_append_posn(ninety[alpha_posn] - 65, 5, binary_string, bp); + } + + read_posn = alpha_posn + 3; /* +2 for 90 and +1 to go beyond alpha position */ + + /* Do Alpha mode encoding of the rest of the AI 90 data field here */ + if (ai90_mode == 2) { + /* Alpha encodation (section 5.3.3) */ + do { + if (z_isupper(source[read_posn])) { + bp = bin_append_posn(source[read_posn] - 65, 5, binary_string, bp); + + } else if (z_isdigit(source[read_posn])) { + bp = bin_append_posn(source[read_posn] + 4, 6, binary_string, bp); + + } else if (source[read_posn] == '\x1D') { + bp = bin_append_posn(31, 5, binary_string, bp); + } + + read_posn++; + } while (source[read_posn - 1] != '\x1D' && source[read_posn - 1] != '\0'); + alpha_pad = 1; /* This is overwritten if a general field is encoded */ + } + + if (debug_print) { + printf("CC-%c Encodation Method: 11, Compaction Field: %.*s, Binary: %.*s (%d)\n", + 'A' + (cc_mode - 1), read_posn, source, bp, binary_string, bp); + } + } else { + /* Use general field encodation instead */ + binary_string[bp++] = '0'; + read_posn = 0; + if (debug_print) printf("CC-%c Encodation Method: 0\n", 'A' + (cc_mode - 1)); + } + } + + /* The compressed data field has been processed if appropriate - the + rest of the data (if any) goes into a general-purpose data compaction field */ + + j = 0; + if (fnc1_latch == 1) { + /* Encodation method "10" has been used but it is not followed by + AI 10, so a FNC1 character needs to be added */ + general_field[j] = '\x1D'; + j++; + } + + for (i = read_posn; i < length; i++) { + /* Skip "[21" or "[8004" AIs if encodation method "11" used */ + if (i == ai_crop_posn) { + i += ai_crop; + } else { + general_field[j] = source[i]; + j++; + } + } + general_field[j] = '\0'; + + if (debug_print) { + printf("Mode %s, General Field: %.40s%s\n", + mode == NUMERIC ? "NUMERIC" : mode == ALPHANUMERIC ? "ALPHANUMERIC" : "ISO646", + general_field, j > 40 ? "..." : ""); + } + + if (j != 0) { /* If general field not empty */ + alpha_pad = 0; + + if (!general_field_encode(general_field, j, &mode, &last_digit, binary_string, &bp)) { + /* Invalid character in input data */ + return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 441, "Invalid character in input (2D component)"); + } + } + + switch (cc_mode) { + case 1: + target_bitsize = cc_a_calc_padding(bp, *p_cc_width); + break; + case 2: + target_bitsize = cc_b_calc_padding(bp, *p_cc_width); + break; + case 3: + target_bitsize = cc_c_calc_padding(bp, p_cc_width, linear_width, p_ecc_level); + break; + } + + if (target_bitsize == 0) { + return errtxt(ZINT_ERROR_TOO_LONG, symbol, 442, "Input too long (2D component)"); + } + + remainder = target_bitsize - bp; + + if (last_digit) { + /* There is still one more numeric digit to encode */ + + if ((remainder >= 4) && (remainder <= 6)) { + /* ISO/IEC 24723:2010 5.4.1 c) 2) "If four to six bits remain, add 1 to the digit value and encode the + result in the next four bits. ..." */ + bp = bin_append_posn(ctoi(last_digit) + 1, 4, binary_string, bp); + if (remainder > 4) { + /* "... The fifth and sixth bits, if present, shall be “0”s." (Covered by adding truncated + alphanumeric latch below but do explicitly anyway) */ + bp = bin_append_posn(0, remainder - 4, binary_string, bp); + } + } else { + bp = bin_append_posn((11 * ctoi(last_digit)) + 18, 7, binary_string, bp); + /* This may push the symbol up to the next size */ + } + } + + switch (cc_mode) { + case 1: + target_bitsize = cc_a_calc_padding(bp, *p_cc_width); + break; + case 2: + target_bitsize = cc_b_calc_padding(bp, *p_cc_width); + break; + case 3: + target_bitsize = cc_c_calc_padding(bp, p_cc_width, linear_width, p_ecc_level); + break; + } + + if (target_bitsize == 0) { + return errtxt(ZINT_ERROR_TOO_LONG, symbol, 444, "Input too long (2D component)"); + } + + if (bp < target_bitsize) { + /* Now add padding to binary string */ + if (alpha_pad == 1) { + bp = bin_append_posn(31, 5, binary_string, bp); /* "11111" */ + /* Extra FNC1 character required after Alpha encodation (section 5.3.3) */ + } + + if (mode == NUMERIC) { + bp = bin_append_posn(0, 4, binary_string, bp); /* "0000" */ + } + + while (bp < target_bitsize) { + bp = bin_append_posn(4, 5, binary_string, bp); /* "00100" */ + } + } + binary_string[target_bitsize] = '\0'; + + if (debug_print) { + printf("ECC: %d, CC width %d\n", *p_ecc_level, *p_cc_width); + printf("Binary: %s (%d)\n", binary_string, target_bitsize); + } + + return 0; +} + +/* Calculate the width of the linear part (primary) */ +static int cc_linear_dummy_run(int input_mode, unsigned char *source, const int length, const int debug, + char *errtxt) { + struct zint_symbol dummy = {0}; + int error_number; + int linear_width; + + dummy.symbology = BARCODE_GS1_128_CC; + dummy.option_1 = -1; + dummy.input_mode = input_mode; + dummy.debug = debug; + error_number = gs1_128_cc(&dummy, source, length, 3 /*cc_mode*/, 0 /*cc_rows*/); + linear_width = dummy.width; + if (error_number >= ZINT_ERROR || (debug & ZINT_DEBUG_TEST)) { + strcpy(errtxt, dummy.errtxt); + } + + if (error_number >= ZINT_ERROR) { + return 0; + } + return linear_width; +} + +INTERNAL int composite(struct zint_symbol *symbol, unsigned char source[], int length) { + int error_number, cc_mode, cc_width = 0, ecc_level = 0; + int j, i, k; + /* Allow for 8 bits + 5-bit latch per char + 1000 bits overhead/padding */ + const unsigned int bs = 13 * length + 1000 + 1; + char *binary_string = (char *) z_alloca(bs); + unsigned int pri_len; + struct zint_symbol *linear; + int top_shift, bottom_shift; + int linear_width = 0; + const int debug_print = symbol->debug & ZINT_DEBUG_PRINT; + + if (debug_print) printf("Reduced length: %d\n", length); + + /* Perform sanity checks on input options first */ + error_number = 0; + pri_len = (int) strlen(symbol->primary); + if (pri_len == 0) { + /* TODO: change to more appropiate ZINT_ERROR_INVALID_DATA */ + return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 445, "No primary (linear) message"); + } + + if (length > 2990) { + return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 446, + "2D component input too long, requires %d characters (maximum 2990)", length); + } + + cc_mode = symbol->option_1; + if ((cc_mode == 3) && (symbol->symbology != BARCODE_GS1_128_CC)) { + /* CC-C can only be used with a GS1-128 linear part */ + return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 447, + "Invalid mode (CC-C only valid with GS1-128 linear component)"); + } + + if (symbol->symbology == BARCODE_GS1_128_CC) { + /* Do a test run of encoding the linear component to establish its width */ + linear_width = cc_linear_dummy_run(symbol->input_mode, (unsigned char *) symbol->primary, pri_len, + symbol->debug, symbol->errtxt); + if (linear_width == 0) { + return errtxt_adj(ZINT_ERROR_INVALID_DATA, symbol, "%1$s%2$s", " (linear component)"); + } + if (debug_print) { + printf("GS1-128 linear width: %d\n", linear_width); + } + } + + switch (symbol->symbology) { + /* Determine width of 2D component according to ISO/IEC 24723 Table 1 */ + case BARCODE_EANX_CC: + if (pri_len < 20) { + int padded_pri_len; + int with_addon; + unsigned char padded_pri[21]; + if (!ean_leading_zeroes(symbol, (unsigned char *) symbol->primary, padded_pri, &with_addon, NULL, + NULL)) { + return errtxt_adj(ZINT_ERROR_TOO_LONG, symbol, "%1$s%2$s", " (linear component)"); + } + padded_pri_len = (int) ustrlen(padded_pri); + if (padded_pri_len <= 7) { /* EAN-8 */ + cc_width = 3; + } else { + switch (padded_pri_len) { + case 10: /* EAN-8 + 2 */ + cc_width = 3; + break; + case 13: /* EAN-13 CHK or EAN-8 + 5 */ + cc_width = with_addon ? 3 : 4; + break; + case 12: /* EAN-13 */ + case 15: /* EAN-13 + 2 */ + case 16: /* EAN-13 CHK + 2 */ + case 18: /* EAN-13 + 5 */ + case 19: /* EAN-13 CHK + 5 */ + cc_width = 4; + break; + } + } + } + if (cc_width == 0) { + return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 449, "Input length %d wrong (linear component)", pri_len); + } + break; + case BARCODE_GS1_128_CC: cc_width = 4; + break; + case BARCODE_DBAR_OMN_CC: cc_width = 4; + break; + case BARCODE_DBAR_LTD_CC: cc_width = 3; + break; + case BARCODE_DBAR_EXP_CC: cc_width = 4; + break; + case BARCODE_UPCA_CC: cc_width = 4; + break; + case BARCODE_UPCE_CC: cc_width = 2; + break; + case BARCODE_DBAR_STK_CC: cc_width = 2; + break; + case BARCODE_DBAR_OMNSTK_CC: cc_width = 2; + break; + case BARCODE_DBAR_EXPSTK_CC: cc_width = 4; + break; + } + + if (cc_mode < 1 || cc_mode > 3) { + cc_mode = 1; + } + + if (cc_mode == 1) { + i = cc_binary_string(symbol, source, length, binary_string, cc_mode, &cc_width, &ecc_level, linear_width); + if (i == ZINT_ERROR_TOO_LONG) { + symbol->errtxt[0] = '\0'; /* Unset error text */ + cc_mode = 2; + } else if (i != 0) { + return i; + } + } + + if (cc_mode == 2) { + /* If the data didn't fit into CC-A it is recalculated for CC-B */ + i = cc_binary_string(symbol, source, length, binary_string, cc_mode, &cc_width, &ecc_level, linear_width); + if (i == ZINT_ERROR_TOO_LONG) { + if (symbol->symbology != BARCODE_GS1_128_CC) { + return ZINT_ERROR_TOO_LONG; + } + symbol->errtxt[0] = '\0'; /* Unset error text */ + cc_mode = 3; + } else if (i != 0) { + return i; + } + } + + if (cc_mode == 3) { + /* If the data didn't fit in CC-B (and linear part is GS1-128) it is recalculated for CC-C */ + i = cc_binary_string(symbol, source, length, binary_string, cc_mode, &cc_width, &ecc_level, linear_width); + if (i != 0) { + return i; + } + } + + switch (cc_mode) { + /* Note that ecc_level is only relevant to CC-C */ + case 1: cc_a(symbol, binary_string, cc_width); + break; + case 2: cc_b(symbol, binary_string, cc_width); + break; + case 3: cc_c(symbol, binary_string, cc_width, ecc_level); + break; + } + + /* 2D component done, now calculate linear component */ + linear = ZBarcode_Create(); /* Symbol contains the 2D component and Linear contains the rest */ + + linear->symbology = symbol->symbology; + linear->input_mode = symbol->input_mode; + linear->output_options = symbol->output_options; + linear->show_hrt = symbol->show_hrt; + linear->option_2 = symbol->option_2; + linear->option_3 = symbol->option_3; + /* If symbol->height given minimum row height will be returned, else default height */ + linear->height = symbol->height; + linear->debug = symbol->debug; + + if (linear->symbology != BARCODE_GS1_128_CC) { + /* Set the "component linkage" flag in the linear component */ + linear->option_1 = 2; + } + + switch (symbol->symbology) { + case BARCODE_EANX_CC: + error_number = eanx_cc(linear, (unsigned char *) symbol->primary, pri_len, symbol->rows); + break; + case BARCODE_GS1_128_CC: + /* GS1-128 needs to know which type of 2D component is used */ + error_number = gs1_128_cc(linear, (unsigned char *) symbol->primary, pri_len, cc_mode, symbol->rows); + break; + case BARCODE_DBAR_OMN_CC: + error_number = dbar_omn_cc(linear, (unsigned char *) symbol->primary, pri_len, symbol->rows); + break; + case BARCODE_DBAR_LTD_CC: + error_number = dbar_ltd_cc(linear, (unsigned char *) symbol->primary, pri_len, symbol->rows); + break; + case BARCODE_DBAR_EXP_CC: + error_number = dbar_exp_cc(linear, (unsigned char *) symbol->primary, pri_len, symbol->rows); + break; + case BARCODE_UPCA_CC: + error_number = eanx_cc(linear, (unsigned char *) symbol->primary, pri_len, symbol->rows); + break; + case BARCODE_UPCE_CC: + error_number = eanx_cc(linear, (unsigned char *) symbol->primary, pri_len, symbol->rows); + break; + case BARCODE_DBAR_STK_CC: + error_number = dbar_omn_cc(linear, (unsigned char *) symbol->primary, pri_len, symbol->rows); + break; + case BARCODE_DBAR_OMNSTK_CC: + error_number = dbar_omn_cc(linear, (unsigned char *) symbol->primary, pri_len, symbol->rows); + break; + case BARCODE_DBAR_EXPSTK_CC: + error_number = dbar_exp_cc(linear, (unsigned char *) symbol->primary, pri_len, symbol->rows); + break; + } + + if (error_number) { + errtxtf(0, symbol, -1, "%1$s%2$s", linear->errtxt, " (linear component)"); + if (error_number >= ZINT_ERROR) { + ZBarcode_Delete(linear); + return error_number; + } + } + + /* Merge the linear component with the 2D component */ + + top_shift = 0; + bottom_shift = 0; + + switch (symbol->symbology) { + /* Determine horizontal alignment (according to section 12.3) */ + case BARCODE_EANX_CC: + switch (ustrlen(linear->text)) { /* Use zero-padded length */ + case 8: /* EAN-8 */ + case 11: /* EAN-8 + 2 */ + case 14: /* EAN-8 + 5 */ + if (cc_mode == 1) { + bottom_shift = 3; + } else { + bottom_shift = 13; + } + break; + case 13: /* EAN-13 */ + case 16: /* EAN-13 + 2 */ + case 19: /* EAN-13 + 5 */ + bottom_shift = 2; + break; + } + break; + case BARCODE_GS1_128_CC: + if (cc_mode == 3) { + bottom_shift = 7; /* ISO/IEC 24723:2010 12.3 f) */ + } else { + /* ISO/IEC 24723:2010 12.3 g) "GS1-128 components linked to the right quiet zone of the CC-A or CC-B: + the CC-A or CC-B component is aligned with the last space module of one of the rightmost symbol + characters of the linear component. To calculate the target Code 128 symbol character position for + alignment, number the positions from right to left (0 is the Stop character, 1 is the Check + character, etc.), and then Position = (total number of Code 128 symbol characters – 9) div 2" + */ + const int num_symbols = (linear_width - 2) / 11; + const int position = (num_symbols - 9) / 2; + /* Less 1 to align with last space module */ + int calc_shift = linear->width - position * 11 - 1 - symbol->width; + if (position) { + calc_shift -= 2; /* Less additional stop modules */ + } + if (calc_shift > 0) { + top_shift = calc_shift; + } else if (calc_shift < 0) { + bottom_shift = -calc_shift; + } + } + break; + case BARCODE_DBAR_OMN_CC: bottom_shift = 4; + break; + case BARCODE_DBAR_LTD_CC: + if (cc_mode == 1) { + top_shift = 1; + } else { + bottom_shift = 9; + } + break; + case BARCODE_DBAR_EXP_CC: + for (k = 1; !module_is_set(linear, 1, k - 1) && module_is_set(linear, 1, k); k++); + top_shift = k; + break; + case BARCODE_UPCA_CC: bottom_shift = 2; + break; + case BARCODE_UPCE_CC: bottom_shift = 2; + break; + case BARCODE_DBAR_STK_CC: top_shift = 1; + break; + case BARCODE_DBAR_OMNSTK_CC: top_shift = 1; + break; + case BARCODE_DBAR_EXPSTK_CC: + for (k = 1; !module_is_set(linear, 1, k - 1) && module_is_set(linear, 1, k); k++); + top_shift = k; + break; + } + + if (debug_print) { + printf("Top shift: %d, Bottom shift: %d\n", top_shift, bottom_shift); + } + + if (top_shift != 0) { + /* Move the 2D component of the symbol horizontally */ + for (i = 0; i <= symbol->rows; i++) { + for (j = (symbol->width + top_shift); j >= top_shift; j--) { + if (module_is_set(symbol, i, j - top_shift)) { + set_module(symbol, i, j); + } else { + unset_module(symbol, i, j); + } + } + for (j = 0; j < top_shift; j++) { + unset_module(symbol, i, j); + } + } + } + + /* Merge linear and 2D components into one structure */ + for (i = 0; i <= linear->rows; i++) { + symbol->row_height[symbol->rows + i] = linear->row_height[i]; + for (j = 0; j <= linear->width; j++) { + if (module_is_set(linear, i, j)) { + set_module(symbol, i + symbol->rows, j + bottom_shift); + } else { + unset_module(symbol, i + symbol->rows, j + bottom_shift); + } + } + } + if ((linear->width + bottom_shift) > symbol->width + top_shift) { + symbol->width = linear->width + bottom_shift; + } else if ((symbol->width + top_shift) > linear->width + bottom_shift) { + symbol->width += top_shift; + } + symbol->rows += linear->rows; + if (symbol->output_options & COMPLIANT_HEIGHT) { + if (symbol->symbology == BARCODE_DBAR_STK_CC) { + /* Databar Stacked needs special treatment due to asymmetric rows */ + error_number = dbar_omnstk_set_height(symbol, symbol->rows - linear->rows + 1 /*first_row*/); + } else if (symbol->symbology == BARCODE_DBAR_EXP_CC || symbol->symbology == BARCODE_DBAR_EXPSTK_CC) { + /* If symbol->height given then min row height was returned, else default height */ + if (error_number == 0) { /* Avoid overwriting any `gs1_verify()` warning */ + error_number = set_height(symbol, symbol->height ? linear->height : 0.0f, + symbol->height ? 0.0f : linear->height, 0.0f, 0 /*no_errtxt*/); + } else { + (void) set_height(symbol, symbol->height ? linear->height : 0.0f, + symbol->height ? 0.0f : linear->height, 0.0f, 1 /*no_errtxt*/); + } + } else { + /* If symbol->height given then min row height was returned, else default height */ + error_number = set_height(symbol, symbol->height ? linear->height : 0.0f, + symbol->height ? 0.0f : linear->height, 0.0f, 0 /*no_errtxt*/); + } + } else { + if (symbol->symbology == BARCODE_DBAR_STK_CC) { + (void) dbar_omnstk_set_height(symbol, symbol->rows - linear->rows + 1 /*first_row*/); + } else { + (void) set_height(symbol, symbol->height ? linear->height : 0.0f, symbol->height ? 0.0f : linear->height, + 0.0f, 1 /*no_errtxt*/); + } + } + + ustrcpy(symbol->text, linear->text); + + ZBarcode_Delete(linear); + + return error_number; +} + +/* vim: set ts=4 sw=4 et : */
