File: channel.c

package info (click to toggle)
zint 2.15.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 23,016 kB
  • sloc: ansic: 133,002; cpp: 8,153; php: 1,846; sh: 1,304; python: 307; makefile: 207; tcl: 74
file content (260 lines) | stat: -rw-r--r-- 12,366 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
/* channel.c - Handles Channel */
/*
    libzint - the open source barcode library
    Copyright (C) 2008-2025 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 */

/* Was in "code.c" */

#include <stdio.h>
#include "common.h"

typedef const struct s_channel_precalc {
    int value; unsigned char B[8]; unsigned char S[8]; unsigned char bmax[7]; unsigned char smax[7];
} channel_precalc;

#if 0
#define CHANNEL_GENERATE_PRECALCS
#endif

#ifdef CHANNEL_GENERATE_PRECALCS
/* To generate precalc tables uncomment CHANNEL_GENERATE_PRECALCS define and run
   "backend/tests/test_channel -f generate -g" and place result in "channel_precalcs.h" */
static void channel_generate_precalc(int channels, int value, int mod, int last, int B[8], int S[8], int bmax[7],
            int smax[7]) {
    int i;
    if (value == mod) printf("static channel_precalc channel_precalcs%d[] = {\n", channels);
    printf("    { %7ld, {", value); for (i = 0; i < 8; i++) printf(" %d,", B[i]); fputs(" },", stdout);
    fputs(" {", stdout); for (i = 0; i < 8; i++) printf(" %d,", S[i]); fputs(" },", stdout);
    fputs(" {", stdout); for (i = 0; i < 7; i++) printf(" %d,", bmax[i]); fputs(" },", stdout);
    fputs(" {", stdout); for (i = 0; i < 7; i++) printf(" %d,", smax[i]); fputs(" }, },\n", stdout);
    if (value == last) fputs("};\n", stdout);
}
#else
#include "channel_precalcs.h"
#endif

static int channel_copy_precalc(channel_precalc *const precalc, int B[8], int S[8], int bmax[7], int smax[7]) {
    int i;

    for (i = 0; i < 7; i++) {
        B[i] = precalc->B[i];
        S[i] = precalc->S[i];
        bmax[i] = precalc->bmax[i];
        smax[i] = precalc->smax[i];
    }
    B[7] = precalc->B[7];
    S[7] = precalc->S[7];

    return precalc->value;
}

/* CHNCHR is adapted from ANSI/AIM BC12-1998 Annex D Figure D5 and is Copyright (c) AIM 1997 */

/* It is used here on the understanding that it forms part of the specification
   for Channel Code and therefore its use is permitted under the following terms
   set out in that document:

   "It is the intent and understanding of AIM [t]hat the symbology presented in this
   specification is entirely in the public domain and free of all use restrictions,
   licenses and fees. AIM USA, its member companies, or individual officers
   assume no liability for the use of this document." */
static void CHNCHR(int channels, int target_value, int B[8], int S[8]) {
    /* Use of initial pre-calculations taken from Barcode Writer in Pure PostScript (BWIPP)
     * Copyright (c) 2004-2020 Terry Burton (MIT/X-Consortium license) */
    static channel_precalc initial_precalcs[6] = {
        { 0, { 1, 1, 1, 1, 1, 2, 1, 2, }, { 1, 1, 1, 1, 1, 1, 1, 3, }, { 1, 1, 1, 1, 1, 3, 2, },
            { 1, 1, 1, 1, 1, 3, 3, }, },
        { 0, { 1, 1, 1, 1, 2, 1, 1, 3, }, { 1, 1, 1, 1, 1, 1, 1, 4, }, { 1, 1, 1, 1, 4, 3, 3, },
            { 1, 1, 1, 1, 4, 4, 4, }, },
        { 0, { 1, 1, 1, 2, 1, 1, 2, 3, }, { 1, 1, 1, 1, 1, 1, 1, 5, }, { 1, 1, 1, 5, 4, 4, 4, },
            { 1, 1, 1, 5, 5, 5, 5, }, },
        { 0, { 1, 1, 2, 1, 1, 2, 1, 4, }, { 1, 1, 1, 1, 1, 1, 1, 6, }, { 1, 1, 6, 5, 5, 5, 4, },
            { 1, 1, 6, 6, 6, 6, 6, }, },
        { 0, { 1, 2, 1, 1, 2, 1, 1, 5, }, { 1, 1, 1, 1, 1, 1, 1, 7, }, { 1, 7, 6, 6, 6, 5, 5, },
            { 1, 7, 7, 7, 7, 7, 7, }, },
        { 0, { 2, 1, 1, 2, 1, 1, 2, 5, }, { 1, 1, 1, 1, 1, 1, 1, 8, }, { 8, 7, 7, 7, 6, 6, 6, },
            { 8, 8, 8, 8, 8, 8, 8, }, },
    };
    int bmax[7], smax[7];
    int value = 0;

    channel_copy_precalc(&initial_precalcs[channels - 3], B, S, bmax, smax);

#ifndef CHANNEL_GENERATE_PRECALCS
    if (channels == 7 && target_value >= channel_precalcs7[0].value) {
        value = channel_copy_precalc(&channel_precalcs7[(target_value / channel_precalcs7[0].value) - 1], B, S, bmax,
                                    smax);
    } else if (channels == 8 && target_value >= channel_precalcs8[0].value) {
        value = channel_copy_precalc(&channel_precalcs8[(target_value / channel_precalcs8[0].value) - 1], B, S, bmax,
                                    smax);
    }
#endif

    goto chkchr;

ls0:smax[1] = smax[0] + 1 - S[0]; B[0] = 1;
    if (S[0] == 1) goto nb0;
lb0:    bmax[1] = bmax[0] + 1 - B[0]; S[1] = 1;
ls1:        smax[2] = smax[1] + 1 - S[1]; B[1] = 1;
            if (S[0] + B[0] + S[1] == 3) goto nb1;
lb1:            bmax[2] = bmax[1] + 1 - B[1]; S[2] = 1;
ls2:                smax[3] = smax[2] + 1 - S[2]; B[2] = 1;
                    if (B[0] + S[1] + B[1] + S[2] == 4) goto nb2;
lb2:                    bmax[3] = bmax[2] + 1 - B[2]; S[3] = 1;
ls3:                        smax[4] = smax[3] + 1 - S[3]; B[3] = 1;
                            if (B[1] + S[2] + B[2] + S[3] == 4) goto nb3;
lb3:                            bmax[4] = bmax[3] + 1 - B[3]; S[4] = 1;
ls4:                                smax[5] = smax[4] + 1 - S[4]; B[4] = 1;
                                    if (B[2] + S[3] + B[3] + S[4] == 4) goto nb4;
lb4:                                    bmax[5] = bmax[4] + 1 - B[4]; S[5] = 1;
ls5:                                        smax[6] = smax[5] + 1 - S[5]; B[5] = 1;
                                            if (B[3] + S[4] + B[4] + S[5] == 4) goto nb5;
lb5:                                            bmax[6] = bmax[5] + 1 - B[5]; S[6] = 1;
ls6:                                                S[7] = smax[6] + 1 - S[6]; B[6] = 1;
                                                    if (B[4] + S[5] + B[5] + S[6] == 4) goto nb6;
lb6:                                                    B[7] = bmax[6] + 1 - B[6];
                                                        if (B[5] + S[6] + B[6] + S[7] + B[7] == 5) goto nb6;
chkchr:
#ifdef CHANNEL_GENERATE_PRECALCS
                                                        /* 115338 == (576688 + 2) / 5 */
                                                        if (channels == 7 && value && value % 115338 == 0) {
                                                            channel_generate_precalc(channels, value, 115338,
                                                                                115338 * (5 - 1), B, S, bmax, smax);
                                                        /* 119121 == (7742862 + 3) / 65 */
                                                        } else if (channels == 8 && value && value % 119121 == 0) {
                                                            channel_generate_precalc(channels, value, 119121,
                                                                                119121 * (65 - 1), B, S, bmax, smax);
                                                        }
#endif
                                                        if (value == target_value) return;
                                                        value++;
nb6:                                                    if (++B[6] <= bmax[6]) goto lb6;
                                                    if (++S[6] <= smax[6]) goto ls6;
nb5:                                            if (++B[5] <= bmax[5]) goto lb5;
                                            if (++S[5] <= smax[5]) goto ls5;
nb4:                                    if (++B[4] <= bmax[4]) goto lb4;
                                    if (++S[4] <= smax[4]) goto ls4;
nb3:                            if (++B[3] <= bmax[3]) goto lb3;
                            if (++S[3] <= smax[3]) goto ls3;
nb2:                    if (++B[2] <= bmax[2]) goto lb2;
                    if (++S[2] <= smax[2]) goto ls2;
nb1:            if (++B[1] <= bmax[1]) goto lb1;
            if (++S[1] <= smax[1]) goto ls1;
nb0:    if (++B[0] <= bmax[0]) goto lb0;
    if (++S[0] <= smax[0]) goto ls0;
}

/* Channel Code - According to ANSI/AIM BC12-1998 */
INTERNAL int channel(struct zint_symbol *symbol, unsigned char source[], int length) {
    static const int max_ranges[] = { -1, -1, -1, 26, 292, 3493, 44072, 576688, 7742862 };
    static const unsigned char zeroes_str[] = "0000000"; /* 7 zeroes */
    int S[8] = {0}, B[8] = {0};
    int target_value;
    char dest[30];
    char *d = dest;
    int channels, i;
    int error_number = 0, zeroes;

    if (length > 7) {
        return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 333, "Input length %d too long (maximum 7)", length);
    }
    if ((i = not_sane(NEON_F, source, length))) {
        return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 334,
                        "Invalid character at position %d in input (digits only)", i);
    }
    target_value = to_int(source, length);

    if ((symbol->option_2 < 3) || (symbol->option_2 > 8)) {
        channels = 0;
    } else {
        channels = symbol->option_2;
    }

    if (channels == 0) {
        channels = length + 1;
        if (target_value > 576688 && channels < 8) {
            channels = 8;
        } else if (target_value > 44072 && channels < 7) {
            channels = 7;
        } else if (target_value > 3493 && channels < 6) {
            channels = 6;
        } else if (target_value > 292 && channels < 5) {
            channels = 5;
        } else if (target_value > 26 && channels < 4) {
            channels = 4;
        }
    }
    if (channels == 2) {
        channels = 3;
    }

    if (target_value > max_ranges[channels]) {
        if (channels == 8) {
            return ZEXT errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 318, "Input value \"%1$d\" out of range (0 to %2$d)",
                                target_value, max_ranges[channels]);
        }
        return ZEXT errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 335,
                            "Input value \"%1$d\" out of range (0 to %2$d for %3$d channels)", target_value,
                            max_ranges[channels], channels);
    }

    CHNCHR(channels, target_value, B, S);

    memcpy(d, "111111111", 9); /* Finder pattern */
    d += 9;
    for (i = 8 - channels; i < 8; i++) {
        *d++ = itoc(S[i]);
        *d++ = itoc(B[i]);
    }

    expand(symbol, dest, d - dest);

    if (symbol->output_options & COMPLIANT_HEIGHT) {
        /* ANSI/AIM BC12-1998 gives min height as 5mm or 15% of length; X left as application specification so use
           length = 1X (left qz) + (9 (finder) + 4 * 8 - 2) * X + 2X (right qz);
           use 20 as default based on figures in spec */
        const float min_height = stripf((1 + 9 + 4 * channels - 2 + 2) * 0.15f);
        error_number = set_height(symbol, min_height, 20.0f, 0.0f, 0 /*no_errtxt*/);
    } else {
        (void) set_height(symbol, 0.0f, 50.0f, 0.0f, 1 /*no_errtxt*/);
    }

    zeroes = channels - 1 - length;
    if (zeroes > 0) {
        hrt_cpy_nochk(symbol, zeroes_str, zeroes);
        hrt_cat_nochk(symbol, source, length);
    } else {
        hrt_cpy_nochk(symbol, source, length);
    }

    return error_number;
}

/* vim: set ts=4 sw=4 et : */