comparison mupdf-source/thirdparty/zint/backend/dxfilmedge.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
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /* dxfilmedge.c - Handles DX Film Edge symbology */
2 /*
3 libzint - the open source barcode library
4 Copyright (C) 2024 Antoine Merino <antoine.merino.dev@gmail.com>
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 3. Neither the name of the project nor the names of its contributors
16 may be used to endorse or promote products derived from this software
17 without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 SUCH DAMAGE.
30 */
31 /* SPDX-License-Identifier: BSD-3-Clause */
32
33 /* DX Film Edge Barcode is used on 35mm and APS films:
34 * https://en.wikipedia.org/wiki/DX_encoding
35 *
36 * A little information about decoding this symbology can be found at
37 * https://www.merinorus.com/blog/identifying-manufacturer-35-mm-films/
38 *
39 * Partial specification and history can be found on this Kodak patent:
40 * https://patents.google.com/patent/US4965628A/en
41 */
42
43 #include <assert.h>
44 #include <stdio.h>
45 #include "common.h"
46
47 #define DX_DEBUG_STR_LEN 20
48 /* Max length of the DX info part. Include the \0. Eg: "018500\0", "150-10\0" */
49 #define DX_MAX_DX_INFO_LENGTH 6
50 #define DX_MAX_DX_INFO_MAX_STR "6" /* String version of above */
51 /* Max length of the frame info part. Eg: "00A\0", "23A\0" */
52 #define DX_MAX_FRAME_INFO_LENGTH 3
53 #define DX_MAX_FRAME_INFO_MAX_STR "3" /* String version of above */
54
55 static void dx_int_to_binary(const int value, const int width, char *output) {
56 int i;
57 for (i = 0; i < width; i++) {
58 output[width - 1 - i] = (value & (1 << i)) ? '1' : '0';
59 }
60 output[width] = '\0';
61 }
62
63 static int dx_parse_code(struct zint_symbol *symbol, const unsigned char *source, const int length,
64 char *binary_output, int *output_length, int *has_frame_info) {
65 int i;
66 int parity_bit = 0;
67 int dx_extract = -1, dx_code_1 = -1, dx_code_2 = -1, frame_number = -1;
68 char binary_dx_code_1[8], binary_dx_code_2[5], binary_frame_number[7];
69 char half_frame_flag = '\0';
70 char dx_info[DX_MAX_DX_INFO_LENGTH + 1] = "\0";
71 char frame_info[DX_MAX_FRAME_INFO_LENGTH + 1] = "\0";
72 int dx_length;
73 const char *frame_start;
74 const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
75
76 *has_frame_info = 0;
77
78 /* All codes should start with a digit*/
79 if (!z_isdigit(source[0])) {
80 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 970,
81 "Invalid first character \"%c\", DX code should start with a number", source[0]);
82 }
83
84 /* Check if there is the '/' separator, which indicates the frame number is present. */
85 dx_length = posn((const char *) source, '/');
86 if (dx_length != -1) {
87 /* Split the DX information from the frame number */
88 int frame_info_len;
89 if (dx_length > DX_MAX_DX_INFO_LENGTH) {
90 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 971,
91 "DX information length %d too long (maximum " DX_MAX_DX_INFO_MAX_STR ")", dx_length);
92 }
93 ustrncat(dx_info, source, dx_length);
94 dx_info[dx_length] = '\0';
95 frame_start = (const char *) source + dx_length + 1;
96 frame_info_len = (int) strlen(frame_start);
97 if (frame_info_len > DX_MAX_FRAME_INFO_LENGTH) {
98 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 972,
99 "Frame number part length %d too long (maximum " DX_MAX_FRAME_INFO_MAX_STR ")", frame_info_len);
100 }
101 ustrcpy(frame_info, frame_start);
102 *has_frame_info = 1;
103 to_upper((unsigned char *) frame_info, frame_info_len);
104 if (not_sane(IS_UPR_F | IS_NUM_F | IS_MNS_F, (const unsigned char *) frame_info, frame_info_len)) {
105 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 973,
106 "Frame number \"%s\" is invalid (expected digits, optionally followed by a single \"A\")",
107 frame_info);
108 }
109 } else {
110 /* No "/" found, store the entire input in dx_info */
111 dx_length = length;
112 if (dx_length > DX_MAX_DX_INFO_LENGTH) {
113 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 974,
114 "DX information length %d too long (maximum " DX_MAX_DX_INFO_MAX_STR ")", dx_length);
115 }
116 ustrcpy(dx_info, source);
117 }
118
119 if ((i = not_sane(IS_NUM_F | IS_MNS_F, (const unsigned char *) dx_info, dx_length))) {
120 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 975,
121 "Invalid character at position %d in DX info (digits and \"-\" character only)", i);
122 }
123
124 if (debug_print) printf("\nDX info part: \"%s\", Frame info part: \"%s\"\n", dx_info, frame_info);
125 /* Parse the DX information */
126 if (strchr(dx_info, '-')) {
127 /* DX code parts 1 and 2 are given directly, separated by a '-'. Eg: "79-7" */
128 if (debug_print) printf("DX code 1 and 2 are separated by a dash \"-\"\n");
129 if (chr_cnt((const unsigned char *) dx_info, dx_length, '-') > 1) {
130 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 976,
131 "The \"-\" is used to separate DX parts 1 and 2, and should be used no more than once");
132 }
133 if (sscanf(dx_info, "%d-%d", &dx_code_1, &dx_code_2) < 2) {
134 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 977,
135 "Wrong format for DX parts 1 and 2 (expected format: XXX-XX, digits)");
136 }
137 if (dx_code_1 <= 0 || dx_code_1 > 127) {
138 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 978, "DX part 1 \"%d\" out of range (1 to 127)",
139 dx_code_1);
140 }
141 if (dx_code_2 < 0 || dx_code_2 > 15) {
142 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 979, "DX part 2 \"%d\" out of range (0 to 15)",
143 dx_code_2);
144 }
145 } else {
146 /* DX format is either 4 digits (DX Extract, eg: 1271) or 6 digits (DX Full, eg: 012710) */
147 if (debug_print) printf("No \"-\" separator, computing from DX Extract (4 digits) or DX Full (6 digits)\n");
148 if (dx_length == 5 || dx_length > 6) {
149 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 980,
150 "DX number \"%s\" is incorrect; expected 4 digits (DX extract) or 6 digits (DX full)", dx_info);
151 }
152 if (dx_length == 6) {
153 if (debug_print) {
154 printf("DX full format detected: %s. Removing the first and the last characters.\n", dx_info);
155 }
156 /* Convert DX Full to DX Extract (remove first and last character) */
157 for (i = 0; i <= 3; ++i) {
158 dx_info[i] = dx_info[i + 1];
159 }
160 dx_info[4] = '\0';
161 dx_length = 4;
162 }
163 /* Compute the DX parts 1 and 2 from the DX extract */
164 dx_extract = to_int((const unsigned char *) dx_info, dx_length);
165 assert(dx_extract != -1);
166 if (dx_extract < 16 || dx_extract > 2047) {
167 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 981, "DX extract \"%d\" out of range (16 to 2047)",
168 dx_extract);
169 }
170 if (debug_print) printf("Computed DX extract: %04d\n", dx_extract);
171 dx_code_1 = dx_extract / 16;
172 dx_code_2 = dx_extract % 16;
173 }
174
175 /* Convert components to binary strings */
176 dx_int_to_binary(dx_code_1, 7, binary_dx_code_1);
177 dx_int_to_binary(dx_code_2, 4, binary_dx_code_2);
178
179 if (debug_print) {
180 printf("%-*s%d\t-> %s\n", DX_DEBUG_STR_LEN, "DX code 1:", dx_code_1, binary_dx_code_1);
181 printf("%-*s%d\t-> %s\n", DX_DEBUG_STR_LEN, "DX code 2:", dx_code_2, binary_dx_code_2);
182 }
183
184 if (*has_frame_info) {
185 int ret_sscanf, n;
186 if (strlen(frame_info) < 1) {
187 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 982,
188 "Frame number indicator \"/\" at position %d, but frame number is empty",
189 dx_length + 1);
190 }
191 /* Some frame numbers are special values, convert them their equivalent number */
192 if (strcmp(frame_info, "S") == 0 || strcmp(frame_info, "X") == 0) {
193 strcpy(frame_info, "62");
194 } else if (strcmp(frame_info, "SA") == 0 || strcmp(frame_info, "XA") == 0) {
195 strcpy(frame_info, "62A");
196 } else if (strcmp(frame_info, "K") == 0 || strcmp(frame_info, "00") == 0) {
197 strcpy(frame_info, "63");
198 } else if (strcmp(frame_info, "KA") == 0 || strcmp(frame_info, "00A") == 0) {
199 strcpy(frame_info, "63A");
200 } else if (strcmp(frame_info, "F") == 0) {
201 strcpy(frame_info, "0");
202 } else if (strcmp(frame_info, "FA") == 0) {
203 strcpy(frame_info, "0A");
204 }
205
206 ret_sscanf = sscanf(frame_info, "%d%c%n", &frame_number, &half_frame_flag, &n);
207 if (ret_sscanf < 1 || (ret_sscanf == 2 && frame_info[n] != '\0')) {
208 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 983,
209 "Frame number \"%s\" is invalid (expected digits, optionally followed by a single \"A\")",
210 frame_info);
211 }
212 if (frame_number < 0 || frame_number > 63) {
213 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 984, "Frame number \"%d\" out of range (0 to 63)",
214 frame_number);
215 }
216 dx_int_to_binary(frame_number, 6, binary_frame_number);
217 if (debug_print) {
218 printf("%-*s%d\t-> %s\n", DX_DEBUG_STR_LEN, "Frame number:", frame_number, binary_frame_number);
219 }
220 }
221
222 /* Build the binary output */
223 strcpy(binary_output, "101010"); /* Start pattern */
224 strcat(binary_output, binary_dx_code_1);
225 strcat(binary_output, "0"); /* Separator between DX part 1 and DX part 2 */
226 strcat(binary_output, binary_dx_code_2);
227 if (*has_frame_info) {
228 strcat(binary_output, binary_frame_number);
229 to_upper((unsigned char *) &half_frame_flag, 1);
230 if (half_frame_flag == 'A') {
231 if (debug_print) printf("%-*s'%c'\t-> 1\n", DX_DEBUG_STR_LEN, "Half frame flag:", half_frame_flag);
232 strcat(binary_output, "1"); /* Half-frame is set */
233 } else {
234 if (half_frame_flag) {
235 return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 985,
236 "Frame number \"%s\" is invalid (expected digits, optionally followed by a single \"A\")",
237 frame_info);
238 }
239 if (debug_print) printf("%-*s'%c'\t-> 0\n", DX_DEBUG_STR_LEN, "Half frame flag:", half_frame_flag);
240 strcat(binary_output, "0"); /* Half-frame is NOT set */
241 }
242 strcat(binary_output, "0"); /* Separator between half frame flag and parity bit*/
243 }
244
245 /* Parity bit */
246 for (i = 6; binary_output[i] != '\0'; i++) {
247 if (binary_output[i] == '1') {
248 parity_bit++;
249 }
250 }
251 parity_bit %= 2;
252 if (debug_print) {
253 printf("%-*s%s\t-> %d\n", DX_DEBUG_STR_LEN, "Parity bit:", parity_bit ? "yes" : "no", parity_bit);
254 }
255 if (parity_bit) {
256 strcat(binary_output, "1");
257 } else {
258 strcat(binary_output, "0");
259 }
260
261 strcat(binary_output, "0101"); /* Stop pattern */
262
263 *output_length = (int) strlen(binary_output);
264 return 0;
265 }
266
267 INTERNAL int dxfilmedge(struct zint_symbol *symbol, unsigned char source[], int length) {
268 int i;
269 int writer = 0;
270 int error_number = 0;
271
272 char char_data[32];
273 int data_length;
274 int has_frame_info;
275
276 const char long_clock_pattern[] = "1111101010101010101010101010111";
277 const char short_clock_pattern[] = "11111010101010101010111";
278 const char *clock_pattern;
279 int clock_length;
280 int parse_result = -1;
281 const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
282
283 if (length > 10) {
284 return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 986, "Input length %d too long (maximum 10)", length);
285 }
286
287 parse_result = dx_parse_code(symbol, source, length, char_data, &data_length, &has_frame_info);
288 if (parse_result != 0) {
289 if (debug_print) printf("Error %s\n\n", symbol->errtxt);
290 return parse_result;
291 }
292
293 /* Clock signal is longer if the frame number is provided */
294 if (has_frame_info) {
295 clock_pattern = long_clock_pattern;
296 clock_length = sizeof(long_clock_pattern) -1;
297 } else {
298 clock_pattern = short_clock_pattern;
299 clock_length = sizeof(short_clock_pattern) -1;
300 }
301
302 /* First row: clock pattern */
303 for (i = 0; i < clock_length; i++) {
304 if (clock_pattern[i] == '1') {
305 set_module(symbol, 0, writer);
306 } else if (clock_pattern[i] == '0') {
307 unset_module(symbol, 0, writer);
308 }
309 writer++;
310 }
311
312 /* Reset writer X position for the second row */
313 writer = 0;
314
315 /* Second row: data signal */
316 for (i = 0; i < clock_length; i++) {
317 if (char_data[i] == '1') {
318 set_module(symbol, 1, writer);
319 } else if (char_data[i] == '0') {
320 unset_module(symbol, 1, writer);
321 }
322 writer++;
323 }
324 symbol->rows = 2;
325 symbol->width = clock_length;
326
327 if (symbol->output_options & COMPLIANT_HEIGHT) {
328 /* Measured ratio on 35mm films. Depending on the brands, one symbol height is about 3 * the X-dim.*/
329 const float default_height = 6.0f;
330
331 /* AFAIK There is no standard on minimum and maximum height, so we stay close to the measurements */
332 const float min_row_height = 2.2f;
333 const float max_height = 7.5f;
334 error_number = set_height(symbol, min_row_height, default_height, max_height, 0 /*no_errtxt*/);
335 } else {
336 /* Using compliant height as default as no backwards compatibility to consider */
337 const float default_height = 6.0f;
338 (void) set_height(symbol, 0.0f, default_height, 0.0f, 1 /*no_errtxt*/);
339 }
340
341 return error_number;
342 }
343
344 /* vim: set ts=4 sw=4 et : */