mirror of
https://github.com/woo-j/zint.git
synced 2024-12-25 09:33:56 +03:00
0004cfb35a
Preserves old names in zint.h to retain backwards compatability.
1622 lines
56 KiB
C
1622 lines
56 KiB
C
/* rss.c - GS1 DataBar (formerly Reduced Space Symbology) */
|
|
|
|
/*
|
|
libzint - the open source barcode library
|
|
Copyright (C) 2008-2020 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.
|
|
*/
|
|
/* vim: set ts=4 sw=4 et : */
|
|
|
|
/* The functions "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>
|
|
#ifdef _MSC_VER
|
|
#include <malloc.h>
|
|
#endif
|
|
#include "common.h"
|
|
#include "large.h"
|
|
#include "rss.h"
|
|
#include "gs1.h"
|
|
#include "general_field.h"
|
|
|
|
/**********************************************************************
|
|
* combins(n,r): returns the number of Combinations of r selected from n:
|
|
* Combinations = n! / ((n - r)! * r!)
|
|
**********************************************************************/
|
|
static int combins(int n, 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, int elements, int maxWidth, 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 = 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 -= 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 += 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;
|
|
}
|
|
|
|
/* Calculate check digit from Annex A */
|
|
static int calc_check_digit(unsigned char *src) {
|
|
int i, check_digit;
|
|
int count = 0;
|
|
|
|
for (i = 0; i < 13; i++) {
|
|
count += (i & 1) ? ctoi(src[i]) : 3 * ctoi(src[i]);
|
|
}
|
|
check_digit = 10 - (count % 10);
|
|
if (check_digit == 10) {
|
|
check_digit = 0;
|
|
}
|
|
|
|
return check_digit;
|
|
}
|
|
|
|
/* Set GTIN-14 human readable text */
|
|
static void set_gtin14_hrt(struct zint_symbol *symbol, unsigned char *source, int src_len) {
|
|
int i;
|
|
unsigned char hrt[15];
|
|
|
|
ustrcpy(symbol->text, "(01)");
|
|
for (i = 0; i < 12; i++) {
|
|
hrt[i] = '0';
|
|
}
|
|
for (i = 0; i < src_len; i++) {
|
|
hrt[12 - i] = source[src_len - i - 1];
|
|
}
|
|
|
|
hrt[13] = itoc(calc_check_digit(hrt));
|
|
hrt[14] = '\0';
|
|
|
|
ustrcat(symbol->text, hrt);
|
|
}
|
|
|
|
/* Expand from a width pattern to a bit pattern */
|
|
static int rss_expand(struct zint_symbol *symbol, int writer, char *p_latch, int width) {
|
|
int j;
|
|
int latch = *p_latch;
|
|
|
|
for (j = 0; j < width; j++) {
|
|
if (latch == '1') {
|
|
set_module(symbol, symbol->rows, writer);
|
|
} else {
|
|
unset_module(symbol, symbol->rows, writer);
|
|
}
|
|
writer++;
|
|
}
|
|
if (latch == '1') {
|
|
*p_latch = '0';
|
|
} else {
|
|
*p_latch = '1';
|
|
}
|
|
|
|
return writer;
|
|
}
|
|
|
|
/* Adjust top/bottom separator for finder patterns */
|
|
static void rss14_finder_adjust(struct zint_symbol *symbol, int separator_row, int above_below, 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 == '1') {
|
|
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 rss14_separator(struct zint_symbol *symbol, int width, int separator_row, int above_below, int finder_start, int finder2_start, 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) {
|
|
rss14_finder_adjust(symbol, separator_row, above_below, finder_start);
|
|
}
|
|
if (finder2_start) {
|
|
rss14_finder_adjust(symbol, separator_row, above_below, finder2_start);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* GS1 DataBar Omnidirectional/Truncated/Stacked */
|
|
INTERNAL int rss14(struct zint_symbol *symbol, unsigned char source[], int src_len) {
|
|
int error_number = 0, i;
|
|
large_int 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;
|
|
char latch;
|
|
int separator_row;
|
|
int widths[4];
|
|
|
|
separator_row = 0;
|
|
|
|
if (src_len > 14) { /* Allow check digit to be specified (will be verified and ignored) */
|
|
strcpy(symbol->errtxt, "380: Input too long");
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
error_number = is_sane(NEON, source, src_len);
|
|
if (error_number == ZINT_ERROR_INVALID_DATA) {
|
|
strcpy(symbol->errtxt, "381: Invalid characters in data");
|
|
return error_number;
|
|
}
|
|
|
|
if (src_len == 14) { /* Verify check digit */
|
|
if (calc_check_digit(source) != ctoi(source[13])) {
|
|
strcpy(symbol->errtxt, "388: Invalid check digit");
|
|
return ZINT_ERROR_INVALID_CHECK;
|
|
}
|
|
src_len--; /* 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, src_len);
|
|
|
|
if (symbol->option_1 == 2) {
|
|
/* 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] = left_pair / 1597;
|
|
data_character[1] = left_pair % 1597;
|
|
|
|
data_character[2] = right_pair / 1597;
|
|
data_character[3] = right_pair % 1597;
|
|
|
|
/* Calculate odd and even subset values */
|
|
|
|
if ((data_character[0] >= 0) && (data_character[0] <= 160)) {
|
|
data_group[0] = 0;
|
|
}
|
|
if ((data_character[0] >= 161) && (data_character[0] <= 960)) {
|
|
data_group[0] = 1;
|
|
}
|
|
if ((data_character[0] >= 961) && (data_character[0] <= 2014)) {
|
|
data_group[0] = 2;
|
|
}
|
|
if ((data_character[0] >= 2015) && (data_character[0] <= 2714)) {
|
|
data_group[0] = 3;
|
|
}
|
|
if ((data_character[0] >= 2715) && (data_character[0] <= 2840)) {
|
|
data_group[0] = 4;
|
|
}
|
|
|
|
if ((data_character[1] >= 0) && (data_character[1] <= 335)) {
|
|
data_group[1] = 5;
|
|
}
|
|
if ((data_character[1] >= 336) && (data_character[1] <= 1035)) {
|
|
data_group[1] = 6;
|
|
}
|
|
if ((data_character[1] >= 1036) && (data_character[1] <= 1515)) {
|
|
data_group[1] = 7;
|
|
}
|
|
if ((data_character[1] >= 1516) && (data_character[1] <= 1596)) {
|
|
data_group[1] = 8;
|
|
}
|
|
|
|
if ((data_character[3] >= 0) && (data_character[3] <= 335)) {
|
|
data_group[3] = 5;
|
|
}
|
|
if ((data_character[3] >= 336) && (data_character[3] <= 1035)) {
|
|
data_group[3] = 6;
|
|
}
|
|
if ((data_character[3] >= 1036) && (data_character[3] <= 1515)) {
|
|
data_group[3] = 7;
|
|
}
|
|
if ((data_character[3] >= 1516) && (data_character[3] <= 1596)) {
|
|
data_group[3] = 8;
|
|
}
|
|
|
|
if ((data_character[2] >= 0) && (data_character[2] <= 160)) {
|
|
data_group[2] = 0;
|
|
}
|
|
if ((data_character[2] >= 161) && (data_character[2] <= 960)) {
|
|
data_group[2] = 1;
|
|
}
|
|
if ((data_character[2] >= 961) && (data_character[2] <= 2014)) {
|
|
data_group[2] = 2;
|
|
}
|
|
if ((data_character[2] >= 2015) && (data_character[2] <= 2714)) {
|
|
data_group[2] = 3;
|
|
}
|
|
if ((data_character[2] >= 2715) && (data_character[2] <= 2840)) {
|
|
data_group[2] = 4;
|
|
}
|
|
|
|
v_odd[0] = (data_character[0] - g_sum_table[data_group[0]]) / t_table[data_group[0]];
|
|
v_even[0] = (data_character[0] - g_sum_table[data_group[0]]) % t_table[data_group[0]];
|
|
v_odd[1] = (data_character[1] - g_sum_table[data_group[1]]) % t_table[data_group[1]];
|
|
v_even[1] = (data_character[1] - g_sum_table[data_group[1]]) / t_table[data_group[1]];
|
|
v_odd[3] = (data_character[3] - g_sum_table[data_group[3]]) % t_table[data_group[3]];
|
|
v_even[3] = (data_character[3] - g_sum_table[data_group[3]]) / t_table[data_group[3]];
|
|
v_odd[2] = (data_character[2] - g_sum_table[data_group[2]]) / t_table[data_group[2]];
|
|
v_even[2] = (data_character[2] - g_sum_table[data_group[2]]) % t_table[data_group[2]];
|
|
|
|
|
|
/* Use RSS subset width algorithm */
|
|
for (i = 0; i < 4; i++) {
|
|
if ((i == 0) || (i == 2)) {
|
|
getRSSwidths(widths, v_odd[i], modules_odd[data_group[i]], 4, 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], modules_even[data_group[i]], 4, 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], modules_odd[data_group[i]], 4, 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], modules_even[data_group[i]], 4, 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 += checksum_weight[i] * data_widths[i][0];
|
|
checksum += checksum_weight[i + 8] * data_widths[i][1];
|
|
checksum += checksum_weight[i + 16] * data_widths[i][2];
|
|
checksum += 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] = finder_pattern[i + (5 * c_left)];
|
|
total_widths[i + 31] = 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 = rss_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 */
|
|
rss14_separator(symbol, 96, separator_row, 1 /*above*/, 18, 63, 0 /*bottom_finder_value_3*/);
|
|
}
|
|
symbol->rows = symbol->rows + 1;
|
|
|
|
/* Set human readable text */
|
|
set_gtin14_hrt(symbol, source, src_len);
|
|
|
|
set_minimum_height(symbol, 14); // Minimum height is 14X for truncated symbol
|
|
}
|
|
|
|
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 = rss_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;
|
|
|
|
/* 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 = rss_expand(symbol, writer, &latch, total_widths[i]);
|
|
}
|
|
symbol->row_height[symbol->rows] = 7;
|
|
|
|
/* 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 */
|
|
rss14_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_OMNSTK) || (symbol->symbology == BARCODE_DBAR_OMNSTK_CC)) {
|
|
/* top row */
|
|
writer = 0;
|
|
latch = '0';
|
|
for (i = 0; i < 23; i++) {
|
|
writer = rss_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 = rss_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 */
|
|
rss14_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) */
|
|
rss14_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 */
|
|
rss14_separator(symbol, 50, separator_row, 1 /*above*/, 18, 0, 0 /*bottom_finder_value_3*/);
|
|
}
|
|
symbol->rows = symbol->rows + 1;
|
|
|
|
set_minimum_height(symbol, 33);
|
|
}
|
|
|
|
return error_number;
|
|
}
|
|
|
|
/* GS1 DataBar Limited */
|
|
INTERNAL int rsslimited(struct zint_symbol *symbol, unsigned char source[], int src_len) {
|
|
int error_number = 0, i;
|
|
large_int 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[46], writer;
|
|
char latch;
|
|
int separator_row;
|
|
int widths[7];
|
|
|
|
separator_row = 0;
|
|
|
|
if (src_len > 14) { /* Allow check digit to be specified (will be verified and ignored) */
|
|
strcpy(symbol->errtxt, "382: Input too long");
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
error_number = is_sane(NEON, source, src_len);
|
|
if (error_number == ZINT_ERROR_INVALID_DATA) {
|
|
strcpy(symbol->errtxt, "383: Invalid characters in data");
|
|
return error_number;
|
|
}
|
|
|
|
if (src_len == 14) { /* Verify check digit */
|
|
if (calc_check_digit(source) != ctoi(source[13])) {
|
|
strcpy(symbol->errtxt, "389: Invalid check digit");
|
|
return ZINT_ERROR_INVALID_CHECK;
|
|
}
|
|
src_len--; /* Ignore */
|
|
}
|
|
|
|
if (src_len == 13) {
|
|
if ((source[0] != '0') && (source[0] != '1')) {
|
|
strcpy(symbol->errtxt, "384: Input out of range");
|
|
return ZINT_ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
|
|
/* 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, src_len);
|
|
|
|
if (symbol->option_1 == 2) {
|
|
/* 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 = left_character / t_even_ltd[left_group];
|
|
left_even = left_character % t_even_ltd[left_group];
|
|
right_odd = right_character / t_even_ltd[right_group];
|
|
right_even = right_character % t_even_ltd[right_group];
|
|
|
|
getRSSwidths(widths, left_odd, modules_odd_ltd[left_group], 7, widest_odd_ltd[left_group], 1);
|
|
for (i = 0; i <= 6; i++) {
|
|
left_widths[i * 2] = widths[i];
|
|
}
|
|
getRSSwidths(widths, left_even, modules_even_ltd[left_group], 7, widest_even_ltd[left_group], 0);
|
|
for (i = 0; i <= 6; i++) {
|
|
left_widths[i * 2 + 1] = widths[i];
|
|
}
|
|
getRSSwidths(widths, right_odd, modules_odd_ltd[right_group], 7, widest_odd_ltd[right_group], 1);
|
|
for (i = 0; i <= 6; i++) {
|
|
right_widths[i * 2] = widths[i];
|
|
}
|
|
getRSSwidths(widths, right_even, modules_even_ltd[right_group], 7, widest_even_ltd[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++) {
|
|
checksum += checksum_weight_ltd[i] * left_widths[i];
|
|
checksum += checksum_weight_ltd[i + 14] * right_widths[i];
|
|
}
|
|
checksum %= 89;
|
|
|
|
for (i = 0; i < 14; i++) {
|
|
check_elements[i] = finder_pattern_ltd[i + (checksum * 14)];
|
|
}
|
|
|
|
total_widths[0] = 1;
|
|
total_widths[1] = 1;
|
|
total_widths[44] = 1;
|
|
total_widths[45] = 1;
|
|
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 < 46; i++) {
|
|
writer = rss_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 */
|
|
set_gtin14_hrt(symbol, source, src_len);
|
|
|
|
set_minimum_height(symbol, 10);
|
|
|
|
return error_number;
|
|
}
|
|
|
|
/* Handles all data encodation from section 7.2.5 of ISO/IEC 24724 */
|
|
static int rss_binary_string(struct zint_symbol *symbol, char source[], char binary_string[]) {
|
|
int encoding_method, i, j, read_posn, last_digit, debug = (symbol->debug & ZINT_DEBUG_PRINT), mode = NUMERIC;
|
|
int symbol_characters, characters_per_row;
|
|
#ifndef _MSC_VER
|
|
char general_field[strlen(source) + 1];
|
|
#else
|
|
char* general_field = (char*) _alloca(strlen(source) + 1);
|
|
#endif
|
|
int remainder, d1, d2;
|
|
char padstring[40];
|
|
|
|
/* Decide whether a compressed data field is required and if so what
|
|
method to use - method 2 = no compressed data field */
|
|
|
|
if ((strlen(source) >= 16) && ((source[0] == '0') && (source[1] == '1'))) {
|
|
/* (01) and other AIs */
|
|
encoding_method = 1;
|
|
if (debug) printf("Choosing Method 1\n");
|
|
} else {
|
|
/* any AIs */
|
|
encoding_method = 2;
|
|
if (debug) printf("Choosing Method 2\n");
|
|
}
|
|
|
|
if (((strlen(source) >= 20) && (encoding_method == 1)) && ((source[2] == '9') && (source[16] == '3'))) {
|
|
/* Possibly encoding method > 2 */
|
|
if (debug) printf("Checking for other methods\n");
|
|
|
|
if ((strlen(source) >= 26) && (source[17] == '1')) {
|
|
/* Methods 3, 7, 9, 11 and 13 */
|
|
|
|
if (source[18] == '0') {
|
|
/* (01) and (310x) */
|
|
char weight_str[7];
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
weight_str[i] = source[20 + i];
|
|
}
|
|
weight_str[6] = '\0';
|
|
|
|
if (weight_str[0] == '0') { /* Maximum weight = 99999 */
|
|
|
|
if ((source[19] == '3') && (strlen(source) == 26)) {
|
|
/* (01) and (3103) */
|
|
float weight; /* In kilos */
|
|
weight = atof(weight_str) / 1000.0;
|
|
|
|
if (weight <= 32.767) {
|
|
encoding_method = 3;
|
|
}
|
|
}
|
|
|
|
if (strlen(source) == 34) {
|
|
if ((source[26] == '1') && (source[27] == '1')) {
|
|
/* (01), (310x) and (11) - metric weight and production date */
|
|
encoding_method = 7;
|
|
}
|
|
|
|
if ((source[26] == '1') && (source[27] == '3')) {
|
|
/* (01), (310x) and (13) - metric weight and packaging date */
|
|
encoding_method = 9;
|
|
}
|
|
|
|
if ((source[26] == '1') && (source[27] == '5')) {
|
|
/* (01), (310x) and (15) - metric weight and "best before" date */
|
|
encoding_method = 11;
|
|
}
|
|
|
|
if ((source[26] == '1') && (source[27] == '7')) {
|
|
/* (01), (310x) and (17) - metric weight and expiration date */
|
|
encoding_method = 13;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (debug) printf("Now using method %d\n", encoding_method);
|
|
}
|
|
|
|
if ((strlen(source) >= 26) && (source[17] == '2')) {
|
|
/* Methods 4, 8, 10, 12 and 14 */
|
|
|
|
if (source[18] == '0') {
|
|
/* (01) and (320x) */
|
|
char weight_str[7];
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
weight_str[i] = source[20 + i];
|
|
}
|
|
weight_str[6] = '\0';
|
|
|
|
if (weight_str[0] == '0') { /* Maximum weight = 99999 */
|
|
|
|
if (((source[19] == '2') || (source[19] == '3')) && (strlen(source) == 26)) {
|
|
/* (01) and (3202)/(3203) */
|
|
float weight; /* In pounds */
|
|
|
|
if (source[19] == '3') {
|
|
weight = (float) (atof(weight_str) / 1000.0F);
|
|
if (weight <= 22.767) {
|
|
encoding_method = 4;
|
|
}
|
|
} else {
|
|
weight = (float) (atof(weight_str) / 100.0F);
|
|
if (weight <= 99.99) {
|
|
encoding_method = 4;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (strlen(source) == 34) {
|
|
if ((source[26] == '1') && (source[27] == '1')) {
|
|
/* (01), (320x) and (11) - English weight and production date */
|
|
encoding_method = 8;
|
|
}
|
|
|
|
if ((source[26] == '1') && (source[27] == '3')) {
|
|
/* (01), (320x) and (13) - English weight and packaging date */
|
|
encoding_method = 10;
|
|
}
|
|
|
|
if ((source[26] == '1') && (source[27] == '5')) {
|
|
/* (01), (320x) and (15) - English weight and "best before" date */
|
|
encoding_method = 12;
|
|
}
|
|
|
|
if ((source[26] == '1') && (source[27] == '7')) {
|
|
/* (01), (320x) and (17) - English weight and expiration date */
|
|
encoding_method = 14;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (debug) printf("Now using method %d\n", encoding_method);
|
|
|
|
}
|
|
|
|
if (source[17] == '9') {
|
|
/* Methods 5 and 6 */
|
|
if ((source[18] == '2') && ((source[19] >= '0') && (source[19] <= '3'))) {
|
|
/* (01) and (392x) */
|
|
encoding_method = 5;
|
|
}
|
|
if ((source[18] == '3') && ((source[19] >= '0') && (source[19] <= '3'))) {
|
|
/* (01) and (393x) */
|
|
encoding_method = 6;
|
|
}
|
|
if (debug) printf("Now using method %d\n", encoding_method);
|
|
}
|
|
}
|
|
|
|
switch (encoding_method) { /* Encoding method - Table 10 */
|
|
case 1: strcat(binary_string, "1XX");
|
|
read_posn = 16;
|
|
break;
|
|
case 2: strcat(binary_string, "00XX");
|
|
read_posn = 0;
|
|
break;
|
|
case 3: // 0100
|
|
case 4: // 0101
|
|
bin_append(4 + (encoding_method - 3), 4, binary_string);
|
|
read_posn = strlen(source);
|
|
break;
|
|
case 5: strcat(binary_string, "01100XX");
|
|
read_posn = 20;
|
|
break;
|
|
case 6: strcat(binary_string, "01101XX");
|
|
read_posn = 23;
|
|
break;
|
|
default: /* modes 7 to 14 */
|
|
bin_append(56 + (encoding_method - 7), 7, binary_string);
|
|
read_posn = strlen(source);
|
|
break;
|
|
}
|
|
if (debug) printf("Setting binary = %s\n", 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 ((source[i] < '0') || (source[i] > '9')) {
|
|
if ((source[i] != '[') && (source[i] != ']')) {
|
|
/* Something is wrong */
|
|
strcpy(symbol->errtxt, "385: Invalid characters in input data");
|
|
return ZINT_ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now encode the compressed data field */
|
|
|
|
if (debug) printf("Proceeding to encode data\n");
|
|
if (encoding_method == 1) {
|
|
/* Encoding method field "1" - general item identification data */
|
|
char group[4];
|
|
|
|
group[0] = source[2];
|
|
group[1] = '\0';
|
|
|
|
bin_append(atoi(group), 4, binary_string);
|
|
|
|
for (i = 1; i < 5; i++) {
|
|
group[0] = source[(i * 3)];
|
|
group[1] = source[(i * 3) + 1];
|
|
group[2] = source[(i * 3) + 2];
|
|
group[3] = '\0';
|
|
|
|
bin_append(atoi(group), 10, binary_string);
|
|
}
|
|
}
|
|
|
|
if ((encoding_method == 3) || (encoding_method == 4)) {
|
|
/* Encoding method field "0100" - variable weight item
|
|
(0,001 kilogram icrements) */
|
|
/* Encoding method field "0101" - variable weight item (0,01 or
|
|
0,001 pound increment) */
|
|
char group[4];
|
|
char weight_str[7];
|
|
|
|
for (i = 1; i < 5; i++) {
|
|
group[0] = source[(i * 3)];
|
|
group[1] = source[(i * 3) + 1];
|
|
group[2] = source[(i * 3) + 2];
|
|
group[3] = '\0';
|
|
|
|
bin_append(atoi(group), 10, binary_string);
|
|
}
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
weight_str[i] = source[20 + i];
|
|
}
|
|
weight_str[6] = '\0';
|
|
|
|
if ((encoding_method == 4) && (source[19] == '3')) {
|
|
bin_append(atoi(weight_str) + 10000, 15, binary_string);
|
|
} else {
|
|
bin_append(atoi(weight_str), 15, binary_string);
|
|
}
|
|
}
|
|
|
|
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 */
|
|
|
|
char group[4];
|
|
|
|
for (i = 1; i < 5; i++) {
|
|
group[0] = source[(i * 3)];
|
|
group[1] = source[(i * 3) + 1];
|
|
group[2] = source[(i * 3) + 2];
|
|
group[3] = '\0';
|
|
|
|
bin_append(atoi(group), 10, binary_string);
|
|
}
|
|
|
|
bin_append(source[19] - '0', 2, binary_string);
|
|
|
|
if (encoding_method == 6) {
|
|
char currency_str[5];
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
currency_str[i] = source[20 + i];
|
|
}
|
|
currency_str[3] = '\0';
|
|
|
|
bin_append(atoi(currency_str), 10, binary_string);
|
|
}
|
|
}
|
|
|
|
if ((encoding_method >= 7) && (encoding_method <= 14)) {
|
|
/* Encoding method fields "0111000" through "0111111" - variable
|
|
weight item plus date */
|
|
char group[4];
|
|
int group_val;
|
|
char weight_str[8];
|
|
|
|
for (i = 1; i < 5; i++) {
|
|
group[0] = source[(i * 3)];
|
|
group[1] = source[(i * 3) + 1];
|
|
group[2] = source[(i * 3) + 2];
|
|
group[3] = '\0';
|
|
|
|
bin_append(atoi(group), 10, binary_string);
|
|
}
|
|
|
|
weight_str[0] = source[19];
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
weight_str[i + 1] = source[21 + i];
|
|
}
|
|
weight_str[6] = '\0';
|
|
|
|
bin_append(atoi(weight_str), 20, binary_string);
|
|
|
|
if (strlen(source) == 34) {
|
|
/* Date information is included */
|
|
char date_str[4];
|
|
date_str[0] = source[28];
|
|
date_str[1] = source[29];
|
|
date_str[2] = '\0';
|
|
group_val = atoi(date_str) * 384;
|
|
|
|
date_str[0] = source[30];
|
|
date_str[1] = source[31];
|
|
group_val += (atoi(date_str) - 1) * 32;
|
|
|
|
date_str[0] = source[32];
|
|
date_str[1] = source[33];
|
|
group_val += atoi(date_str);
|
|
} else {
|
|
group_val = 38400;
|
|
}
|
|
|
|
bin_append(group_val, 16, binary_string);
|
|
}
|
|
|
|
/* 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 < (int) strlen(source); i++) {
|
|
general_field[j] = source[i];
|
|
j++;
|
|
}
|
|
general_field[j] = '\0';
|
|
if (debug) printf("General field data = %s\n", general_field);
|
|
|
|
if (!general_field_encode(general_field, &mode, &last_digit, binary_string)) {
|
|
/* Invalid characters in input data */
|
|
strcpy(symbol->errtxt, "386: Invalid characters in input data");
|
|
return ZINT_ERROR_INVALID_DATA;
|
|
}
|
|
if (debug) printf("Resultant binary = %s\n", binary_string);
|
|
if (debug) printf("\tLength: %d\n", (int) strlen(binary_string));
|
|
|
|
remainder = 12 - (strlen(binary_string) % 12);
|
|
if (remainder == 12) {
|
|
remainder = 0;
|
|
}
|
|
symbol_characters = ((strlen(binary_string) + remainder) / 12) + 1;
|
|
|
|
if ((symbol->symbology == BARCODE_DBAR_EXPSTK) || (symbol->symbology == BARCODE_DBAR_EXPSTK_CC)) {
|
|
characters_per_row = symbol->option_2 * 2;
|
|
|
|
if ((characters_per_row < 2) || (characters_per_row > 20)) {
|
|
characters_per_row = 4;
|
|
}
|
|
|
|
if ((symbol_characters % characters_per_row) == 1) {
|
|
symbol_characters++;
|
|
}
|
|
}
|
|
|
|
if (symbol_characters < 4) {
|
|
symbol_characters = 4;
|
|
}
|
|
|
|
remainder = (12 * (symbol_characters - 1)) - strlen(binary_string);
|
|
|
|
if (last_digit) {
|
|
/* There is still one more numeric digit to encode */
|
|
if (debug) printf("Adding extra (odd) numeric digit\n");
|
|
|
|
if ((remainder >= 4) && (remainder <= 6)) {
|
|
bin_append(ctoi(last_digit) + 1, 4, binary_string);
|
|
} else {
|
|
d1 = ctoi(last_digit);
|
|
d2 = 10;
|
|
|
|
bin_append((11 * d1) + d2 + 8, 7, binary_string);
|
|
}
|
|
|
|
remainder = 12 - (strlen(binary_string) % 12);
|
|
if (remainder == 12) {
|
|
remainder = 0;
|
|
}
|
|
symbol_characters = ((strlen(binary_string) + remainder) / 12) + 1;
|
|
|
|
if ((symbol->symbology == BARCODE_DBAR_EXPSTK) || (symbol->symbology == BARCODE_DBAR_EXPSTK_CC)) {
|
|
characters_per_row = symbol->option_2 * 2;
|
|
|
|
if ((characters_per_row < 2) || (characters_per_row > 20)) {
|
|
characters_per_row = 4;
|
|
}
|
|
|
|
if ((symbol_characters % characters_per_row) == 1) {
|
|
symbol_characters++;
|
|
}
|
|
}
|
|
|
|
if (symbol_characters < 4) {
|
|
symbol_characters = 4;
|
|
}
|
|
|
|
remainder = (12 * (symbol_characters - 1)) - strlen(binary_string);
|
|
|
|
if (debug) printf("Resultant binary = %s\n", binary_string);
|
|
if (debug) printf("\tLength: %d\n", (int) strlen(binary_string));
|
|
}
|
|
|
|
if (strlen(binary_string) > 252) { /* 252 = (21 * 12) */
|
|
strcpy(symbol->errtxt, "387: Input too long");
|
|
return ZINT_ERROR_TOO_LONG;
|
|
}
|
|
|
|
/* Now add padding to binary string (7.2.5.5.4) */
|
|
i = remainder;
|
|
if (mode == NUMERIC) {
|
|
strcpy(padstring, "0000");
|
|
i -= 4;
|
|
} else {
|
|
strcpy(padstring, "");
|
|
}
|
|
for (; i > 0; i -= 5) {
|
|
strcat(padstring, "00100");
|
|
}
|
|
|
|
padstring[remainder] = '\0';
|
|
strcat(binary_string, padstring);
|
|
|
|
/* 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';
|
|
}
|
|
if (encoding_method == 2) {
|
|
binary_string[3] = d1 ? '1' : '0';
|
|
binary_string[4] = d2 ? '1' : '0';
|
|
}
|
|
if ((encoding_method == 5) || (encoding_method == 6)) {
|
|
binary_string[6] = d1 ? '1' : '0';
|
|
binary_string[7] = d2 ? '1' : '0';
|
|
}
|
|
if (debug) printf("Resultant binary = %s\n", binary_string);
|
|
if (debug) printf("\tLength: %d\n", (int) strlen(binary_string));
|
|
return 0;
|
|
}
|
|
|
|
static void rssexp_separator(struct zint_symbol *symbol, int width, int cols, int separator_row, int above_below, int special_case_row, int left_to_right, 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++) {
|
|
k = (49 * j) + 19 + special_case_row; /* 49 == data (17) + finder (15) + data(17) triplet, 19 == 2 (guard) + 17 (initial check/data character) */
|
|
if (left_to_right) {
|
|
i_start = v2_latch ? 2 : 0; /* Last 13 modules of version 2 finder and first 13 modules of version 1 finder */
|
|
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 */
|
|
}
|
|
i_start = v2_latch ? 14 : 12; /* First 13 modules of version 1 finder and last 13 modules of version 2 finder */
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* GS1 DataBar Expanded */
|
|
INTERNAL int rssexpanded(struct zint_symbol *symbol, unsigned char source[], int src_len) {
|
|
int i, j, k, p, data_chars, vs[21], group[21], v_odd[21], v_even[21];
|
|
char substring[21][14], 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;
|
|
unsigned int bin_len = 13 * src_len + 200 + 1; /* Allow for 8 bits + 5-bit latch per char + 200 bits overhead/padding */
|
|
int widths[4];
|
|
#ifndef _MSC_VER
|
|
char reduced[src_len + 1], binary_string[bin_len];
|
|
#else
|
|
char* reduced = (char*) _alloca(src_len + 1);
|
|
char* binary_string = (char*) _alloca(bin_len);
|
|
#endif
|
|
|
|
separator_row = 0;
|
|
|
|
i = gs1_verify(symbol, source, src_len, reduced);
|
|
if (i != 0) {
|
|
return i;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
strcpy(binary_string, "");
|
|
|
|
if (symbol->option_1 == 2) { /* The "component linkage" flag */
|
|
strcat(binary_string, "1");
|
|
} else {
|
|
strcat(binary_string, "0");
|
|
}
|
|
|
|
i = rss_binary_string(symbol, reduced, binary_string);
|
|
if (i != 0) {
|
|
return i;
|
|
}
|
|
|
|
data_chars = strlen(binary_string) / 12;
|
|
|
|
for (i = 0; i < data_chars; i++) {
|
|
for (j = 0; j < 12; j++) {
|
|
substring[i][j] = binary_string[(i * 12) + j];
|
|
}
|
|
substring[i][12] = '\0';
|
|
}
|
|
|
|
for (i = 0; i < data_chars; i++) {
|
|
vs[i] = 0;
|
|
for (p = 0; p < 12; p++) {
|
|
if (substring[i][p] == '1') {
|
|
vs[i] += (0x800 >> p);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < data_chars; i++) {
|
|
if (vs[i] <= 347) {
|
|
group[i] = 1;
|
|
}
|
|
if ((vs[i] >= 348) && (vs[i] <= 1387)) {
|
|
group[i] = 2;
|
|
}
|
|
if ((vs[i] >= 1388) && (vs[i] <= 2947)) {
|
|
group[i] = 3;
|
|
}
|
|
if ((vs[i] >= 2948) && (vs[i] <= 3987)) {
|
|
group[i] = 4;
|
|
}
|
|
if (vs[i] >= 3988) {
|
|
group[i] = 5;
|
|
}
|
|
v_odd[i] = (vs[i] - g_sum_exp[group[i] - 1]) / t_even_exp[group[i] - 1];
|
|
v_even[i] = (vs[i] - g_sum_exp[group[i] - 1]) % t_even_exp[group[i] - 1];
|
|
|
|
getRSSwidths(widths, v_odd[i], modules_odd_exp[group[i] - 1], 4, widest_odd_exp[group[i] - 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[i], modules_even_exp[group[i] - 1], 4, widest_even_exp[group[i] - 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];
|
|
}
|
|
|
|
/* 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 = weight_rows[(((data_chars - 2) / 2) * 21) + i];
|
|
for (j = 0; j < 8; j++) {
|
|
checksum += (char_widths[i][j] * checksum_weight_exp[(row * 8) + j]);
|
|
|
|
}
|
|
}
|
|
|
|
check_char = (211 * ((data_chars + 1) - 4)) + (checksum % 211);
|
|
|
|
if (symbol->debug & ZINT_DEBUG_PRINT) {
|
|
printf("Data chars: %d, Check char: %d\n", data_chars, check_char);
|
|
}
|
|
|
|
if (check_char <= 347) {
|
|
c_group = 1;
|
|
}
|
|
if ((check_char >= 348) && (check_char <= 1387)) {
|
|
c_group = 2;
|
|
}
|
|
if ((check_char >= 1388) && (check_char <= 2947)) {
|
|
c_group = 3;
|
|
}
|
|
if ((check_char >= 2948) && (check_char <= 3987)) {
|
|
c_group = 4;
|
|
}
|
|
if (check_char >= 3988) {
|
|
c_group = 5;
|
|
}
|
|
|
|
c_odd = (check_char - g_sum_exp[c_group - 1]) / t_even_exp[c_group - 1];
|
|
c_even = (check_char - g_sum_exp[c_group - 1]) % t_even_exp[c_group - 1];
|
|
|
|
getRSSwidths(widths, c_odd, modules_odd_exp[c_group - 1], 4, widest_odd_exp[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, modules_even_exp[c_group - 1], 4, widest_even_exp[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 */
|
|
pattern_width = ((((data_chars + 1) / 2) + ((data_chars + 1) & 1)) * 5) + ((data_chars + 1) * 8) + 4;
|
|
for (i = 0; i < pattern_width; i++) {
|
|
elements[i] = 0;
|
|
}
|
|
|
|
/* Put finder patterns in element array */
|
|
for (i = 0; i < (((data_chars + 1) / 2) + ((data_chars + 1) & 1)); i++) {
|
|
k = ((((((data_chars + 1) - 2) / 2) + ((data_chars + 1) & 1)) - 1) * 11) + i;
|
|
for (j = 0; j < 5; j++) {
|
|
elements[(21 * i) + j + 10] = finder_pattern_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) {
|
|
for (j = 0; j < 8; j++) {
|
|
elements[(((i - 1) / 2) * 21) + 23 + j] = char_widths[i][j];
|
|
}
|
|
}
|
|
|
|
/* Put reversed data characters in element array */
|
|
for (i = 0; i < data_chars; i += 2) {
|
|
for (j = 0; j < 8; j++) {
|
|
elements[((i / 2) * 21) + 15 + 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 = rss_expand(symbol, writer, &latch, elements[i]);
|
|
}
|
|
if (symbol->width < writer) {
|
|
symbol->width = writer;
|
|
}
|
|
symbol->rows = symbol->rows + 1;
|
|
|
|
/* Add human readable text */
|
|
for (i = 0; i <= src_len; i++) {
|
|
if ((source[i] != '[') && (source[i] != ']')) {
|
|
symbol->text[i] = source[i];
|
|
} else {
|
|
if (source[i] == '[') {
|
|
symbol->text[i] = '(';
|
|
}
|
|
if (source[i] == ']') {
|
|
symbol->text[i] = ')';
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
int stack_rows;
|
|
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
|
|
*/
|
|
int codeblocks = (data_chars + 1) / 2 + ((data_chars + 1) % 2);
|
|
|
|
if ((symbol->option_2 < 1) || (symbol->option_2 > 11)) {
|
|
symbol->option_2 = 2;
|
|
}
|
|
if ((symbol->option_1 == 2) && (symbol->option_2 == 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." */
|
|
symbol->option_2 = 2;
|
|
}
|
|
|
|
stack_rows = codeblocks / symbol->option_2;
|
|
if (codeblocks % symbol->option_2 > 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 * symbol->option_2 > codeblocks) {
|
|
num_columns = codeblocks - current_block;
|
|
} else {
|
|
num_columns = symbol->option_2;
|
|
}
|
|
|
|
/* 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 */
|
|
if ((current_row == stack_rows) && (num_columns != symbol->option_2) &&
|
|
!(current_row & 1) && !(symbol->option_2 & 1) && (num_columns & 1)) { /* Odd number of finders == odd number of columns */
|
|
/* 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 ((symbol->option_2 & 1) || (current_row & 1) || special_case_row) {
|
|
left_to_right = 1;
|
|
} else {
|
|
left_to_right = 0;
|
|
}
|
|
|
|
if (symbol->debug & ZINT_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, symbol->option_2, 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 < symbol->option_2) && (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 = rss_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 * symbol->option_2); j += 2) {
|
|
set_module(symbol, symbol->rows - 2, j);
|
|
}
|
|
symbol->row_height[symbol->rows - 2] = 1;
|
|
|
|
/* bottom separator pattern (above current row) */
|
|
rssexp_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) */
|
|
rssexp_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 */
|
|
rssexp_separator(symbol, symbol->width, 4, separator_row, 1 /*above*/, 0 /*special_case_row*/, 1 /*left_to_right*/, 0 /*odd_last_row*/, NULL);
|
|
}
|
|
|
|
for (i = 0; i < symbol->rows; i++) {
|
|
if (symbol->row_height[i] == 0) {
|
|
symbol->row_height[i] = 34;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|