Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/zint/backend/rss.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/rss.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,1621 @@ +/* rss.c - GS1 DataBar (formerly Reduced Space Symbology) */ +/* + 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 "rss_combins" and "getRSSwidths" 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 30 November 2006 + */ + +/* Includes numerous bugfixes thanks to Pablo Orduña @ the PIRAmIDE project */ + +/* Note: This code reflects the symbol names as used in ISO/IEC 24724:2006. These names + * were updated in ISO/IEC 24724:2011 as follows: + * + * RSS-14 > GS1 DataBar Omnidirectional + * RSS-14 Truncated > GS1 DataBar Truncated + * RSS-14 Stacked > GS1 DataBar Stacked + * RSS-14 Stacked Omnidirectional > GS1 DataBar Stacked Omnidirectional + * RSS Limited > GS1 DataBar Limited + * RSS Expanded > GS1 DataBar Expanded Omnidirectional + * RSS Expanded Stacked > GS1 DataBar Expanded Stacked Omnidirectional + */ + +#include <stdio.h> +#include "common.h" +#include "large.h" +#include "rss.h" +#include "gs1.h" +#include "general_field.h" + +/**************************************************************************** + * rss_combins(n,r): returns the number of Combinations of r selected from n: + * Combinations = n! / ((n - r)! * r!) + ****************************************************************************/ +static int rss_combins(const int n, const int r) { + int i, j; + int maxDenom, minDenom; + int val; + + if (n - r > r) { + minDenom = r; + maxDenom = n - r; + } else { + minDenom = n - r; + maxDenom = r; + } + val = 1; + j = 1; + for (i = n; i > maxDenom; i--) { + val *= i; + if (j <= minDenom) { + val /= j; + j++; + } + } + for (; j <= minDenom; j++) { + val /= j; + } + return (val); +} + +/********************************************************************** + * getRSSwidths + * routine to generate widths for RSS elements for a given value.# + * + * Calling arguments: + * int widths[] = element widths + * val = required value + * n = number of modules + * elements = elements in a set (RSS-14 & Expanded = 4; RSS Limited = 7) + * maxWidth = maximum module width of an element + * noNarrow = 0 will skip patterns without a one module wide element + * + **********************************************************************/ +static void getRSSwidths(int widths[], int val, int n, const int elements, const int maxWidth, const int noNarrow) { + int bar; + int elmWidth; + int mxwElement; + int subVal, lessVal; + int narrowMask = 0; + for (bar = 0; bar < elements - 1; bar++) { + for (elmWidth = 1, narrowMask |= (1 << bar); + ; + elmWidth++, narrowMask &= ~(1 << bar)) { + /* Get all combinations */ + subVal = rss_combins(n - elmWidth - 1, elements - bar - 2); + /* Less combinations with no single-module element */ + if ((!noNarrow) && (!narrowMask) + && (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) { + subVal -= rss_combins(n - elmWidth - (elements - bar), elements - bar - 2); + } + /* Less combinations with elements > maxVal */ + if (elements - bar - 1 > 1) { + lessVal = 0; + for (mxwElement = n - elmWidth - (elements - bar - 2); + mxwElement > maxWidth; + mxwElement--) { + lessVal += rss_combins(n - elmWidth - mxwElement - 1, elements - bar - 3); + } + subVal -= lessVal * (elements - 1 - bar); + } else if (n - elmWidth > maxWidth) { + subVal--; + } + val -= subVal; + if (val < 0) break; + } + val += subVal; + n -= elmWidth; + widths[bar] = elmWidth; + } + widths[bar] = n; + return; +} + +/* Set GTIN-14 human readable text */ +static void dbar_set_gtin14_hrt(struct zint_symbol *symbol, const unsigned char *source, const int length) { + unsigned char *hrt = symbol->text + 4; + const int leading_zeroes = 13 - length; + + ustrcpy(symbol->text, "(01)"); + if (leading_zeroes) { + memset(hrt, '0', leading_zeroes); + } + memcpy(hrt + leading_zeroes, source, length); + hrt[13] = gs1_check_digit(hrt, 13); + hrt[14] = '\0'; +} + +/* Expand from a width pattern to a bit pattern */ +static int dbar_expand(struct zint_symbol *symbol, int writer, int *p_latch, const int width) { + int j; + + if (*p_latch) { + for (j = 0; j < width; j++) { + set_module(symbol, symbol->rows, writer); + writer++; + } + } else { + for (j = 0; j < width; j++) { + unset_module(symbol, symbol->rows, writer); + writer++; + } + } + + *p_latch = !*p_latch; + + return writer; +} + +/* Adjust top/bottom separator for finder patterns */ +static void dbar_omn_finder_adjust(struct zint_symbol *symbol, const int separator_row, const int above_below, + const int finder_start) { + int i, finder_end; + int module_row = separator_row + above_below; + int latch; + + /* Alternation is always left-to-right for Omnidirectional separators (unlike for Expanded) */ + latch = 1; + for (i = finder_start, finder_end = finder_start + 13; i < finder_end; i++) { + if (!module_is_set(symbol, module_row, i)) { + if (latch) { + set_module(symbol, separator_row, i); + latch = 0; + } else { + unset_module(symbol, separator_row, i); + latch = 1; + } + } else { + unset_module(symbol, separator_row, i); + latch = 1; + } + } +} + +/* Top/bottom separator for DataBar */ +static void dbar_omn_separator(struct zint_symbol *symbol, int width, const int separator_row, const int above_below, + const int finder_start, const int finder2_start, const int bottom_finder_value_3) { + int i, finder_end, finder_value_3_set; + int module_row = separator_row + above_below; + + for (i = 4, width -= 4; i < width; i++) { + if (!module_is_set(symbol, module_row, i)) { + set_module(symbol, separator_row, i); + } + } + if (bottom_finder_value_3) { + /* ISO/IEC 24724:2011 5.3.2.2 "The single dark module that occurs in the 13 modules over finder value 3 is + shifted one module to the right so that it is over the start of the three module-wide finder bar." */ + finder_value_3_set = finder_start + 10; + for (i = finder_start, finder_end = finder_start + 13; i < finder_end; i++) { + if (i == finder_value_3_set) { + set_module(symbol, separator_row, i); + } else { + unset_module(symbol, separator_row, i); + } + } + } else { + if (finder_start) { + dbar_omn_finder_adjust(symbol, separator_row, above_below, finder_start); + } + if (finder2_start) { + dbar_omn_finder_adjust(symbol, separator_row, above_below, finder2_start); + } + } +} + +/* Set Databar Stacked height, maintaining 5:7 ratio of the 2 main row heights */ +INTERNAL int dbar_omnstk_set_height(struct zint_symbol *symbol, const int first_row) { + float fixed_height = 0.0f; + int second_row = first_row + 2; /* 2 row separator */ + int i; + + for (i = 0; i < symbol->rows; i++) { + if (i != first_row && i != second_row) { + fixed_height += symbol->row_height[i]; + } + } + if (symbol->height) { + symbol->row_height[first_row] = stripf((symbol->height - fixed_height) * symbol->row_height[first_row] / + (symbol->row_height[first_row] + symbol->row_height[second_row])); + if (symbol->row_height[first_row] < 0.5f) { /* Absolute minimum */ + symbol->row_height[first_row] = 0.5f; + symbol->row_height[second_row] = 0.7f; + } else { + symbol->row_height[second_row] = stripf(symbol->height - fixed_height - symbol->row_height[first_row]); + if (symbol->row_height[second_row] < 0.7f) { + symbol->row_height[second_row] = 0.7f; + } + } + } + symbol->height = stripf(stripf(symbol->row_height[first_row] + symbol->row_height[second_row]) + fixed_height); + + if (symbol->output_options & COMPLIANT_HEIGHT) { + if (symbol->row_height[first_row] < 5.0f || symbol->row_height[second_row] < 7.0f) { + return errtxt(ZINT_WARN_NONCOMPLIANT, symbol, 379, "Height not compliant with standards"); + } + } + + return 0; +} + +/* GS1 DataBar Omnidirectional/Truncated/Stacked, allowing for composite if `cc_rows` set */ +INTERNAL int dbar_omn_cc(struct zint_symbol *symbol, unsigned char source[], int length, const int cc_rows) { + int error_number = 0, i; + large_uint accum; + uint64_t left_pair, right_pair; + int data_character[4] = {0}, data_group[4] = {0}, v_odd[4], v_even[4]; + int data_widths[8][4], checksum, c_left, c_right, total_widths[46], writer; + int latch; + int separator_row; + int widths[4]; + + separator_row = 0; + + if (length > 14) { /* Allow check digit to be specified (will be verified and ignored) */ + return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 380, "Input length %d too long (maximum 14)", length); + } + if ((i = not_sane(NEON_F, source, length))) { + return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 381, + "Invalid character at position %d in input (digits only)", i); + } + + if (length == 14) { /* Verify check digit */ + if (gs1_check_digit(source, 13) != source[13]) { + return errtxtf(ZINT_ERROR_INVALID_CHECK, symbol, 388, "Invalid check digit '%1$c', expecting '%2$c'", + source[13], gs1_check_digit(source, 13)); + } + length--; /* Ignore */ + } + + /* Make some room for a separator row for composite symbols */ + switch (symbol->symbology) { + case BARCODE_DBAR_OMN_CC: + case BARCODE_DBAR_STK_CC: + case BARCODE_DBAR_OMNSTK_CC: + separator_row = symbol->rows; + symbol->row_height[separator_row] = 1; + symbol->rows += 1; + break; + } + + large_load_str_u64(&accum, source, length); + + if (cc_rows) { + /* Add symbol linkage flag */ + large_add_u64(&accum, 10000000000000); + } + + /* Calculate left and right pair values */ + + right_pair = large_div_u64(&accum, 4537077); + left_pair = large_lo(&accum); + + /* Calculate four data characters */ + + data_character[0] = (int) (left_pair / 1597); + data_character[1] = (int) (left_pair % 1597); + + data_character[2] = (int) (right_pair / 1597); + data_character[3] = (int) (right_pair % 1597); + + /* Calculate odd and even subset values */ + + if (data_character[0] <= 160) { + data_group[0] = 0; + } else if (data_character[0] <= 960) { + data_group[0] = 1; + } else if (data_character[0] <= 2014) { + data_group[0] = 2; + } else if (data_character[0] <= 2714) { + data_group[0] = 3; + } else { + data_group[0] = 4; + } + + if (data_character[1] <= 335) { + data_group[1] = 5; + } else if (data_character[1] <= 1035) { + data_group[1] = 6; + } else if (data_character[1] <= 1515) { + data_group[1] = 7; + } else { + data_group[1] = 8; + } + + if (data_character[3] <= 335) { + data_group[3] = 5; + } else if (data_character[3] <= 1035) { + data_group[3] = 6; + } else if (data_character[3] <= 1515) { + data_group[3] = 7; + } else { + data_group[3] = 8; + } + + if (data_character[2] <= 160) { + data_group[2] = 0; + } else if (data_character[2] <= 960) { + data_group[2] = 1; + } else if (data_character[2] <= 2014) { + data_group[2] = 2; + } else if (data_character[2] <= 2714) { + data_group[2] = 3; + } else { + data_group[2] = 4; + } + + v_odd[0] = (data_character[0] - dbar_g_sum_table[data_group[0]]) / dbar_t_table[data_group[0]]; + v_even[0] = (data_character[0] - dbar_g_sum_table[data_group[0]]) % dbar_t_table[data_group[0]]; + v_odd[1] = (data_character[1] - dbar_g_sum_table[data_group[1]]) % dbar_t_table[data_group[1]]; + v_even[1] = (data_character[1] - dbar_g_sum_table[data_group[1]]) / dbar_t_table[data_group[1]]; + v_odd[3] = (data_character[3] - dbar_g_sum_table[data_group[3]]) % dbar_t_table[data_group[3]]; + v_even[3] = (data_character[3] - dbar_g_sum_table[data_group[3]]) / dbar_t_table[data_group[3]]; + v_odd[2] = (data_character[2] - dbar_g_sum_table[data_group[2]]) / dbar_t_table[data_group[2]]; + v_even[2] = (data_character[2] - dbar_g_sum_table[data_group[2]]) % dbar_t_table[data_group[2]]; + + /* Use DataBar subset width algorithm */ + for (i = 0; i < 4; i++) { + if ((i == 0) || (i == 2)) { + getRSSwidths(widths, v_odd[i], dbar_modules_odd[data_group[i]], 4, dbar_widest_odd[data_group[i]], 1); + data_widths[0][i] = widths[0]; + data_widths[2][i] = widths[1]; + data_widths[4][i] = widths[2]; + data_widths[6][i] = widths[3]; + getRSSwidths(widths, v_even[i], dbar_modules_even[data_group[i]], 4, dbar_widest_even[data_group[i]], 0); + data_widths[1][i] = widths[0]; + data_widths[3][i] = widths[1]; + data_widths[5][i] = widths[2]; + data_widths[7][i] = widths[3]; + } else { + getRSSwidths(widths, v_odd[i], dbar_modules_odd[data_group[i]], 4, dbar_widest_odd[data_group[i]], 0); + data_widths[0][i] = widths[0]; + data_widths[2][i] = widths[1]; + data_widths[4][i] = widths[2]; + data_widths[6][i] = widths[3]; + getRSSwidths(widths, v_even[i], dbar_modules_even[data_group[i]], 4, dbar_widest_even[data_group[i]], 1); + data_widths[1][i] = widths[0]; + data_widths[3][i] = widths[1]; + data_widths[5][i] = widths[2]; + data_widths[7][i] = widths[3]; + } + } + + checksum = 0; + /* Calculate the checksum */ + for (i = 0; i < 8; i++) { + checksum += dbar_checksum_weight[i] * data_widths[i][0]; + checksum += dbar_checksum_weight[i + 8] * data_widths[i][1]; + checksum += dbar_checksum_weight[i + 16] * data_widths[i][2]; + checksum += dbar_checksum_weight[i + 24] * data_widths[i][3]; + } + checksum %= 79; + + /* Calculate the two check characters */ + if (checksum >= 8) { + checksum++; + } + if (checksum >= 72) { + checksum++; + } + c_left = checksum / 9; + c_right = checksum % 9; + + if (symbol->debug & ZINT_DEBUG_PRINT) { + printf("c_left: %d, c_right: %d\n", c_left, c_right); + } + + /* Put element widths together */ + total_widths[0] = 1; + total_widths[1] = 1; + total_widths[44] = 1; + total_widths[45] = 1; + for (i = 0; i < 8; i++) { + total_widths[i + 2] = data_widths[i][0]; + total_widths[i + 15] = data_widths[7 - i][1]; + total_widths[i + 23] = data_widths[i][3]; + total_widths[i + 36] = data_widths[7 - i][2]; + } + for (i = 0; i < 5; i++) { + total_widths[i + 10] = dbar_finder_pattern[i + (5 * c_left)]; + total_widths[i + 31] = dbar_finder_pattern[(4 - i) + (5 * c_right)]; + } + + /* Put this data into the symbol */ + if ((symbol->symbology == BARCODE_DBAR_OMN) || (symbol->symbology == BARCODE_DBAR_OMN_CC)) { + writer = 0; + latch = 0; + for (i = 0; i < 46; i++) { + writer = dbar_expand(symbol, writer, &latch, total_widths[i]); + } + if (symbol->width < writer) { + symbol->width = writer; + } + if (symbol->symbology == BARCODE_DBAR_OMN_CC) { + /* Separator pattern for composite symbol */ + dbar_omn_separator(symbol, 96, separator_row, 1 /*above*/, 18, 63, 0 /*bottom_finder_value_3*/); + } + symbol->rows = symbol->rows + 1; + + /* Set human readable text */ + dbar_set_gtin14_hrt(symbol, source, length); + + if (symbol->output_options & COMPLIANT_HEIGHT) { + /* Minimum height is 13X for truncated symbol ISO/IEC 24724:2011 5.3.1 + Default height is 33X for DataBar Omnidirectional ISO/IEC 24724:2011 5.2 */ + if (symbol->symbology == BARCODE_DBAR_OMN_CC) { + symbol->height = symbol->height ? 13.0f : 33.0f; /* Pass back min row or default height */ + } else { + error_number = set_height(symbol, 13.0f, 33.0f, 0.0f, 0 /*no_errtxt*/); + } + } else { + if (symbol->symbology == BARCODE_DBAR_OMN_CC) { + symbol->height = 14.0f; /* 14X truncated min row height used (should be 13X) */ + } else { + (void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/); + } + } + + } else if ((symbol->symbology == BARCODE_DBAR_STK) || (symbol->symbology == BARCODE_DBAR_STK_CC)) { + /* Top row */ + writer = 0; + latch = 0; + for (i = 0; i < 23; i++) { + writer = dbar_expand(symbol, writer, &latch, total_widths[i]); + } + set_module(symbol, symbol->rows, writer); + unset_module(symbol, symbol->rows, writer + 1); + symbol->row_height[symbol->rows] = 5.0f; /* ISO/IEC 24724:2011 5.3.2.1 set to 5X */ + + /* Bottom row */ + symbol->rows = symbol->rows + 2; + set_module(symbol, symbol->rows, 0); + unset_module(symbol, symbol->rows, 1); + writer = 2; + latch = 1; + for (i = 23; i < 46; i++) { + writer = dbar_expand(symbol, writer, &latch, total_widths[i]); + } + symbol->row_height[symbol->rows] = 7.0f; /* ISO/IEC 24724:2011 5.3.2.1 set to 7X */ + + /* Separator pattern */ + /* See #183 for this interpretation of ISO/IEC 24724:2011 5.3.2.1 */ + for (i = 1; i < 46; i++) { + if (module_is_set(symbol, symbol->rows - 2, i) == module_is_set(symbol, symbol->rows, i)) { + if (!(module_is_set(symbol, symbol->rows - 2, i))) { + set_module(symbol, symbol->rows - 1, i); + } + } else { + if (!(module_is_set(symbol, symbol->rows - 1, i - 1))) { + set_module(symbol, symbol->rows - 1, i); + } + } + } + unset_module(symbol, symbol->rows - 1, 1); + unset_module(symbol, symbol->rows - 1, 2); + unset_module(symbol, symbol->rows - 1, 3); + symbol->row_height[symbol->rows - 1] = 1; + + if (symbol->symbology == BARCODE_DBAR_STK_CC) { + /* Separator pattern for composite symbol */ + dbar_omn_separator(symbol, 50, separator_row, 1 /*above*/, 18, 0, 0 /*bottom_finder_value_3*/); + } + symbol->rows = symbol->rows + 1; + if (symbol->width < 50) { + symbol->width = 50; + } + + if (symbol->symbology != BARCODE_DBAR_STK_CC) { /* Composite calls dbar_omnstk_set_height() itself */ + error_number = dbar_omnstk_set_height(symbol, 0 /*first_row*/); + } + + } else if ((symbol->symbology == BARCODE_DBAR_OMNSTK) || (symbol->symbology == BARCODE_DBAR_OMNSTK_CC)) { + /* Top row */ + writer = 0; + latch = 0; + for (i = 0; i < 23; i++) { + writer = dbar_expand(symbol, writer, &latch, total_widths[i]); + } + set_module(symbol, symbol->rows, writer); + unset_module(symbol, symbol->rows, writer + 1); + + /* Bottom row */ + symbol->rows = symbol->rows + 4; + set_module(symbol, symbol->rows, 0); + unset_module(symbol, symbol->rows, 1); + writer = 2; + latch = 1; + for (i = 23; i < 46; i++) { + writer = dbar_expand(symbol, writer, &latch, total_widths[i]); + } + + /* Middle separator */ + for (i = 5; i < 46; i += 2) { + set_module(symbol, symbol->rows - 2, i); + } + symbol->row_height[symbol->rows - 2] = 1; + + /* Top separator */ + dbar_omn_separator(symbol, 50, symbol->rows - 3, -1 /*below*/, 18, 0, 0 /*bottom_finder_value_3*/); + symbol->row_height[symbol->rows - 3] = 1; + + /* Bottom separator */ + /* 17 == 2 (guard) + 15 (inner char); +2 to skip over finder elements 4 & 5 (right to left) */ + dbar_omn_separator(symbol, 50, symbol->rows - 1, 1 /*above*/, 17 + 2, 0, c_right == 3); + symbol->row_height[symbol->rows - 1] = 1; + if (symbol->width < 50) { + symbol->width = 50; + } + + if (symbol->symbology == BARCODE_DBAR_OMNSTK_CC) { + /* Separator pattern for composite symbol */ + dbar_omn_separator(symbol, 50, separator_row, 1 /*above*/, 18, 0, 0 /*bottom_finder_value_3*/); + } + symbol->rows = symbol->rows + 1; + + /* ISO/IEC 24724:2011 5.3.2.2 minimum 33X height per row */ + if (symbol->symbology == BARCODE_DBAR_OMNSTK_CC) { + symbol->height = symbol->height ? 33.0f : 66.0f; /* Pass back min row or default height */ + } else { + if (symbol->output_options & COMPLIANT_HEIGHT) { + error_number = set_height(symbol, 33.0f, 66.0f, 0.0f, 0 /*no_errtxt*/); + } else { + (void) set_height(symbol, 0.0f, 66.0f, 0.0f, 1 /*no_errtxt*/); + } + } + } + + return error_number; +} + +/* GS1 DataBar Omnidirectional/Truncated/Stacked */ +INTERNAL int dbar_omn(struct zint_symbol *symbol, unsigned char source[], int length) { + return dbar_omn_cc(symbol, source, length, 0 /*cc_rows*/); +} + +/* GS1 DataBar Limited, allowing for composite if `cc_rows` set */ +INTERNAL int dbar_ltd_cc(struct zint_symbol *symbol, unsigned char source[], int length, const int cc_rows) { + int error_number = 0, i; + large_uint accum; + uint64_t left_character, right_character; + int left_group, right_group, left_odd, left_even, right_odd, right_even; + int left_widths[14], right_widths[14]; + int checksum, check_elements[14], total_widths[47], writer; + int latch; + int separator_row; + int widths[7]; + + separator_row = 0; + + if (length > 14) { /* Allow check digit to be specified (will be verified and ignored) */ + return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 382, "Input length %d too long (maximum 14)", length); + } + if ((i = not_sane(NEON_F, source, length))) { + return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 383, + "Invalid character at position %d in input (digits only)", i); + } + + if (length == 14) { /* Verify check digit */ + if (gs1_check_digit(source, 13) != source[13]) { + return errtxtf(ZINT_ERROR_INVALID_CHECK, symbol, 389, "Invalid check digit '%1$c', expecting '%2$c'", + source[13], gs1_check_digit(source, 13)); + } + length--; /* Ignore */ + } + + if (length == 13) { + if ((source[0] != '0') && (source[0] != '1')) { + return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 384, "Input value out of range (0 to 1999999999999)"); + } + } + + /* Make some room for a separator row for composite symbols */ + if (symbol->symbology == BARCODE_DBAR_LTD_CC) { + separator_row = symbol->rows; + symbol->row_height[separator_row] = 1; + symbol->rows += 1; + } + + large_load_str_u64(&accum, source, length); + + if (cc_rows) { + /* Add symbol linkage flag */ + large_add_u64(&accum, 2015133531096); + } + + /* Calculate left and right pair values */ + + right_character = large_div_u64(&accum, 2013571); + left_character = large_lo(&accum); + + if (left_character >= 1996939) { + left_group = 6; + left_character -= 1996939; + } else if (left_character >= 1979845) { + left_group = 5; + left_character -= 1979845; + } else if (left_character >= 1491021) { + left_group = 4; + left_character -= 1491021; + } else if (left_character >= 1000776) { + left_group = 3; + left_character -= 1000776; + } else if (left_character >= 820064) { + left_group = 2; + left_character -= 820064; + } else if (left_character >= 183064) { + left_group = 1; + left_character -= 183064; + } else { + left_group = 0; + } + + if (right_character >= 1996939) { + right_group = 6; + right_character -= 1996939; + } else if (right_character >= 1979845) { + right_group = 5; + right_character -= 1979845; + } else if (right_character >= 1491021) { + right_group = 4; + right_character -= 1491021; + } else if (right_character >= 1000776) { + right_group = 3; + right_character -= 1000776; + } else if (right_character >= 820064) { + right_group = 2; + right_character -= 820064; + } else if (right_character >= 183064) { + right_group = 1; + right_character -= 183064; + } else { + right_group = 0; + } + + left_odd = (int) (left_character / dbar_ltd_t_even[left_group]); + left_even = (int) (left_character % dbar_ltd_t_even[left_group]); + right_odd = (int) (right_character / dbar_ltd_t_even[right_group]); + right_even = (int) (right_character % dbar_ltd_t_even[right_group]); + + getRSSwidths(widths, left_odd, dbar_ltd_modules_odd[left_group], 7, dbar_ltd_widest_odd[left_group], 1); + for (i = 0; i <= 6; i++) { + left_widths[i * 2] = widths[i]; + } + getRSSwidths(widths, left_even, dbar_ltd_modules_even[left_group], 7, dbar_ltd_widest_even[left_group], 0); + for (i = 0; i <= 6; i++) { + left_widths[i * 2 + 1] = widths[i]; + } + getRSSwidths(widths, right_odd, dbar_ltd_modules_odd[right_group], 7, dbar_ltd_widest_odd[right_group], 1); + for (i = 0; i <= 6; i++) { + right_widths[i * 2] = widths[i]; + } + getRSSwidths(widths, right_even, dbar_ltd_modules_even[right_group], 7, dbar_ltd_widest_even[right_group], 0); + for (i = 0; i <= 6; i++) { + right_widths[i * 2 + 1] = widths[i]; + } + + checksum = 0; + /* Calculate the checksum */ + for (i = 0; i < 14; i++) { +#if defined(_MSC_VER) && _MSC_VER == 1900 && defined(_WIN64) /* MSVC 2015 x64 */ + checksum %= 89; /* Hack to get around optimizer bug */ +#endif + checksum += dbar_ltd_checksum_weight[i] * left_widths[i]; + checksum += dbar_ltd_checksum_weight[i + 14] * right_widths[i]; + } + checksum %= 89; + + for (i = 0; i < 14; i++) { + check_elements[i] = dbar_ltd_finder_pattern[i + (checksum * 14)]; + } + + total_widths[0] = 1; + total_widths[1] = 1; + total_widths[44] = 1; + total_widths[45] = 1; + total_widths[46] = 5; + for (i = 0; i < 14; i++) { + total_widths[i + 2] = left_widths[i]; + total_widths[i + 16] = check_elements[i]; + total_widths[i + 30] = right_widths[i]; + } + + writer = 0; + latch = 0; + for (i = 0; i < 47; i++) { + writer = dbar_expand(symbol, writer, &latch, total_widths[i]); + } + if (symbol->width < writer) { + symbol->width = writer; + } + symbol->rows = symbol->rows + 1; + + /* Add separator pattern if composite symbol */ + if (symbol->symbology == BARCODE_DBAR_LTD_CC) { + for (i = 4; i < 70; i++) { + if (!(module_is_set(symbol, separator_row + 1, i))) { + set_module(symbol, separator_row, i); + } + } + } + + /* Set human readable text */ + dbar_set_gtin14_hrt(symbol, source, length); + + /* ISO/IEC 24724:2011 6.2 10X minimum height, use as default also */ + if (symbol->symbology == BARCODE_DBAR_LTD_CC) { + symbol->height = 10.0f; /* Pass back min row == default height */ + } else { + if (symbol->output_options & COMPLIANT_HEIGHT) { + error_number = set_height(symbol, 10.0f, 10.0f, 0.0f, 0 /*no_errtxt*/); + } else { + (void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/); + } + } + + return error_number; +} + +/* GS1 DataBar Limited */ +INTERNAL int dbar_ltd(struct zint_symbol *symbol, unsigned char source[], int length) { + return dbar_ltd_cc(symbol, source, length, 0 /*cc_rows*/); +} + +/* Check and convert date to DataBar date value */ +INTERNAL int dbar_date(const unsigned char source[], const int length, const int src_posn) { + int yy, mm, dd; + + if (src_posn + 4 + 2 > length) { + return -1; + } + yy = to_int(source + src_posn, 2); + mm = to_int(source + src_posn + 2, 2); + dd = to_int(source + src_posn + 4, 2); + + /* Month can't be zero but day can (means last day of month, + GS1 General Specifications Sections 3.4.2 to 3.4.7) */ + if (yy < 0 || mm <= 0 || mm > 12 || dd < 0 || dd > 31) { + return -1; + } + return yy * 384 + (mm - 1) * 32 + dd; +} + +/* Handles all data encodation from section 7.2.5 of ISO/IEC 24724 */ +static int dbar_exp_binary_string(struct zint_symbol *symbol, const unsigned char source[], char binary_string[], + int *p_cols_per_row, const int max_rows, int *p_bp) { + int encoding_method, i, j, read_posn, mode = NUMERIC; + char last_digit = '\0'; + int symbol_characters, characters_per_row = *p_cols_per_row * 2; + int min_cols_per_row = 0; + int length = (int) ustrlen(source); + const int debug_print = (symbol->debug & ZINT_DEBUG_PRINT); + char *general_field = (char *) z_alloca(length + 1); + int bp = *p_bp; + int remainder, d1, d2; + int cdf_bp_start; /* Compressed data field start - debug only */ + + if (length > 77) { /* ISO/IEC 24724:2011 4.2.d.2 */ + /* Caught below anyway but catch here also for better feedback */ + return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 378, "Processed input length %d too long (maximum 77)", length); + } + + /* Decide whether a compressed data field is required and if so what + method to use - method 2 = no compressed data field */ + + if ((length >= 16) && ((source[0] == '0') && (source[1] == '1'))) { + /* (01) and other AIs */ + encoding_method = 1; + if (debug_print) fputs("Choosing Method 1\n", stdout); + } else { + /* Any AIs */ + encoding_method = 2; + if (debug_print) fputs("Choosing Method 2\n", stdout); + } + + if (((length >= 20) && (encoding_method == 1)) && ((source[2] == '9') && (source[16] == '3'))) { + /* Possibly encoding method > 2 */ + + if (debug_print) fputs("Checking for other methods\n", stdout); + + if ((length >= 26) && (source[17] == '1') && (source[18] == '0')) { + /* Methods 3, 7, 9, 11 and 13 */ + + /* (01) and (310x) */ + int weight = to_int(source + 20, 6); + + /* Maximum weight = 99999 for 7 to 14 (ISO/IEC 24724:2011 7.2.5.4.4) */ + if (weight >= 0 && weight <= 99999) { + + if (length == 26) { + if ((source[19] == '3') && weight <= 32767) { /* In grams, max 32.767 kilos */ + /* (01) and (3103) */ + encoding_method = 3; + } else { + /* (01), (310x) - use method 7 with dummy date 38400 */ + encoding_method = 7; + } + + } else if ((length == 34) && (source[26] == '1') + && (source[27] == '1' || source[27] == '3' || source[27] == '5' || source[27] == '7') + && dbar_date(source, length, 28) >= 0) { + + /* (01), (310x) and (11) - metric weight and production date */ + /* (01), (310x) and (13) - metric weight and packaging date */ + /* (01), (310x) and (15) - metric weight and "best before" date */ + /* (01), (310x) and (17) - metric weight and expiration date */ + encoding_method = 6 + (source[27] - '0'); + } + } + + } else if ((length >= 26) && (source[17] == '2') && (source[18] == '0')) { + /* Methods 4, 8, 10, 12 and 14 */ + + /* (01) and (320x) */ + int weight = to_int(source + 20, 6); + + /* Maximum weight = 99999 for 7 to 14 (ISO/IEC 24724:2011 7.2.5.4.4) */ + if (weight >= 0 && weight <= 99999) { + + /* (3202) in 0.01 pounds, max 99.99 pounds; (3203) in 0.001 pounds, max 22.767 pounds */ + if (length == 26) { + if ((source[19] == '2' && weight <= 9999) || (source[19] == '3' && weight <= 22767)) { + /* (01) and (3202)/(3203) */ + encoding_method = 4; + } else { + /* (01), (320x) - use method 8 with dummy date 38400 */ + encoding_method = 8; + } + + } else if ((length == 34) && (source[26] == '1') + && (source[27] == '1' || source[27] == '3' || source[27] == '5' || source[27] == '7') + && dbar_date(source, length, 28) >= 0) { + + /* (01), (320x) and (11) - English weight and production date */ + /* (01), (320x) and (13) - English weight and packaging date */ + /* (01), (320x) and (15) - English weight and "best before" date */ + /* (01), (320x) and (17) - English weight and expiration date */ + encoding_method = 7 + (source[27] - '0'); + } + } + + } else if ((source[17] == '9') && ((source[19] >= '0') && (source[19] <= '3'))) { + /* Methods 5 and 6 */ + if (source[18] == '2') { + /* (01) and (392x) */ + encoding_method = 5; + } else if (source[18] == '3' && to_int(source + 20, 3) >= 0) { /* Check 3-digit currency string */ + /* (01) and (393x) */ + encoding_method = 6; + } + } + + if (debug_print && encoding_method != 1) printf("Now using method %d\n", encoding_method); + } + + switch (encoding_method) { /* Encoding method - Table 10 */ + case 1: bp = bin_append_posn(4, 3, binary_string, bp); /* "1XX" */ + read_posn = 16; + break; + case 2: bp = bin_append_posn(0, 4, binary_string, bp); /* "00XX" */ + read_posn = 0; + break; + case 3: /* 0100 */ + case 4: /* 0101 */ + bp = bin_append_posn(4 + (encoding_method - 3), 4, binary_string, bp); + read_posn = 26; + break; + case 5: bp = bin_append_posn(0x30, 7, binary_string, bp); /* "01100XX" */ + read_posn = 20; + break; + case 6: bp = bin_append_posn(0x34, 7, binary_string, bp); /* "01101XX" */ + read_posn = 23; + break; + default: /* Modes 7 to 14 */ + bp = bin_append_posn(56 + (encoding_method - 7), 7, binary_string, bp); + read_posn = length; /* 34 or 26 */ + break; + } + if (debug_print) printf("Setting binary = %.*s\n", bp, binary_string); + + /* Variable length symbol bit field is just given a place holder (XX) for the time being */ + + /* Verify that the data to be placed in the compressed data field is all numeric data + before carrying out compression */ + for (i = 0; i < read_posn; i++) { + if (!z_isdigit(source[i])) { + if (source[i] != '\x1D') { + /* Something is wrong */ + return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 385, + "Invalid character in Compressed Field data (digits only)"); + } + } + } + + /* Now encode the compressed data field */ + + if (debug_print) fputs("Proceeding to encode data\n", stdout); + cdf_bp_start = bp; /* Debug use only */ + + if (encoding_method == 1) { + /* Encoding method field "1" - general item identification data */ + + bp = bin_append_posn(ctoi(source[2]), 4, binary_string, bp); /* Leading digit after stripped "01" */ + + for (i = 3; i < 15; i += 3) { /* Next 12 digits, excluding final check digit */ + bp = bin_append_posn(to_int(source + i, 3), 10, binary_string, bp); + } + + } else if ((encoding_method == 3) || (encoding_method == 4)) { + /* Encoding method field "0100" - variable weight item (0,001 kilogram increments) */ + /* Encoding method field "0101" - variable weight item (0,01 or 0,001 pound increment) */ + + for (i = 3; i < 15; i += 3) { /* Leading "019" stripped, and final check digit excluded */ + bp = bin_append_posn(to_int(source + i, 3), 10, binary_string, bp); + } + + if ((encoding_method == 4) && (source[19] == '3')) { + bp = bin_append_posn(to_int(source + 20, 6) + 10000, 15, binary_string, bp); + } else { + bp = bin_append_posn(to_int(source + 20, 6), 15, binary_string, bp); + } + + } else if ((encoding_method == 5) || (encoding_method == 6)) { + /* Encoding method "01100" - variable measure item and price */ + /* Encoding method "01101" - variable measure item and price with ISO 4217 Currency Code */ + + for (i = 3; i < 15; i += 3) { /* Leading "019" stripped, and final check digit excluded */ + bp = bin_append_posn(to_int(source + i, 3), 10, binary_string, bp); + } + + bp = bin_append_posn(source[19] - '0', 2, binary_string, bp); /* 0-3 x of 392x/393x */ + + if (encoding_method == 6) { + bp = bin_append_posn(to_int(source + 20, 3), 10, binary_string, bp); /* 3-digit currency */ + } + + } else if ((encoding_method >= 7) && (encoding_method <= 14)) { + /* Encoding method fields "0111000" through "0111111" - variable weight item plus date */ + int group_val; + char weight_str[8]; + + for (i = 3; i < 15; i += 3) { /* Leading "019" stripped, and final check digit excluded */ + bp = bin_append_posn(to_int(source + i, 3), 10, binary_string, bp); + } + + weight_str[0] = source[19]; /* 0-9 x of 310x/320x */ + + for (i = 1; i < 6; i++) { /* Leading "0" of weight excluded */ + weight_str[i] = source[20 + i]; + } + weight_str[6] = '\0'; + + bp = bin_append_posn(atoi(weight_str), 20, binary_string, bp); + + if (length == 34) { + /* Date information is included */ + group_val = dbar_date(source, length, 28); + } else { + group_val = 38400; + } + + bp = bin_append_posn((int) group_val, 16, binary_string, bp); + } + + if (debug_print && bp > cdf_bp_start) { + printf("Compressed data field (%d) = %.*s\n", bp - cdf_bp_start, bp - cdf_bp_start, + binary_string + cdf_bp_start); + } + + /* 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; + for (i = read_posn; i < length; i++) { + general_field[j] = source[i]; + j++; + } + general_field[j] = '\0'; + + if (debug_print) printf("General field data = %s\n", general_field); + + if (j != 0) { /* If general field not empty */ + if (!general_field_encode(general_field, j, &mode, &last_digit, binary_string, &bp)) { + /* Will happen if character not in CSET 82 + space */ + return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 386, "Invalid character in General Field data"); + } + } + + if (debug_print) printf("Resultant binary (%d): %.*s\n", bp, bp, binary_string); + + remainder = 12 - (bp % 12); + if (remainder == 12) { + remainder = 0; + } + symbol_characters = ((bp + remainder) / 12) + 1; + + if (max_rows) { + min_cols_per_row = ((symbol_characters + 1) / 2 + max_rows - 1) / max_rows; + if (min_cols_per_row > *p_cols_per_row) { + characters_per_row = min_cols_per_row * 2; + } + } + + if (characters_per_row && (symbol_characters % characters_per_row) == 1) { /* DBAR_EXPSTK */ + symbol_characters++; + } + + if (symbol_characters < 4) { + symbol_characters = 4; + } + + remainder = (12 * (symbol_characters - 1)) - bp; + + if (last_digit) { + /* There is still one more numeric digit to encode */ + if (debug_print) fputs("Adding extra (odd) numeric digit\n", stdout); + + if ((remainder >= 4) && (remainder <= 6)) { + bp = bin_append_posn(ctoi(last_digit) + 1, 4, binary_string, bp); + } else { + d1 = ctoi(last_digit); + d2 = 10; + + bp = bin_append_posn((11 * d1) + d2 + 8, 7, binary_string, bp); + } + + remainder = 12 - (bp % 12); + if (remainder == 12) { + remainder = 0; + } + symbol_characters = ((bp + remainder) / 12) + 1; + + if (max_rows) { + min_cols_per_row = ((symbol_characters + 1) / 2 + max_rows - 1) / max_rows; + if (min_cols_per_row > *p_cols_per_row) { + characters_per_row = min_cols_per_row * 2; + } + } + + if (characters_per_row && (symbol_characters % characters_per_row) == 1) { /* DBAR_EXPSTK */ + symbol_characters++; + } + + if (symbol_characters < 4) { + symbol_characters = 4; + } + + remainder = (12 * (symbol_characters - 1)) - bp; + + if (debug_print) printf(" Expanded binary (%d): %.*s\n", bp, bp, binary_string); + } + + if (bp > 252) { /* 252 = (21 * 12) */ + return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 387, /* TODO: Better error message */ + "Input too long, requires %d symbol characters (maximum 21)", (bp + 11) / 12); + } + + if (min_cols_per_row && min_cols_per_row > *p_cols_per_row) { + *p_cols_per_row = min_cols_per_row; + } + + /* Now add padding to binary string (7.2.5.5.4) */ + i = remainder; + if (mode == NUMERIC) { + bp = bin_append_posn(0, 4, binary_string, bp); /* "0000" */ + i -= 4; + } + for (; i > 0; i -= 5) { + bp = bin_append_posn(4, 5, binary_string, bp); /* "00100" */ + } + + /* Patch variable length symbol bit field */ + d1 = symbol_characters & 1; + + if (symbol_characters <= 14) { + d2 = 0; + } else { + d2 = 1; + } + + if (encoding_method == 1) { + binary_string[2] = d1 ? '1' : '0'; + binary_string[3] = d2 ? '1' : '0'; + } else if (encoding_method == 2) { + binary_string[3] = d1 ? '1' : '0'; + binary_string[4] = d2 ? '1' : '0'; + } else if ((encoding_method == 5) || (encoding_method == 6)) { + binary_string[6] = d1 ? '1' : '0'; + binary_string[7] = d2 ? '1' : '0'; + } + if (debug_print) { + printf(" Final binary (%d): %.*s\n Symbol chars: %d, Remainder: %d\n", + bp, bp, binary_string, symbol_characters, remainder); + } + + *p_bp = bp; + + return 0; +} + +/* Separator for DataBar Expanded Stacked and DataBar Expanded Composite */ +static void dbar_exp_separator(struct zint_symbol *symbol, int width, const int cols, const int separator_row, + const int above_below, const int special_case_row, const int left_to_right, const int odd_last_row, + int *p_v2_latch) { + int i, i_start, i_end, j, k; + int module_row = separator_row + above_below; + int v2_latch = p_v2_latch ? *p_v2_latch : 0; + int space_latch = 0; + + for (j = 4 + special_case_row, width -= 4; j < width; j++) { + if (module_is_set(symbol, module_row, j)) { + unset_module(symbol, separator_row, j); + } else { + set_module(symbol, separator_row, j); + } + } + + /* Finder adjustment */ + for (j = 0; j < cols; j++) { + /* 49 == data (17) + finder (15) + data(17) triplet, 19 == 2 (guard) + 17 (initial check/data character) */ + k = (49 * j) + 19 + special_case_row; + if (left_to_right) { + /* Last 13 modules of version 2 finder and first 13 modules of version 1 finder */ + i_start = v2_latch ? 2 : 0; + i_end = v2_latch ? 15 : 13; + for (i = i_start; i < i_end; i++) { + if (module_is_set(symbol, module_row, i + k)) { + unset_module(symbol, separator_row, i + k); + space_latch = 0; + } else { + if (space_latch) { + unset_module(symbol, separator_row, i + k); + } else { + set_module(symbol, separator_row, i + k); + } + space_latch = !space_latch; + } + } + } else { + if (odd_last_row) { + k -= 17; /* No data char at beginning of row, i.e. ends with finder */ + } + /* First 13 modules of version 1 finder and last 13 modules of version 2 finder */ + i_start = v2_latch ? 14 : 12; + i_end = v2_latch ? 2 : 0; + for (i = i_start; i >= i_end; i--) { + if (module_is_set(symbol, module_row, i + k)) { + unset_module(symbol, separator_row, i + k); + space_latch = 0; + } else { + if (space_latch) { + unset_module(symbol, separator_row, i + k); + } else { + set_module(symbol, separator_row, i + k); + } + space_latch = !space_latch; + } + } + } + v2_latch = !v2_latch; + } + + if (p_v2_latch && above_below == -1) { /* Only set if below */ + *p_v2_latch = v2_latch; + } +} + +/* Set HRT for DataBar Expanded */ +static void dbar_exp_hrt(struct zint_symbol *symbol, unsigned char source[], const int length) { + + /* Max possible length is 77 digits so will fit */ + if (symbol->input_mode & GS1PARENS_MODE) { + memcpy(symbol->text, source, length + 1); /* Include terminating NUL */ + } else { + int i; + /* Can't have square brackets in content so bracket level not required */ + for (i = 0; i <= length /* Include terminating NUL */; i++) { + if (source[i] == '[') { + symbol->text[i] = '('; + } else if (source[i] == ']') { + symbol->text[i] = ')'; + } else { + symbol->text[i] = source[i]; + } + } + } +} + +/* GS1 DataBar Expanded, setting linkage for composite if `cc_rows` set */ +INTERNAL int dbar_exp_cc(struct zint_symbol *symbol, unsigned char source[], int length, const int cc_rows) { + int error_number, warn_number; + int i, j, k, p, codeblocks, data_chars, vs, group, v_odd, v_even; + int latch; + int char_widths[21][8], checksum, check_widths[8], c_group; + int check_char, c_odd, c_even, elements[235], pattern_width, reader, writer; + int separator_row; + /* Allow for 8 bits + 5-bit latch per char + 200 bits overhead/padding */ + unsigned int bin_len = 13 * length + 200 + 1; + int widths[4]; + int bp = 0; + int cols_per_row = 0; + int max_rows = 0; + int stack_rows = 1; + const int debug_print = (symbol->debug & ZINT_DEBUG_PRINT); + unsigned char *reduced = (unsigned char *) z_alloca(length + 1); + char *binary_string = (char *) z_alloca(bin_len); + + separator_row = 0; + + error_number = gs1_verify(symbol, source, length, reduced); + if (error_number >= ZINT_ERROR) { + return error_number; + } + warn_number = error_number; + + if (debug_print) { + printf("Reduced (%d): %s\n", (int) ustrlen(reduced), reduced); + } + + if ((symbol->symbology == BARCODE_DBAR_EXP_CC) || (symbol->symbology == BARCODE_DBAR_EXPSTK_CC)) { + /* Make space for a composite separator pattern */ + separator_row = symbol->rows; + symbol->row_height[separator_row] = 1; + symbol->rows += 1; + } + + if (cc_rows) { /* The "component linkage" flag */ + binary_string[bp++] = '1'; + } else { + binary_string[bp++] = '0'; + } + + if ((symbol->symbology == BARCODE_DBAR_EXPSTK) || (symbol->symbology == BARCODE_DBAR_EXPSTK_CC)) { + cols_per_row = 2; /* Default */ + if (symbol->option_2 >= 1 && symbol->option_2 <= 11) { + cols_per_row = symbol->option_2; + if (cc_rows && (cols_per_row == 1)) { + /* "There shall be a minimum of four symbol characters in the + first row of an RSS Expanded Stacked symbol when it is the linear + component of an EAN.UCC Composite symbol." */ + cols_per_row = 2; + } + } else if (symbol->option_3 >= 2 && symbol->option_3 <= 11) { + max_rows = symbol->option_3; + } + } + + error_number = dbar_exp_binary_string(symbol, reduced, binary_string, &cols_per_row, max_rows, &bp); + if (error_number != 0) { + return error_number; + } + + data_chars = bp / 12; + + if (debug_print) fputs("Data:", stdout); + for (i = 0; i < data_chars; i++) { + k = i * 12; + vs = 0; + for (j = 0; j < 12; j++) { + if (binary_string[k + j] == '1') { + vs |= (0x800 >> j); + } + } + + if (vs <= 347) { + group = 1; + } else if (vs <= 1387) { + group = 2; + } else if (vs <= 2947) { + group = 3; + } else if (vs <= 3987) { + group = 4; + } else { + group = 5; + } + v_odd = (vs - dbar_exp_g_sum[group - 1]) / dbar_exp_t_even[group - 1]; + v_even = (vs - dbar_exp_g_sum[group - 1]) % dbar_exp_t_even[group - 1]; + if (debug_print) printf("%s%d", i == 0 || (i & 1) ? " " : ",", vs); + + getRSSwidths(widths, v_odd, dbar_exp_modules_odd[group - 1], 4, dbar_exp_widest_odd[group - 1], 0); + char_widths[i][0] = widths[0]; + char_widths[i][2] = widths[1]; + char_widths[i][4] = widths[2]; + char_widths[i][6] = widths[3]; + getRSSwidths(widths, v_even, dbar_exp_modules_even[group - 1], 4, dbar_exp_widest_even[group - 1], 1); + char_widths[i][1] = widths[0]; + char_widths[i][3] = widths[1]; + char_widths[i][5] = widths[2]; + char_widths[i][7] = widths[3]; + } + if (debug_print) fputc('\n', stdout); + + /* 7.2.6 Check character */ + /* The checksum value is equal to the mod 211 residue of the weighted sum of the widths of the + elements in the data characters. */ + checksum = 0; + for (i = 0; i < data_chars; i++) { + int row = dbar_exp_weight_rows[(((data_chars - 2) / 2) * 21) + i]; + for (j = 0; j < 8; j++) { + checksum += (char_widths[i][j] * dbar_exp_checksum_weight[(row * 8) + j]); + + } + } + + check_char = (211 * ((data_chars + 1) - 4)) + (checksum % 211); + + if (debug_print) { + printf("Data chars: %d, Check char: %d\n", data_chars, check_char); + } + + if (check_char <= 347) { + c_group = 1; + } else if (check_char <= 1387) { + c_group = 2; + } else if (check_char <= 2947) { + c_group = 3; + } else if (check_char <= 3987) { + c_group = 4; + } else { + c_group = 5; + } + + c_odd = (check_char - dbar_exp_g_sum[c_group - 1]) / dbar_exp_t_even[c_group - 1]; + c_even = (check_char - dbar_exp_g_sum[c_group - 1]) % dbar_exp_t_even[c_group - 1]; + + getRSSwidths(widths, c_odd, dbar_exp_modules_odd[c_group - 1], 4, dbar_exp_widest_odd[c_group - 1], 0); + check_widths[0] = widths[0]; + check_widths[2] = widths[1]; + check_widths[4] = widths[2]; + check_widths[6] = widths[3]; + getRSSwidths(widths, c_even, dbar_exp_modules_even[c_group - 1], 4, dbar_exp_widest_even[c_group - 1], 1); + check_widths[1] = widths[0]; + check_widths[3] = widths[1]; + check_widths[5] = widths[2]; + check_widths[7] = widths[3]; + + /* Initialise element array */ + codeblocks = (data_chars + 1) / 2 + ((data_chars + 1) & 1); + pattern_width = (codeblocks * 5) + ((data_chars + 1) * 8) + 4; + memset(elements, 0, sizeof(int) * pattern_width); + + /* Put finder patterns in element array */ + p = (((((data_chars + 1) - 2) / 2) + ((data_chars + 1) & 1)) - 1) * 11; + for (i = 0; i < codeblocks; i++) { + k = p + i; + for (j = 0; j < 5; j++) { + elements[(21 * i) + j + 10] = dbar_exp_finder_pattern[((dbar_exp_finder_sequence[k] - 1) * 5) + j]; + } + } + + /* Put check character in element array */ + for (i = 0; i < 8; i++) { + elements[i + 2] = check_widths[i]; + } + + /* Put forward reading data characters in element array */ + for (i = 1; i < data_chars; i += 2) { + k = (((i - 1) / 2) * 21) + 23; + for (j = 0; j < 8; j++) { + elements[k + j] = char_widths[i][j]; + } + } + + /* Put reversed data characters in element array */ + for (i = 0; i < data_chars; i += 2) { + k = ((i / 2) * 21) + 15; + for (j = 0; j < 8; j++) { + elements[k + j] = char_widths[i][7 - j]; + } + } + + if ((symbol->symbology == BARCODE_DBAR_EXP) || (symbol->symbology == BARCODE_DBAR_EXP_CC)) { + /* Copy elements into symbol */ + + elements[0] = 1; /* Left guard */ + elements[1] = 1; + + elements[pattern_width - 2] = 1; /* Right guard */ + elements[pattern_width - 1] = 1; + + writer = 0; + latch = 0; + for (i = 0; i < pattern_width; i++) { + writer = dbar_expand(symbol, writer, &latch, elements[i]); + } + if (symbol->width < writer) { + symbol->width = writer; + } + symbol->rows = symbol->rows + 1; + + dbar_exp_hrt(symbol, source, length); + + } else { + int current_row, current_block, left_to_right; + int v2_latch = 0; + /* RSS Expanded Stacked */ + + /* Bug corrected: Character missing for message + * [01]90614141999996[10]1234222222222221 + * Patch by Daniel Frede + */ + + stack_rows = codeblocks / cols_per_row; + if (codeblocks % cols_per_row > 0) { + stack_rows++; + } + + current_block = 0; + for (current_row = 1; current_row <= stack_rows; current_row++) { + int special_case_row = 0; + int elements_in_sub; + int sub_elements[235] = {0}; + int num_columns; + + /* Number of columns in current row */ + if (current_row * cols_per_row > codeblocks) { + num_columns = codeblocks - current_block; + } else { + num_columns = cols_per_row; + } + + /* Row Start */ + sub_elements[0] = 1; /* Left guard */ + sub_elements[1] = 1; + elements_in_sub = 2; + + /* If last row and is partial and even-numbered, and have even columns (segment pairs), + and odd number of finders (== odd number of columns) */ + if ((current_row == stack_rows) && (num_columns != cols_per_row) && !(current_row & 1) + && !(cols_per_row & 1) && (num_columns & 1)) { + /* Special case bottom row */ + special_case_row = 1; + sub_elements[0] = 2; /* Extra space (latch set below) */ + } + + /* If odd number of columns or current row odd-numbered or special case last row then left-to-right, + else right-to-left */ + if ((cols_per_row & 1) || (current_row & 1) || special_case_row) { + left_to_right = 1; + } else { + left_to_right = 0; + } + + if (debug_print) { + if (current_row == stack_rows) { + printf("Last row: number of columns: %d / %d, left to right: %d, special case: %d\n", + num_columns, cols_per_row, left_to_right, special_case_row); + } + } + + /* Row Data */ + reader = 0; + do { + i = 2 + (current_block * 21); + for (j = 0; j < 21; j++) { + if ((i + j) < pattern_width) { + if (left_to_right) { + sub_elements[j + (reader * 21) + 2] = elements[i + j]; + } else { + sub_elements[(20 - j) + (num_columns - 1 - reader) * 21 + 2] = elements[i + j]; + } + } + elements_in_sub++; + } + reader++; + current_block++; + } while ((reader < cols_per_row) && (current_block < codeblocks)); + + /* Row Stop */ + sub_elements[elements_in_sub] = 1; /* Right guard */ + sub_elements[elements_in_sub + 1] = 1; + elements_in_sub += 2; + + latch = (current_row & 1) || special_case_row ? 0 : 1; + + writer = 0; + for (i = 0; i < elements_in_sub; i++) { + writer = dbar_expand(symbol, writer, &latch, sub_elements[i]); + } + if (symbol->width < writer) { + symbol->width = writer; + } + + if (current_row != 1) { + int odd_last_row = (current_row == stack_rows) && (data_chars % 2 == 0); + + /* Middle separator pattern (above current row) */ + for (j = 5; j < (49 * cols_per_row); j += 2) { + set_module(symbol, symbol->rows - 2, j); + } + symbol->row_height[symbol->rows - 2] = 1; + + /* Bottom separator pattern (above current row) */ + dbar_exp_separator(symbol, writer, reader, symbol->rows - 1, 1 /*above*/, special_case_row, + left_to_right, odd_last_row, &v2_latch); + symbol->row_height[symbol->rows - 1] = 1; + } + + if (current_row != stack_rows) { + /* Top separator pattern (below current row) */ + dbar_exp_separator(symbol, writer, reader, symbol->rows + 1, -1 /*below*/, 0 /*special_case_row*/, + left_to_right, 0 /*odd_last_row*/, &v2_latch); + symbol->row_height[symbol->rows + 1] = 1; + } + + symbol->rows = symbol->rows + 4; + } + symbol->rows = symbol->rows - 3; + } + + if (symbol->symbology == BARCODE_DBAR_EXP_CC || symbol->symbology == BARCODE_DBAR_EXPSTK_CC) { + /* Composite separator */ + dbar_exp_separator(symbol, symbol->width, 4, separator_row, 1 /*above*/, 0 /*special_case_row*/, + 1 /*left_to_right*/, 0 /*odd_last_row*/, NULL); + } + + /* DataBar Expanded ISO/IEC 24724:2011 7.2.1 and DataBar Expanded Stacked ISO/IEC 24724:2011 7.2.8 + 34X min per row */ + if (symbol->symbology == BARCODE_DBAR_EXP_CC || symbol->symbology == BARCODE_DBAR_EXPSTK_CC) { + symbol->height = symbol->height ? 34.0f : 34.0f * stack_rows; /* Pass back min row or default height */ + } else { + if (symbol->output_options & COMPLIANT_HEIGHT) { + if (warn_number == 0) { + warn_number = set_height(symbol, 34.0f, 34.0f * stack_rows, 0.0f, 0 /*no_errtxt*/); + } else { + (void) set_height(symbol, 34.0f, 34.0f * stack_rows, 0.0f, 1 /*no_errtxt*/); + } + } else { + (void) set_height(symbol, 0.0f, 34.0f * stack_rows, 0.0f, 1 /*no_errtxt*/); + } + } + + return warn_number; +} + +/* GS1 DataBar Expanded */ +INTERNAL int dbar_exp(struct zint_symbol *symbol, unsigned char source[], int length) { + return dbar_exp_cc(symbol, source, length, 0 /*cc_rows*/); +} + +/* vim: set ts=4 sw=4 et : */
