DXFILMEDGE: various tweaks:

change # 147 -> 127 to use up barcode # holes;
  use "common.h" funcs `str_to_uppercase()` -> `to_upper()`,
  `count_char_occurrences()` -> `chr_cnt()`;
  prefix defines with `DX_` and funcs with `dx_`;
  `ZINT_DEBUG_PRINT` -> `symbol->debug & ZINT_DEBUG_PRINT`;
  bools to ints; use `posn()` to check for slash (returns length);
  restrict line lengths to 118; suppress some clang-tidy warnings;
  normalize some error messages;
  check for single "A" if any (`sscanf()`);
  use compliant height default; some whitespace formatting;
Tcl: add DXFILMEDGE support
docs: document DXFILMEDGE; update to latest pandoc
test suite: ZXingCPP: DXFILMEDGE support
This commit is contained in:
gitlost 2024-12-23 20:52:08 +00:00
parent d13a3aaf1a
commit fe3907c2cb
21 changed files with 451 additions and 255 deletions

View file

@ -1,7 +1,7 @@
/* dxfilmedge.c - Handles DX Film Edge symbology */
/*
libzint - the open source barcode library
Copyright (C) 2024-2025 Antoine Merino <antoine.merino.dev@gmail.com>
Copyright (C) 2024 Antoine Merino <antoine.merino.dev@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@ -40,19 +40,19 @@
* https://patents.google.com/patent/US4965628A/en
*/
#include <ctype.h>
#include <stdbool.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "common.h"
#define DEBUG_STR_LEN 20
#define DX_DEBUG_STR_LEN 20
/* Max length of the DX info part. Include the \0. Eg: "018500\0", "150-10\0" */
#define MAX_DX_INFO_LENGTH 7
#define DX_MAX_DX_INFO_LENGTH 6
#define DX_MAX_DX_INFO_MAX_STR "6" /* String version of above */
/* Max length of the frame info part. Eg: "00A\0", "23A\0" */
#define MAX_FRAME_INFO_LENGTH 4
#define DX_MAX_FRAME_INFO_LENGTH 3
#define DX_MAX_FRAME_INFO_MAX_STR "3" /* String version of above */
void int_to_binary(int value, int width, char *output) {
static void dx_int_to_binary(const int value, const int width, char *output) {
int i;
for (i = 0; i < width; i++) {
output[width - 1 - i] = (value & (1 << i)) ? '1' : '0';
@ -60,134 +60,133 @@ void int_to_binary(int value, int width, char *output) {
output[width] = '\0';
}
void str_to_uppercase(char *str) {
int i;
for (i = 0; str[i] != '\0'; i++) {
str[i] = toupper(str[i]);
}
}
int count_char_occurrences(const char *str, char target) {
int i, count = 0;
for (i = 0; str[i] != '\0'; i++) {
if (str[i] == target) {
count++;
if (count > 1) {
return count;
}
}
}
return count;
}
int parse_dx_code(struct zint_symbol *symbol, const char *source, char *binary_output, int *output_length, bool *has_frame_info) {
static int dx_parse_code(struct zint_symbol *symbol, const unsigned char *source, const int length,
char *binary_output, int *output_length, int *has_frame_info) {
int i;
int parity_bit = 0;
int dx_extract = -1, dx_code_1 = -1, dx_code_2 = -1, frame_number = -1;
char binary_dx_code_1[8], binary_dx_code_2[5], binary_frame_number[7];
char half_frame_flag = '\0';
char dx_info[MAX_DX_INFO_LENGTH] = "\0";
char frame_info[MAX_FRAME_INFO_LENGTH] = "\0";
char *detected_char = strchr((const char *)(source), ' ');
char dx_info[DX_MAX_DX_INFO_LENGTH + 1] = "\0";
char frame_info[DX_MAX_FRAME_INFO_LENGTH + 1] = "\0";
int dx_length;
const char *frame_start;
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
*has_frame_info = false;
*has_frame_info = 0;
/* All codes should start with a digit*/
if (not_sane(IS_NUM_F, (unsigned char *)source, 1)){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1018, "Invalid character \"%c\", DX code should start with a number", source[0]);
if (!z_isdigit(source[0])) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 970,
"Invalid first character \"%c\", DX code should start with a number", source[0]);
}
/* Check if there is the '/' separator, which indicates the frame number is present. */
detected_char = strchr((const char *)(source), '/');
if (detected_char){
dx_length = posn((const char *) source, '/');
if (dx_length != -1) {
/* Split the DX information from the frame number */
size_t dx_length = detected_char - (char *)source;
if (dx_length >= MAX_DX_INFO_LENGTH){
return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 1014, "DX information is too long");
int frame_info_len;
if (dx_length > DX_MAX_DX_INFO_LENGTH) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 971,
"DX information length %d too long (maximum " DX_MAX_DX_INFO_MAX_STR ")", dx_length);
}
strncat(dx_info, source, dx_length);
ustrncat(dx_info, source, dx_length);
dx_info[dx_length] = '\0';
frame_start = detected_char + 1;
if (strlen(frame_start) >= MAX_FRAME_INFO_LENGTH) {
return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 1002, "Frame number part is too long");
frame_start = (const char *) source + dx_length + 1;
frame_info_len = (int) strlen(frame_start);
if (frame_info_len > DX_MAX_FRAME_INFO_LENGTH) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 972,
"Frame number part length %d too long (maximum " DX_MAX_FRAME_INFO_MAX_STR ")", frame_info_len);
}
strncat(frame_info, frame_start, sizeof(frame_info) - 1);
*has_frame_info = true;
str_to_uppercase(frame_info);
if ((i = not_sane(IS_UPR_F | IS_NUM_F | IS_MNS_F, (unsigned char *)(frame_info), strlen(frame_info)))){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1012, "Frame number \"%s\" is invalid (expected digits, eventually followed by \'A\')", frame_info);
ustrcpy(frame_info, frame_start);
*has_frame_info = 1;
to_upper((unsigned char *) frame_info, frame_info_len);
if (not_sane(IS_UPR_F | IS_NUM_F | IS_MNS_F, (const unsigned char *) frame_info, frame_info_len)) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 973,
"Frame number \"%s\" is invalid (expected digits, optionally followed by a single \"A\")",
frame_info);
}
}
else{
} else {
/* No "/" found, store the entire input in dx_info */
if (strlen(source) >= MAX_DX_INFO_LENGTH) {
return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 1003, "DX information is too long");
dx_length = length;
if (dx_length > DX_MAX_DX_INFO_LENGTH) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 974,
"DX information length %d too long (maximum " DX_MAX_DX_INFO_MAX_STR ")", dx_length);
}
strncat(dx_info, source, sizeof(dx_info) - 1);
ustrcpy(dx_info, source);
}
if ((i = not_sane(IS_NUM_F | IS_MNS_F, (unsigned char *)dx_info, strlen(dx_info)))){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1016, "Invalid character at position %d in DX info (digits and \'-\' character only)", i);
if ((i = not_sane(IS_NUM_F | IS_MNS_F, (const unsigned char *) dx_info, dx_length))) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 975,
"Invalid character at position %d in DX info (digits and \"-\" character only)", i);
}
if (ZINT_DEBUG_PRINT) printf("\nDX info part: \"%s\", Frame info part: \"%s\"\n", dx_info, frame_info);
if (debug_print) printf("\nDX info part: \"%s\", Frame info part: \"%s\"\n", dx_info, frame_info);
/* Parse the DX information */
if (strchr(dx_info, '-')){
if (strchr(dx_info, '-')) {
/* DX code parts 1 and 2 are given directly, separated by a '-'. Eg: "79-7" */
if (ZINT_DEBUG_PRINT) printf("DX code 1 and 2 are separated by a dash \'-\'\n");
if (count_char_occurrences(dx_info, '-') > 1){
return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 1009, "The \'-\' is used to separate DX parts 1 and 2, and should be used no more than once");
if (debug_print) printf("DX code 1 and 2 are separated by a dash \"-\"\n");
if (chr_cnt((const unsigned char *) dx_info, dx_length, '-') > 1) {
return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 976,
"The \"-\" is used to separate DX parts 1 and 2, and should be used no more than once");
}
if (sscanf(dx_info, "%d-%d", &dx_code_1, &dx_code_2) < 2){
return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 1004, "Wrong format for DX parts 1 and 2 (expected format: XXX-XX, digits)");
if (sscanf(dx_info, "%d-%d", &dx_code_1, &dx_code_2) < 2) {
return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 977,
"Wrong format for DX parts 1 and 2 (expected format: XXX-XX, digits)");
}
if (dx_code_1 <= 0 || dx_code_1 > 127){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1006, "DX part 1 \"%d\" must be between 1 and 127", dx_code_1);
if (dx_code_1 <= 0 || dx_code_1 > 127) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 978, "DX part 1 \"%d\" out of range (1 to 127)",
dx_code_1);
}
if (dx_code_2 < 0 || dx_code_2 > 15){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1007, "DX part 2 \"%d\" must be between 0 and 15", dx_code_2);
if (dx_code_2 < 0 || dx_code_2 > 15) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 979, "DX part 2 \"%d\" out of range (0 to 15)",
dx_code_2);
}
}
else{
} else {
/* DX format is either 4 digits (DX Extract, eg: 1271) or 6 digits (DX Full, eg: 012710) */
if (ZINT_DEBUG_PRINT) printf("No \'-\' separator, computing from DX Extract (4 digits) or DX Full (6 digits)\n");
if (strlen(dx_info) == 5 || strlen(dx_info) > 6){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1005, "DX number \"%s\" is incorrect; expected 4 digits (DX extract) or 6 digits (DX full)", dx_info);
if (debug_print) printf("No \"-\" separator, computing from DX Extract (4 digits) or DX Full (6 digits)\n");
if (dx_length == 5 || dx_length > 6) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 980,
"DX number \"%s\" is incorrect; expected 4 digits (DX extract) or 6 digits (DX full)", dx_info);
}
if (strlen(dx_info) == 6){
if (ZINT_DEBUG_PRINT) printf("DX full format detected: %s. Removing the first and the last characters.\n", dx_info);
if (dx_length == 6) {
if (debug_print) {
printf("DX full format detected: %s. Removing the first and the last characters.\n", dx_info);
}
/* Convert DX Full to DX Extract (remove first and last character) */
for (i=0; i <= 3; ++i){
dx_info[i] = dx_info[i+1];
for (i = 0; i <= 3; ++i) {
dx_info[i] = dx_info[i + 1];
}
dx_info[4] = '\0';
dx_length = 4;
}
/* Compute the DX parts 1 and 2 from the DX extract */
if (sscanf(dx_info, "%d", &dx_extract) < 1){
/* Should not happen (DX info format has been checked above), but better safe than sorry */
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1005, "DX number \"%s\" is incorrect; expected 4 digits (DX extract) or 6 digits (DX full)", dx_info);
dx_extract = to_int((const unsigned char *) dx_info, dx_length);
assert(dx_extract != -1);
if (dx_extract < 16 || dx_extract > 2047) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 981, "DX extract \"%d\" out of range (16 to 2047)",
dx_extract);
}
if (dx_extract < 16 || dx_extract > 2047){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1015, "DX extract \"%d\" must be between 16 and 2047", dx_extract);
}
if (ZINT_DEBUG_PRINT) printf("Computed DX extract: %04d\n", dx_extract);
if (debug_print) printf("Computed DX extract: %04d\n", dx_extract);
dx_code_1 = dx_extract / 16;
dx_code_2 = dx_extract % 16;
}
/* Convert components to binary strings */
int_to_binary(dx_code_1, 7, binary_dx_code_1);
int_to_binary(dx_code_2, 4, binary_dx_code_2);
dx_int_to_binary(dx_code_1, 7, binary_dx_code_1);
dx_int_to_binary(dx_code_2, 4, binary_dx_code_2);
if (ZINT_DEBUG_PRINT) printf("%-*s%d\t-> %s\n", DEBUG_STR_LEN, "DX code 1:", dx_code_1, binary_dx_code_1);
if (ZINT_DEBUG_PRINT) printf("%-*s%d\t-> %s\n", DEBUG_STR_LEN, "DX code 2:", dx_code_2, binary_dx_code_2);
if (debug_print) {
printf("%-*s%d\t-> %s\n", DX_DEBUG_STR_LEN, "DX code 1:", dx_code_1, binary_dx_code_1);
printf("%-*s%d\t-> %s\n", DX_DEBUG_STR_LEN, "DX code 2:", dx_code_2, binary_dx_code_2);
}
if (*has_frame_info) {
if (strlen(frame_info) < 1){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1017, "Frame number indicator \"/\" at position %d, but frame number is empty", (int)(detected_char - (char *)source + 1));
int ret_sscanf, n;
if (strlen(frame_info) < 1) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 982,
"Frame number indicator \"/\" at position %d, but frame number is empty",
dx_length + 1);
}
/* Some frame numbers are special values, convert them their equivalent number */
if (strcmp(frame_info, "S") == 0 || strcmp(frame_info, "X") == 0) {
@ -204,14 +203,20 @@ int parse_dx_code(struct zint_symbol *symbol, const char *source, char *binary_o
strcpy(frame_info, "0A");
}
if (sscanf(frame_info, "%d%c", &frame_number, &half_frame_flag) < 1){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1012, "Frame number \"%s\" is invalid (expected digits, eventually followed by \'A\')", frame_info);
ret_sscanf = sscanf(frame_info, "%d%c%n", &frame_number, &half_frame_flag, &n);
if (ret_sscanf < 1 || (ret_sscanf == 2 && frame_info[n] != '\0')) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 983,
"Frame number \"%s\" is invalid (expected digits, optionally followed by a single \"A\")",
frame_info);
}
if (frame_number < 0 || frame_number > 63){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1008, "Frame number \"%d\"should be between 0 and 63", frame_number);
if (frame_number < 0 || frame_number > 63) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 984, "Frame number \"%d\" out of range (0 to 63)",
frame_number);
}
dx_int_to_binary(frame_number, 6, binary_frame_number);
if (debug_print) {
printf("%-*s%d\t-> %s\n", DX_DEBUG_STR_LEN, "Frame number:", frame_number, binary_frame_number);
}
int_to_binary(frame_number, 6, binary_frame_number);
if (ZINT_DEBUG_PRINT) printf("%-*s%d\t-> %s\n", DEBUG_STR_LEN, "Frame number:", frame_number, binary_frame_number);
}
/* Build the binary output */
@ -221,19 +226,22 @@ int parse_dx_code(struct zint_symbol *symbol, const char *source, char *binary_o
strcat(binary_output, binary_dx_code_2);
if (*has_frame_info) {
strcat(binary_output, binary_frame_number);
if (toupper(half_frame_flag) == 'A') {
if (ZINT_DEBUG_PRINT) printf("%-*s\'%c\'\t-> 1\n", DEBUG_STR_LEN, "Half frame flag:", half_frame_flag);
to_upper((unsigned char *) &half_frame_flag, 1);
if (half_frame_flag == 'A') {
if (debug_print) printf("%-*s'%c'\t-> 1\n", DX_DEBUG_STR_LEN, "Half frame flag:", half_frame_flag);
strcat(binary_output, "1"); /* Half-frame is set */
} else {
if (half_frame_flag){
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 1012, "Frame number \"%s\" is invalid (expected digits, eventually followed by \'A\')", frame_info);
if (half_frame_flag) {
return errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 985,
"Frame number \"%s\" is invalid (expected digits, optionally followed by a single \"A\")",
frame_info);
}
if (ZINT_DEBUG_PRINT) printf("%-*s\'%c\'\t-> 0\n", DEBUG_STR_LEN, "Half frame flag:", half_frame_flag);
if (debug_print) printf("%-*s'%c'\t-> 0\n", DX_DEBUG_STR_LEN, "Half frame flag:", half_frame_flag);
strcat(binary_output, "0"); /* Half-frame is NOT set */
}
strcat(binary_output, "0"); /* Separator between half frame flag and parity bit*/
}
/* Parity bit */
for (i = 6; binary_output[i] != '\0'; i++) {
if (binary_output[i] == '1') {
@ -241,56 +249,56 @@ int parse_dx_code(struct zint_symbol *symbol, const char *source, char *binary_o
}
}
parity_bit %= 2;
if (ZINT_DEBUG_PRINT) printf("%-*s%s\t-> %d\n", DEBUG_STR_LEN, "Parity bit:", parity_bit?"yes":"no", parity_bit);
if (parity_bit){
strcat(binary_output, "1");
if (debug_print) {
printf("%-*s%s\t-> %d\n", DX_DEBUG_STR_LEN, "Parity bit:", parity_bit ? "yes" : "no", parity_bit);
}
else{
if (parity_bit) {
strcat(binary_output, "1");
} else {
strcat(binary_output, "0");
}
strcat(binary_output, "0101"); /* Stop pattern */
*output_length = strlen(binary_output);
*output_length = (int) strlen(binary_output);
return 0;
}
INTERNAL int dxfilmedge(struct zint_symbol *symbol, char source[], int length) {
INTERNAL int dxfilmedge(struct zint_symbol *symbol, unsigned char source[], int length) {
int i;
int writer = 0;
int error_number = 0;
char char_data[32];
int data_length;
bool has_frame_info;
int has_frame_info;
const char long_clock_pattern[] = "1111101010101010101010101010111";
const char short_clock_pattern[] = "11111010101010101010111";
const char *clock_pattern;
int clock_length;
int parse_result = -1;
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
if (length > 10) {
return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 1013, "Input length %d too long (maximum 10)", length);
return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 986, "Input length %d too long (maximum 10)", length);
}
parse_result = parse_dx_code(symbol, source, char_data, &data_length, &has_frame_info);
if (parse_result != 0){
if (ZINT_DEBUG_PRINT) printf("Error %s\n\n", symbol->errtxt);
parse_result = dx_parse_code(symbol, source, length, char_data, &data_length, &has_frame_info);
if (parse_result != 0) {
if (debug_print) printf("Error %s\n\n", symbol->errtxt);
return parse_result;
}
/* Clock signal is longer if the frame number is provided */
if (has_frame_info){
if (has_frame_info) {
clock_pattern = long_clock_pattern;
clock_length = sizeof(long_clock_pattern) -1;
}
else{
} else {
clock_pattern = short_clock_pattern;
clock_length = sizeof(short_clock_pattern) -1;
}
/* First row: clock pattern */
for (i = 0; i < clock_length; i++) {
if (clock_pattern[i] == '1') {
@ -313,7 +321,7 @@ INTERNAL int dxfilmedge(struct zint_symbol *symbol, char source[], int length) {
}
writer++;
}
symbol->rows = 2;
symbol->rows = 2;
symbol->width = clock_length;
if (symbol->output_options & COMPLIANT_HEIGHT) {
@ -324,7 +332,13 @@ INTERNAL int dxfilmedge(struct zint_symbol *symbol, char source[], int length) {
const float min_row_height = 2.2f;
const float max_height = 7.5f;
error_number = set_height(symbol, min_row_height, default_height, max_height, 0 /*no_errtxt*/);
} else {
/* Using compliant height as default as no backwards compatibility to consider */
const float default_height = 6.0f;
(void) set_height(symbol, 0.0f, default_height, 0.0f, 1 /*no_errtxt*/);
}
return error_number;
}
/* vim: set ts=4 sw=4 et : */