zint/backend/hanxin.c

1708 lines
56 KiB
C
Raw Permalink Normal View History

/* hanxin.c - Han Xin Code */
/*
2016-04-08 04:13:43 +12:00
libzint - the open source barcode library
Copyright (C) 2009-2022 Robin Stuart <rstuart114@gmail.com>
2016-04-08 04:13:43 +12:00
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 */
2016-04-08 04:13:43 +12:00
/* This code attempts to implement Han Xin Code according to ISO/IEC 20830:2021
* (previously ISO/IEC 20830 (draft 2019-10-10) and AIMD-015:2010 (Rev 0.8)) */
2016-04-08 04:13:43 +12:00
#include <stdio.h>
#include "common.h"
#include "reedsol.h"
#include "hanxin.h"
#include "eci.h"
#include <assert.h>
2016-04-08 04:13:43 +12:00
/* Find which submode to use for a text character */
static int hx_getsubmode(const unsigned int input) {
2017-10-24 08:37:52 +13:00
if (z_isdigit(input)) {
return 1;
}
2017-10-24 08:37:52 +13:00
if (z_isupper(input)) {
return 1;
}
2017-10-24 08:37:52 +13:00
if (z_islower(input)) {
return 1;
}
2017-10-24 08:37:52 +13:00
return 2;
}
/* Return length of terminator for encoding mode */
static int hx_terminator_length(const char mode) {
int result = 0;
switch (mode) {
case 'n':
result = 10;
break;
case 't':
result = 6;
break;
case '1':
case '2':
result = 12;
break;
case 'd':
result = 15;
break;
}
return result;
}
/* Calculate the length of the binary string */
static int hx_calc_binlen(const char mode[], const unsigned int ddata[], const int length, const int eci) {
int i;
char lastmode = '\0';
int est_binlen = 0;
int submode = 1;
int numeric_run = 0;
2017-10-24 08:37:52 +13:00
if (eci != 0) {
est_binlen += 4;
if (eci <= 127) {
est_binlen += 8;
} else if (eci <= 16383) {
est_binlen += 16;
} else {
est_binlen += 24;
}
2016-08-16 23:43:41 +12:00
}
2017-10-24 08:37:52 +13:00
i = 0;
do {
if (mode[i] != lastmode) {
if (i > 0) {
est_binlen += hx_terminator_length(lastmode);
}
/* GB 4-byte has indicator for each character (and no terminator) so not included here */
/* Region1/Region2 have special terminator to go directly into each other's mode so not included here */
if (mode[i] != 'f' || ((mode[i] == '1' && lastmode == '2') || (mode[i] == '2' && lastmode == '1'))) {
est_binlen += 4;
}
if (mode[i] == 'b') { /* Byte mode has byte count (and no terminator) */
est_binlen += 13;
}
lastmode = mode[i];
submode = 1;
numeric_run = 0;
}
switch (mode[i]) {
case 'n':
if (numeric_run % 3 == 0) {
est_binlen += 10;
}
numeric_run++;
break;
case 't':
if (hx_getsubmode(ddata[i]) != submode) {
est_binlen += 6;
submode = hx_getsubmode(ddata[i]);
}
est_binlen += 6;
break;
case 'b':
est_binlen += ddata[i] > 0xFF ? 16 : 8;
break;
case '1':
case '2':
est_binlen += 12;
break;
case 'd':
est_binlen += 15;
break;
case 'f':
est_binlen += 25;
i++;
break;
}
i++;
} while (i < length);
est_binlen += hx_terminator_length(lastmode);
return est_binlen;
}
/* Call `hx_calc_binlen()` for each segment */
static int hx_calc_binlen_segs(const char mode[], const unsigned int ddata[], const struct zint_seg segs[],
const int seg_count) {
int i;
int count = 0;
const unsigned int *dd = ddata;
const char *m = mode;
for (i = 0; i < seg_count; i++) {
count += hx_calc_binlen(m, dd, segs[i].length, segs[i].eci);
m += segs[i].length;
dd += segs[i].length;
}
return count;
}
static int hx_isRegion1(const unsigned int glyph) {
unsigned int byte;
byte = glyph >> 8;
if ((byte >= 0xb0) && (byte <= 0xd7)) {
byte = glyph & 0xff;
if ((byte >= 0xa1) && (byte <= 0xfe)) {
return 1;
}
} else if ((byte >= 0xa1) && (byte <= 0xa3)) {
byte = glyph & 0xff;
if ((byte >= 0xa1) && (byte <= 0xfe)) {
return 1;
}
} else if ((glyph >= 0xa8a1) && (glyph <= 0xa8c0)) {
return 1;
}
return 0;
}
static int hx_isRegion2(const unsigned int glyph) {
unsigned int byte;
byte = glyph >> 8;
if ((byte >= 0xd8) && (byte <= 0xf7)) {
byte = glyph & 0xff;
if ((byte >= 0xa1) && (byte <= 0xfe)) {
return 1;
}
}
return 0;
}
static int hx_isDoubleByte(const unsigned int glyph) {
unsigned int byte;
byte = glyph >> 8;
if ((byte >= 0x81) && (byte <= 0xfe)) {
byte = glyph & 0xff;
if ((byte >= 0x40) && (byte <= 0x7e)) {
return 1;
}
if ((byte >= 0x80) && (byte <= 0xfe)) {
return 1;
}
}
return 0;
}
static int hx_isFourByte(const unsigned int glyph, const unsigned int glyph2) {
unsigned int byte;
byte = glyph >> 8;
if ((byte >= 0x81) && (byte <= 0xfe)) {
byte = glyph & 0xff;
if ((byte >= 0x30) && (byte <= 0x39)) {
byte = glyph2 >> 8;
if ((byte >= 0x81) && (byte <= 0xfe)) {
byte = glyph2 & 0xff;
if ((byte >= 0x30) && (byte <= 0x39)) {
return 1;
}
}
}
}
2017-10-24 08:37:52 +13:00
return 0;
}
/* Convert Text 1 sub-mode character to encoding value, as given in table 3 */
static int hx_lookup_text1(const unsigned int input) {
2017-10-24 08:37:52 +13:00
if (z_isdigit(input)) {
return input - '0';
}
2017-10-24 08:37:52 +13:00
if (z_isupper(input)) {
return input - 'A' + 10;
}
2017-10-24 08:37:52 +13:00
if (z_islower(input)) {
return input - 'a' + 36;
}
2017-10-24 08:37:52 +13:00
return -1;
}
/* Convert Text 2 sub-mode character to encoding value, as given in table 4 */
static int hx_lookup_text2(const unsigned int input) {
2017-10-24 08:37:52 +13:00
if (input <= 27) {
return input;
}
2017-10-24 08:37:52 +13:00
if ((input >= ' ') && (input <= '/')) {
return input - ' ' + 28;
}
2017-10-24 08:37:52 +13:00
if ((input >= ':') && (input <= '@')) {
return input - ':' + 44;
}
if ((input >= '[') && (input <= 96)) {
return input - '[' + 51;
}
2017-10-24 08:37:52 +13:00
if ((input >= '{') && (input <= 127)) {
return input - '{' + 57;
}
2017-10-24 08:37:52 +13:00
return -1;
}
/* hx_define_mode() stuff */
/* Bits multiplied by this for costs, so as to be whole integer divisible by 2 and 3 */
#define HX_MULT 6
/* Whether in numeric or not. If in numeric, *p_end is set to position after numeric,
* and *p_cost is set to per-numeric cost */
static int hx_in_numeric(const unsigned int ddata[], const int length, const int in_posn,
unsigned int *p_end, unsigned int *p_cost) {
int i, digit_cnt;
if (in_posn < (int) *p_end) {
return 1;
}
/* Attempt to calculate the average 'cost' of using numeric mode in number of bits (times HX_MULT) */
for (i = in_posn; i < length && i < in_posn + 4 && z_isdigit(ddata[i]); i++);
digit_cnt = i - in_posn;
if (digit_cnt == 0) {
*p_end = 0;
return 0;
}
*p_end = i;
*p_cost = digit_cnt == 1
? 60 /* 10 * HX_MULT */ : digit_cnt == 2 ? 30 /* (10 / 2) * HX_MULT */ : 20 /* (10 / 3) * HX_MULT */;
return 1;
}
/* Whether in four-byte or not. If in four-byte, *p_fourbyte is set to position after four-byte,
* and *p_fourbyte_cost is set to per-position cost */
static int hx_in_fourbyte(const unsigned int ddata[], const int length, const int in_posn,
unsigned int *p_end, unsigned int *p_cost) {
if (in_posn < (int) *p_end) {
return 1;
}
if (in_posn == length - 1 || !hx_isFourByte(ddata[in_posn], ddata[in_posn + 1])) {
*p_end = 0;
return 0;
}
*p_end = in_posn + 2;
*p_cost = 75; /* ((4 + 21) / 2) * HX_MULT */
return 1;
}
/* Indexes into mode_types array */
#define HX_N 0 /* Numeric */
#define HX_T 1 /* Text */
#define HX_B 2 /* Binary */
#define HX_1 3 /* Common Chinese Region One */
#define HX_2 4 /* Common Chinese Region Two */
#define HX_D 5 /* GB 18030 2-byte Region */
#define HX_F 6 /* GB 18030 4-byte Region */
/* Note Unicode, GS1 and URI modes not implemented */
#define HX_NUM_MODES 7
/* Calculate optimized encoding modes. Adapted from Project Nayuki */
/* Copyright (c) Project Nayuki. (MIT License) See qr.c for detailed notice */
static void hx_define_mode(char *mode, const unsigned int ddata[], const int length, const int debug_print) {
/* Must be in same order as HX_N etc */
static const char mode_types[] = { 'n', 't', 'b', '1', '2', 'd', 'f', '\0' };
/* Initial mode costs */
static const unsigned int head_costs[HX_NUM_MODES] = {
/* N T B 1 2 D F */
4 * HX_MULT, 4 * HX_MULT, (4 + 13) * HX_MULT, 4 * HX_MULT, 4 * HX_MULT, 4 * HX_MULT, 0
};
/* Cost of switching modes from k to j */
static const unsigned int switch_costs[HX_NUM_MODES][HX_NUM_MODES] = {
/* N T B 1 2 D F */
/*N*/ { 0, (10 + 4) * HX_MULT, (10 + 4 + 13) * HX_MULT, (10 + 4) * HX_MULT, (10 + 4) * HX_MULT, (10 + 4) * HX_MULT, 10 * HX_MULT },
/*T*/ { (6 + 4) * HX_MULT, 0, (6 + 4 + 13) * HX_MULT, (6 + 4) * HX_MULT, (6 + 4) * HX_MULT, (6 + 4) * HX_MULT, 6 * HX_MULT },
/*B*/ { 4 * HX_MULT, 4 * HX_MULT, 0, 4 * HX_MULT, 4 * HX_MULT, 4 * HX_MULT, 0 },
/*1*/ { (12 + 4) * HX_MULT, (12 + 4) * HX_MULT, (12 + 4 + 13) * HX_MULT, 0, 12 * HX_MULT, (12 + 4) * HX_MULT, 12 * HX_MULT },
/*2*/ { (12 + 4) * HX_MULT, (12 + 4) * HX_MULT, (12 + 4 + 13) * HX_MULT, 12 * HX_MULT, 0, (12 + 4) * HX_MULT, 12 * HX_MULT },
/*D*/ { (15 + 4) * HX_MULT, (15 + 4) * HX_MULT, (15 + 4 + 13) * HX_MULT, (15 + 4) * HX_MULT, (15 + 4) * HX_MULT, 0, 15 * HX_MULT },
/*F*/ { 4 * HX_MULT, 4 * HX_MULT, (4 + 13) * HX_MULT, 4 * HX_MULT, 4 * HX_MULT, 4 * HX_MULT, 0 },
};
/* Final end-of-data costs */
static const unsigned int eod_costs[HX_NUM_MODES] = {
/* N T B 1 2 D F */
10 * HX_MULT, 6 * HX_MULT, 0, 12 * HX_MULT, 12 * HX_MULT, 15 * HX_MULT, 0
};
unsigned int numeric_end = 0, numeric_cost = 0, text_submode = 1, fourbyte_end = 0, fourbyte_cost = 0; /* State */
int text1, text2;
int i, j, k, cm_i;
unsigned int min_cost;
char cur_mode;
unsigned int prev_costs[HX_NUM_MODES];
unsigned int cur_costs[HX_NUM_MODES];
char *char_modes = (char *) z_alloca(length * HX_NUM_MODES);
/* char_modes[i * HX_NUM_MODES + j] represents the mode to encode the code point at index i such that the final
* segment ends in mode_types[j] and the total number of bits is minimized over all possible choices */
memset(char_modes, 0, length * HX_NUM_MODES);
/* At the beginning of each iteration of the loop below, prev_costs[j] is the minimum number of 1/6 (1/XX_MULT)
* bits needed to encode the entire string prefix of length i, and end in mode_types[j] */
memcpy(prev_costs, head_costs, HX_NUM_MODES * sizeof(unsigned int));
/* Calculate costs using dynamic programming */
for (i = 0, cm_i = 0; i < length; i++, cm_i += HX_NUM_MODES) {
memset(cur_costs, 0, HX_NUM_MODES * sizeof(unsigned int));
if (hx_in_numeric(ddata, length, i, &numeric_end, &numeric_cost)) {
cur_costs[HX_N] = prev_costs[HX_N] + numeric_cost;
char_modes[cm_i + HX_N] = 'n';
2020-11-28 04:24:37 +13:00
text1 = 1;
text2 = 0;
} else {
text1 = hx_lookup_text1(ddata[i]) != -1;
text2 = hx_lookup_text2(ddata[i]) != -1;
}
if (text1 || text2) {
if ((text_submode == 1 && text2) || (text_submode == 2 && text1)) {
cur_costs[HX_T] = prev_costs[HX_T] + 72; /* (6 + 6) * HX_MULT */
text_submode = text2 ? 2 : 1;
} else {
cur_costs[HX_T] = prev_costs[HX_T] + 36; /* 6 * HX_MULT */
}
char_modes[cm_i + HX_T] = 't';
} else {
text_submode = 1;
}
/* Binary mode can encode anything */
cur_costs[HX_B] = prev_costs[HX_B] + (ddata[i] > 0xFF ? 96 : 48); /* (16 : 8) * HX_MULT */
char_modes[cm_i + HX_B] = 'b';
if (hx_in_fourbyte(ddata, length, i, &fourbyte_end, &fourbyte_cost)) {
cur_costs[HX_F] = prev_costs[HX_F] + fourbyte_cost;
char_modes[cm_i + HX_F] = 'f';
2020-11-28 04:24:37 +13:00
} else {
if (hx_isDoubleByte(ddata[i])) {
2020-11-28 04:24:37 +13:00
cur_costs[HX_D] = prev_costs[HX_D] + 90; /* 15 * HX_MULT */
char_modes[cm_i + HX_D] = 'd';
if (hx_isRegion1(ddata[i])) { /* Subset */
2020-11-28 04:24:37 +13:00
cur_costs[HX_1] = prev_costs[HX_1] + 72; /* 12 * HX_MULT */
char_modes[cm_i + HX_1] = '1';
} else if (hx_isRegion2(ddata[i])) { /* Subset */
2020-11-28 04:24:37 +13:00
cur_costs[HX_2] = prev_costs[HX_2] + 72; /* 12 * HX_MULT */
char_modes[cm_i + HX_2] = '2';
}
}
}
if (i == length - 1) { /* Add end of data costs if last character */
for (j = 0; j < HX_NUM_MODES; j++) {
if (char_modes[cm_i + j]) {
cur_costs[j] += eod_costs[j];
}
}
}
/* Start new segment at the end to switch modes */
for (j = 0; j < HX_NUM_MODES; j++) { /* To mode */
for (k = 0; k < HX_NUM_MODES; k++) { /* From mode */
if (j != k && char_modes[cm_i + k]) {
const unsigned int new_cost = cur_costs[k] + switch_costs[k][j];
if (!char_modes[cm_i + j] || new_cost < cur_costs[j]) {
cur_costs[j] = new_cost;
char_modes[cm_i + j] = mode_types[k];
}
}
}
}
memcpy(prev_costs, cur_costs, HX_NUM_MODES * sizeof(unsigned int));
}
/* Find optimal ending mode */
min_cost = prev_costs[0];
cur_mode = mode_types[0];
for (i = 1; i < HX_NUM_MODES; i++) {
if (prev_costs[i] < min_cost) {
min_cost = prev_costs[i];
cur_mode = mode_types[i];
}
}
/* Get optimal mode for each code point by tracing backwards */
for (i = length - 1, cm_i = i * HX_NUM_MODES; i >= 0; i--, cm_i -= HX_NUM_MODES) {
Performance improvements for linear encoding and raster output - use fixed-length string tables (mostly) instead of (char *) pointer ones (saves ~40K) - re-use C128Table for CODABLOCKF and CODE16K (required removal of Stop character and extra CODE16K-only entry) - use pointer to destination and copy (memcpy/strcpy(), bin_append_posn()) instead of concatenating (strcat()) (mostly) - replace last remaining bin_append()s with bin_append_posn(); bin_append() removed - add length arg to toupper() and expand() (avoids strlen()) - change is_sane() to use table-based flags (avoids an iteration) - rename lookup() to is_sane_lookup() and change to check and return posns and use in pointer to destination loops (avoids strcat()s) - remove special case PHARMA in expand() (dealt with in pharma()) - make #define SILVER/CALCIUM/TECHNETIUM/KRSET etc static strings - replace strchr() -> posn() - CODE128: populate destination once in checksum loop; re-use and export some more routines (c128_set_a/b/c(), c128_put_in_set()) for sharing; prefix defines (SHIFTA -> C128_SHIFTA etc) and existing exported routines - use factor XOR toggle trick in checksum calcs (avoids branch) - raster.c: fill out single 1-pixel row and copy using new draw_bar_line(), copy_bar_line() routines; similarly in buffer_plot compare previous line & copy if same (same technique as used to improve non-half-integer scaling, significant performance increase, (c) codemonkey82); also done for PNG (BMP/GIF/PCX/TIFF not done) - raster/vector/output.c: shorten "output_" prefix -> "out_"; sync vector to other raster changes to try to keep source files similar - 2of5.c: prefix "c25_" JAPANPOST: return error if input data truncated (backward incompatible) DAFT: max chars 50 -> 100 common.c: istwodigit() -> is_twodigit() common.c/emf.c/output.c: use some further stripf()s (MSVC6 float variations) library.c: new check_output_args() helper zint.h: add BARCODE_LAST marker and use in library.c QRCODE: remove a NOLINT (requires clang-tidy-13), one remaining CMake: separate no-optimize from ZINT_DEBUG into new ZINT_NOOPT option
2021-10-21 11:05:30 +13:00
j = posn(mode_types, cur_mode);
cur_mode = char_modes[cm_i + j];
mode[i] = cur_mode;
}
if (debug_print) {
printf(" Mode: %.*s\n", length, mode);
}
}
/* Call `hx_define_mode()` for each segment */
static void hx_define_mode_segs(char mode[], const unsigned int ddata[], const struct zint_seg segs[],
const int seg_count, const int debug_print) {
int i;
const unsigned int *dd = ddata;
char *m = mode;
for (i = 0; i < seg_count; i++) {
hx_define_mode(m, dd, segs[i].length, debug_print);
m += segs[i].length;
dd += segs[i].length;
}
}
/* Convert input data to binary stream */
static void hx_calculate_binary(char binary[], const char mode[], const unsigned int ddata[], const int length,
const int eci, int *p_bp, const int debug_print) {
int position = 0;
int i, count, encoding_value;
int first_byte, second_byte;
int third_byte, fourth_byte;
int glyph;
2016-08-16 23:43:41 +12:00
int submode;
int bp = *p_bp;
if (eci != 0) {
2017-06-19 00:00:22 +12:00
/* Encoding ECI assignment number, according to Table 5 */
bp = bin_append_posn(8, 4, binary, bp); /* ECI */
2017-06-19 00:00:22 +12:00
if (eci <= 127) {
bp = bin_append_posn(eci, 8, binary, bp);
} else if (eci <= 16383) {
bp = bin_append_posn(2, 2, binary, bp);
bp = bin_append_posn(eci, 14, binary, bp);
} else {
bp = bin_append_posn(6, 3, binary, bp);
bp = bin_append_posn(eci, 21, binary, bp);
2017-06-19 00:00:22 +12:00
}
2016-08-16 23:43:41 +12:00
}
do {
2017-09-11 03:03:09 +12:00
int block_length = 0;
int double_byte = 0;
do {
if (mode[position] == 'b' && ddata[position + block_length] > 0xFF) {
double_byte++;
}
block_length++;
} while (position + block_length < length && mode[position + block_length] == mode[position]);
switch (mode[position]) {
case 'n':
/* Numeric mode */
/* Mode indicator */
bp = bin_append_posn(1, 4, binary, bp);
if (debug_print) {
printf("Numeric\n");
}
count = 0; /* Suppress gcc -Wmaybe-uninitialized */
i = 0;
while (i < block_length) {
const int first = ctoi((const char) ddata[position + i]);
count = 1;
encoding_value = first;
if (i + 1 < block_length && mode[position + i + 1] == 'n') {
const int second = ctoi((const char) ddata[position + i + 1]);
count = 2;
encoding_value = (encoding_value * 10) + second;
if (i + 2 < block_length && mode[position + i + 2] == 'n') {
const int third = ctoi((const char) ddata[position + i + 2]);
count = 3;
encoding_value = (encoding_value * 10) + third;
}
}
bp = bin_append_posn(encoding_value, 10, binary, bp);
if (debug_print) {
printf("0x%3x (%d)", encoding_value, encoding_value);
}
i += count;
}
/* Mode terminator depends on number of characters in last group (Table 2) */
switch (count) {
case 1:
bp = bin_append_posn(1021, 10, binary, bp);
break;
case 2:
bp = bin_append_posn(1022, 10, binary, bp);
break;
case 3:
bp = bin_append_posn(1023, 10, binary, bp);
break;
}
if (debug_print) {
printf(" (TERM %d)\n", count);
}
break;
case 't':
/* Text mode */
/* Mode indicator */
bp = bin_append_posn(2, 4, binary, bp);
if (debug_print) {
printf("Text\n");
}
submode = 1;
i = 0;
while (i < block_length) {
if (hx_getsubmode(ddata[i + position]) != submode) {
/* Change submode */
bp = bin_append_posn(62, 6, binary, bp);
submode = hx_getsubmode(ddata[i + position]);
if (debug_print) {
printf("SWITCH ");
}
}
if (submode == 1) {
encoding_value = hx_lookup_text1(ddata[i + position]);
} else {
encoding_value = hx_lookup_text2(ddata[i + position]);
}
bp = bin_append_posn(encoding_value, 6, binary, bp);
if (debug_print) {
printf("%.2x [ASC %.2x] ", encoding_value, ddata[i + position]);
}
i++;
}
/* Terminator */
bp = bin_append_posn(63, 6, binary, bp);
if (debug_print) {
printf("\n");
}
break;
case 'b':
/* Binary Mode */
/* Mode indicator */
bp = bin_append_posn(3, 4, binary, bp);
/* Count indicator */
bp = bin_append_posn(block_length + double_byte, 13, binary, bp);
if (debug_print) {
printf("Binary Mode (%d):", block_length + double_byte);
}
i = 0;
while (i < block_length) {
/* 8-bit bytes with no conversion */
bp = bin_append_posn(ddata[i + position], ddata[i + position] > 0xFF ? 16 : 8, binary, bp);
if (debug_print) {
printf(" %02x", (int) ddata[i + position]);
}
i++;
}
if (debug_print) {
printf("\n");
}
break;
case '1':
/* Region One encoding */
/* Mode indicator */
if (position == 0 || mode[position - 1] != '2') { /* Unless previous mode Region Two */
bp = bin_append_posn(4, 4, binary, bp);
}
if (debug_print) {
printf("Region One%s\n", position == 0 || mode[position - 1] != '2' ? "" : " (NO indicator)" );
}
i = 0;
while (i < block_length) {
first_byte = (ddata[i + position] & 0xff00) >> 8;
second_byte = ddata[i + position] & 0xff;
/* Subset 1 */
glyph = (0x5e * (first_byte - 0xb0)) + (second_byte - 0xa1);
/* Subset 2 */
if ((first_byte >= 0xa1) && (first_byte <= 0xa3)) {
if ((second_byte >= 0xa1) && (second_byte <= 0xfe)) {
glyph = (0x5e * (first_byte - 0xa1)) + (second_byte - 0xa1) + 0xeb0;
}
}
/* Subset 3 */
if ((ddata[i + position] >= 0xa8a1) && (ddata[i + position] <= 0xa8c0)) {
glyph = (second_byte - 0xa1) + 0xfca;
}
if (debug_print) {
printf("%.3x [GB %.4x] ", glyph, ddata[i + position]);
}
bp = bin_append_posn(glyph, 12, binary, bp);
i++;
}
/* Terminator */
bp = bin_append_posn(position + block_length == length || mode[position + block_length] != '2'
? 4095 : 4094, 12, binary, bp);
if (debug_print) {
printf("(TERM %x)\n", position + block_length == length || mode[position + block_length] != '2'
? 4095 : 4094);
}
break;
case '2':
/* Region Two encoding */
/* Mode indicator */
if (position == 0 || mode[position - 1] != '1') { /* Unless previous mode Region One */
bp = bin_append_posn(5, 4, binary, bp);
}
if (debug_print) {
printf("Region Two%s\n", position == 0 || mode[position - 1] != '1' ? "" : " (NO indicator)" );
}
i = 0;
while (i < block_length) {
first_byte = (ddata[i + position] & 0xff00) >> 8;
second_byte = ddata[i + position] & 0xff;
glyph = (0x5e * (first_byte - 0xd8)) + (second_byte - 0xa1);
if (debug_print) {
printf("%.3x [GB %.4x] ", glyph, ddata[i + position]);
}
bp = bin_append_posn(glyph, 12, binary, bp);
i++;
}
/* Terminator */
bp = bin_append_posn(position + block_length == length || mode[position + block_length] != '1'
? 4095 : 4094, 12, binary, bp);
if (debug_print) {
printf("(TERM %x)\n", position + block_length == length || mode[position + block_length] != '1'
? 4095 : 4094);
}
break;
case 'd':
/* Double byte encoding */
/* Mode indicator */
bp = bin_append_posn(6, 4, binary, bp);
if (debug_print) {
printf("Double byte\n");
}
i = 0;
while (i < block_length) {
first_byte = (ddata[i + position] & 0xff00) >> 8;
second_byte = ddata[i + position] & 0xff;
if (second_byte <= 0x7e) {
glyph = (0xbe * (first_byte - 0x81)) + (second_byte - 0x40);
} else {
glyph = (0xbe * (first_byte - 0x81)) + (second_byte - 0x41);
}
if (debug_print) {
printf("%.4x ", glyph);
}
bp = bin_append_posn(glyph, 15, binary, bp);
i++;
}
/* Terminator */
bp = bin_append_posn(32767, 15, binary, bp);
/* Terminator sequence of length 12 is a mistake
- confirmed by Wang Yi */
if (debug_print) {
printf("\n");
}
break;
case 'f':
/* Four-byte encoding */
if (debug_print) {
printf("Four byte\n");
}
i = 0;
while (i < block_length) {
/* Mode indicator */
bp = bin_append_posn(7, 4, binary, bp);
first_byte = (ddata[i + position] & 0xff00) >> 8;
second_byte = ddata[i + position] & 0xff;
third_byte = (ddata[i + position + 1] & 0xff00) >> 8;
fourth_byte = ddata[i + position + 1] & 0xff;
glyph = (0x3138 * (first_byte - 0x81)) + (0x04ec * (second_byte - 0x30)) +
(0x0a * (third_byte - 0x81)) + (fourth_byte - 0x30);
if (debug_print) {
printf("%d ", glyph);
}
bp = bin_append_posn(glyph, 21, binary, bp);
i += 2;
}
/* No terminator */
if (debug_print) {
printf("\n");
}
break;
}
position += block_length;
} while (position < length);
if (debug_print) printf("Binary (%d): %.*s\n", bp, bp, binary);
*p_bp = bp;
}
/* Call `hx_calculate_binary()` for each segment */
static void hx_calculate_binary_segs(char binary[], const char mode[], const unsigned int ddata[],
const struct zint_seg segs[], const int seg_count, int *p_bin_len, const int debug_print) {
int i;
const unsigned int *dd = ddata;
const char *m = mode;
int bp = 0;
for (i = 0; i < seg_count; i++) {
hx_calculate_binary(binary, m, dd, segs[i].length, segs[i].eci, &bp, debug_print);
m += segs[i].length;
dd += segs[i].length;
}
*p_bin_len = bp;
}
/* Finder pattern for top left of symbol */
static void hx_place_finder_top_left(unsigned char *grid, const int size) {
int xp, yp;
int x = 0, y = 0;
2017-08-07 19:37:02 +12:00
char finder[] = {0x7F, 0x40, 0x5F, 0x50, 0x57, 0x57, 0x57};
2017-10-24 08:37:52 +13:00
for (xp = 0; xp < 7; xp++) {
for (yp = 0; yp < 7; yp++) {
2017-08-07 19:37:02 +12:00
if (finder[yp] & 0x40 >> xp) {
grid[((yp + y) * size) + (xp + x)] = 0x11;
} else {
grid[((yp + y) * size) + (xp + x)] = 0x10;
}
}
}
}
/* Finder pattern for top right and bottom left of symbol */
static void hx_place_finder(unsigned char *grid, const int size, const int x, const int y) {
int xp, yp;
static const char finder[] = {0x7F, 0x01, 0x7D, 0x05, 0x75, 0x75, 0x75};
for (xp = 0; xp < 7; xp++) {
for (yp = 0; yp < 7; yp++) {
2017-08-07 19:37:02 +12:00
if (finder[yp] & 0x40 >> xp) {
grid[((yp + y) * size) + (xp + x)] = 0x11;
} else {
grid[((yp + y) * size) + (xp + x)] = 0x10;
}
}
}
}
/* Finder pattern for bottom right of symbol */
static void hx_place_finder_bottom_right(unsigned char *grid, const int size) {
int xp, yp;
const int x = size - 7;
const int y = x;
static const char finder[] = {0x75, 0x75, 0x75, 0x05, 0x7D, 0x01, 0x7F};
for (xp = 0; xp < 7; xp++) {
for (yp = 0; yp < 7; yp++) {
2017-08-07 19:37:02 +12:00
if (finder[yp] & 0x40 >> xp) {
grid[((yp + y) * size) + (xp + x)] = 0x11;
} else {
grid[((yp + y) * size) + (xp + x)] = 0x10;
}
}
}
}
/* Avoid plotting outside symbol or over finder patterns */
static void hx_safe_plot(unsigned char *grid, const int size, const int x, const int y, const int value) {
if ((x >= 0) && (x < size)) {
if ((y >= 0) && (y < size)) {
if (grid[(y * size) + x] == 0) {
grid[(y * size) + x] = value;
}
}
}
}
/* Plot an alignment pattern around top and right of a module */
static void hx_plot_alignment(unsigned char *grid, const int size, const int x, const int y, const int w,
const int h) {
int i;
hx_safe_plot(grid, size, x, y, 0x11);
hx_safe_plot(grid, size, x - 1, y + 1, 0x10);
for (i = 1; i <= w; i++) {
/* Top */
hx_safe_plot(grid, size, x - i, y, 0x11);
hx_safe_plot(grid, size, x - i - 1, y + 1, 0x10);
}
for (i = 1; i < h; i++) {
/* Right */
hx_safe_plot(grid, size, x, y + i, 0x11);
hx_safe_plot(grid, size, x - 1, y + i + 1, 0x10);
}
}
/* Plot assistant alignment patterns */
static void hx_plot_assistant(unsigned char *grid, const int size, const int x, const int y) {
hx_safe_plot(grid, size, x - 1, y - 1, 0x10);
hx_safe_plot(grid, size, x, y - 1, 0x10);
hx_safe_plot(grid, size, x + 1, y - 1, 0x10);
hx_safe_plot(grid, size, x - 1, y, 0x10);
hx_safe_plot(grid, size, x, y, 0x11);
hx_safe_plot(grid, size, x + 1, y, 0x10);
hx_safe_plot(grid, size, x - 1, y + 1, 0x10);
hx_safe_plot(grid, size, x, y + 1, 0x10);
hx_safe_plot(grid, size, x + 1, y + 1, 0x10);
}
/* Put static elements in the grid */
static void hx_setup_grid(unsigned char *grid, const int size, const int version) {
int i;
memset(grid, 0, (size_t) size * size);
/* Add finder patterns */
hx_place_finder_top_left(grid, size);
hx_place_finder(grid, size, 0, size - 7);
hx_place_finder(grid, size, size - 7, 0);
hx_place_finder_bottom_right(grid, size);
/* Add finder pattern separator region */
for (i = 0; i < 8; i++) {
/* Top left */
grid[(7 * size) + i] = 0x10;
grid[(i * size) + 7] = 0x10;
/* Top right */
grid[(7 * size) + (size - i - 1)] = 0x10;
grid[((size - i - 1) * size) + 7] = 0x10;
/* Bottom left */
grid[(i * size) + (size - 8)] = 0x10;
grid[((size - 8) * size) + i] = 0x10;
/* Bottom right */
grid[((size - 8) * size) + (size - i - 1)] = 0x10;
grid[((size - i - 1) * size) + (size - 8)] = 0x10;
}
/* Reserve function information region */
for (i = 0; i < 9; i++) {
/* Top left */
grid[(8 * size) + i] = 0x10;
grid[(i * size) + 8] = 0x10;
/* Top right */
grid[(8 * size) + (size - i - 1)] = 0x10;
grid[((size - i - 1) * size) + 8] = 0x10;
/* Bottom left */
grid[(i * size) + (size - 9)] = 0x10;
grid[((size - 9) * size) + i] = 0x10;
/* Bottom right */
grid[((size - 9) * size) + (size - i - 1)] = 0x10;
grid[((size - i - 1) * size) + (size - 9)] = 0x10;
}
if (version > 3) {
const int k = hx_module_k[version - 1];
const int r = hx_module_r[version - 1];
const int m = hx_module_m[version - 1];
int x, y, row_switch, column_switch;
int module_height, module_width;
int mod_x, mod_y;
/* Add assistant alignment patterns to left and right */
y = 0;
mod_y = 0;
do {
if (mod_y < m) {
module_height = k;
} else {
module_height = r - 1;
}
if ((mod_y & 1) == 0) {
if ((m & 1) == 1) {
hx_plot_assistant(grid, size, 0, y);
}
} else {
if ((m & 1) == 0) {
hx_plot_assistant(grid, size, 0, y);
}
hx_plot_assistant(grid, size, size - 1, y);
}
mod_y++;
y += module_height;
} while (y < size);
/* Add assistant alignment patterns to top and bottom */
x = (size - 1);
mod_x = 0;
do {
if (mod_x < m) {
module_width = k;
} else {
module_width = r - 1;
}
if ((mod_x & 1) == 0) {
if ((m & 1) == 1) {
hx_plot_assistant(grid, size, x, (size - 1));
}
} else {
if ((m & 1) == 0) {
hx_plot_assistant(grid, size, x, (size - 1));
}
hx_plot_assistant(grid, size, x, 0);
}
mod_x++;
x -= module_width;
} while (x >= 0);
/* Add alignment pattern */
column_switch = 1;
y = 0;
mod_y = 0;
do {
if (mod_y < m) {
module_height = k;
} else {
module_height = r - 1;
}
if (column_switch == 1) {
row_switch = 1;
column_switch = 0;
} else {
row_switch = 0;
column_switch = 1;
}
x = (size - 1);
mod_x = 0;
do {
if (mod_x < m) {
module_width = k;
} else {
module_width = r - 1;
}
if (row_switch == 1) {
if (!(y == 0 && x == (size - 1))) {
hx_plot_alignment(grid, size, x, y, module_width, module_height);
}
row_switch = 0;
} else {
row_switch = 1;
}
mod_x++;
x -= module_width;
} while (x >= 0);
mod_y++;
y += module_height;
} while (y < size);
}
}
/* Calculate error correction codes */
static void hx_add_ecc(unsigned char fullstream[], const unsigned char datastream[], const int data_codewords,
const int version, const int ecc_level) {
unsigned char data_block[180];
unsigned char ecc_block[36];
int i, j, block;
int input_position = -1;
int output_position = -1;
const int table_d1_pos = ((version - 1) * 36) + ((ecc_level - 1) * 9);
rs_t rs;
rs_init_gf(&rs, 0x163); /* x^8 + x^6 + x^5 + x + 1 = 0 */
for (i = 0; i < 3; i++) {
const int batch_size = hx_table_d1[table_d1_pos + (3 * i)];
const int data_length = hx_table_d1[table_d1_pos + (3 * i) + 1];
const int ecc_length = hx_table_d1[table_d1_pos + (3 * i) + 2];
rs_init_code(&rs, ecc_length, 1);
for (block = 0; block < batch_size; block++) {
for (j = 0; j < data_length; j++) {
input_position++;
output_position++;
data_block[j] = input_position < data_codewords ? datastream[input_position] : 0;
fullstream[output_position] = data_block[j];
}
rs_encode(&rs, data_length, data_block, ecc_block);
for (j = 0; j < ecc_length; j++) {
output_position++;
fullstream[output_position] = ecc_block[ecc_length - j - 1];
}
}
}
}
static void hx_set_function_info(unsigned char *grid, const int size, const int version, const int ecc_level,
const int bitmask, const int debug_print) {
int i, j;
char function_information[34];
unsigned char fi_cw[3] = {0};
unsigned char fi_ecc[4];
int bp = 0;
rs_t rs;
/* Form function information string */
bp = bin_append_posn(version + 20, 8, function_information, bp);
bp = bin_append_posn(ecc_level - 1, 2, function_information, bp);
bp = bin_append_posn(bitmask, 2, function_information, bp);
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) {
if (function_information[(i * 4) + j] == '1') {
fi_cw[i] += (0x08 >> j);
}
}
}
rs_init_gf(&rs, 0x13);
rs_init_code(&rs, 4, 1);
rs_encode(&rs, 3, fi_cw, fi_ecc);
for (i = 3; i >= 0; i--) {
bp = bin_append_posn(fi_ecc[i], 4, function_information, bp);
}
/* Previously added alternating filler pattern here (as does BWIPP) but not mentioned in ISO/IEC 20830:2021 and
does not appear in Figure 1 nor in the figures in Annex K (although does appear in Figure 2 and Figures 4-9)
nor in the AIM ITS/04-023:2022 examples: so just clear */
for (i = 28; i < 34; i++) {
function_information[i] = '0';
}
if (debug_print) {
printf("Version: %d, ECC: %d, Mask: %d, Structural Info: %.34s\n", version, ecc_level, bitmask,
function_information);
}
/* Add function information to symbol */
for (i = 0; i < 9; i++) {
if (function_information[i] == '1') {
grid[(8 * size) + i] = 0x01;
grid[((size - 8 - 1) * size) + (size - i - 1)] = 0x01;
}
if (function_information[i + 8] == '1') {
grid[((8 - i) * size) + 8] = 0x01;
grid[((size - 8 - 1 + i) * size) + (size - 8 - 1)] = 0x01;
}
if (function_information[i + 17] == '1') {
grid[(i * size) + (size - 1 - 8)] = 0x01;
grid[((size - 1 - i) * size) + 8] = 0x01;
}
if (function_information[i + 25] == '1') {
grid[(8 * size) + (size - 1 - 8 + i)] = 0x01;
grid[((size - 1 - 8) * size) + (8 - i)] = 0x01;
}
}
}
/* Rearrange data in batches of 13 codewords (section 5.8.2) */
static void hx_make_picket_fence(const unsigned char fullstream[], unsigned char picket_fence[],
const int streamsize) {
int i, start;
int output_position = 0;
2017-10-24 08:37:52 +13:00
for (start = 0; start < 13; start++) {
for (i = start; i < streamsize; i += 13) {
picket_fence[output_position] = fullstream[i];
output_position++;
}
}
}
2016-04-21 07:44:59 +12:00
/* Evaluate a bitmask according to table 9 */
static int hx_evaluate(const unsigned char *local, const int size) {
static const unsigned char h1010111[7] = { 1, 0, 1, 0, 1, 1, 1 };
static const unsigned char h1110101[7] = { 1, 1, 1, 0, 1, 0, 1 };
int x, y, r, block;
2016-04-21 07:44:59 +12:00
int result = 0;
int state;
2016-04-21 07:44:59 +12:00
int a, b, afterCount, beforeCount;
/* Test 1: 1:1:1:1:3 or 3:1:1:1:1 ratio pattern in row/column */
2016-04-21 07:44:59 +12:00
/* Vertical */
for (x = 0; x < size; x++) {
for (y = 0; y <= (size - 7); y++) {
if (local[y * size + x] && local[(y + 1) * size + x] != local[(y + 5) * size + x] &&
local[(y + 2) * size + x] && !local[(y + 3) * size + x] &&
local[(y + 4) * size + x] && local[(y + 6) * size + x]) {
2016-04-21 07:44:59 +12:00
/* Pattern found, check before and after */
beforeCount = 0;
for (b = (y - 1); b >= (y - 3); b--) {
if (b < 0) { /* Count < edge as whitespace */
beforeCount = 3;
break;
}
if (local[(b * size) + x]) {
break;
2016-04-21 07:44:59 +12:00
}
beforeCount++;
2016-04-21 07:44:59 +12:00
}
if (beforeCount == 3) {
/* Pattern is preceded by light area 3 modules wide */
result += 50;
} else {
afterCount = 0;
for (a = (y + 7); a <= (y + 9); a++) {
if (a >= size) { /* Count > edge as whitespace */
afterCount = 3;
break;
}
if (local[(a * size) + x]) {
break;
2016-04-21 07:44:59 +12:00
}
afterCount++;
}
if (afterCount == 3) {
/* Pattern is followed by light area 3 modules wide */
result += 50;
2016-04-21 07:44:59 +12:00
}
}
y++; /* Skip to next possible match */
2016-04-21 07:44:59 +12:00
}
}
}
/* Horizontal */
for (y = 0; y < size; y++) {
r = y * size;
for (x = 0; x <= (size - 7); x++) {
if (memcmp(local + r + x, h1010111, 7) == 0 || memcmp(local + r + x, h1110101, 7) == 0) {
2016-04-21 07:44:59 +12:00
/* Pattern found, check before and after */
beforeCount = 0;
for (b = (x - 1); b >= (x - 3); b--) {
if (b < 0) { /* Count < edge as whitespace */
beforeCount = 3;
break;
}
if (local[r + b]) {
break;
2016-04-21 07:44:59 +12:00
}
beforeCount++;
2016-04-21 07:44:59 +12:00
}
if (beforeCount == 3) {
/* Pattern is preceded by light area 3 modules wide */
result += 50;
} else {
afterCount = 0;
for (a = (x + 7); a <= (x + 9); a++) {
if (a >= size) { /* Count > edge as whitespace */
afterCount = 3;
break;
2016-04-21 07:44:59 +12:00
}
if (local[r + a]) {
break;
}
afterCount++;
}
if (afterCount == 3) {
/* Pattern is followed by light area 3 modules wide */
result += 50;
2016-04-21 07:44:59 +12:00
}
}
x++; /* Skip to next possible match */
2016-04-21 07:44:59 +12:00
}
}
}
2016-04-21 07:44:59 +12:00
/* Test 2: Adjacent modules in row/column in same colour */
/* In AIMD-15 section 5.8.3.2 it is stated... “In Table 9 below, i refers to the row
* position of the module. - however i being the length of the run of the
* same colour (i.e. "block" below) in the same fashion as ISO/IEC 18004
* makes more sense. -- Confirmed by Wang Yi */
/* Fixed in ISO/IEC 20830 section 5.8.4.3 "In Table, i refers to the modules with
same color." */
2016-04-21 07:44:59 +12:00
/* Vertical */
for (x = 0; x < size; x++) {
block = 0;
state = 0;
2016-04-21 07:44:59 +12:00
for (y = 0; y < size; y++) {
if (local[(y * size) + x] == state) {
2016-04-21 07:44:59 +12:00
block++;
} else {
if (block >= 3) {
result += block * 4;
2016-04-21 07:44:59 +12:00
}
block = 1;
2016-04-21 07:44:59 +12:00
state = local[(y * size) + x];
}
}
if (block >= 3) {
result += block * 4;
2016-04-21 07:44:59 +12:00
}
}
/* Horizontal */
for (y = 0; y < size; y++) {
r = y * size;
2016-04-21 07:44:59 +12:00
block = 0;
state = 0;
2016-04-21 07:44:59 +12:00
for (x = 0; x < size; x++) {
if (local[r + x] == state) {
2016-04-21 07:44:59 +12:00
block++;
} else {
if (block >= 3) {
result += block * 4;
2016-04-21 07:44:59 +12:00
}
block = 1;
state = local[r + x];
2016-04-21 07:44:59 +12:00
}
}
if (block >= 3) {
result += block * 4;
2016-04-21 07:44:59 +12:00
}
}
return result;
}
/* Apply the four possible bitmasks for evaluation */
/* TODO: Haven't been able to replicate (or even get close to) the penalty scores in ISO/IEC 20830:2021
* Annex K examples */
static void hx_apply_bitmask(unsigned char *grid, const int size, const int version, const int ecc_level,
const int user_mask, const int debug_print) {
2016-04-21 07:44:59 +12:00
int x, y;
int i, j, r, k;
int pattern, penalty[4] = {0};
int best_pattern;
2016-04-21 07:44:59 +12:00
int bit;
const int size_squared = size * size;
unsigned char *mask = (unsigned char *) z_alloca(size_squared);
unsigned char *local = (unsigned char *) z_alloca(size_squared);
2016-04-21 07:44:59 +12:00
/* Perform data masking */
memset(mask, 0, size_squared);
for (y = 0; y < size; y++) {
r = y * size;
for (x = 0; x < size; x++) {
k = r + x;
if (!(grid[k] & 0xf0)) {
j = x + 1;
i = y + 1;
if (((i + j) & 1) == 0) {
mask[k] |= 0x02;
2016-04-21 07:44:59 +12:00
}
if (((((i + j) % 3) + (j % 3)) & 1) == 0) {
mask[k] |= 0x04;
2016-04-21 07:44:59 +12:00
}
if ((((i % j) + (j % i) + (i % 3) + (j % 3)) & 1) == 0) {
mask[k] |= 0x08;
2016-04-21 07:44:59 +12:00
}
}
}
}
if (user_mask) {
best_pattern = user_mask - 1;
} else {
/* apply data masks to grid, result in local */
2016-04-21 07:44:59 +12:00
/* Do null pattern 00 separately first */
pattern = 0;
for (k = 0; k < size_squared; k++) {
local[k] = grid[k] & 0x0f;
2016-04-21 07:44:59 +12:00
}
/* Set the Structural Info */
hx_set_function_info(local, size, version, ecc_level, pattern, 0 /*debug_print*/);
/* Evaluate result */
penalty[pattern] = hx_evaluate(local, size);
best_pattern = 0;
for (pattern = 1; pattern < 4; pattern++) {
bit = 1 << pattern;
for (k = 0; k < size_squared; k++) {
if (mask[k] & bit) {
local[k] = grid[k] ^ 0x01;
} else {
local[k] = grid[k] & 0x0f;
}
}
/* Set the Structural Info */
hx_set_function_info(local, size, version, ecc_level, pattern, 0 /*debug_print*/);
/* Evaluate result */
penalty[pattern] = hx_evaluate(local, size);
if (penalty[pattern] < penalty[best_pattern]) {
best_pattern = pattern;
}
}
2016-04-21 07:44:59 +12:00
}
if (debug_print) {
printf("Mask: %d (%s)", best_pattern, user_mask ? "specified" : "automatic");
if (!user_mask) {
for (pattern = 0; pattern < 4; pattern++) printf(" %d:%d", pattern, penalty[pattern]);
2016-04-21 07:44:59 +12:00
}
printf("\n");
2016-04-21 07:44:59 +12:00
}
2016-04-21 07:44:59 +12:00
/* Apply mask */
if (best_pattern) { /* If not null mask */
if (!user_mask && best_pattern == 3) { /* Reuse last */
memcpy(grid, local, size_squared);
} else {
bit = 1 << best_pattern;
for (k = 0; k < size_squared; k++) {
if (mask[k] & bit) {
grid[k] ^= 0x01;
2016-04-21 07:44:59 +12:00
}
}
}
}
/* Set the Structural Info */
hx_set_function_info(grid, size, version, ecc_level, best_pattern, debug_print);
2016-04-21 07:44:59 +12:00
}
/* Han Xin Code - main */
INTERNAL int hanxin(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
int warn_number = 0;
int est_binlen;
int ecc_level = symbol->option_1;
int i, j, j_max, version;
int full_multibyte;
int user_mask;
int data_codewords = 0, size;
int size_squared;
int codewords;
int bin_len;
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
const int eci_length_segs = get_eci_length_segs(segs, seg_count);
struct zint_seg *local_segs = (struct zint_seg *) z_alloca(sizeof(struct zint_seg) * seg_count);
unsigned int *ddata = (unsigned int *) z_alloca(sizeof(unsigned int) * eci_length_segs);
char *mode = (char *) z_alloca(eci_length_segs);
char *binary;
unsigned char *datastream;
2016-08-16 23:43:41 +12:00
unsigned char *fullstream;
unsigned char *picket_fence;
unsigned char *grid;
segs_cpy(symbol, segs, seg_count, local_segs); /* Shallow copy (needed to set default ECI & protect lengths) */
/* If ZINT_FULL_MULTIBYTE set use Hanzi mode in DATA_MODE or for non-GB 18030 in UNICODE_MODE */
full_multibyte = (symbol->option_3 & 0xFF) == ZINT_FULL_MULTIBYTE;
user_mask = (symbol->option_3 >> 8) & 0x0F; /* User mask is pattern + 1, so >= 1 and <= 4 */
if (user_mask > 4) {
user_mask = 0; /* Ignore */
}
if ((symbol->input_mode & 0x07) == DATA_MODE) {
gb18030_cpy_segs(local_segs, seg_count, ddata, full_multibyte);
2016-08-16 23:43:41 +12:00
} else {
unsigned int *dd = ddata;
for (i = 0; i < seg_count; i++) {
int done = 0;
if (local_segs[i].eci != 32 || seg_count > 1) { /* Unless ECI 32 (GB 18030) or have multiple segments */
/* Try other conversions (ECI 0 defaults to ISO/IEC 8859-1) */
int error_number = gb18030_utf8_to_eci(local_segs[i].eci, local_segs[i].source, &local_segs[i].length,
dd, full_multibyte);
if (error_number == 0) {
done = 1;
} else if (local_segs[i].eci || seg_count > 1) {
sprintf(symbol->errtxt, "545: Invalid character in input data for ECI %d", local_segs[i].eci);
return error_number;
}
}
if (!done) {
/* Try GB 18030 */
int error_number = gb18030_utf8(symbol, local_segs[i].source, &local_segs[i].length, dd);
if (error_number != 0) {
return error_number;
}
if (local_segs[i].eci != 32) {
strcpy(symbol->errtxt, "543: Converted to GB 18030 but no ECI specified");
warn_number = ZINT_WARN_NONCOMPLIANT;
}
}
dd += local_segs[i].length;
2016-08-16 23:43:41 +12:00
}
}
hx_define_mode_segs(mode, ddata, local_segs, seg_count, debug_print);
est_binlen = hx_calc_binlen_segs(mode, ddata, local_segs, seg_count);
binary = (char *) z_alloca((est_binlen + 1));
if ((ecc_level <= 0) || (ecc_level >= 5)) {
ecc_level = 1;
}
hx_calculate_binary_segs(binary, mode, ddata, local_segs, seg_count, &bin_len, debug_print);
codewords = bin_len >> 3;
if (bin_len & 0x07) {
codewords++;
}
if (debug_print) {
printf("Num. of codewords: %d\n", codewords);
}
version = 85;
for (i = 84; i > 0; i--) {
switch (ecc_level) {
case 1:
if (hx_data_codewords_L1[i - 1] >= codewords) {
version = i;
data_codewords = hx_data_codewords_L1[i - 1];
}
break;
case 2:
if (hx_data_codewords_L2[i - 1] >= codewords) {
version = i;
data_codewords = hx_data_codewords_L2[i - 1];
}
break;
case 3:
if (hx_data_codewords_L3[i - 1] >= codewords) {
version = i;
data_codewords = hx_data_codewords_L3[i - 1];
}
break;
case 4:
if (hx_data_codewords_L4[i - 1] >= codewords) {
version = i;
data_codewords = hx_data_codewords_L4[i - 1];
}
break;
default: /* Not reached */
assert(0);
break;
}
}
if (version == 85) {
2017-07-28 03:01:53 +12:00
strcpy(symbol->errtxt, "541: Input too long for selected error correction level");
return ZINT_ERROR_TOO_LONG;
}
if ((symbol->option_2 < 0) || (symbol->option_2 > 84)) {
symbol->option_2 = 0;
}
if (symbol->option_2 > version) {
version = symbol->option_2;
}
2017-10-24 08:37:52 +13:00
if ((symbol->option_2 != 0) && (symbol->option_2 < version)) {
2017-07-28 03:01:53 +12:00
strcpy(symbol->errtxt, "542: Input too long for selected symbol size");
return ZINT_ERROR_TOO_LONG;
}
/* If there is spare capacity, increase the level of ECC */
/* Unless explicitly specified (within min/max bounds) by user */
if (symbol->option_1 == -1 || symbol->option_1 != ecc_level) {
if ((ecc_level == 1) && (codewords <= hx_data_codewords_L2[version - 1])) {
ecc_level = 2;
data_codewords = hx_data_codewords_L2[version - 1];
}
if ((ecc_level == 2) && (codewords <= hx_data_codewords_L3[version - 1])) {
ecc_level = 3;
data_codewords = hx_data_codewords_L3[version - 1];
}
if ((ecc_level == 3) && (codewords <= hx_data_codewords_L4[version - 1])) {
ecc_level = 4;
data_codewords = hx_data_codewords_L4[version - 1];
}
}
size = (version * 2) + 21;
size_squared = size * size;
datastream = (unsigned char *) z_alloca(data_codewords);
fullstream = (unsigned char *) z_alloca(hx_total_codewords[version - 1]);
picket_fence = (unsigned char *) z_alloca(hx_total_codewords[version - 1]);
grid = (unsigned char *) z_alloca(size_squared);
memset(datastream, 0, data_codewords);
for (i = 0; i < bin_len; i++) {
if (binary[i] == '1') {
datastream[i >> 3] |= 0x80 >> (i & 0x07);
}
}
if (debug_print) {
printf("Datastream (%d): ", data_codewords);
for (i = 0; i < data_codewords; i++) {
printf("%.2x ", datastream[i]);
}
printf("\n");
}
#ifdef ZINT_TEST
2019-12-17 06:31:52 +13:00
if (symbol->debug & ZINT_DEBUG_TEST) debug_test_codeword_dump(symbol, datastream, data_codewords);
#endif
hx_setup_grid(grid, size, version);
hx_add_ecc(fullstream, datastream, data_codewords, version, ecc_level);
if (debug_print) {
printf("Fullstream (%d): ", hx_total_codewords[version - 1]);
for (i = 0; i < hx_total_codewords[version - 1]; i++) {
printf("%.2x ", fullstream[i]);
}
printf("\n");
}
hx_make_picket_fence(fullstream, picket_fence, hx_total_codewords[version - 1]);
2016-04-21 07:44:59 +12:00
/* Populate grid */
j = 0;
j_max = hx_total_codewords[version - 1] * 8;
for (i = 0; i < size_squared; i++) {
2016-04-21 07:44:59 +12:00
if (grid[i] == 0x00) {
if (j < j_max) {
if (picket_fence[(j >> 3)] & (0x80 >> (j & 0x07))) {
2016-04-21 07:44:59 +12:00
grid[i] = 0x01;
}
j++;
2016-04-24 02:26:51 +12:00
} else {
break;
2016-04-24 02:26:51 +12:00
}
}
}
hx_apply_bitmask(grid, size, version, ecc_level, user_mask, debug_print);
symbol->width = size;
symbol->rows = size;
for (i = 0; i < size; i++) {
const int r = i * size;
for (j = 0; j < size; j++) {
if (grid[r + j] & 0x01) {
set_module(symbol, i, j);
}
}
symbol->row_height[i] = 1;
}
symbol->height = size;
return warn_number;
}
/* vim: set ts=4 sw=4 et : */