2019-11-26 08:08:25 +13:00
|
|
|
/* qr.c Handles QR Code, Micro QR Code, UPNQR and rMQR
|
2016-02-17 23:52:48 +13:00
|
|
|
|
|
|
|
libzint - the open source barcode library
|
2021-01-12 07:11:41 +13:00
|
|
|
Copyright (C) 2009 - 2021 Robin Stuart <rstuart114@gmail.com>
|
2016-02-17 23:52:48 +13:00
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
2016-02-17 23:52:48 +13:00
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
2016-02-21 00:29:19 +13:00
|
|
|
documentation and/or other materials provided with the distribution.
|
2016-02-17 23:52:48 +13:00
|
|
|
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
|
2016-02-21 00:29:19 +13:00
|
|
|
without specific prior written permission.
|
2016-02-17 23:52:48 +13:00
|
|
|
|
|
|
|
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
|
2016-02-21 00:29:19 +13:00
|
|
|
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
2016-02-17 23:52:48 +13:00
|
|
|
SUCH DAMAGE.
|
2016-02-21 00:29:19 +13:00
|
|
|
*/
|
2019-11-28 05:16:14 +13:00
|
|
|
/* vim: set ts=4 sw=4 et : */
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
#include <math.h>
|
2016-02-17 23:52:48 +13:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#include <malloc.h>
|
|
|
|
#endif
|
|
|
|
#include "common.h"
|
|
|
|
#include <stdio.h>
|
2021-01-12 07:11:41 +13:00
|
|
|
#include "eci.h"
|
2016-02-17 23:52:48 +13:00
|
|
|
#include "sjis.h"
|
|
|
|
#include "qr.h"
|
|
|
|
#include "reedsol.h"
|
2016-03-01 08:42:32 +13:00
|
|
|
#include <assert.h>
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2021-07-06 23:13:34 +12:00
|
|
|
#define LEVEL_L 1
|
|
|
|
#define LEVEL_M 2
|
|
|
|
#define LEVEL_Q 3
|
|
|
|
#define LEVEL_H 4
|
|
|
|
|
2021-08-21 03:50:39 +12:00
|
|
|
static const char ecc_level_names[] = { 'L', 'M', 'Q', 'H' };
|
|
|
|
|
2021-07-06 23:13:34 +12:00
|
|
|
#define QR_PERCENT 38 /* Alphanumeric mode % */
|
|
|
|
|
|
|
|
#define RMQR_VERSION 41
|
|
|
|
#define MICROQR_VERSION 73
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Returns true if input glyph is in the Alphanumeric set */
|
2019-12-17 06:31:52 +13:00
|
|
|
static int is_alpha(const unsigned int glyph, const int gs1) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int retval = 0;
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
if ((glyph >= '0') && (glyph <= '9')) {
|
2016-02-21 00:29:19 +13:00
|
|
|
retval = 1;
|
2019-12-17 06:31:52 +13:00
|
|
|
} else if ((glyph >= 'A') && (glyph <= 'Z')) {
|
2016-02-21 00:29:19 +13:00
|
|
|
retval = 1;
|
2019-12-17 06:31:52 +13:00
|
|
|
} else if (gs1 && glyph == '[') {
|
|
|
|
retval = 1;
|
|
|
|
} else {
|
|
|
|
switch (glyph) {
|
|
|
|
case ' ':
|
|
|
|
case '$':
|
|
|
|
case '%':
|
|
|
|
case '*':
|
|
|
|
case '+':
|
|
|
|
case '-':
|
|
|
|
case '.':
|
|
|
|
case '/':
|
|
|
|
case ':':
|
|
|
|
retval = 1;
|
|
|
|
break;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
return retval;
|
2016-02-17 23:52:48 +13:00
|
|
|
}
|
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
/* Bits multiplied by this for costs, so as to be whole integer divisible by 2 and 3 */
|
|
|
|
#define QR_MULT 6
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
/* 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 in_numeric(const unsigned int jisdata[], const int length, const int in_posn,
|
|
|
|
unsigned int *p_end, unsigned int *p_cost) {
|
|
|
|
int i, digit_cnt;
|
2019-12-17 06:31:52 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (in_posn < (int) *p_end) {
|
2019-12-17 06:31:52 +13:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Attempt to calculate the average 'cost' of using numeric mode in number of bits (times QR_MULT) */
|
2020-11-28 01:54:44 +13:00
|
|
|
for (i = in_posn; i < length && i < in_posn + 4 && jisdata[i] >= '0' && jisdata[i] <= '9'; i++);
|
2019-12-17 06:31:52 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
digit_cnt = i - in_posn;
|
2019-12-17 06:31:52 +13:00
|
|
|
|
|
|
|
if (digit_cnt == 0) {
|
|
|
|
*p_end = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*p_end = i;
|
2021-06-20 00:11:23 +12:00
|
|
|
*p_cost = digit_cnt == 1
|
|
|
|
? 24 /* 4 * QR_MULT */ : digit_cnt == 2 ? 21 /* (7 / 2) * QR_MULT */ : 20 /* (10 / 3) * QR_MULT) */;
|
2019-12-17 06:31:52 +13:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
/* Whether in alpha or not. If in alpha, *p_end is set to position after alpha, and *p_cost is set to per-alpha cost.
|
|
|
|
* For GS1, *p_pcent set if 2nd char percent */
|
|
|
|
static int in_alpha(const unsigned int jisdata[], const int length, const int in_posn,
|
|
|
|
unsigned int *p_end, unsigned int *p_cost, unsigned int *p_pcent, unsigned int gs1) {
|
2019-12-17 06:31:52 +13:00
|
|
|
int two_alphas;
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (in_posn < (int) *p_end) {
|
2019-12-17 06:31:52 +13:00
|
|
|
if (gs1 && *p_pcent) {
|
|
|
|
/* Previous 2nd char was a percent, so allow for second half of doubled-up percent here */
|
2020-11-28 01:54:44 +13:00
|
|
|
two_alphas = in_posn < length - 1 && is_alpha(jisdata[in_posn + 1], gs1);
|
2019-12-17 06:31:52 +13:00
|
|
|
*p_cost = two_alphas ? 33 /* (11 / 2) * QR_MULT */ : 36 /* 6 * QR_MULT */;
|
|
|
|
*p_pcent = 0;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2019-12-17 06:31:52 +13:00
|
|
|
return 1;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
/* Attempt to calculate the average 'cost' of using alphanumeric mode in number of bits (times QR_MULT) */
|
2020-11-28 01:54:44 +13:00
|
|
|
if (!is_alpha(jisdata[in_posn], gs1)) {
|
2019-12-17 06:31:52 +13:00
|
|
|
*p_end = 0;
|
|
|
|
*p_pcent = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (gs1 && jisdata[in_posn] == '%') { /* Must double-up so counts as 2 chars */
|
|
|
|
*p_end = in_posn + 1;
|
2019-12-17 06:31:52 +13:00
|
|
|
*p_cost = 66; /* 11 * QR_MULT */
|
|
|
|
*p_pcent = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
two_alphas = in_posn < length - 1 && is_alpha(jisdata[in_posn + 1], gs1);
|
2019-12-17 06:31:52 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
*p_end = two_alphas ? in_posn + 2 : in_posn + 1;
|
2019-12-17 06:31:52 +13:00
|
|
|
*p_cost = two_alphas ? 33 /* (11 / 2) * QR_MULT */ : 36 /* 6 * QR_MULT */;
|
2020-11-28 01:54:44 +13:00
|
|
|
*p_pcent = two_alphas && gs1 && jisdata[in_posn + 1] == '%'; /* 2nd char is percent */
|
2019-12-17 06:31:52 +13:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Indexes into mode_types array (and state array) */
|
|
|
|
#define QR_N 0 /* Numeric */
|
|
|
|
#define QR_A 1 /* Alphanumeric */
|
|
|
|
#define QR_B 2 /* Byte */
|
|
|
|
#define QR_K 3 /* Kanji */
|
|
|
|
|
2020-10-01 00:19:12 +13:00
|
|
|
static const char mode_types[] = { 'N', 'A', 'B', 'K', '\0' }; /* Must be in same order as QR_N etc */
|
2019-12-17 06:31:52 +13:00
|
|
|
|
|
|
|
#define QR_NUM_MODES 4
|
|
|
|
|
|
|
|
/* Indexes into state array (0..3 head costs) */
|
|
|
|
#define QR_VER 4 /* Version */
|
2020-11-28 01:54:44 +13:00
|
|
|
#define QR_N_END 5 /* Numeric end index */
|
|
|
|
#define QR_N_COST 6 /* Numeric cost */
|
|
|
|
#define QR_A_END 7 /* Alpha end index */
|
|
|
|
#define QR_A_COST 8 /* Alpha cost */
|
|
|
|
#define QR_A_PCENT 9 /* Alpha 2nd char percent (GS1-specific) */
|
2019-12-17 06:31:52 +13:00
|
|
|
|
|
|
|
/* Costs set to this for invalid MICROQR modes for versions M1 and M2.
|
|
|
|
* 128 is the max number of data bits for M4-L (ISO/IEC 18004:2015 Table 7) */
|
|
|
|
#define QR_MICROQR_MAX 774 /* (128 + 1) * QR_MULT */
|
|
|
|
|
|
|
|
/* Initial mode costs */
|
2020-11-28 01:54:44 +13:00
|
|
|
static unsigned int *qr_head_costs(unsigned int state[10]) {
|
|
|
|
static const unsigned int head_costs[7][QR_NUM_MODES] = {
|
2019-12-17 06:31:52 +13:00
|
|
|
/* N A B K */
|
|
|
|
{ (10 + 4) * QR_MULT, (9 + 4) * QR_MULT, (8 + 4) * QR_MULT, (8 + 4) * QR_MULT, }, /* QR */
|
|
|
|
{ (12 + 4) * QR_MULT, (11 + 4) * QR_MULT, (16 + 4) * QR_MULT, (10 + 4) * QR_MULT, },
|
|
|
|
{ (14 + 4) * QR_MULT, (13 + 4) * QR_MULT, (16 + 4) * QR_MULT, (12 + 4) * QR_MULT, },
|
|
|
|
{ 3 * QR_MULT, QR_MICROQR_MAX, QR_MICROQR_MAX, QR_MICROQR_MAX, }, /* MICROQR */
|
|
|
|
{ (4 + 1) * QR_MULT, (3 + 1) * QR_MULT, QR_MICROQR_MAX, QR_MICROQR_MAX, },
|
|
|
|
{ (5 + 2) * QR_MULT, (4 + 2) * QR_MULT, (4 + 2) * QR_MULT, (3 + 2) * QR_MULT, },
|
|
|
|
{ (6 + 3) * QR_MULT, (5 + 3) * QR_MULT, (5 + 3) * QR_MULT, (4 + 3) * QR_MULT, }
|
|
|
|
};
|
|
|
|
int version;
|
|
|
|
|
|
|
|
/* Head costs kept in states 0..3 */
|
|
|
|
|
|
|
|
version = state[QR_VER];
|
|
|
|
|
|
|
|
if (version < RMQR_VERSION) { /* QRCODE */
|
|
|
|
if (version < 10) {
|
2020-11-28 01:54:44 +13:00
|
|
|
memcpy(state, head_costs[0], QR_NUM_MODES * sizeof(unsigned int));
|
2019-12-17 06:31:52 +13:00
|
|
|
} else if (version < 27) {
|
2020-11-28 01:54:44 +13:00
|
|
|
memcpy(state, head_costs[1], QR_NUM_MODES * sizeof(unsigned int));
|
2019-12-17 06:31:52 +13:00
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
memcpy(state, head_costs[2], QR_NUM_MODES * sizeof(unsigned int));
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2019-12-17 06:31:52 +13:00
|
|
|
} else if (version < MICROQR_VERSION) { /* RMQR */
|
|
|
|
version -= RMQR_VERSION;
|
|
|
|
state[QR_N] = (rmqr_numeric_cci[version] + 3) * QR_MULT;
|
|
|
|
state[QR_A] = (rmqr_alphanum_cci[version] + 3) * QR_MULT;
|
|
|
|
state[QR_B] = (rmqr_byte_cci[version] + 3) * QR_MULT;
|
|
|
|
state[QR_K] = (rmqr_kanji_cci[version] + 3) * QR_MULT;
|
|
|
|
} else { /* MICROQR */
|
2020-11-28 01:54:44 +13:00
|
|
|
memcpy(state, head_costs[3 + (version - MICROQR_VERSION)], QR_NUM_MODES * sizeof(unsigned int));
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
/* Calculate optimized encoding modes. Adapted from Project Nayuki */
|
|
|
|
static void qr_define_mode(char mode[], const unsigned int jisdata[], const int length, const int gs1,
|
|
|
|
const int version, const int debug_print) {
|
|
|
|
/*
|
|
|
|
* Copyright (c) Project Nayuki. (MIT License)
|
|
|
|
* https://www.nayuki.io/page/qr-code-generator-library
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
|
|
* the Software without restriction, including without limitation the rights to
|
|
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
|
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
|
|
* subject to the following conditions:
|
|
|
|
* - The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*/
|
|
|
|
unsigned int state[10] = {
|
|
|
|
0 /*N*/, 0 /*A*/, 0 /*B*/, 0 /*K*/, /* Head/switch costs */
|
|
|
|
(unsigned int) version,
|
|
|
|
0 /*numeric_end*/, 0 /*numeric_cost*/, 0 /*alpha_end*/, 0 /*alpha_cost*/, 0 /*alpha_pcent*/
|
|
|
|
};
|
|
|
|
int m1, m2;
|
2019-12-17 06:31:52 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
int i, j, k, cm_i;
|
|
|
|
unsigned int min_cost;
|
|
|
|
char cur_mode;
|
|
|
|
unsigned int prev_costs[QR_NUM_MODES];
|
|
|
|
unsigned int cur_costs[QR_NUM_MODES];
|
|
|
|
#ifndef _MSC_VER
|
|
|
|
char char_modes[length * QR_NUM_MODES];
|
|
|
|
#else
|
|
|
|
char *char_modes = (char *) _alloca(length * QR_NUM_MODES);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* char_modes[i * QR_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 * QR_NUM_MODES);
|
|
|
|
|
|
|
|
/* At the beginning of each iteration of the loop below, prev_costs[j] is the minimum number of 1/6 (1/QR_MULT)
|
|
|
|
* bits needed to encode the entire string prefix of length i, and end in mode_types[j] */
|
|
|
|
memcpy(prev_costs, qr_head_costs(state), QR_NUM_MODES * sizeof(unsigned int));
|
|
|
|
|
|
|
|
/* Calculate costs using dynamic programming */
|
|
|
|
for (i = 0, cm_i = 0; i < length; i++, cm_i += QR_NUM_MODES) {
|
|
|
|
memset(cur_costs, 0, QR_NUM_MODES * sizeof(unsigned int));
|
|
|
|
|
|
|
|
m1 = version == MICROQR_VERSION;
|
|
|
|
m2 = version == MICROQR_VERSION + 1;
|
|
|
|
|
|
|
|
if (jisdata[i] > 0xFF) {
|
|
|
|
cur_costs[QR_B] = prev_costs[QR_B] + ((m1 || m2) ? QR_MICROQR_MAX : 96); /* 16 * QR_MULT */
|
|
|
|
char_modes[cm_i + QR_B] = 'B';
|
|
|
|
cur_costs[QR_K] = prev_costs[QR_K] + ((m1 || m2) ? QR_MICROQR_MAX : 78); /* 13 * QR_MULT */
|
|
|
|
char_modes[cm_i + QR_K] = 'K';
|
|
|
|
} else {
|
|
|
|
if (in_numeric(jisdata, length, i, &state[QR_N_END], &state[QR_N_COST])) {
|
|
|
|
cur_costs[QR_N] = prev_costs[QR_N] + state[QR_N_COST];
|
|
|
|
char_modes[cm_i + QR_N] = 'N';
|
|
|
|
}
|
|
|
|
if (in_alpha(jisdata, length, i, &state[QR_A_END], &state[QR_A_COST], &state[QR_A_PCENT], gs1)) {
|
|
|
|
cur_costs[QR_A] = prev_costs[QR_A] + (m1 ? QR_MICROQR_MAX : state[QR_A_COST]);
|
|
|
|
char_modes[cm_i + QR_A] = 'A';
|
|
|
|
}
|
|
|
|
cur_costs[QR_B] = prev_costs[QR_B] + ((m1 || m2) ? QR_MICROQR_MAX : 48); /* 8 * QR_MULT */
|
|
|
|
char_modes[cm_i + QR_B] = 'B';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start new segment at the end to switch modes */
|
|
|
|
for (j = 0; j < QR_NUM_MODES; j++) { /* To mode */
|
|
|
|
for (k = 0; k < QR_NUM_MODES; k++) { /* From mode */
|
|
|
|
if (j != k && char_modes[cm_i + k]) {
|
|
|
|
unsigned int new_cost = cur_costs[k] + state[j]; /* Switch costs same as head costs */
|
|
|
|
if (!char_modes[cm_i + j] || new_cost < cur_costs[j]) {
|
|
|
|
cur_costs[j] = new_cost;
|
|
|
|
char_modes[cm_i + j] = mode_types[k];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-17 06:31:52 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
|
|
|
|
memcpy(prev_costs, cur_costs, QR_NUM_MODES * sizeof(unsigned int));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find optimal ending mode */
|
|
|
|
min_cost = prev_costs[0];
|
|
|
|
cur_mode = mode_types[0];
|
|
|
|
for (i = 1; i < QR_NUM_MODES; i++) {
|
|
|
|
if (prev_costs[i] < min_cost) {
|
|
|
|
min_cost = prev_costs[i];
|
|
|
|
cur_mode = mode_types[i];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get optimal mode for each code point by tracing backwards */
|
|
|
|
for (i = length - 1, cm_i = i * QR_NUM_MODES; i >= 0; i--, cm_i -= QR_NUM_MODES) {
|
|
|
|
j = strchr(mode_types, cur_mode) - mode_types;
|
|
|
|
cur_mode = char_modes[cm_i + j];
|
|
|
|
mode[i] = cur_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debug_print) {
|
|
|
|
printf(" Mode: %.*s\n", length, mode);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
/* Returns mode indicator based on version and mode */
|
|
|
|
static int mode_indicator(const int version, const int mode) {
|
|
|
|
static const int mode_indicators[6][QR_NUM_MODES] = {
|
|
|
|
/*N A B K */
|
|
|
|
{ 1, 2, 4, 8, }, /* QRCODE */
|
|
|
|
{ 1, 2, 3, 4, }, /* RMQR */
|
|
|
|
{ 0, 0, 0, 0, }, /* MICROQR */
|
|
|
|
{ 0, 1, 0, 0, },
|
|
|
|
{ 0, 1, 2, 3, },
|
|
|
|
{ 0, 1, 2, 3, },
|
2019-12-17 06:31:52 +13:00
|
|
|
};
|
2017-06-07 08:11:11 +12:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
int mode_index = strchr(mode_types, mode) - mode_types;
|
|
|
|
|
|
|
|
if (version < RMQR_VERSION) {
|
|
|
|
return mode_indicators[0][mode_index]; /* QRCODE */
|
|
|
|
}
|
|
|
|
if (version < MICROQR_VERSION) {
|
|
|
|
return mode_indicators[1][mode_index] /* RMQR */;
|
|
|
|
}
|
|
|
|
return mode_indicators[2 + version - MICROQR_VERSION][mode_index]; /* MICROQR */
|
2019-12-17 06:31:52 +13:00
|
|
|
}
|
2017-06-07 08:11:11 +12:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
/* Return mode indicator bits based on version */
|
|
|
|
static int mode_bits(const int version) {
|
|
|
|
if (version < RMQR_VERSION) {
|
|
|
|
return 4; /* QRCODE */
|
2017-06-07 08:11:11 +12:00
|
|
|
}
|
2019-12-17 06:31:52 +13:00
|
|
|
if (version < MICROQR_VERSION) {
|
|
|
|
return 3; /* RMQR */
|
|
|
|
}
|
|
|
|
return version - MICROQR_VERSION; /* MICROQR */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return character count indicator bits based on version and mode */
|
|
|
|
static int cci_bits(const int version, const int mode) {
|
2019-12-19 13:37:55 +13:00
|
|
|
static const int cci_bits[7][QR_NUM_MODES] = {
|
2019-12-17 06:31:52 +13:00
|
|
|
/* N A B K */
|
|
|
|
{ 10, 9, 8, 8, }, /* QRCODE */
|
|
|
|
{ 12, 11, 16, 10, },
|
|
|
|
{ 14, 13, 16, 12, },
|
|
|
|
{ 3, 0, 0, 0, }, /* MICROQR */
|
|
|
|
{ 4, 3, 0, 0, },
|
|
|
|
{ 5, 4, 4, 3, },
|
|
|
|
{ 6, 5, 5, 4, }
|
|
|
|
};
|
2020-11-28 01:54:44 +13:00
|
|
|
static const unsigned short int *rmqr_ccis[QR_NUM_MODES] = {
|
2019-12-17 06:31:52 +13:00
|
|
|
rmqr_numeric_cci, rmqr_alphanum_cci, rmqr_byte_cci, rmqr_kanji_cci,
|
|
|
|
};
|
|
|
|
int mode_index = strchr(mode_types, mode) - mode_types;
|
|
|
|
|
|
|
|
if (version < RMQR_VERSION) { /* QRCODE */
|
|
|
|
if (version < 10) {
|
|
|
|
return cci_bits[0][mode_index];
|
|
|
|
}
|
|
|
|
if (version < 27) {
|
|
|
|
return cci_bits[1][mode_index];
|
|
|
|
}
|
|
|
|
return cci_bits[2][mode_index];
|
|
|
|
}
|
|
|
|
if (version < MICROQR_VERSION) { /* RMQR */
|
|
|
|
return rmqr_ccis[mode_index][version - RMQR_VERSION];
|
|
|
|
}
|
|
|
|
return cci_bits[3 + (version - MICROQR_VERSION)][mode_index]; /* MICROQR */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns terminator bits based on version */
|
|
|
|
static int terminator_bits(const int version) {
|
|
|
|
if (version < RMQR_VERSION) {
|
|
|
|
return 4; /* QRCODE */
|
|
|
|
}
|
|
|
|
if (version < MICROQR_VERSION) {
|
|
|
|
return 3; /* RMQR */
|
|
|
|
}
|
|
|
|
return 3 + (version - MICROQR_VERSION) * 2; /* MICROQR (Note not actually using this at the moment) */
|
2016-02-17 23:52:48 +13:00
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Convert input data to a binary stream and add padding */
|
2020-11-28 01:54:44 +13:00
|
|
|
static void qr_binary(unsigned char datastream[], const int version, const int target_codewords, const char mode[],
|
|
|
|
const unsigned int jisdata[], const int length, const int gs1, const int eci, const int est_binlen,
|
|
|
|
const int debug_print) {
|
|
|
|
int position = 0;
|
|
|
|
int i, j, bp;
|
|
|
|
int termbits, padbits, modebits;
|
|
|
|
int current_bytes;
|
2016-02-21 00:29:19 +13:00
|
|
|
int toggle, percent;
|
2018-06-21 10:07:48 +12:00
|
|
|
int percent_count;
|
2016-02-17 23:52:48 +13:00
|
|
|
|
|
|
|
#ifndef _MSC_VER
|
2016-02-21 00:29:19 +13:00
|
|
|
char binary[est_binlen + 12];
|
2016-02-17 23:52:48 +13:00
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
char *binary = (char *) _alloca(est_binlen + 12);
|
2016-02-17 23:52:48 +13:00
|
|
|
#endif
|
2020-11-28 01:54:44 +13:00
|
|
|
*binary = '\0';
|
|
|
|
bp = 0;
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
if (gs1) { /* Not applicable to MICROQR */
|
2019-11-26 08:08:25 +13:00
|
|
|
if (version < RMQR_VERSION) {
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(5, 4, binary, bp); /* FNC1 */
|
2019-11-26 08:08:25 +13:00
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(5, 3, binary, bp);
|
2019-11-26 08:08:25 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2021-08-21 03:50:39 +12:00
|
|
|
if (eci != 0) { /* Not applicable to MICROQR */
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(7, 4, binary, bp); /* ECI (Table 4) */
|
2017-06-07 08:11:11 +12:00
|
|
|
if (eci <= 127) {
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(eci, 8, binary, bp); /* 000000 to 000127 */
|
2017-06-07 08:11:11 +12:00
|
|
|
} else if (eci <= 16383) {
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(0x8000 + eci, 16, binary, bp); /* 000128 to 016383 */
|
2017-06-07 08:11:11 +12:00
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(0xC00000 + eci, 24, binary, bp); /* 016384 to 999999 */
|
2017-06-07 08:11:11 +12:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
percent = 0;
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
modebits = mode_bits(version);
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
do {
|
2017-09-11 03:03:09 +12:00
|
|
|
char data_block = mode[position];
|
|
|
|
int short_data_block_length = 0;
|
2019-11-28 05:16:14 +13:00
|
|
|
int double_byte = 0;
|
2016-02-21 00:29:19 +13:00
|
|
|
do {
|
2019-11-28 05:16:14 +13:00
|
|
|
if (data_block == 'B' && jisdata[position + short_data_block_length] > 0xFF) {
|
|
|
|
double_byte++;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
short_data_block_length++;
|
2016-03-03 10:12:38 +13:00
|
|
|
} while (((short_data_block_length + position) < length)
|
2016-02-21 00:29:19 +13:00
|
|
|
&& (mode[position + short_data_block_length] == data_block));
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
/* Mode indicator */
|
|
|
|
if (modebits) {
|
|
|
|
bp = bin_append_posn(mode_indicator(version, data_block), modebits, binary, bp);
|
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
switch (data_block) {
|
|
|
|
case 'K':
|
|
|
|
/* Kanji mode */
|
|
|
|
|
|
|
|
/* Character count indicator */
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(short_data_block_length, cci_bits(version, data_block), binary, bp);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
printf("Kanji block (length %d)\n\t", short_data_block_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Character representation */
|
|
|
|
for (i = 0; i < short_data_block_length; i++) {
|
2019-11-28 05:16:14 +13:00
|
|
|
unsigned int jis = jisdata[position + i];
|
2017-04-22 06:54:35 +12:00
|
|
|
int prod;
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2017-04-22 06:54:35 +12:00
|
|
|
if (jis >= 0x8140 && jis <= 0x9ffc)
|
|
|
|
jis -= 0x8140;
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2017-04-22 06:54:35 +12:00
|
|
|
else if (jis >= 0xe040 && jis <= 0xebbf)
|
2016-02-21 00:29:19 +13:00
|
|
|
jis -= 0xc140;
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2017-04-22 06:54:35 +12:00
|
|
|
prod = ((jis >> 8) * 0xc0) + (jis & 0xff);
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(prod, 13, binary, bp);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2019-12-17 06:31:52 +13:00
|
|
|
printf("0x%04X ", prod);
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
printf("\n");
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
|
|
|
case 'B':
|
|
|
|
/* Byte mode */
|
|
|
|
|
|
|
|
/* Character count indicator */
|
2021-06-20 00:11:23 +12:00
|
|
|
bp = bin_append_posn(short_data_block_length + double_byte, cci_bits(version, data_block), binary,
|
|
|
|
bp);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2019-11-28 05:16:14 +13:00
|
|
|
printf("Byte block (length %d)\n\t", short_data_block_length + double_byte);
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Character representation */
|
|
|
|
for (i = 0; i < short_data_block_length; i++) {
|
2019-11-28 05:16:14 +13:00
|
|
|
unsigned int byte = jisdata[position + i];
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (gs1 && (byte == '[')) {
|
|
|
|
byte = 0x1d; /* FNC1 */
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(byte, byte > 0xFF ? 16 : 8, binary, bp);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2021-07-06 23:13:34 +12:00
|
|
|
printf("0x%02X(%d) ", byte, (int) byte);
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case 'A':
|
|
|
|
/* Alphanumeric mode */
|
|
|
|
|
2018-06-21 10:07:48 +12:00
|
|
|
percent_count = 0;
|
2019-12-17 06:31:52 +13:00
|
|
|
if (gs1) {
|
|
|
|
for (i = 0; i < short_data_block_length; i++) {
|
|
|
|
if (jisdata[position + i] == '%') {
|
|
|
|
percent_count++;
|
|
|
|
}
|
2018-06-21 10:07:48 +12:00
|
|
|
}
|
|
|
|
}
|
2019-11-28 05:16:14 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Character count indicator */
|
2021-06-20 00:11:23 +12:00
|
|
|
bp = bin_append_posn(short_data_block_length + percent_count, cci_bits(version, data_block), binary,
|
|
|
|
bp);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2018-06-21 10:07:48 +12:00
|
|
|
printf("Alpha block (length %d)\n\t", short_data_block_length + percent_count);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Character representation */
|
|
|
|
i = 0;
|
|
|
|
while (i < short_data_block_length) {
|
|
|
|
int count;
|
|
|
|
int first = 0, second = 0, prod;
|
|
|
|
|
|
|
|
if (percent == 0) {
|
|
|
|
if (gs1 && (jisdata[position + i] == '%')) {
|
2020-11-28 01:54:44 +13:00
|
|
|
first = QR_PERCENT;
|
|
|
|
second = QR_PERCENT;
|
2016-02-21 00:29:19 +13:00
|
|
|
count = 2;
|
|
|
|
prod = (first * 45) + second;
|
|
|
|
i++;
|
2016-02-18 09:31:50 +13:00
|
|
|
} else {
|
2016-02-21 00:29:19 +13:00
|
|
|
if (gs1 && (jisdata[position + i] == '[')) {
|
2020-11-28 01:54:44 +13:00
|
|
|
first = QR_PERCENT; /* FNC1 */
|
2016-02-18 09:31:50 +13:00
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
first = qr_alphanumeric[jisdata[position + i] - 32];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
count = 1;
|
|
|
|
i++;
|
|
|
|
prod = first;
|
|
|
|
|
|
|
|
if (i < short_data_block_length && mode[position + i] == 'A') {
|
|
|
|
if (gs1 && (jisdata[position + i] == '%')) {
|
2020-11-28 01:54:44 +13:00
|
|
|
second = QR_PERCENT;
|
2016-02-21 00:29:19 +13:00
|
|
|
count = 2;
|
|
|
|
prod = (first * 45) + second;
|
|
|
|
percent = 1;
|
|
|
|
} else {
|
|
|
|
if (gs1 && (jisdata[position + i] == '[')) {
|
2020-11-28 01:54:44 +13:00
|
|
|
second = QR_PERCENT; /* FNC1 */
|
2016-02-21 00:29:19 +13:00
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
second = qr_alphanumeric[jisdata[position + i] - 32];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
count = 2;
|
|
|
|
i++;
|
|
|
|
prod = (first * 45) + second;
|
|
|
|
}
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
first = QR_PERCENT;
|
2016-02-21 00:29:19 +13:00
|
|
|
count = 1;
|
|
|
|
i++;
|
|
|
|
prod = first;
|
|
|
|
percent = 0;
|
|
|
|
|
|
|
|
if (i < short_data_block_length && mode[position + i] == 'A') {
|
|
|
|
if (gs1 && (jisdata[position + i] == '%')) {
|
2020-11-28 01:54:44 +13:00
|
|
|
second = QR_PERCENT;
|
2016-02-21 00:29:19 +13:00
|
|
|
count = 2;
|
|
|
|
prod = (first * 45) + second;
|
|
|
|
percent = 1;
|
2016-02-18 09:31:50 +13:00
|
|
|
} else {
|
2016-02-21 00:29:19 +13:00
|
|
|
if (gs1 && (jisdata[position + i] == '[')) {
|
2020-11-28 01:54:44 +13:00
|
|
|
second = QR_PERCENT; /* FNC1 */
|
2016-02-21 00:29:19 +13:00
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
second = qr_alphanumeric[jisdata[position + i] - 32];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
count = 2;
|
|
|
|
i++;
|
|
|
|
prod = (first * 45) + second;
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(prod, 1 + (5 * count), binary, bp);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2019-12-17 06:31:52 +13:00
|
|
|
printf("0x%X ", prod);
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
2019-12-17 06:31:52 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
printf("\n");
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
|
|
|
case 'N':
|
|
|
|
/* Numeric mode */
|
|
|
|
|
|
|
|
/* Character count indicator */
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(short_data_block_length, cci_bits(version, data_block), binary, bp);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
printf("Number block (length %d)\n\t", short_data_block_length);
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Character representation */
|
|
|
|
i = 0;
|
|
|
|
while (i < short_data_block_length) {
|
|
|
|
int count;
|
2017-09-11 03:03:09 +12:00
|
|
|
int first = 0, prod;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
first = posn(NEON, (char) jisdata[position + i]);
|
|
|
|
count = 1;
|
|
|
|
prod = first;
|
|
|
|
|
|
|
|
if (i + 1 < short_data_block_length && mode[position + i + 1] == 'N') {
|
2017-09-11 03:03:09 +12:00
|
|
|
int second = posn(NEON, (char) jisdata[position + i + 1]);
|
2016-02-21 00:29:19 +13:00
|
|
|
count = 2;
|
|
|
|
prod = (prod * 10) + second;
|
|
|
|
|
|
|
|
if (i + 2 < short_data_block_length && mode[position + i + 2] == 'N') {
|
2017-09-11 03:03:09 +12:00
|
|
|
int third = posn(NEON, (char) jisdata[position + i + 2]);
|
2016-02-21 00:29:19 +13:00
|
|
|
count = 3;
|
|
|
|
prod = (prod * 10) + third;
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(prod, 1 + (3 * count), binary, bp);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2019-12-17 06:31:52 +13:00
|
|
|
printf("0x%X(%d) ", prod, prod);
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
i += count;
|
|
|
|
};
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
printf("\n");
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
break;
|
2016-02-18 09:31:50 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
position += short_data_block_length;
|
|
|
|
} while (position < length);
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
if (version >= MICROQR_VERSION && version < MICROQR_VERSION + 4) {
|
|
|
|
/* MICROQR does its own terminating/padding */
|
2020-11-28 01:54:44 +13:00
|
|
|
binary[bp] = '\0';
|
2021-06-20 00:11:23 +12:00
|
|
|
ustrcpy(datastream, binary);
|
2019-12-17 06:31:52 +13:00
|
|
|
return;
|
2019-11-26 08:08:25 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
/* Terminator */
|
2020-11-28 01:54:44 +13:00
|
|
|
termbits = 8 - bp % 8;
|
2019-12-17 06:31:52 +13:00
|
|
|
if (termbits == 8) {
|
|
|
|
termbits = 0;
|
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
current_bytes = (bp + termbits) / 8;
|
2019-12-17 06:31:52 +13:00
|
|
|
if (termbits || current_bytes < target_codewords) {
|
|
|
|
int max_termbits = terminator_bits(version);
|
|
|
|
termbits = termbits < max_termbits && current_bytes == target_codewords ? termbits : max_termbits;
|
2020-11-28 01:54:44 +13:00
|
|
|
bp = bin_append_posn(0, termbits, binary, bp);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Padding bits */
|
2020-11-28 01:54:44 +13:00
|
|
|
padbits = 8 - bp % 8;
|
2019-12-17 06:31:52 +13:00
|
|
|
if (padbits == 8) {
|
|
|
|
padbits = 0;
|
|
|
|
}
|
|
|
|
if (padbits) {
|
2020-11-28 01:54:44 +13:00
|
|
|
current_bytes = (bp + padbits) / 8;
|
|
|
|
(void) bin_append_posn(0, padbits, binary, bp); /* Last use so not setting bp */
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2021-08-21 03:50:39 +12:00
|
|
|
if (debug_print) printf("Terminated binary (%d): %.*s (padbits %d)\n", bp, bp, binary, padbits);
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Put data into 8-bit codewords */
|
|
|
|
for (i = 0; i < current_bytes; i++) {
|
2016-02-24 20:42:15 +13:00
|
|
|
int p;
|
2020-11-28 01:54:44 +13:00
|
|
|
j = i * 8;
|
2016-02-21 00:29:19 +13:00
|
|
|
datastream[i] = 0x00;
|
2016-02-24 20:42:15 +13:00
|
|
|
for (p = 0; p < 8; p++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
if (binary[j + p] == '1') {
|
|
|
|
datastream[i] |= (0x80 >> p);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add pad codewords */
|
|
|
|
toggle = 0;
|
2019-11-26 08:08:25 +13:00
|
|
|
for (i = current_bytes; i < target_codewords; i++) {
|
2016-02-21 00:29:19 +13:00
|
|
|
if (toggle == 0) {
|
|
|
|
datastream[i] = 0xec;
|
|
|
|
toggle = 1;
|
|
|
|
} else {
|
|
|
|
datastream[i] = 0x11;
|
|
|
|
toggle = 0;
|
|
|
|
}
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
printf("Resulting codewords:\n\t");
|
2019-11-26 08:08:25 +13:00
|
|
|
for (i = 0; i < target_codewords; i++) {
|
2019-12-17 06:31:52 +13:00
|
|
|
printf("0x%02X ", datastream[i]);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Split data into blocks, add error correction and then interleave the blocks and error correction data */
|
2021-06-20 00:11:23 +12:00
|
|
|
static void add_ecc(unsigned char fullstream[], const unsigned char datastream[], const int version,
|
|
|
|
const int data_cw, const int blocks, const int debug_print) {
|
2019-11-26 08:08:25 +13:00
|
|
|
int ecc_cw;
|
2019-12-06 07:19:37 +13:00
|
|
|
int short_data_block_length;
|
|
|
|
int qty_long_blocks;
|
|
|
|
int qty_short_blocks;
|
|
|
|
int ecc_block_length;
|
2020-11-28 01:54:44 +13:00
|
|
|
int i, j, length_this_block, in_posn;
|
|
|
|
rs_t rs;
|
2019-12-06 07:19:37 +13:00
|
|
|
#ifdef _MSC_VER
|
2021-06-20 00:11:23 +12:00
|
|
|
unsigned char *data_block;
|
|
|
|
unsigned char *ecc_block;
|
|
|
|
unsigned char *interleaved_data;
|
|
|
|
unsigned char *interleaved_ecc;
|
2019-12-06 07:19:37 +13:00
|
|
|
#endif
|
|
|
|
|
2019-11-26 08:08:25 +13:00
|
|
|
if (version < RMQR_VERSION) {
|
|
|
|
ecc_cw = qr_total_codewords[version - 1] - data_cw;
|
|
|
|
} else {
|
|
|
|
ecc_cw = rmqr_total_codewords[version - RMQR_VERSION] - data_cw;
|
|
|
|
}
|
|
|
|
|
2019-12-06 07:36:00 +13:00
|
|
|
short_data_block_length = data_cw / blocks;
|
|
|
|
qty_long_blocks = data_cw % blocks;
|
|
|
|
qty_short_blocks = blocks - qty_long_blocks;
|
|
|
|
ecc_block_length = ecc_cw / blocks;
|
|
|
|
|
2020-07-19 21:31:12 +12:00
|
|
|
/* Suppress some clang-tidy clang-analyzer-core.UndefinedBinaryOperatorResult/uninitialized.Assign warnings */
|
|
|
|
assert(short_data_block_length >= 0);
|
|
|
|
assert(ecc_block_length * blocks == ecc_cw);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2016-02-17 23:52:48 +13:00
|
|
|
#ifndef _MSC_VER
|
2020-04-29 08:10:54 +12:00
|
|
|
unsigned char data_block[short_data_block_length + 1];
|
|
|
|
unsigned char ecc_block[ecc_block_length];
|
|
|
|
unsigned char interleaved_data[data_cw];
|
|
|
|
unsigned char interleaved_ecc[ecc_cw];
|
2016-02-17 23:52:48 +13:00
|
|
|
#else
|
2020-04-29 08:10:54 +12:00
|
|
|
data_block = (unsigned char *) _alloca(short_data_block_length + 1);
|
|
|
|
ecc_block = (unsigned char *) _alloca(ecc_block_length);
|
|
|
|
interleaved_data = (unsigned char *) _alloca(data_cw);
|
|
|
|
interleaved_ecc = (unsigned char *) _alloca(ecc_cw);
|
2016-02-17 23:52:48 +13:00
|
|
|
#endif
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
rs_init_gf(&rs, 0x11d);
|
|
|
|
rs_init_code(&rs, ecc_block_length, 0);
|
|
|
|
|
|
|
|
in_posn = 0;
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
for (i = 0; i < blocks; i++) {
|
|
|
|
if (i < qty_short_blocks) {
|
|
|
|
length_this_block = short_data_block_length;
|
|
|
|
} else {
|
|
|
|
length_this_block = short_data_block_length + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < ecc_block_length; j++) {
|
|
|
|
ecc_block[j] = 0;
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
for (j = 0; j < length_this_block; j++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
data_block[j] = datastream[in_posn + j];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
rs_encode(&rs, length_this_block, data_block, ecc_block);
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
printf("Block %d: ", i + 1);
|
|
|
|
for (j = 0; j < length_this_block; j++) {
|
|
|
|
printf("%2X ", data_block[j]);
|
|
|
|
}
|
|
|
|
if (i < qty_short_blocks) {
|
|
|
|
printf(" ");
|
|
|
|
}
|
|
|
|
printf(" // ");
|
|
|
|
for (j = 0; j < ecc_block_length; j++) {
|
|
|
|
printf("%2X ", ecc_block[ecc_block_length - j - 1]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
for (j = 0; j < short_data_block_length; j++) {
|
2019-11-28 05:16:14 +13:00
|
|
|
interleaved_data[(j * blocks) + i] = data_block[j];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (i >= qty_short_blocks) {
|
2021-06-20 00:11:23 +12:00
|
|
|
interleaved_data[(short_data_block_length * blocks) + (i - qty_short_blocks)]
|
|
|
|
= data_block[short_data_block_length];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
for (j = 0; j < ecc_block_length; j++) {
|
2019-11-28 05:16:14 +13:00
|
|
|
interleaved_ecc[(j * blocks) + i] = ecc_block[ecc_block_length - j - 1];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
in_posn += length_this_block;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
for (j = 0; j < data_cw; j++) {
|
2021-06-20 00:11:23 +12:00
|
|
|
// NOLINTNEXTLINE suppress clang-tidy warning: interleaved_data[data_cw] fully set
|
|
|
|
fullstream[j] = interleaved_data[j];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
for (j = 0; j < ecc_cw; j++) {
|
2021-06-20 00:11:23 +12:00
|
|
|
// NOLINTNEXTLINE suppress clang-tidy warning: interleaved_ecc[ecc_cw] fully set
|
|
|
|
fullstream[j + data_cw] = interleaved_ecc[j];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
printf("\nData Stream: \n");
|
|
|
|
for (j = 0; j < (data_cw + ecc_cw); j++) {
|
|
|
|
printf("%2X ", fullstream[j]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static void place_finder(unsigned char grid[], const int size, const int x, const int y) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int xp, yp;
|
2017-08-07 19:37:02 +12:00
|
|
|
char finder[] = {0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F};
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2016-02-21 00:29:19 +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) {
|
2016-02-21 00:29:19 +13:00
|
|
|
grid[((yp + y) * size) + (xp + x)] = 0x11;
|
|
|
|
} else {
|
|
|
|
grid[((yp + y) * size) + (xp + x)] = 0x10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static void place_align(unsigned char grid[], const int size, int x, int y) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int xp, yp;
|
2017-08-07 19:37:02 +12:00
|
|
|
char alignment[] = {0x1F, 0x11, 0x15, 0x11, 0x1F};
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
x -= 2;
|
|
|
|
y -= 2; /* Input values represent centre of pattern */
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
for (xp = 0; xp < 5; xp++) {
|
|
|
|
for (yp = 0; yp < 5; yp++) {
|
2017-08-07 19:37:02 +12:00
|
|
|
if (alignment[yp] & 0x10 >> xp) {
|
2016-02-21 00:29:19 +13:00
|
|
|
grid[((yp + y) * size) + (xp + x)] = 0x11;
|
|
|
|
} else {
|
|
|
|
grid[((yp + y) * size) + (xp + x)] = 0x10;
|
|
|
|
}
|
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static void setup_grid(unsigned char *grid, const int size, const int version) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int i, toggle = 1;
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Add timing patterns */
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
if (toggle == 1) {
|
|
|
|
grid[(6 * size) + i] = 0x21;
|
|
|
|
grid[(i * size) + 6] = 0x21;
|
|
|
|
toggle = 0;
|
|
|
|
} else {
|
|
|
|
grid[(6 * size) + i] = 0x20;
|
|
|
|
grid[(i * size) + 6] = 0x20;
|
|
|
|
toggle = 1;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Add finder patterns */
|
|
|
|
place_finder(grid, size, 0, 0);
|
|
|
|
place_finder(grid, size, 0, size - 7);
|
|
|
|
place_finder(grid, size, size - 7, 0);
|
|
|
|
|
|
|
|
/* Add separators */
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
|
|
grid[(7 * size) + i] = 0x10;
|
|
|
|
grid[(i * size) + 7] = 0x10;
|
|
|
|
grid[(7 * size) + (size - 1 - i)] = 0x10;
|
|
|
|
grid[(i * size) + (size - 8)] = 0x10;
|
|
|
|
grid[((size - 8) * size) + i] = 0x10;
|
|
|
|
grid[((size - 1 - i) * size) + 7] = 0x10;
|
|
|
|
}
|
|
|
|
grid[(7 * size) + 7] = 0x10;
|
|
|
|
grid[(7 * size) + (size - 8)] = 0x10;
|
|
|
|
grid[((size - 8) * size) + 7] = 0x10;
|
|
|
|
|
|
|
|
/* Add alignment patterns */
|
|
|
|
if (version != 1) {
|
|
|
|
/* Version 1 does not have alignment patterns */
|
|
|
|
|
2017-09-11 03:03:09 +12:00
|
|
|
int loopsize = qr_align_loopsize[version - 1];
|
|
|
|
int x, y;
|
2016-02-21 00:29:19 +13:00
|
|
|
for (x = 0; x < loopsize; x++) {
|
|
|
|
for (y = 0; y < loopsize; y++) {
|
2017-09-11 03:03:09 +12:00
|
|
|
int xcoord = qr_table_e1[((version - 2) * 7) + x];
|
|
|
|
int ycoord = qr_table_e1[((version - 2) * 7) + y];
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
if (!(grid[(ycoord * size) + xcoord] & 0x10)) {
|
|
|
|
place_align(grid, size, xcoord, ycoord);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Reserve space for format information */
|
|
|
|
for (i = 0; i < 8; i++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + i] |= 0x20;
|
|
|
|
grid[(i * size) + 8] |= 0x20;
|
2016-02-21 00:29:19 +13:00
|
|
|
grid[(8 * size) + (size - 1 - i)] = 0x20;
|
|
|
|
grid[((size - 1 - i) * size) + 8] = 0x20;
|
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + 8] |= 0x20;
|
2016-02-21 00:29:19 +13:00
|
|
|
grid[((size - 1 - 7) * size) + 8] = 0x21; /* Dark Module from Figure 25 */
|
|
|
|
|
|
|
|
/* Reserve space for version information */
|
|
|
|
if (version >= 7) {
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
|
|
grid[((size - 9) * size) + i] = 0x20;
|
|
|
|
grid[((size - 10) * size) + i] = 0x20;
|
|
|
|
grid[((size - 11) * size) + i] = 0x20;
|
|
|
|
grid[(i * size) + (size - 9)] = 0x20;
|
|
|
|
grid[(i * size) + (size - 10)] = 0x20;
|
|
|
|
grid[(i * size) + (size - 11)] = 0x20;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2021-06-20 00:11:23 +12:00
|
|
|
static int cwbit(const unsigned char *fullstream, const int i) {
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (fullstream[(i >> 3)] & (0x80 >> (i & 0x07))) {
|
|
|
|
return 1;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
return 0;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static void populate_grid(unsigned char *grid, const int h_size, const int v_size, const unsigned char *fullstream,
|
|
|
|
const int cw) {
|
2021-08-10 23:04:25 +12:00
|
|
|
const int not_rmqr = v_size == h_size;
|
|
|
|
const int x_start = h_size - (not_rmqr ? 2 : 3); /* For rMQR allow for righthand vertical timing pattern */
|
2016-02-21 00:29:19 +13:00
|
|
|
int direction = 1; /* up */
|
|
|
|
int row = 0; /* right hand side */
|
|
|
|
|
2017-09-11 03:03:09 +12:00
|
|
|
int i, n, y;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
n = cw * 8;
|
2019-11-26 08:08:25 +13:00
|
|
|
y = v_size - 1;
|
2016-02-21 00:29:19 +13:00
|
|
|
i = 0;
|
2020-04-29 08:10:54 +12:00
|
|
|
while (i < n) {
|
2021-08-10 23:04:25 +12:00
|
|
|
int x = x_start - (row * 2);
|
2020-11-28 01:54:44 +13:00
|
|
|
int r = y * h_size;
|
2019-11-28 05:16:14 +13:00
|
|
|
|
2021-08-10 23:04:25 +12:00
|
|
|
if ((x < 6) && (not_rmqr))
|
2016-02-21 00:29:19 +13:00
|
|
|
x--; /* skip over vertical timing pattern */
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (!(grid[r + (x + 1)] & 0xf0)) {
|
|
|
|
grid[r + (x + 1)] = cwbit(fullstream, i);
|
2016-02-21 00:29:19 +13:00
|
|
|
i++;
|
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (i < n) {
|
2020-11-28 01:54:44 +13:00
|
|
|
if (!(grid[r + x] & 0xf0)) {
|
|
|
|
grid[r + x] = cwbit(fullstream, i);
|
2016-02-21 00:29:19 +13:00
|
|
|
i++;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (direction) {
|
|
|
|
y--;
|
2020-11-28 01:54:44 +13:00
|
|
|
if (y == -1) {
|
|
|
|
/* reached the top */
|
|
|
|
row++;
|
|
|
|
y = 0;
|
|
|
|
direction = 0;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
} else {
|
|
|
|
y++;
|
2020-11-28 01:54:44 +13:00
|
|
|
if (y == v_size) {
|
|
|
|
/* reached the bottom */
|
|
|
|
row++;
|
|
|
|
y = v_size - 1;
|
|
|
|
direction = 1;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-04-29 08:10:54 +12:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ZINTLOG
|
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
static int append_log(char log) {
|
2016-02-21 00:29:19 +13:00
|
|
|
FILE *file;
|
|
|
|
|
|
|
|
file = fopen("zintlog.txt", "a+");
|
|
|
|
fprintf(file, "%c", log);
|
|
|
|
fclose(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
static int write_log(char log[]) {
|
2016-02-21 00:29:19 +13:00
|
|
|
FILE *file;
|
|
|
|
|
|
|
|
file = fopen("zintlog.txt", "a+");
|
|
|
|
fprintf(file, log); /*writes*/
|
|
|
|
fprintf(file, "\r\n"); /*writes*/
|
|
|
|
fclose(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static int evaluate(unsigned char *local, const int size) {
|
|
|
|
static const unsigned char h1011101[7] = { 1, 0, 1, 1, 1, 0, 1 };
|
|
|
|
|
|
|
|
int x, y, r, k, block;
|
2016-02-21 00:29:19 +13:00
|
|
|
int result = 0;
|
|
|
|
char state;
|
|
|
|
int dark_mods;
|
2020-11-28 01:54:44 +13:00
|
|
|
double percentage;
|
2016-02-21 00:29:19 +13:00
|
|
|
int a, b, afterCount, beforeCount;
|
|
|
|
#ifdef ZINTLOG
|
|
|
|
int result_b = 0;
|
|
|
|
char str[15];
|
|
|
|
#endif
|
|
|
|
|
2020-07-19 21:31:12 +12:00
|
|
|
/* Suppresses clang-tidy clang-analyzer-core.UndefinedBinaryOperatorResult warnings */
|
|
|
|
assert(size > 0);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
#ifdef ZINTLOG
|
|
|
|
//bitmask output
|
|
|
|
for (y = 0; y < size; y++) {
|
|
|
|
strcpy(str, "");
|
|
|
|
for (x = 0; x < size; x++) {
|
|
|
|
state = local[(y * size) + x];
|
|
|
|
append_log(state);
|
|
|
|
}
|
|
|
|
write_log("");
|
|
|
|
}
|
|
|
|
write_log("");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Test 1: Adjacent modules in row/column in same colour */
|
|
|
|
/* Vertical */
|
|
|
|
for (x = 0; x < size; x++) {
|
|
|
|
block = 0;
|
2020-11-28 01:54:44 +13:00
|
|
|
state = 0;
|
2016-02-21 00:29:19 +13:00
|
|
|
for (y = 0; y < size; y++) {
|
|
|
|
if (local[(y * size) + x] == state) {
|
|
|
|
block++;
|
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
if (block >= 5) {
|
|
|
|
result += block - 2;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
block = 1;
|
2016-02-21 00:29:19 +13:00
|
|
|
state = local[(y * size) + x];
|
|
|
|
}
|
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
if (block >= 5) {
|
|
|
|
result += block - 2;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Horizontal */
|
2020-12-11 08:48:52 +13:00
|
|
|
dark_mods = 0; /* Count dark mods simultaneously (see Test 4 below) */
|
2016-02-21 00:29:19 +13:00
|
|
|
for (y = 0; y < size; y++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
r = y * size;
|
2016-02-21 00:29:19 +13:00
|
|
|
block = 0;
|
2020-11-28 01:54:44 +13:00
|
|
|
state = 0;
|
2016-02-21 00:29:19 +13:00
|
|
|
for (x = 0; x < size; x++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
if (local[r + x] == state) {
|
2016-02-21 00:29:19 +13:00
|
|
|
block++;
|
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
if (block >= 5) {
|
|
|
|
result += block - 2;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
block = 1;
|
|
|
|
state = local[r + x];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-12-11 08:48:52 +13:00
|
|
|
if (state) {
|
|
|
|
dark_mods++;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
if (block >= 5) {
|
|
|
|
result += block - 2;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ZINTLOG
|
|
|
|
/* output Test 1 */
|
|
|
|
sprintf(str, "%d", result);
|
|
|
|
result_b = result;
|
|
|
|
write_log(str);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Test 2: Block of modules in same color */
|
|
|
|
for (x = 0; x < size - 1; x++) {
|
|
|
|
for (y = 0; y < size - 1; y++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
k = local[(y * size) + x];
|
|
|
|
if (((k == local[((y + 1) * size) + x]) &&
|
|
|
|
(k == local[(y * size) + (x + 1)])) &&
|
|
|
|
(k == local[((y + 1) * size) + (x + 1)])) {
|
2016-02-21 00:29:19 +13:00
|
|
|
result += 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ZINTLOG
|
|
|
|
/* output Test 2 */
|
|
|
|
sprintf(str, "%d", result - result_b);
|
|
|
|
result_b = result;
|
|
|
|
write_log(str);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Test 3: 1:1:3:1:1 ratio pattern in row/column */
|
|
|
|
/* Vertical */
|
|
|
|
for (x = 0; x < size; x++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
for (y = 0; y <= (size - 7); y++) {
|
|
|
|
if (local[y * size + x] && !local[(y + 1) * size + x] && local[(y + 2) * size + x] &&
|
|
|
|
local[(y + 3) * size + x] && local[(y + 4) * size + x] &&
|
|
|
|
!local[(y + 5) * size + x] && local[(y + 6) * size + x]) {
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Pattern found, check before and after */
|
|
|
|
beforeCount = 0;
|
2020-11-28 01:54:44 +13:00
|
|
|
for (b = (y - 1); b >= (y - 4); b--) {
|
|
|
|
if (b < 0) { /* Count < edge as whitespace */
|
|
|
|
beforeCount = 4;
|
|
|
|
break;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
if (local[(b * size) + x]) {
|
|
|
|
break;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
beforeCount++;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
if (beforeCount == 4) {
|
|
|
|
/* Pattern is preceded by light area 4 modules wide */
|
2016-02-21 00:29:19 +13:00
|
|
|
result += 40;
|
2020-11-28 01:54:44 +13:00
|
|
|
} else {
|
|
|
|
afterCount = 0;
|
|
|
|
for (a = (y + 7); a <= (y + 10); a++) {
|
|
|
|
if (a >= size) { /* Count > edge as whitespace */
|
|
|
|
afterCount = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (local[(a * size) + x]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
afterCount++;
|
|
|
|
}
|
|
|
|
if (afterCount == 4) {
|
|
|
|
/* Pattern is followed by light area 4 modules wide */
|
|
|
|
result += 40;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
y += 3; /* Skip to next possible match */
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Horizontal */
|
|
|
|
for (y = 0; y < size; y++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
r = y * size;
|
|
|
|
for (x = 0; x <= (size - 7); x++) {
|
|
|
|
if (memcmp(local + r + x, h1011101, 7) == 0) {
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Pattern found, check before and after */
|
|
|
|
beforeCount = 0;
|
2020-11-28 01:54:44 +13:00
|
|
|
for (b = (x - 1); b >= (x - 4); b--) {
|
|
|
|
if (b < 0) { /* Count < edge as whitespace */
|
|
|
|
beforeCount = 4;
|
|
|
|
break;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
if (local[r + b]) {
|
|
|
|
break;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
beforeCount++;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (beforeCount == 4) {
|
|
|
|
/* Pattern is preceded by light area 4 modules wide */
|
2016-02-21 00:29:19 +13:00
|
|
|
result += 40;
|
2020-11-28 01:54:44 +13:00
|
|
|
} else {
|
|
|
|
afterCount = 0;
|
|
|
|
for (a = (x + 7); a <= (x + 10); a++) {
|
|
|
|
if (a >= size) { /* Count > edge as whitespace */
|
|
|
|
afterCount = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (local[r + a]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
afterCount++;
|
|
|
|
}
|
|
|
|
if (afterCount == 4) {
|
|
|
|
/* Pattern is followed by light area 4 modules wide */
|
|
|
|
result += 40;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
x += 3; /* Skip to next possible match */
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ZINTLOG
|
|
|
|
/* output Test 3 */
|
|
|
|
sprintf(str, "%d", result - result_b);
|
|
|
|
result_b = result;
|
|
|
|
write_log(str);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Test 4: Proportion of dark modules in entire symbol */
|
2020-11-28 01:54:44 +13:00
|
|
|
percentage = (100.0 * dark_mods) / (size * size);
|
2020-12-11 08:48:52 +13:00
|
|
|
k = (int) (fabs(percentage - 50.0) / 5.0);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
result += 10 * k;
|
|
|
|
|
|
|
|
#ifdef ZINTLOG
|
|
|
|
/* output Test 4+summary */
|
|
|
|
sprintf(str, "%d", result - result_b);
|
|
|
|
write_log(str);
|
|
|
|
write_log("==========");
|
|
|
|
sprintf(str, "%d", result);
|
|
|
|
write_log(str);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
/* Add format information to grid */
|
|
|
|
static void add_format_info(unsigned char *grid, const int size, const int ecc_level, const int pattern) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int format = pattern;
|
|
|
|
unsigned int seq;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
switch (ecc_level) {
|
2020-11-28 01:54:44 +13:00
|
|
|
case LEVEL_L: format |= 0x08;
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
2020-11-28 01:54:44 +13:00
|
|
|
case LEVEL_Q: format |= 0x18;
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
2020-11-28 01:54:44 +13:00
|
|
|
case LEVEL_H: format |= 0x10;
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
seq = qr_annex_c[format];
|
|
|
|
|
|
|
|
for (i = 0; i < 6; i++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(i * size) + 8] |= (seq >> i) & 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + (size - i - 1)] |= (seq >> i) & 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 6; i++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + (5 - i)] |= (seq >> (i + 9)) & 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 7; i++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(((size - 7) + i) * size) + 8] |= (seq >> (i + 8)) & 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(7 * size) + 8] |= (seq >> 6) & 0x01;
|
|
|
|
grid[(8 * size) + 8] |= (seq >> 7) & 0x01;
|
|
|
|
grid[(8 * size) + 7] |= (seq >> 8) & 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static int apply_bitmask(unsigned char *grid, const int size, const int ecc_level, const int user_mask,
|
|
|
|
const int debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int x, y;
|
2020-11-28 01:54:44 +13:00
|
|
|
int r, k;
|
|
|
|
int bit;
|
2016-02-21 00:29:19 +13:00
|
|
|
int pattern, penalty[8];
|
2020-11-28 01:54:44 +13:00
|
|
|
int best_pattern;
|
|
|
|
int size_squared = size * size;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
#ifndef _MSC_VER
|
2020-11-28 01:54:44 +13:00
|
|
|
unsigned char mask[size_squared];
|
|
|
|
unsigned char local[size_squared];
|
2016-02-21 00:29:19 +13:00
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
unsigned char *mask = (unsigned char *) _alloca(size_squared);
|
|
|
|
unsigned char *local = (unsigned char *) _alloca(size_squared);
|
2016-02-21 00:29:19 +13:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Perform data masking */
|
2020-11-28 01:54:44 +13:00
|
|
|
memset(mask, 0, size_squared);
|
|
|
|
for (y = 0; y < size; y++) {
|
|
|
|
r = y * size;
|
|
|
|
for (x = 0; x < size; x++) {
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
// all eight bitmask variants are encoded in the 8 bits of the bytes that make up the mask array.
|
2020-11-28 01:54:44 +13:00
|
|
|
if (!(grid[r + x] & 0xf0)) { // exclude areas not to be masked.
|
2016-02-21 00:29:19 +13:00
|
|
|
if (((y + x) & 1) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if ((y & 1) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x02;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if ((x % 3) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x04;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (((y + x) % 3) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x08;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if ((((y / 2) + (x / 3)) & 1) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x10;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
if ((y * x) % 6 == 0) { /* Equivalent to (y * x) % 2 + (y * x) % 3 == 0 */
|
|
|
|
mask[r + x] |= 0x20;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (((((y * x) & 1) + ((y * x) % 3)) & 1) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x40;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (((((y + x) & 1) + ((y * x) % 3)) & 1) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x80;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (user_mask) {
|
|
|
|
best_pattern = user_mask - 1;
|
|
|
|
} else {
|
|
|
|
/* all eight bitmask variants have been encoded in the 8 bits of the bytes
|
|
|
|
* that make up the mask array. select them for evaluation according to the
|
|
|
|
* desired pattern.*/
|
|
|
|
best_pattern = 0;
|
|
|
|
for (pattern = 0; pattern < 8; 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;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
add_format_info(local, size, ecc_level, pattern);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
penalty[pattern] = evaluate(local, size);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (penalty[pattern] < penalty[best_pattern]) {
|
|
|
|
best_pattern = pattern;
|
|
|
|
}
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
|
|
|
printf("Mask: %d (%s)", best_pattern, user_mask ? "specified" : "automatic");
|
|
|
|
if (!user_mask) {
|
|
|
|
for (pattern = 0; pattern < 8; pattern++) printf(" %d:%d", pattern, penalty[pattern]);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
printf("\n");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ZINTLOG
|
|
|
|
char str[15];
|
|
|
|
sprintf(str, "%d", best_val);
|
|
|
|
write_log("choosed pattern:");
|
|
|
|
write_log(str);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Apply mask */
|
2020-11-28 01:54:44 +13:00
|
|
|
if (!user_mask && best_pattern == 7) { /* Reuse last */
|
|
|
|
memcpy(grid, local, size_squared);
|
|
|
|
} else {
|
|
|
|
bit = 1 << best_pattern;
|
|
|
|
for (y = 0; y < size_squared; y++) {
|
|
|
|
if (mask[y] & bit) {
|
|
|
|
grid[y] ^= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return best_pattern;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add version information */
|
2020-11-28 01:54:44 +13:00
|
|
|
static void add_version_info(unsigned char *grid, const int size, const int version) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int i;
|
|
|
|
|
|
|
|
long int version_data = qr_annex_d[version - 7];
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
|
|
grid[((size - 11) * size) + i] += (version_data >> (i * 3)) & 0x41;
|
|
|
|
grid[((size - 10) * size) + i] += (version_data >> ((i * 3) + 1)) & 0x41;
|
|
|
|
grid[((size - 9) * size) + i] += (version_data >> ((i * 3) + 2)) & 0x41;
|
|
|
|
grid[(i * size) + (size - 11)] += (version_data >> (i * 3)) & 0x41;
|
|
|
|
grid[(i * size) + (size - 10)] += (version_data >> ((i * 3) + 1)) & 0x41;
|
|
|
|
grid[(i * size) + (size - 9)] += (version_data >> ((i * 3) + 2)) & 0x41;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static int blockLength(const int start, const char inputMode[], const int inputLength) {
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Find the length of the block starting from 'start' */
|
2020-11-28 01:54:44 +13:00
|
|
|
int i;
|
2017-05-29 21:43:47 +12:00
|
|
|
int count;
|
2016-02-21 00:29:19 +13:00
|
|
|
char mode = inputMode[start];
|
|
|
|
|
|
|
|
count = 0;
|
|
|
|
i = start;
|
|
|
|
|
|
|
|
do {
|
|
|
|
count++;
|
|
|
|
} while (((i + count) < inputLength) && (inputMode[i + count] == mode));
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static int getBinaryLength(const int version, char inputMode[], const unsigned int inputData[], const int inputLength,
|
|
|
|
const int gs1, const int eci, const int debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Calculate the actual bitlength of the proposed binary string */
|
2020-11-28 01:54:44 +13:00
|
|
|
int i, j;
|
2016-02-21 00:29:19 +13:00
|
|
|
char currentMode;
|
|
|
|
int count = 0;
|
2018-06-20 14:38:50 +12:00
|
|
|
int alphalength;
|
2019-12-17 06:31:52 +13:00
|
|
|
int blocklength;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
qr_define_mode(inputMode, inputData, inputLength, gs1, version, debug_print);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
currentMode = ' '; // Null
|
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
if (gs1 == 1) { /* Not applicable to MICROQR */
|
2019-11-26 08:08:25 +13:00
|
|
|
if (version < RMQR_VERSION) {
|
|
|
|
count += 4;
|
|
|
|
} else {
|
|
|
|
count += 3;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
if (eci != 0) { // RMQR and MICROQR do not support ECI
|
2019-11-28 05:16:14 +13:00
|
|
|
count += 4;
|
|
|
|
if (eci <= 127) {
|
|
|
|
count += 8;
|
|
|
|
} else if (eci <= 16383) {
|
|
|
|
count += 16;
|
|
|
|
} else {
|
|
|
|
count += 24;
|
|
|
|
}
|
2016-08-16 23:43:41 +12:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
for (i = 0; i < inputLength; i++) {
|
|
|
|
if (inputMode[i] != currentMode) {
|
2019-12-17 06:31:52 +13:00
|
|
|
count += mode_bits(version) + cci_bits(version, inputMode[i]);
|
|
|
|
blocklength = blockLength(i, inputMode, inputLength);
|
2016-02-21 00:29:19 +13:00
|
|
|
switch (inputMode[i]) {
|
|
|
|
case 'K':
|
2019-12-17 06:31:52 +13:00
|
|
|
count += (blocklength * 13);
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
|
|
|
case 'B':
|
2019-12-17 06:31:52 +13:00
|
|
|
for (j = i; j < (i + blocklength); j++) {
|
2016-02-21 00:29:19 +13:00
|
|
|
if (inputData[j] > 0xff) {
|
|
|
|
count += 16;
|
|
|
|
} else {
|
|
|
|
count += 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'A':
|
2019-12-17 06:31:52 +13:00
|
|
|
alphalength = blocklength;
|
|
|
|
if (gs1) {
|
|
|
|
// In alphanumeric mode % becomes %%
|
|
|
|
for (j = i; j < (i + blocklength); j++) {
|
|
|
|
if (inputData[j] == '%') {
|
|
|
|
alphalength++;
|
|
|
|
}
|
2018-06-20 14:38:50 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (alphalength % 2) {
|
2016-02-21 00:29:19 +13:00
|
|
|
case 0:
|
2018-06-20 14:38:50 +12:00
|
|
|
count += (alphalength / 2) * 11;
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
|
|
|
case 1:
|
2018-06-20 14:38:50 +12:00
|
|
|
count += ((alphalength - 1) / 2) * 11;
|
2016-02-21 00:29:19 +13:00
|
|
|
count += 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'N':
|
2019-12-17 06:31:52 +13:00
|
|
|
switch (blocklength % 3) {
|
2016-02-21 00:29:19 +13:00
|
|
|
case 0:
|
2019-12-17 06:31:52 +13:00
|
|
|
count += (blocklength / 3) * 10;
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
|
|
|
case 1:
|
2019-12-17 06:31:52 +13:00
|
|
|
count += ((blocklength - 1) / 3) * 10;
|
2016-02-21 00:29:19 +13:00
|
|
|
count += 4;
|
|
|
|
break;
|
|
|
|
case 2:
|
2019-12-17 06:31:52 +13:00
|
|
|
count += ((blocklength - 2) / 3) * 10;
|
2016-02-21 00:29:19 +13:00
|
|
|
count += 7;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
currentMode = inputMode[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2019-12-17 06:31:52 +13:00
|
|
|
printf("Estimated Binary Length: %d (version %d, eci %d, gs1 %d)\n", count, version, eci, gs1);
|
2019-11-28 05:16:14 +13:00
|
|
|
}
|
2019-12-17 06:31:52 +13:00
|
|
|
|
|
|
|
return count;
|
2019-11-28 05:16:14 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
INTERNAL int qr_code(struct zint_symbol *symbol, unsigned char source[], int length) {
|
|
|
|
int i, j, est_binlen, prev_est_binlen;
|
2019-11-26 08:08:25 +13:00
|
|
|
int ecc_level, autosize, version, max_cw, target_codewords, blocks, size;
|
2016-02-21 00:29:19 +13:00
|
|
|
int bitmask, gs1;
|
2020-04-03 02:41:13 +13:00
|
|
|
int full_multibyte;
|
2020-11-28 01:54:44 +13:00
|
|
|
int user_mask;
|
2016-02-21 00:29:19 +13:00
|
|
|
int canShrink;
|
2020-11-28 01:54:44 +13:00
|
|
|
int size_squared;
|
|
|
|
int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
|
2021-01-12 07:11:41 +13:00
|
|
|
int eci_length = get_eci_length(symbol->eci, source, length);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
#ifndef _MSC_VER
|
2021-01-12 07:11:41 +13:00
|
|
|
unsigned int jisdata[eci_length + 1];
|
|
|
|
char mode[eci_length];
|
|
|
|
char prev_mode[eci_length];
|
2016-02-21 00:29:19 +13:00
|
|
|
#else
|
2021-01-12 07:11:41 +13:00
|
|
|
unsigned char *datastream;
|
|
|
|
unsigned char *fullstream;
|
|
|
|
unsigned char *grid;
|
|
|
|
unsigned int *jisdata = (unsigned int *) _alloca((eci_length + 1) * sizeof(unsigned int));
|
|
|
|
char *mode = (char *) _alloca(eci_length);
|
|
|
|
char *prev_mode = (char *) _alloca(eci_length);
|
2016-02-21 00:29:19 +13:00
|
|
|
#endif
|
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
gs1 = ((symbol->input_mode & 0x07) == GS1_MODE);
|
2021-01-12 07:11:41 +13:00
|
|
|
/* If ZINT_FULL_MULTIBYTE use Kanji mode in DATA_MODE or for non-Shift JIS in UNICODE_MODE */
|
2020-11-28 01:54:44 +13:00
|
|
|
full_multibyte = (symbol->option_3 & 0xFF) == ZINT_FULL_MULTIBYTE;
|
|
|
|
user_mask = (symbol->option_3 >> 8) & 0x0F; /* User mask is pattern + 1, so >= 1 and <= 8 */
|
|
|
|
if (user_mask > 8) {
|
|
|
|
user_mask = 0; /* Ignore */
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
if ((symbol->input_mode & 0x07) == DATA_MODE) {
|
2020-04-03 02:41:13 +13:00
|
|
|
sjis_cpy(source, &length, jisdata, full_multibyte);
|
2016-08-16 23:43:41 +12:00
|
|
|
} else {
|
2019-11-28 05:16:14 +13:00
|
|
|
int done = 0;
|
|
|
|
if (symbol->eci != 20) { /* Unless ECI 20 (Shift JIS) */
|
2021-01-12 07:11:41 +13:00
|
|
|
/* Try other encodings (ECI 0 defaults to ISO/IEC 8859-1) */
|
|
|
|
int error_number = sjis_utf8_to_eci(symbol->eci, source, &length, jisdata, full_multibyte);
|
2019-11-28 05:16:14 +13:00
|
|
|
if (error_number == 0) {
|
|
|
|
done = 1;
|
2021-01-12 07:11:41 +13:00
|
|
|
} else if (symbol->eci) {
|
2021-07-07 06:53:31 +12:00
|
|
|
sprintf(symbol->errtxt, "575: Invalid character in input data for ECI %d", symbol->eci);
|
2019-11-28 05:16:14 +13:00
|
|
|
return error_number;
|
|
|
|
}
|
2016-08-16 23:43:41 +12:00
|
|
|
}
|
2019-11-28 05:16:14 +13:00
|
|
|
if (!done) {
|
|
|
|
/* Try Shift-JIS */
|
2021-01-12 07:11:41 +13:00
|
|
|
int error_number = sjis_utf8(symbol, source, &length, jisdata);
|
2019-11-28 05:16:14 +13:00
|
|
|
if (error_number != 0) {
|
|
|
|
return error_number;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-08-16 23:43:41 +12:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
est_binlen = getBinaryLength(40, mode, jisdata, length, gs1, symbol->eci, debug_print);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
ecc_level = LEVEL_L;
|
|
|
|
max_cw = 2956;
|
|
|
|
if ((symbol->option_1 >= 1) && (symbol->option_1 <= 4)) {
|
|
|
|
switch (symbol->option_1) {
|
2019-12-17 06:31:52 +13:00
|
|
|
case 1:
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
|
|
|
case 2: ecc_level = LEVEL_M;
|
|
|
|
max_cw = 2334;
|
|
|
|
break;
|
|
|
|
case 3: ecc_level = LEVEL_Q;
|
|
|
|
max_cw = 1666;
|
|
|
|
break;
|
|
|
|
case 4: ecc_level = LEVEL_H;
|
|
|
|
max_cw = 1276;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (est_binlen > (8 * max_cw)) {
|
2017-07-28 03:01:53 +12:00
|
|
|
strcpy(symbol->errtxt, "561: Input too long for selected error correction level");
|
2016-02-21 00:29:19 +13:00
|
|
|
return ZINT_ERROR_TOO_LONG;
|
|
|
|
}
|
|
|
|
|
|
|
|
autosize = 40;
|
|
|
|
for (i = 39; i >= 0; i--) {
|
|
|
|
switch (ecc_level) {
|
|
|
|
case LEVEL_L:
|
|
|
|
if ((8 * qr_data_codewords_L[i]) >= est_binlen) {
|
|
|
|
autosize = i + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LEVEL_M:
|
|
|
|
if ((8 * qr_data_codewords_M[i]) >= est_binlen) {
|
|
|
|
autosize = i + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LEVEL_Q:
|
|
|
|
if ((8 * qr_data_codewords_Q[i]) >= est_binlen) {
|
|
|
|
autosize = i + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LEVEL_H:
|
|
|
|
if ((8 * qr_data_codewords_H[i]) >= est_binlen) {
|
|
|
|
autosize = i + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-12-17 06:31:52 +13:00
|
|
|
if (autosize != 40) {
|
2020-11-28 01:54:44 +13:00
|
|
|
est_binlen = getBinaryLength(autosize, mode, jisdata, length, gs1, symbol->eci, debug_print);
|
2019-12-17 06:31:52 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
// Now see if the optimised binary will fit in a smaller symbol.
|
|
|
|
canShrink = 1;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (autosize == 1) {
|
|
|
|
canShrink = 0;
|
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
prev_est_binlen = est_binlen;
|
|
|
|
memcpy(prev_mode, mode, length);
|
|
|
|
est_binlen = getBinaryLength(autosize - 1, mode, jisdata, length, gs1, symbol->eci, debug_print);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
switch (ecc_level) {
|
|
|
|
case LEVEL_L:
|
|
|
|
if ((8 * qr_data_codewords_L[autosize - 2]) < est_binlen) {
|
|
|
|
canShrink = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LEVEL_M:
|
|
|
|
if ((8 * qr_data_codewords_M[autosize - 2]) < est_binlen) {
|
|
|
|
canShrink = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LEVEL_Q:
|
|
|
|
if ((8 * qr_data_codewords_Q[autosize - 2]) < est_binlen) {
|
|
|
|
canShrink = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LEVEL_H:
|
|
|
|
if ((8 * qr_data_codewords_H[autosize - 2]) < est_binlen) {
|
|
|
|
canShrink = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canShrink == 1) {
|
|
|
|
// Optimisation worked - data will fit in a smaller symbol
|
|
|
|
autosize--;
|
|
|
|
} else {
|
|
|
|
// Data did not fit in the smaller symbol, revert to original size
|
2020-11-28 01:54:44 +13:00
|
|
|
est_binlen = prev_est_binlen;
|
|
|
|
memcpy(mode, prev_mode, length);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (canShrink == 1);
|
|
|
|
|
|
|
|
version = autosize;
|
|
|
|
|
|
|
|
if ((symbol->option_2 >= 1) && (symbol->option_2 <= 40)) {
|
|
|
|
/* If the user has selected a larger symbol than the smallest available,
|
|
|
|
then use the size the user has selected, and re-optimise for this
|
|
|
|
symbol size.
|
|
|
|
*/
|
|
|
|
if (symbol->option_2 > version) {
|
|
|
|
version = symbol->option_2;
|
2020-11-28 01:54:44 +13:00
|
|
|
est_binlen = getBinaryLength(symbol->option_2, mode, jisdata, length, gs1, symbol->eci, debug_print);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2017-04-11 20:26:39 +12:00
|
|
|
if (symbol->option_2 < version) {
|
2017-07-28 03:01:53 +12:00
|
|
|
strcpy(symbol->errtxt, "569: Input too long for selected symbol size");
|
2017-04-11 20:26:39 +12:00
|
|
|
return ZINT_ERROR_TOO_LONG;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2019-11-28 05:16:14 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
/* Ensure maxium error correction capacity unless user-specified */
|
|
|
|
if (symbol->option_1 == -1 || symbol->option_1 != ecc_level) {
|
|
|
|
if (est_binlen <= qr_data_codewords_M[version - 1] * 8) {
|
|
|
|
ecc_level = LEVEL_M;
|
|
|
|
}
|
|
|
|
if (est_binlen <= qr_data_codewords_Q[version - 1] * 8) {
|
|
|
|
ecc_level = LEVEL_Q;
|
|
|
|
}
|
|
|
|
if (est_binlen <= qr_data_codewords_H[version - 1] * 8) {
|
|
|
|
ecc_level = LEVEL_H;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2019-11-26 08:08:25 +13:00
|
|
|
target_codewords = qr_data_codewords_L[version - 1];
|
2016-02-21 00:29:19 +13:00
|
|
|
blocks = qr_blocks_L[version - 1];
|
|
|
|
switch (ecc_level) {
|
2019-11-26 08:08:25 +13:00
|
|
|
case LEVEL_M: target_codewords = qr_data_codewords_M[version - 1];
|
2016-02-21 00:29:19 +13:00
|
|
|
blocks = qr_blocks_M[version - 1];
|
|
|
|
break;
|
2019-11-26 08:08:25 +13:00
|
|
|
case LEVEL_Q: target_codewords = qr_data_codewords_Q[version - 1];
|
2016-02-21 00:29:19 +13:00
|
|
|
blocks = qr_blocks_Q[version - 1];
|
|
|
|
break;
|
2019-11-26 08:08:25 +13:00
|
|
|
case LEVEL_H: target_codewords = qr_data_codewords_H[version - 1];
|
2016-02-21 00:29:19 +13:00
|
|
|
blocks = qr_blocks_H[version - 1];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-08-21 03:50:39 +12:00
|
|
|
if (debug_print) {
|
|
|
|
printf("Minimum codewords: %d\n", (est_binlen + 7) / 8);
|
|
|
|
printf("Selected version: %d-%c (%dx%d)\n",
|
|
|
|
version, ecc_level_names[ecc_level - 1], qr_sizes[version - 1], qr_sizes[version - 1]);
|
|
|
|
printf("Number of data codewords in symbol: %d\n", target_codewords);
|
|
|
|
printf("Number of ECC blocks: %d\n", blocks);
|
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
#ifndef _MSC_VER
|
2019-12-02 04:17:08 +13:00
|
|
|
unsigned char datastream[target_codewords + 1];
|
2019-11-28 05:16:14 +13:00
|
|
|
unsigned char fullstream[qr_total_codewords[version - 1] + 1];
|
2016-02-21 00:29:19 +13:00
|
|
|
#else
|
2019-12-02 04:17:08 +13:00
|
|
|
datastream = (unsigned char *) _alloca(target_codewords + 1);
|
2019-11-28 05:16:14 +13:00
|
|
|
fullstream = (unsigned char *) _alloca(qr_total_codewords[version - 1] + 1);
|
2016-02-21 00:29:19 +13:00
|
|
|
#endif
|
|
|
|
|
2021-06-20 00:11:23 +12:00
|
|
|
qr_binary(datastream, version, target_codewords, mode, jisdata, length, gs1, symbol->eci, est_binlen,
|
|
|
|
debug_print);
|
2019-12-19 13:37:55 +13:00
|
|
|
#ifdef ZINT_TEST
|
2019-12-17 06:31:52 +13:00
|
|
|
if (symbol->debug & ZINT_DEBUG_TEST) debug_test_codeword_dump(symbol, datastream, target_codewords);
|
2019-12-19 13:37:55 +13:00
|
|
|
#endif
|
2020-11-28 01:54:44 +13:00
|
|
|
add_ecc(fullstream, datastream, version, target_codewords, blocks, debug_print);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
size = qr_sizes[version - 1];
|
2020-11-28 01:54:44 +13:00
|
|
|
size_squared = size * size;
|
2016-02-21 00:29:19 +13:00
|
|
|
#ifndef _MSC_VER
|
2020-11-28 01:54:44 +13:00
|
|
|
unsigned char grid[size_squared];
|
2016-02-21 00:29:19 +13:00
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
grid = (unsigned char *) _alloca(size_squared);
|
2016-02-21 00:29:19 +13:00
|
|
|
#endif
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
memset(grid, 0, size_squared);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
setup_grid(grid, size, version);
|
2019-11-26 08:08:25 +13:00
|
|
|
populate_grid(grid, size, size, fullstream, qr_total_codewords[version - 1]);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
if (version >= 7) {
|
|
|
|
add_version_info(grid, size, version);
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
bitmask = apply_bitmask(grid, size, ecc_level, user_mask, debug_print);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
add_format_info(grid, size, ecc_level, bitmask);
|
|
|
|
|
|
|
|
symbol->width = size;
|
|
|
|
symbol->rows = size;
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
int r = i * size;
|
2016-02-21 00:29:19 +13:00
|
|
|
for (j = 0; j < size; j++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
if (grid[r + j] & 0x01) {
|
2016-02-21 00:29:19 +13:00
|
|
|
set_module(symbol, i, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
symbol->row_height[i] = 1;
|
|
|
|
}
|
2021-06-20 00:11:23 +12:00
|
|
|
symbol->height = size;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
static void micro_qr_m1(struct zint_symbol *symbol, char binary_data[]) {
|
2016-09-18 05:22:26 +12:00
|
|
|
int i, j, latch;
|
2017-09-11 03:03:09 +12:00
|
|
|
int bits_total, bits_left;
|
2016-02-21 00:29:19 +13:00
|
|
|
int data_codewords, ecc_codewords;
|
|
|
|
unsigned char data_blocks[4], ecc_blocks[3];
|
2020-11-28 01:54:44 +13:00
|
|
|
rs_t rs;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
bits_total = 20;
|
|
|
|
latch = 0;
|
|
|
|
|
|
|
|
/* Add terminator */
|
2021-08-27 04:49:05 +12:00
|
|
|
bits_left = bits_total - (int) strlen(binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (bits_left <= 3) {
|
|
|
|
for (i = 0; i < bits_left; i++) {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "0");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
latch = 1;
|
|
|
|
} else {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "000");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2021-08-27 04:49:05 +12:00
|
|
|
if (symbol->debug & ZINT_DEBUG_PRINT) {
|
|
|
|
printf("M1 Terminated binary (%d): %s (bits_left %d)\n", (int) strlen(binary_data), binary_data, bits_left);
|
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (latch == 0) {
|
|
|
|
/* Manage last (4-bit) block */
|
2021-08-27 04:49:05 +12:00
|
|
|
bits_left = bits_total - (int) strlen(binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (bits_left <= 4) {
|
|
|
|
for (i = 0; i < bits_left; i++) {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "0");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
latch = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (latch == 0) {
|
|
|
|
/* Complete current byte */
|
2017-09-11 03:03:09 +12:00
|
|
|
int remainder = 8 - (strlen(binary_data) % 8);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (remainder == 8) {
|
|
|
|
remainder = 0;
|
|
|
|
}
|
|
|
|
for (i = 0; i < remainder; i++) {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "0");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add padding */
|
2021-08-27 04:49:05 +12:00
|
|
|
bits_left = bits_total - (int) strlen(binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (bits_left > 4) {
|
|
|
|
remainder = (bits_left - 4) / 8;
|
|
|
|
for (i = 0; i < remainder; i++) {
|
2017-09-11 03:03:09 +12:00
|
|
|
strcat(binary_data, (i & 1) ? "00010001" : "11101100");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
2017-06-07 08:31:03 +12:00
|
|
|
bin_append(0, 4, binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
data_codewords = 3;
|
|
|
|
ecc_codewords = 2;
|
|
|
|
|
|
|
|
/* Copy data into codewords */
|
|
|
|
for (i = 0; i < (data_codewords - 1); i++) {
|
|
|
|
data_blocks[i] = 0;
|
2016-09-18 05:22:26 +12:00
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
if (binary_data[(i * 8) + j] == '1') {
|
2020-11-28 01:54:44 +13:00
|
|
|
data_blocks[i] |= 0x80 >> j;
|
2016-09-18 05:22:26 +12:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
data_blocks[2] = 0;
|
2016-09-18 05:22:26 +12:00
|
|
|
for (j = 0; j < 4; j++) {
|
|
|
|
if (binary_data[16 + j] == '1') {
|
2020-11-28 01:54:44 +13:00
|
|
|
data_blocks[2] |= 0x80 >> j;
|
2016-09-18 05:22:26 +12:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2019-12-19 13:37:55 +13:00
|
|
|
#ifdef ZINT_TEST
|
2019-12-17 06:31:52 +13:00
|
|
|
if (symbol->debug & ZINT_DEBUG_TEST) debug_test_codeword_dump(symbol, data_blocks, data_codewords);
|
2019-12-19 13:37:55 +13:00
|
|
|
#endif
|
2019-11-28 05:16:14 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Calculate Reed-Solomon error codewords */
|
2020-11-28 01:54:44 +13:00
|
|
|
rs_init_gf(&rs, 0x11d);
|
|
|
|
rs_init_code(&rs, ecc_codewords, 0);
|
|
|
|
rs_encode(&rs, data_codewords, data_blocks, ecc_blocks);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Add Reed-Solomon codewords to binary data */
|
|
|
|
for (i = 0; i < ecc_codewords; i++) {
|
2017-06-07 08:11:11 +12:00
|
|
|
bin_append(ecc_blocks[ecc_codewords - i - 1], 8, binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
static void micro_qr_m2(struct zint_symbol *symbol, char binary_data[], const int ecc_mode) {
|
2016-09-18 05:22:26 +12:00
|
|
|
int i, j, latch;
|
2017-09-11 03:03:09 +12:00
|
|
|
int bits_total=0, bits_left;
|
2017-05-29 21:43:47 +12:00
|
|
|
int data_codewords=0, ecc_codewords=0;
|
2016-02-21 00:29:19 +13:00
|
|
|
unsigned char data_blocks[6], ecc_blocks[7];
|
2020-11-28 01:54:44 +13:00
|
|
|
rs_t rs;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
latch = 0;
|
|
|
|
|
|
|
|
if (ecc_mode == LEVEL_L) {
|
|
|
|
bits_total = 40;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else if (ecc_mode == LEVEL_M) {
|
2016-02-21 00:29:19 +13:00
|
|
|
bits_total = 32;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else assert(0);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Add terminator */
|
2021-08-27 04:49:05 +12:00
|
|
|
bits_left = bits_total - (int) strlen(binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (bits_left <= 5) {
|
|
|
|
for (i = 0; i < bits_left; i++) {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "0");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
latch = 1;
|
|
|
|
} else {
|
2017-06-07 08:31:03 +12:00
|
|
|
bin_append(0, 5, binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2021-08-27 04:49:05 +12:00
|
|
|
if (symbol->debug & ZINT_DEBUG_PRINT) {
|
|
|
|
printf("M2 Terminated binary (%d): %s (bits_left %d)\n", (int) strlen(binary_data), binary_data, bits_left);
|
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (latch == 0) {
|
|
|
|
/* Complete current byte */
|
2017-09-11 03:03:09 +12:00
|
|
|
int remainder = 8 - (strlen(binary_data) % 8);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (remainder == 8) {
|
|
|
|
remainder = 0;
|
|
|
|
}
|
|
|
|
for (i = 0; i < remainder; i++) {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "0");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add padding */
|
2021-08-27 04:49:05 +12:00
|
|
|
bits_left = bits_total - (int) strlen(binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
remainder = bits_left / 8;
|
|
|
|
for (i = 0; i < remainder; i++) {
|
2017-09-11 03:03:09 +12:00
|
|
|
strcat(binary_data, (i & 1) ? "00010001" : "11101100");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ecc_mode == LEVEL_L) {
|
|
|
|
data_codewords = 5;
|
|
|
|
ecc_codewords = 5;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else if (ecc_mode == LEVEL_M) {
|
2016-02-21 00:29:19 +13:00
|
|
|
data_codewords = 4;
|
|
|
|
ecc_codewords = 6;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else assert(0);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Copy data into codewords */
|
|
|
|
for (i = 0; i < data_codewords; i++) {
|
|
|
|
data_blocks[i] = 0;
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2016-09-18 05:22:26 +12:00
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
if (binary_data[(i * 8) + j] == '1') {
|
2020-11-28 01:54:44 +13:00
|
|
|
data_blocks[i] |= 0x80 >> j;
|
2016-09-18 05:22:26 +12:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
2019-12-19 13:37:55 +13:00
|
|
|
#ifdef ZINT_TEST
|
2019-12-17 06:31:52 +13:00
|
|
|
if (symbol->debug & ZINT_DEBUG_TEST) debug_test_codeword_dump(symbol, data_blocks, data_codewords);
|
2019-12-19 13:37:55 +13:00
|
|
|
#endif
|
2019-11-28 05:16:14 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Calculate Reed-Solomon error codewords */
|
2020-11-28 01:54:44 +13:00
|
|
|
rs_init_gf(&rs, 0x11d);
|
|
|
|
rs_init_code(&rs, ecc_codewords, 0);
|
|
|
|
rs_encode(&rs, data_codewords, data_blocks, ecc_blocks);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Add Reed-Solomon codewords to binary data */
|
|
|
|
for (i = 0; i < ecc_codewords; i++) {
|
2017-06-07 08:11:11 +12:00
|
|
|
bin_append(ecc_blocks[ecc_codewords - i - 1], 8, binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
static void micro_qr_m3(struct zint_symbol *symbol, char binary_data[], const int ecc_mode) {
|
2016-09-18 05:22:26 +12:00
|
|
|
int i, j, latch;
|
2017-09-11 03:03:09 +12:00
|
|
|
int bits_total=0, bits_left;
|
2017-05-29 21:43:47 +12:00
|
|
|
int data_codewords=0, ecc_codewords=0;
|
2016-02-21 00:29:19 +13:00
|
|
|
unsigned char data_blocks[12], ecc_blocks[9];
|
2020-11-28 01:54:44 +13:00
|
|
|
rs_t rs;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
latch = 0;
|
|
|
|
|
|
|
|
if (ecc_mode == LEVEL_L) {
|
|
|
|
bits_total = 84;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else if (ecc_mode == LEVEL_M) {
|
2016-02-21 00:29:19 +13:00
|
|
|
bits_total = 68;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else assert(0);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Add terminator */
|
2021-08-27 04:49:05 +12:00
|
|
|
bits_left = bits_total - (int) strlen(binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (bits_left <= 7) {
|
|
|
|
for (i = 0; i < bits_left; i++) {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "0");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
latch = 1;
|
|
|
|
} else {
|
2017-06-07 08:31:03 +12:00
|
|
|
bin_append(0, 7, binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2021-08-27 04:49:05 +12:00
|
|
|
if (symbol->debug & ZINT_DEBUG_PRINT) {
|
|
|
|
printf("M3 Terminated binary (%d): %s (bits_left %d)\n", (int) strlen(binary_data), binary_data, bits_left);
|
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (latch == 0) {
|
|
|
|
/* Manage last (4-bit) block */
|
2021-08-27 04:49:05 +12:00
|
|
|
bits_left = bits_total - (int) strlen(binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (bits_left <= 4) {
|
|
|
|
for (i = 0; i < bits_left; i++) {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "0");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
latch = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (latch == 0) {
|
|
|
|
/* Complete current byte */
|
2017-09-11 03:03:09 +12:00
|
|
|
int remainder = 8 - (strlen(binary_data) % 8);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (remainder == 8) {
|
|
|
|
remainder = 0;
|
|
|
|
}
|
|
|
|
for (i = 0; i < remainder; i++) {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "0");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add padding */
|
2021-08-27 04:49:05 +12:00
|
|
|
bits_left = bits_total - (int) strlen(binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (bits_left > 4) {
|
|
|
|
remainder = (bits_left - 4) / 8;
|
|
|
|
for (i = 0; i < remainder; i++) {
|
2017-09-11 03:03:09 +12:00
|
|
|
strcat(binary_data, (i & 1) ? "00010001" : "11101100");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
2017-06-07 08:31:03 +12:00
|
|
|
bin_append(0, 4, binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ecc_mode == LEVEL_L) {
|
|
|
|
data_codewords = 11;
|
|
|
|
ecc_codewords = 6;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else if (ecc_mode == LEVEL_M) {
|
2016-02-21 00:29:19 +13:00
|
|
|
data_codewords = 9;
|
|
|
|
ecc_codewords = 8;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else assert(0);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Copy data into codewords */
|
|
|
|
for (i = 0; i < (data_codewords - 1); i++) {
|
|
|
|
data_blocks[i] = 0;
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2016-09-18 05:22:26 +12:00
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
if (binary_data[(i * 8) + j] == '1') {
|
2020-11-28 01:54:44 +13:00
|
|
|
data_blocks[i] |= 0x80 >> j;
|
2016-09-18 05:22:26 +12:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ecc_mode == LEVEL_L) {
|
|
|
|
data_blocks[10] = 0;
|
2016-11-26 11:21:30 +13:00
|
|
|
for (j = 0; j < 4; j++) {
|
|
|
|
if (binary_data[80 + j] == '1') {
|
2020-11-28 01:54:44 +13:00
|
|
|
data_blocks[10] |= 0x80 >> j;
|
2016-11-26 11:21:30 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ecc_mode == LEVEL_M) {
|
|
|
|
data_blocks[8] = 0;
|
2016-11-26 11:21:30 +13:00
|
|
|
for (j = 0; j < 4; j++) {
|
|
|
|
if (binary_data[64 + j] == '1') {
|
2020-11-28 01:54:44 +13:00
|
|
|
data_blocks[8] |= 0x80 >> j;
|
2016-11-26 11:21:30 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
2019-12-19 13:37:55 +13:00
|
|
|
#ifdef ZINT_TEST
|
2019-12-17 06:31:52 +13:00
|
|
|
if (symbol->debug & ZINT_DEBUG_TEST) debug_test_codeword_dump(symbol, data_blocks, data_codewords);
|
2019-12-19 13:37:55 +13:00
|
|
|
#endif
|
2019-11-28 05:16:14 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Calculate Reed-Solomon error codewords */
|
2020-11-28 01:54:44 +13:00
|
|
|
rs_init_gf(&rs, 0x11d);
|
|
|
|
rs_init_code(&rs, ecc_codewords, 0);
|
|
|
|
rs_encode(&rs, data_codewords, data_blocks, ecc_blocks);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Add Reed-Solomon codewords to binary data */
|
|
|
|
for (i = 0; i < ecc_codewords; i++) {
|
2017-06-07 08:11:11 +12:00
|
|
|
bin_append(ecc_blocks[ecc_codewords - i - 1], 8, binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
static void micro_qr_m4(struct zint_symbol *symbol, char binary_data[], const int ecc_mode) {
|
2016-09-18 05:22:26 +12:00
|
|
|
int i, j, latch;
|
2017-09-11 03:03:09 +12:00
|
|
|
int bits_total=0, bits_left;
|
2017-05-29 21:43:47 +12:00
|
|
|
int data_codewords=0, ecc_codewords=0;
|
2016-02-21 00:29:19 +13:00
|
|
|
unsigned char data_blocks[17], ecc_blocks[15];
|
2020-11-28 01:54:44 +13:00
|
|
|
rs_t rs;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
latch = 0;
|
|
|
|
|
|
|
|
if (ecc_mode == LEVEL_L) {
|
|
|
|
bits_total = 128;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else if (ecc_mode == LEVEL_M) {
|
2016-02-21 00:29:19 +13:00
|
|
|
bits_total = 112;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else if (ecc_mode == LEVEL_Q) {
|
2016-02-21 00:29:19 +13:00
|
|
|
bits_total = 80;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else assert(0);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Add terminator */
|
2021-08-27 04:49:05 +12:00
|
|
|
bits_left = bits_total - (int) strlen(binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (bits_left <= 9) {
|
|
|
|
for (i = 0; i < bits_left; i++) {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "0");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
latch = 1;
|
|
|
|
} else {
|
2017-06-07 08:31:03 +12:00
|
|
|
bin_append(0, 9, binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2021-08-27 04:49:05 +12:00
|
|
|
if (symbol->debug & ZINT_DEBUG_PRINT) {
|
|
|
|
printf("M4 Terminated binary (%d): %s (bits_left %d)\n", (int) strlen(binary_data), binary_data, bits_left);
|
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (latch == 0) {
|
|
|
|
/* Complete current byte */
|
2017-09-11 03:03:09 +12:00
|
|
|
int remainder = 8 - (strlen(binary_data) % 8);
|
2016-02-21 00:29:19 +13:00
|
|
|
if (remainder == 8) {
|
|
|
|
remainder = 0;
|
|
|
|
}
|
|
|
|
for (i = 0; i < remainder; i++) {
|
2016-03-03 10:12:38 +13:00
|
|
|
strcat(binary_data, "0");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add padding */
|
2021-08-27 04:49:05 +12:00
|
|
|
bits_left = bits_total - (int) strlen(binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
remainder = bits_left / 8;
|
|
|
|
for (i = 0; i < remainder; i++) {
|
2017-09-11 03:03:09 +12:00
|
|
|
strcat(binary_data, (i & 1) ? "00010001" : "11101100");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ecc_mode == LEVEL_L) {
|
|
|
|
data_codewords = 16;
|
|
|
|
ecc_codewords = 8;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else if (ecc_mode == LEVEL_M) {
|
2016-02-21 00:29:19 +13:00
|
|
|
data_codewords = 14;
|
|
|
|
ecc_codewords = 10;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else if (ecc_mode == LEVEL_Q) {
|
2016-02-21 00:29:19 +13:00
|
|
|
data_codewords = 10;
|
|
|
|
ecc_codewords = 14;
|
|
|
|
}
|
2017-05-29 21:43:47 +12:00
|
|
|
else assert(0);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Copy data into codewords */
|
|
|
|
for (i = 0; i < data_codewords; i++) {
|
|
|
|
data_blocks[i] = 0;
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2016-09-18 05:22:26 +12:00
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
if (binary_data[(i * 8) + j] == '1') {
|
2020-11-28 01:54:44 +13:00
|
|
|
data_blocks[i] |= 0x80 >> j;
|
2016-09-18 05:22:26 +12:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
2019-12-19 13:37:55 +13:00
|
|
|
#ifdef ZINT_TEST
|
2019-12-17 06:31:52 +13:00
|
|
|
if (symbol->debug & ZINT_DEBUG_TEST) debug_test_codeword_dump(symbol, data_blocks, data_codewords);
|
2019-12-19 13:37:55 +13:00
|
|
|
#endif
|
2019-11-28 05:16:14 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Calculate Reed-Solomon error codewords */
|
2020-11-28 01:54:44 +13:00
|
|
|
rs_init_gf(&rs, 0x11d);
|
|
|
|
rs_init_code(&rs, ecc_codewords, 0);
|
|
|
|
rs_encode(&rs, data_codewords, data_blocks, ecc_blocks);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Add Reed-Solomon codewords to binary data */
|
|
|
|
for (i = 0; i < ecc_codewords; i++) {
|
2017-06-07 08:11:11 +12:00
|
|
|
bin_append(ecc_blocks[ecc_codewords - i - 1], 8, binary_data);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static void micro_setup_grid(unsigned char *grid, const int size) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int i, toggle = 1;
|
|
|
|
|
|
|
|
/* Add timing patterns */
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
if (toggle == 1) {
|
|
|
|
grid[i] = 0x21;
|
|
|
|
grid[(i * size)] = 0x21;
|
|
|
|
toggle = 0;
|
|
|
|
} else {
|
|
|
|
grid[i] = 0x20;
|
|
|
|
grid[(i * size)] = 0x20;
|
|
|
|
toggle = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add finder patterns */
|
|
|
|
place_finder(grid, size, 0, 0);
|
|
|
|
|
|
|
|
/* Add separators */
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
|
|
grid[(7 * size) + i] = 0x10;
|
|
|
|
grid[(i * size) + 7] = 0x10;
|
|
|
|
}
|
|
|
|
grid[(7 * size) + 7] = 0x10;
|
|
|
|
|
|
|
|
|
|
|
|
/* Reserve space for format information */
|
|
|
|
for (i = 0; i < 8; i++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + i] |= 0x20;
|
|
|
|
grid[(i * size) + 8] |= 0x20;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + 8] |= 20;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static void micro_populate_grid(unsigned char *grid, const int size, const char full_stream[]) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int direction = 1; /* up */
|
|
|
|
int row = 0; /* right hand side */
|
2020-11-28 01:54:44 +13:00
|
|
|
int n, i;
|
2020-03-30 23:59:16 +13:00
|
|
|
int y;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2021-06-10 22:15:39 +12:00
|
|
|
n = (int) strlen(full_stream);
|
2016-02-21 00:29:19 +13:00
|
|
|
y = size - 1;
|
|
|
|
i = 0;
|
|
|
|
do {
|
2017-09-11 03:03:09 +12:00
|
|
|
int x = (size - 2) - (row * 2);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
if (!(grid[(y * size) + (x + 1)] & 0xf0)) {
|
|
|
|
if (full_stream[i] == '1') {
|
|
|
|
grid[(y * size) + (x + 1)] = 0x01;
|
|
|
|
} else {
|
|
|
|
grid[(y * size) + (x + 1)] = 0x00;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
i++;
|
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (i < n) {
|
|
|
|
if (!(grid[(y * size) + x] & 0xf0)) {
|
|
|
|
if (full_stream[i] == '1') {
|
|
|
|
grid[(y * size) + x] = 0x01;
|
|
|
|
} else {
|
|
|
|
grid[(y * size) + x] = 0x00;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
i++;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
if (direction) {
|
|
|
|
y--;
|
|
|
|
} else {
|
|
|
|
y++;
|
|
|
|
}
|
|
|
|
if (y == 0) {
|
|
|
|
/* reached the top */
|
|
|
|
row++;
|
|
|
|
y = 1;
|
|
|
|
direction = 0;
|
|
|
|
}
|
|
|
|
if (y == size) {
|
|
|
|
/* reached the bottom */
|
|
|
|
row++;
|
|
|
|
y = size - 1;
|
|
|
|
direction = 1;
|
|
|
|
}
|
|
|
|
} while (i < n);
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static int micro_evaluate(const unsigned char *grid, const int size, const int pattern) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int sum1, sum2, i, filter = 0, retval;
|
|
|
|
|
|
|
|
switch (pattern) {
|
|
|
|
case 0: filter = 0x01;
|
|
|
|
break;
|
|
|
|
case 1: filter = 0x02;
|
|
|
|
break;
|
|
|
|
case 2: filter = 0x04;
|
|
|
|
break;
|
|
|
|
case 3: filter = 0x08;
|
|
|
|
break;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
sum1 = 0;
|
|
|
|
sum2 = 0;
|
|
|
|
for (i = 1; i < size; i++) {
|
|
|
|
if (grid[(i * size) + size - 1] & filter) {
|
|
|
|
sum1++;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
if (grid[((size - 1) * size) + i] & filter) {
|
|
|
|
sum2++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sum1 <= sum2) {
|
|
|
|
retval = (sum1 * 16) + sum2;
|
|
|
|
} else {
|
|
|
|
retval = (sum2 * 16) + sum1;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
return retval;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
static int micro_apply_bitmask(unsigned char *grid, const int size, const int user_mask, const int debug_print) {
|
2016-02-21 00:29:19 +13:00
|
|
|
int x, y;
|
2020-11-28 01:54:44 +13:00
|
|
|
int r, k;
|
|
|
|
int bit;
|
|
|
|
int pattern, value[4];
|
|
|
|
int best_pattern;
|
|
|
|
int size_squared = size * size;
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
#ifndef _MSC_VER
|
2020-11-28 01:54:44 +13:00
|
|
|
unsigned char mask[size_squared];
|
|
|
|
unsigned char eval[size_squared];
|
2016-02-21 00:29:19 +13:00
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
unsigned char *mask = (unsigned char *) _alloca(size_squared);
|
|
|
|
unsigned char *eval = (unsigned char *) _alloca(size_squared);
|
2016-02-21 00:29:19 +13:00
|
|
|
#endif
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Perform data masking */
|
2020-11-28 01:54:44 +13:00
|
|
|
memset(mask, 0, size_squared);
|
|
|
|
for (y = 0; y < size; y++) {
|
|
|
|
r = y * size;
|
|
|
|
for (x = 0; x < size; x++) {
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (!(grid[r + x] & 0xf0)) {
|
2016-02-21 00:29:19 +13:00
|
|
|
if ((y & 1) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if ((((y / 2) + (x / 3)) & 1) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x02;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (((((y * x) & 1) + ((y * x) % 3)) & 1) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x04;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (((((y + x) & 1) + ((y * x) % 3)) & 1) == 0) {
|
2020-11-28 01:54:44 +13:00
|
|
|
mask[r + x] |= 0x08;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (user_mask) {
|
|
|
|
best_pattern = user_mask - 1;
|
|
|
|
} else {
|
|
|
|
for (k = 0; k < size_squared; k++) {
|
|
|
|
if (grid[k] & 0x01) {
|
|
|
|
eval[k] = mask[k] ^ 0xff;
|
2016-02-21 00:29:19 +13:00
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
eval[k] = mask[k];
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
/* Evaluate result */
|
|
|
|
best_pattern = 0;
|
|
|
|
for (pattern = 0; pattern < 4; pattern++) {
|
|
|
|
value[pattern] = micro_evaluate(eval, size, pattern);
|
|
|
|
if (value[pattern] > value[best_pattern]) {
|
|
|
|
best_pattern = pattern;
|
|
|
|
}
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13: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, value[pattern]);
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
printf("\n");
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Apply mask */
|
2020-11-28 01:54:44 +13:00
|
|
|
bit = 1 << best_pattern;
|
|
|
|
for (k = 0; k < size_squared; k++) {
|
|
|
|
if (mask[k] & bit) {
|
|
|
|
if (grid[k] & 0x01) {
|
|
|
|
grid[k] = 0x00;
|
|
|
|
} else {
|
|
|
|
grid[k] = 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
return best_pattern;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
INTERNAL int microqr(struct zint_symbol *symbol, unsigned char source[], int length) {
|
|
|
|
int i, size, j;
|
2016-02-21 00:29:19 +13:00
|
|
|
char full_stream[200];
|
2020-04-03 02:41:13 +13:00
|
|
|
int full_multibyte;
|
2020-11-28 01:54:44 +13:00
|
|
|
int user_mask;
|
2017-05-29 21:43:47 +12:00
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
unsigned int jisdata[40];
|
2016-02-21 00:29:19 +13:00
|
|
|
char mode[40];
|
2019-12-17 06:31:52 +13:00
|
|
|
int alpha_used = 0, byte_or_kanji_used = 0;
|
2016-02-21 00:29:19 +13:00
|
|
|
int version_valid[4];
|
|
|
|
int binary_count[4];
|
|
|
|
int ecc_level, autoversion, version;
|
2019-12-17 06:31:52 +13:00
|
|
|
int bitmask, format, format_full;
|
2020-11-28 01:54:44 +13:00
|
|
|
int size_squared;
|
|
|
|
int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
|
2016-02-21 00:29:19 +13:00
|
|
|
#ifdef _MSC_VER
|
2021-06-20 00:11:23 +12:00
|
|
|
unsigned char *grid;
|
2016-02-17 23:52:48 +13:00
|
|
|
#endif
|
2016-02-19 01:17:50 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
if (length > 35) {
|
2017-07-28 03:01:53 +12:00
|
|
|
strcpy(symbol->errtxt, "562: Input data too long");
|
2016-02-21 00:29:19 +13:00
|
|
|
return ZINT_ERROR_TOO_LONG;
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-09-02 22:43:14 +12:00
|
|
|
/* Check option 1 in combination with option 2 */
|
|
|
|
ecc_level = LEVEL_L;
|
|
|
|
if (symbol->option_1 >= 1 && symbol->option_1 <= 4) {
|
|
|
|
if (symbol->option_1 == 4) {
|
|
|
|
strcpy(symbol->errtxt, "566: Error correction level H not available");
|
|
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
|
|
}
|
|
|
|
if (symbol->option_2 >= 1 && symbol->option_2 <= 4) {
|
|
|
|
if (symbol->option_2 == 1 && symbol->option_1 != 1) {
|
|
|
|
strcpy(symbol->errtxt, "574: Version M1 supports error correction level L only");
|
|
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
|
|
}
|
|
|
|
if (symbol->option_2 != 4 && symbol->option_1 == 3) {
|
2021-08-21 03:50:39 +12:00
|
|
|
strcpy(symbol->errtxt, "563: Error correction level Q requires Version M4");
|
2019-09-02 22:43:14 +12:00
|
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ecc_level = symbol->option_1;
|
|
|
|
}
|
|
|
|
|
2021-01-12 07:11:41 +13:00
|
|
|
/* If ZINT_FULL_MULTIBYTE use Kanji mode in DATA_MODE or for non-Shift JIS in UNICODE_MODE */
|
2020-11-28 01:54:44 +13:00
|
|
|
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 */
|
|
|
|
}
|
2020-04-03 02:41:13 +13:00
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
if ((symbol->input_mode & 0x07) == DATA_MODE) {
|
2020-04-03 02:41:13 +13:00
|
|
|
sjis_cpy(source, &length, jisdata, full_multibyte);
|
2016-08-16 23:43:41 +12:00
|
|
|
} else {
|
2019-11-28 05:16:14 +13:00
|
|
|
/* Try ISO 8859-1 conversion first */
|
2021-01-12 07:11:41 +13:00
|
|
|
int error_number = sjis_utf8_to_eci(3, source, &length, jisdata, full_multibyte);
|
2016-08-16 23:43:41 +12:00
|
|
|
if (error_number != 0) {
|
2019-11-28 05:16:14 +13:00
|
|
|
/* Try Shift-JIS */
|
2021-01-12 07:11:41 +13:00
|
|
|
error_number = sjis_utf8(symbol, source, &length, jisdata);
|
2019-11-28 05:16:14 +13:00
|
|
|
if (error_number != 0) {
|
|
|
|
return error_number;
|
2016-02-19 01:17:50 +13:00
|
|
|
}
|
2016-08-16 23:43:41 +12:00
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
/* Determine if alpha (excluding numerics), byte or kanji used */
|
|
|
|
for (i = 0; i < length && (alpha_used == 0 || byte_or_kanji_used == 0); i++) {
|
|
|
|
if (jisdata[i] < '0' || jisdata[i] > '9') {
|
|
|
|
if (is_alpha(jisdata[i], 0 /*gs1*/)) {
|
|
|
|
alpha_used = 1;
|
|
|
|
} else {
|
|
|
|
byte_or_kanji_used = 1;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
version_valid[i] = 1;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
/* Eliminate possible versions depending on type of content */
|
2019-12-17 06:31:52 +13:00
|
|
|
if (byte_or_kanji_used) {
|
2016-02-21 00:29:19 +13:00
|
|
|
version_valid[0] = 0;
|
|
|
|
version_valid[1] = 0;
|
2019-12-17 06:31:52 +13:00
|
|
|
} else if (alpha_used) {
|
2016-02-21 00:29:19 +13:00
|
|
|
version_valid[0] = 0;
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
/* Eliminate possible versions depending on error correction level specified */
|
|
|
|
if (ecc_level == LEVEL_Q) {
|
2016-02-21 00:29:19 +13:00
|
|
|
version_valid[0] = 0;
|
|
|
|
version_valid[1] = 0;
|
2019-12-17 06:31:52 +13:00
|
|
|
version_valid[2] = 0;
|
|
|
|
} else if (ecc_level == LEVEL_M) {
|
|
|
|
version_valid[0] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine length of binary data */
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
if (version_valid[i]) {
|
2021-06-20 00:11:23 +12:00
|
|
|
binary_count[i] = getBinaryLength(MICROQR_VERSION + i, mode, jisdata, length, 0 /*gs1*/, 0 /*eci*/,
|
|
|
|
debug_print);
|
2019-12-17 06:31:52 +13:00
|
|
|
} else {
|
|
|
|
binary_count[i] = 128 + 1;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
/* Eliminate possible versions depending on length of binary data */
|
|
|
|
if (binary_count[0] > 20) {
|
|
|
|
version_valid[0] = 0;
|
|
|
|
}
|
|
|
|
if (binary_count[1] > 40) {
|
|
|
|
version_valid[1] = 0;
|
|
|
|
}
|
|
|
|
if (binary_count[2] > 84) {
|
|
|
|
version_valid[2] = 0;
|
|
|
|
}
|
|
|
|
if (binary_count[3] > 128) {
|
2017-07-28 03:01:53 +12:00
|
|
|
strcpy(symbol->errtxt, "565: Input data too long");
|
2016-02-21 00:29:19 +13:00
|
|
|
return ZINT_ERROR_TOO_LONG;
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
/* Eliminate possible versions depending on binary length and error correction level specified */
|
2016-02-21 00:29:19 +13:00
|
|
|
if (ecc_level == LEVEL_Q) {
|
|
|
|
if (binary_count[3] > 80) {
|
2017-07-28 03:01:53 +12:00
|
|
|
strcpy(symbol->errtxt, "567: Input data too long");
|
2016-02-21 00:29:19 +13:00
|
|
|
return ZINT_ERROR_TOO_LONG;
|
|
|
|
}
|
2019-12-17 06:31:52 +13:00
|
|
|
} else if (ecc_level == LEVEL_M) {
|
2016-02-21 00:29:19 +13:00
|
|
|
if (binary_count[1] > 32) {
|
|
|
|
version_valid[1] = 0;
|
|
|
|
}
|
|
|
|
if (binary_count[2] > 68) {
|
|
|
|
version_valid[2] = 0;
|
|
|
|
}
|
|
|
|
if (binary_count[3] > 112) {
|
2017-07-28 03:01:53 +12:00
|
|
|
strcpy(symbol->errtxt, "568: Input data too long");
|
2016-02-21 00:29:19 +13:00
|
|
|
return ZINT_ERROR_TOO_LONG;
|
|
|
|
}
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
autoversion = 3;
|
|
|
|
if (version_valid[2]) {
|
|
|
|
autoversion = 2;
|
|
|
|
}
|
|
|
|
if (version_valid[1]) {
|
|
|
|
autoversion = 1;
|
|
|
|
}
|
|
|
|
if (version_valid[0]) {
|
|
|
|
autoversion = 0;
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
version = autoversion;
|
|
|
|
/* Get version from user */
|
|
|
|
if ((symbol->option_2 >= 1) && (symbol->option_2 <= 4)) {
|
2019-09-02 22:43:14 +12:00
|
|
|
if (symbol->option_2 - 1 >= autoversion) {
|
|
|
|
version = symbol->option_2 - 1;
|
2017-04-11 20:26:39 +12:00
|
|
|
} else {
|
2017-07-28 03:01:53 +12:00
|
|
|
strcpy(symbol->errtxt, "570: Input too long for selected symbol size");
|
2017-04-11 20:26:39 +12:00
|
|
|
return ZINT_ERROR_TOO_LONG;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2019-12-17 06:31:52 +13:00
|
|
|
/* If there is enough unused space then increase the error correction level, unless user-specified */
|
|
|
|
if (symbol->option_1 == -1 || symbol->option_1 != ecc_level) {
|
|
|
|
if (version == 3) {
|
|
|
|
if (binary_count[3] <= 112) {
|
|
|
|
ecc_level = LEVEL_M;
|
|
|
|
}
|
|
|
|
if (binary_count[3] <= 80) {
|
|
|
|
ecc_level = LEVEL_Q;
|
|
|
|
}
|
|
|
|
} else if (version == 2) {
|
|
|
|
if (binary_count[2] <= 68) {
|
|
|
|
ecc_level = LEVEL_M;
|
|
|
|
}
|
|
|
|
} else if (version == 1) {
|
|
|
|
if (binary_count[1] <= 32) {
|
|
|
|
ecc_level = LEVEL_M;
|
|
|
|
}
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
}
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
qr_define_mode(mode, jisdata, length, 0 /*gs1*/, MICROQR_VERSION + version, debug_print);
|
2016-02-17 23:52:48 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
qr_binary((unsigned char *) full_stream, MICROQR_VERSION + version, 0 /*target_codewords*/, mode, jisdata, length,
|
|
|
|
0 /*gs1*/, 0 /*eci*/, binary_count[version], debug_print);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2021-08-27 04:49:05 +12:00
|
|
|
if (debug_print) printf("Binary (%d): %s\n", (int) strlen(full_stream), full_stream);
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
switch (version) {
|
2019-11-28 05:16:14 +13:00
|
|
|
case 0: micro_qr_m1(symbol, full_stream);
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
2019-11-28 05:16:14 +13:00
|
|
|
case 1: micro_qr_m2(symbol, full_stream, ecc_level);
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
2019-11-28 05:16:14 +13:00
|
|
|
case 2: micro_qr_m3(symbol, full_stream, ecc_level);
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
2019-11-28 05:16:14 +13:00
|
|
|
case 3: micro_qr_m4(symbol, full_stream, ecc_level);
|
2016-02-21 00:29:19 +13:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = micro_qr_sizes[version];
|
2020-11-28 01:54:44 +13:00
|
|
|
size_squared = size * size;
|
2016-02-17 23:52:48 +13:00
|
|
|
#ifndef _MSC_VER
|
2020-11-28 01:54:44 +13:00
|
|
|
unsigned char grid[size_squared];
|
2016-02-17 23:52:48 +13:00
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
grid = (unsigned char *) _alloca(size_squared);
|
2016-02-17 23:52:48 +13:00
|
|
|
#endif
|
2016-02-21 00:29:19 +13:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
memset(grid, 0, size_squared);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
micro_setup_grid(grid, size);
|
|
|
|
micro_populate_grid(grid, size, full_stream);
|
2020-11-28 01:54:44 +13:00
|
|
|
bitmask = micro_apply_bitmask(grid, size, user_mask, debug_print);
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
/* Add format data */
|
|
|
|
format = 0;
|
|
|
|
switch (version) {
|
|
|
|
case 1: switch (ecc_level) {
|
|
|
|
case 1: format = 1;
|
|
|
|
break;
|
|
|
|
case 2: format = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2: switch (ecc_level) {
|
|
|
|
case 1: format = 3;
|
|
|
|
break;
|
|
|
|
case 2: format = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3: switch (ecc_level) {
|
|
|
|
case 1: format = 5;
|
|
|
|
break;
|
|
|
|
case 2: format = 6;
|
|
|
|
break;
|
|
|
|
case 3: format = 7;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-08-10 23:04:25 +12:00
|
|
|
if (debug_print) {
|
2021-08-21 03:50:39 +12:00
|
|
|
printf("Version: M%d-%c, Size: %dx%d, Format: %d\n",
|
|
|
|
version + 1, ecc_level_names[ecc_level - 1], size, size, format);
|
2021-08-10 23:04:25 +12:00
|
|
|
}
|
|
|
|
|
2016-02-21 00:29:19 +13:00
|
|
|
format_full = qr_annex_c1[(format << 2) + bitmask];
|
|
|
|
|
|
|
|
if (format_full & 0x4000) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + 1] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x2000) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + 2] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x1000) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + 3] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x800) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + 4] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x400) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + 5] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x200) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + 6] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x100) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + 7] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x80) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(8 * size) + 8] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x40) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(7 * size) + 8] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x20) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(6 * size) + 8] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x10) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(5 * size) + 8] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x08) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(4 * size) + 8] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x04) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(3 * size) + 8] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x02) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(2 * size) + 8] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
if (format_full & 0x01) {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[(1 * size) + 8] |= 0x01;
|
2016-02-21 00:29:19 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
symbol->width = size;
|
|
|
|
symbol->rows = size;
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
for (j = 0; j < size; j++) {
|
|
|
|
if (grid[(i * size) + j] & 0x01) {
|
|
|
|
set_module(symbol, i, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
symbol->row_height[i] = 1;
|
|
|
|
}
|
2021-06-20 00:11:23 +12:00
|
|
|
symbol->height = size;
|
2016-02-21 00:29:19 +13:00
|
|
|
|
|
|
|
return 0;
|
2016-02-17 23:52:48 +13:00
|
|
|
}
|
2017-05-21 10:37:50 +12:00
|
|
|
|
|
|
|
/* For UPNQR the symbol size and error correction capacity is fixed */
|
2020-11-28 01:54:44 +13:00
|
|
|
INTERNAL int upnqr(struct zint_symbol *symbol, unsigned char source[], int length) {
|
|
|
|
int i, j, r, est_binlen;
|
2019-11-26 08:08:25 +13:00
|
|
|
int ecc_level, version, target_codewords, blocks, size;
|
2017-05-21 20:20:03 +12:00
|
|
|
int bitmask, error_number;
|
2020-11-28 01:54:44 +13:00
|
|
|
int size_squared;
|
|
|
|
int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
|
2017-05-21 10:37:50 +12:00
|
|
|
|
|
|
|
#ifndef _MSC_VER
|
2019-11-28 05:16:14 +13:00
|
|
|
unsigned int jisdata[length + 1];
|
2017-05-21 10:37:50 +12:00
|
|
|
char mode[length + 1];
|
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
unsigned char *datastream;
|
|
|
|
unsigned char *fullstream;
|
|
|
|
unsigned char *grid;
|
|
|
|
unsigned int *jisdata = (unsigned int *) _alloca((length + 1) * sizeof(unsigned int));
|
|
|
|
char *mode = (char *) _alloca(length + 1);
|
2017-05-21 10:37:50 +12:00
|
|
|
#endif
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2017-05-21 20:20:03 +12:00
|
|
|
#ifndef _MSC_VER
|
|
|
|
unsigned char preprocessed[length + 1];
|
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
unsigned char *preprocessed = (unsigned char *) _alloca(length + 1);
|
2017-05-21 20:20:03 +12:00
|
|
|
#endif
|
2017-05-21 10:37:50 +12:00
|
|
|
|
2019-11-28 05:16:14 +13:00
|
|
|
symbol->eci = 4; /* Set before any processing */
|
|
|
|
|
|
|
|
switch (symbol->input_mode & 0x07) {
|
2017-05-21 20:20:03 +12:00
|
|
|
case DATA_MODE:
|
2017-05-21 10:37:50 +12:00
|
|
|
/* Input is already in ISO-8859-2 format */
|
2020-11-28 01:54:44 +13:00
|
|
|
for (i = 0; i < length; i++) {
|
2019-11-28 05:16:14 +13:00
|
|
|
jisdata[i] = source[i];
|
2017-05-21 10:37:50 +12:00
|
|
|
mode[i] = 'B';
|
|
|
|
}
|
|
|
|
break;
|
2021-08-06 03:34:45 +12:00
|
|
|
case GS1_MODE: /* Should never happen as checked before being called */
|
|
|
|
strcpy(symbol->errtxt, "571: UPNQR does not support GS-1 encoding"); /* Not reached */
|
2017-05-21 20:20:03 +12:00
|
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
|
|
break;
|
|
|
|
case UNICODE_MODE:
|
2021-01-12 07:11:41 +13:00
|
|
|
error_number = utf8_to_eci(4, source, preprocessed, &length);
|
2017-05-21 20:20:03 +12:00
|
|
|
if (error_number != 0) {
|
2021-07-07 06:53:31 +12:00
|
|
|
strcpy(symbol->errtxt, "572: Invalid character in input data for ECI 4");
|
2017-05-21 20:20:03 +12:00
|
|
|
return error_number;
|
|
|
|
}
|
2020-11-28 01:54:44 +13:00
|
|
|
for (i = 0; i < length; i++) {
|
2019-11-28 05:16:14 +13:00
|
|
|
jisdata[i] = preprocessed[i];
|
2017-05-21 20:20:03 +12:00
|
|
|
mode[i] = 'B';
|
|
|
|
}
|
2017-05-21 10:37:50 +12:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
est_binlen = getBinaryLength(15, mode, jisdata, length, 0, symbol->eci, debug_print);
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2017-05-21 10:37:50 +12:00
|
|
|
ecc_level = LEVEL_M;
|
|
|
|
|
2017-06-06 07:05:58 +12:00
|
|
|
if (est_binlen > 3320) {
|
2017-07-28 03:01:53 +12:00
|
|
|
strcpy(symbol->errtxt, "573: Input too long for selected symbol");
|
2017-05-21 10:37:50 +12:00
|
|
|
return ZINT_ERROR_TOO_LONG;
|
|
|
|
}
|
|
|
|
|
|
|
|
version = 15; // 77 x 77
|
|
|
|
|
2019-11-26 08:08:25 +13:00
|
|
|
target_codewords = qr_data_codewords_M[version - 1];
|
2017-05-21 10:37:50 +12:00
|
|
|
blocks = qr_blocks_M[version - 1];
|
|
|
|
#ifndef _MSC_VER
|
2019-12-02 04:17:08 +13:00
|
|
|
unsigned char datastream[target_codewords + 1];
|
2019-11-28 05:16:14 +13:00
|
|
|
unsigned char fullstream[qr_total_codewords[version - 1] + 1];
|
2017-05-21 10:37:50 +12:00
|
|
|
#else
|
2019-12-02 04:17:08 +13:00
|
|
|
datastream = (unsigned char *) _alloca(target_codewords + 1);
|
2019-11-28 05:16:14 +13:00
|
|
|
fullstream = (unsigned char *) _alloca(qr_total_codewords[version - 1] + 1);
|
2017-05-21 10:37:50 +12:00
|
|
|
#endif
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
qr_binary(datastream, version, target_codewords, mode, jisdata, length, 0, symbol->eci, est_binlen, debug_print);
|
2019-12-19 13:37:55 +13:00
|
|
|
#ifdef ZINT_TEST
|
2019-12-17 06:31:52 +13:00
|
|
|
if (symbol->debug & ZINT_DEBUG_TEST) debug_test_codeword_dump(symbol, datastream, target_codewords);
|
2019-12-19 13:37:55 +13:00
|
|
|
#endif
|
2020-11-28 01:54:44 +13:00
|
|
|
add_ecc(fullstream, datastream, version, target_codewords, blocks, debug_print);
|
2017-05-21 10:37:50 +12:00
|
|
|
|
|
|
|
size = qr_sizes[version - 1];
|
2020-11-28 01:54:44 +13:00
|
|
|
size_squared = size * size;
|
2017-05-21 10:37:50 +12:00
|
|
|
#ifndef _MSC_VER
|
2020-11-28 01:54:44 +13:00
|
|
|
unsigned char grid[size_squared];
|
2017-05-21 10:37:50 +12:00
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
grid = (unsigned char *) _alloca(size_squared);
|
2017-05-21 10:37:50 +12:00
|
|
|
#endif
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
memset(grid, 0, size_squared);
|
2017-05-21 10:37:50 +12:00
|
|
|
|
|
|
|
setup_grid(grid, size, version);
|
2019-11-26 08:08:25 +13:00
|
|
|
populate_grid(grid, size, size, fullstream, qr_total_codewords[version - 1]);
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2017-05-30 19:40:08 +12:00
|
|
|
add_version_info(grid, size, version);
|
2017-05-21 10:37:50 +12:00
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
bitmask = apply_bitmask(grid, size, ecc_level, 0 /*user_mask*/, debug_print);
|
2017-05-21 10:37:50 +12:00
|
|
|
|
|
|
|
add_format_info(grid, size, ecc_level, bitmask);
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2017-05-21 10:37:50 +12:00
|
|
|
symbol->width = size;
|
|
|
|
symbol->rows = size;
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
r = i * size;
|
2017-05-21 10:37:50 +12:00
|
|
|
for (j = 0; j < size; j++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
if (grid[r + j] & 0x01) {
|
2017-05-21 10:37:50 +12:00
|
|
|
set_module(symbol, i, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
symbol->row_height[i] = 1;
|
|
|
|
}
|
2021-06-20 00:11:23 +12:00
|
|
|
symbol->height = size;
|
2017-05-21 10:37:50 +12:00
|
|
|
|
|
|
|
return 0;
|
2017-05-29 21:43:47 +12:00
|
|
|
}
|
2017-10-24 08:34:31 +13:00
|
|
|
|
2021-06-20 00:11:23 +12:00
|
|
|
static void setup_rmqr_grid(unsigned char *grid, const int h_size, const int v_size) {
|
2019-11-26 08:08:25 +13:00
|
|
|
int i, j;
|
|
|
|
char alignment[] = {0x1F, 0x11, 0x15, 0x11, 0x1F};
|
|
|
|
int h_version, finder_position;
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2019-11-26 08:08:25 +13:00
|
|
|
/* Add timing patterns - top and bottom */
|
|
|
|
for (i = 0; i < h_size; i++) {
|
|
|
|
if (i % 2) {
|
|
|
|
grid[i] = 0x20;
|
|
|
|
grid[((v_size - 1) * h_size) + i] = 0x20;
|
|
|
|
} else {
|
|
|
|
grid[i] = 0x21;
|
|
|
|
grid[((v_size - 1) * h_size) + i] = 0x21;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add timing patterns - left and right */
|
|
|
|
for (i = 0; i < v_size; i++) {
|
|
|
|
if (i % 2) {
|
|
|
|
grid[i * h_size] = 0x20;
|
|
|
|
grid[(i * h_size) + (h_size - 1)] = 0x20;
|
|
|
|
} else {
|
|
|
|
grid[i * h_size] = 0x21;
|
|
|
|
grid[(i * h_size) + (h_size - 1)] = 0x21;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add finder pattern */
|
|
|
|
place_finder(grid, h_size, 0, 0); // This works because finder is always top left
|
|
|
|
|
|
|
|
/* Add finder sub-pattern to bottom right */
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
|
|
for (j = 0; j < 5; j++) {
|
|
|
|
if (alignment[j] & 0x10 >> i) {
|
|
|
|
grid[((v_size - 5) * h_size) + (h_size * i) + (h_size - 5) + j] = 0x11;
|
|
|
|
} else {
|
|
|
|
grid[((v_size - 5) * h_size) + (h_size * i) + (h_size - 5) + j] = 0x10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add corner finder pattern - bottom left */
|
|
|
|
grid[(v_size - 2) * h_size] = 0x11;
|
|
|
|
grid[((v_size - 2) * h_size) + 1] = 0x10;
|
|
|
|
grid[((v_size - 1) * h_size) + 1] = 0x11;
|
|
|
|
|
|
|
|
/* Add corner finder pattern - top right */
|
|
|
|
grid[h_size - 2] = 0x11;
|
|
|
|
grid[(h_size * 2) - 2] = 0x10;
|
|
|
|
grid[(h_size * 2) - 1] = 0x11;
|
|
|
|
|
|
|
|
/* Add seperator */
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
|
|
grid[(i * h_size) + 7] = 0x20;
|
|
|
|
}
|
|
|
|
if (v_size > 7) {
|
|
|
|
// Note for v_size = 9 this overrides the bottom right corner finder pattern
|
2021-08-10 23:04:25 +12:00
|
|
|
for (i = 0; i < 8; i++) {
|
2019-11-26 08:08:25 +13:00
|
|
|
grid[(7 * h_size) + i] = 0x20;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add alignment patterns */
|
|
|
|
if (h_size > 27) {
|
2020-11-28 01:54:44 +13:00
|
|
|
h_version = 0; // Suppress compiler warning [-Wmaybe-uninitialized]
|
2021-08-10 23:04:25 +12:00
|
|
|
for (i = 0; i < 5; i++) {
|
2019-11-26 08:08:25 +13:00
|
|
|
if (h_size == rmqr_width[i]) {
|
|
|
|
h_version = i;
|
2020-11-28 01:54:44 +13:00
|
|
|
break;
|
2019-11-26 08:08:25 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-10 23:04:25 +12:00
|
|
|
for (i = 0; i < 4; i++) {
|
2019-11-26 08:08:25 +13:00
|
|
|
finder_position = rmqr_table_d1[(h_version * 4) + i];
|
|
|
|
|
|
|
|
if (finder_position != 0) {
|
|
|
|
for (j = 0; j < v_size; j++) {
|
|
|
|
if (j % 2) {
|
|
|
|
grid[(j * h_size) + finder_position] = 0x10;
|
|
|
|
} else {
|
|
|
|
grid[(j * h_size) + finder_position] = 0x11;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Top square
|
|
|
|
grid[h_size + finder_position - 1] = 0x11;
|
|
|
|
grid[(h_size * 2) + finder_position - 1] = 0x11;
|
|
|
|
grid[h_size + finder_position + 1] = 0x11;
|
|
|
|
grid[(h_size * 2) + finder_position + 1] = 0x11;
|
|
|
|
|
|
|
|
// Bottom square
|
|
|
|
grid[(h_size * (v_size - 3)) + finder_position - 1] = 0x11;
|
|
|
|
grid[(h_size * (v_size - 2)) + finder_position - 1] = 0x11;
|
|
|
|
grid[(h_size * (v_size - 3)) + finder_position + 1] = 0x11;
|
|
|
|
grid[(h_size * (v_size - 2)) + finder_position + 1] = 0x11;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reserve space for format information */
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
|
|
grid[(h_size * (i + 1)) + j + 8] = 0x20;
|
|
|
|
grid[(h_size * (v_size - 6)) + (h_size * i) + j + (h_size - 8)] = 0x20;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
grid[(h_size * 1) + 11] = 0x20;
|
|
|
|
grid[(h_size * 2) + 11] = 0x20;
|
|
|
|
grid[(h_size * 3) + 11] = 0x20;
|
|
|
|
grid[(h_size * (v_size - 6)) + (h_size - 5)] = 0x20;
|
|
|
|
grid[(h_size * (v_size - 6)) + (h_size - 4)] = 0x20;
|
|
|
|
grid[(h_size * (v_size - 6)) + (h_size - 3)] = 0x20;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* rMQR according to 2018 draft standard */
|
2020-11-28 01:54:44 +13:00
|
|
|
INTERNAL int rmqr(struct zint_symbol *symbol, unsigned char source[], int length) {
|
2019-11-26 08:08:25 +13:00
|
|
|
int i, j, est_binlen;
|
|
|
|
int ecc_level, autosize, version, max_cw, target_codewords, blocks, h_size, v_size;
|
|
|
|
int gs1;
|
2020-04-03 02:41:13 +13:00
|
|
|
int full_multibyte;
|
2019-11-26 08:08:25 +13:00
|
|
|
int footprint, best_footprint, format_data;
|
|
|
|
unsigned int left_format_info, right_format_info;
|
2020-11-28 01:54:44 +13:00
|
|
|
int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
|
2021-08-21 03:50:39 +12:00
|
|
|
int eci_length = get_eci_length(symbol->eci, source, length);
|
2019-11-26 08:08:25 +13:00
|
|
|
|
|
|
|
#ifndef _MSC_VER
|
2021-08-21 03:50:39 +12:00
|
|
|
unsigned int jisdata[eci_length + 1];
|
|
|
|
char mode[eci_length + 1];
|
2019-11-26 08:08:25 +13:00
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
unsigned char *datastream;
|
|
|
|
unsigned char *fullstream;
|
|
|
|
unsigned char *grid;
|
2021-08-21 03:50:39 +12:00
|
|
|
unsigned int *jisdata = (unsigned int *) _alloca((eci_length + 1) * sizeof(unsigned int));
|
|
|
|
char *mode = (char *) _alloca(eci_length + 1);
|
2019-11-26 08:08:25 +13:00
|
|
|
#endif
|
|
|
|
|
2019-12-02 04:17:08 +13:00
|
|
|
gs1 = ((symbol->input_mode & 0x07) == GS1_MODE);
|
2021-01-12 07:11:41 +13:00
|
|
|
/* If ZINT_FULL_MULTIBYTE use Kanji mode in DATA_MODE or for non-Shift JIS in UNICODE_MODE */
|
2020-11-28 01:54:44 +13:00
|
|
|
full_multibyte = (symbol->option_3 & 0xFF) == ZINT_FULL_MULTIBYTE;
|
2017-10-24 08:37:52 +13:00
|
|
|
|
2019-12-02 04:17:08 +13:00
|
|
|
if ((symbol->input_mode & 0x07) == DATA_MODE) {
|
2020-04-03 02:41:13 +13:00
|
|
|
sjis_cpy(source, &length, jisdata, full_multibyte);
|
2019-11-26 08:08:25 +13:00
|
|
|
} else {
|
2021-08-21 03:50:39 +12:00
|
|
|
int done = 0;
|
|
|
|
if (symbol->eci != 20) { /* Unless ECI 20 (Shift JIS) */
|
|
|
|
/* Try other encodings (ECI 0 defaults to ISO/IEC 8859-1) */
|
|
|
|
int error_number = sjis_utf8_to_eci(symbol->eci, source, &length, jisdata, full_multibyte);
|
|
|
|
if (error_number == 0) {
|
|
|
|
done = 1;
|
|
|
|
} else if (symbol->eci) {
|
|
|
|
sprintf(symbol->errtxt, "564: Invalid character in input data for ECI %d", symbol->eci);
|
|
|
|
return error_number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!done) {
|
2019-12-02 04:17:08 +13:00
|
|
|
/* Try Shift-JIS */
|
2021-08-21 03:50:39 +12:00
|
|
|
int error_number = sjis_utf8(symbol, source, &length, jisdata);
|
2019-12-02 04:17:08 +13:00
|
|
|
if (error_number != 0) {
|
|
|
|
return error_number;
|
2019-11-26 08:08:25 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-21 03:50:39 +12:00
|
|
|
est_binlen = getBinaryLength(RMQR_VERSION + 31, mode, jisdata, length, gs1, symbol->eci, debug_print);
|
2019-11-26 08:08:25 +13:00
|
|
|
|
|
|
|
ecc_level = LEVEL_M;
|
|
|
|
max_cw = 152;
|
|
|
|
if (symbol->option_1 == 1) {
|
|
|
|
strcpy(symbol->errtxt, "576: Error correction level L not available in rMQR");
|
|
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (symbol->option_1 == 3) {
|
|
|
|
strcpy(symbol->errtxt, "577: Error correction level Q not available in rMQR");
|
|
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (symbol->option_1 == 4) {
|
|
|
|
ecc_level = LEVEL_H;
|
|
|
|
max_cw = 76;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (est_binlen > (8 * max_cw)) {
|
|
|
|
strcpy(symbol->errtxt, "578: Input too long for selected error correction level");
|
|
|
|
return ZINT_ERROR_TOO_LONG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((symbol->option_2 < 0) || (symbol->option_2 > 38)) {
|
|
|
|
strcpy(symbol->errtxt, "579: Invalid rMQR symbol size");
|
|
|
|
return ZINT_ERROR_INVALID_OPTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
version = 31; // Set default to keep compiler happy
|
|
|
|
|
|
|
|
if (symbol->option_2 == 0) {
|
|
|
|
// Automatic symbol size
|
|
|
|
autosize = 31;
|
|
|
|
best_footprint = rmqr_height[31] * rmqr_width[31];
|
|
|
|
for (version = 30; version >= 0; version--) {
|
2021-08-23 00:59:01 +12:00
|
|
|
est_binlen = getBinaryLength(RMQR_VERSION + version, mode, jisdata, length, gs1, symbol->eci,
|
|
|
|
debug_print);
|
2019-11-26 08:08:25 +13:00
|
|
|
footprint = rmqr_height[version] * rmqr_width[version];
|
|
|
|
if (ecc_level == LEVEL_M) {
|
2019-11-28 08:32:40 +13:00
|
|
|
if (8 * rmqr_data_codewords_M[version] >= est_binlen) {
|
2019-11-26 08:08:25 +13:00
|
|
|
if (footprint < best_footprint) {
|
|
|
|
autosize = version;
|
|
|
|
best_footprint = footprint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2019-11-28 08:32:40 +13:00
|
|
|
if (8 * rmqr_data_codewords_H[version] >= est_binlen) {
|
2019-11-26 08:08:25 +13:00
|
|
|
if (footprint < best_footprint) {
|
|
|
|
autosize = version;
|
|
|
|
best_footprint = footprint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
version = autosize;
|
2021-08-21 03:50:39 +12:00
|
|
|
est_binlen = getBinaryLength(RMQR_VERSION + version, mode, jisdata, length, gs1, symbol->eci, debug_print);
|
2019-11-26 08:08:25 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((symbol->option_2 >= 1) && (symbol->option_2 <= 32)) {
|
|
|
|
// User specified symbol size
|
|
|
|
version = symbol->option_2 - 1;
|
2021-08-21 03:50:39 +12:00
|
|
|
est_binlen = getBinaryLength(RMQR_VERSION + version, mode, jisdata, length, gs1, symbol->eci, debug_print);
|
2019-11-26 08:08:25 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
if (symbol->option_2 >= 33) {
|
|
|
|
// User has specified symbol height only
|
|
|
|
version = rmqr_fixed_height_upper_bound[symbol->option_2 - 32];
|
2021-08-10 23:04:25 +12:00
|
|
|
for (i = version - 1; i > rmqr_fixed_height_upper_bound[symbol->option_2 - 33]; i--) {
|
2021-08-21 03:50:39 +12:00
|
|
|
est_binlen = getBinaryLength(RMQR_VERSION + i, mode, jisdata, length, gs1, symbol->eci, debug_print);
|
2019-11-26 08:08:25 +13:00
|
|
|
if (ecc_level == LEVEL_M) {
|
2019-11-28 08:32:40 +13:00
|
|
|
if (8 * rmqr_data_codewords_M[i] >= est_binlen) {
|
2019-11-26 08:08:25 +13:00
|
|
|
version = i;
|
|
|
|
}
|
|
|
|
} else {
|
2019-11-28 08:32:40 +13:00
|
|
|
if (8 * rmqr_data_codewords_H[i] >= est_binlen) {
|
2019-11-26 08:08:25 +13:00
|
|
|
version = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-21 03:50:39 +12:00
|
|
|
est_binlen = getBinaryLength(RMQR_VERSION + version, mode, jisdata, length, gs1, symbol->eci, debug_print);
|
2019-11-26 08:08:25 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
if (symbol->option_1 == -1) {
|
|
|
|
// Detect if there is enough free space to increase ECC level
|
|
|
|
if (est_binlen < (rmqr_data_codewords_H[version] * 8)) {
|
|
|
|
ecc_level = LEVEL_H;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ecc_level == LEVEL_M) {
|
|
|
|
target_codewords = rmqr_data_codewords_M[version];
|
|
|
|
blocks = rmqr_blocks_M[version];
|
|
|
|
} else {
|
|
|
|
target_codewords = rmqr_data_codewords_H[version];
|
|
|
|
blocks = rmqr_blocks_H[version];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (est_binlen > (target_codewords * 8)) {
|
|
|
|
// User has selected a symbol too small for the data
|
|
|
|
strcpy(symbol->errtxt, "580: Input too long for selected symbol size");
|
|
|
|
return ZINT_ERROR_TOO_LONG;
|
|
|
|
}
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
if (debug_print) {
|
2021-08-21 03:50:39 +12:00
|
|
|
printf("Minimum codewords: %d\n", (est_binlen + 7) / 8);
|
|
|
|
printf("Selected version: %d = R%dx%d-%c\n",
|
|
|
|
(version + 1), rmqr_height[version], rmqr_width[version], ecc_level_names[ecc_level - 1]);
|
|
|
|
printf("Number of data codewords in symbol: %d\n", target_codewords);
|
|
|
|
printf("Number of ECC blocks: %d\n", blocks);
|
2019-11-26 08:08:25 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef _MSC_VER
|
2019-12-02 04:17:08 +13:00
|
|
|
unsigned char datastream[target_codewords + 1];
|
|
|
|
unsigned char fullstream[rmqr_total_codewords[version] + 1];
|
2019-11-26 08:08:25 +13:00
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
datastream = (unsigned char *) _alloca(target_codewords + 1);
|
|
|
|
fullstream = (unsigned char *) _alloca(rmqr_total_codewords[version] + 1);
|
2019-11-26 08:08:25 +13:00
|
|
|
#endif
|
|
|
|
|
2021-08-23 00:59:01 +12:00
|
|
|
qr_binary(datastream, RMQR_VERSION + version, target_codewords, mode, jisdata, length, gs1, symbol->eci,
|
|
|
|
est_binlen, debug_print);
|
2019-12-19 13:37:55 +13:00
|
|
|
#ifdef ZINT_TEST
|
2019-12-17 06:31:52 +13:00
|
|
|
if (symbol->debug & ZINT_DEBUG_TEST) debug_test_codeword_dump(symbol, datastream, target_codewords);
|
2019-12-19 13:37:55 +13:00
|
|
|
#endif
|
2020-11-28 01:54:44 +13:00
|
|
|
add_ecc(fullstream, datastream, RMQR_VERSION + version, target_codewords, blocks, debug_print);
|
2019-11-26 08:08:25 +13:00
|
|
|
|
|
|
|
h_size = rmqr_width[version];
|
|
|
|
v_size = rmqr_height[version];
|
|
|
|
|
|
|
|
#ifndef _MSC_VER
|
|
|
|
unsigned char grid[h_size * v_size];
|
|
|
|
#else
|
2021-06-20 00:11:23 +12:00
|
|
|
grid = (unsigned char *) _alloca(h_size * v_size);
|
2019-11-26 08:08:25 +13:00
|
|
|
#endif
|
|
|
|
|
2020-11-28 01:54:44 +13:00
|
|
|
memset(grid, 0, h_size * v_size);
|
2019-11-26 08:08:25 +13:00
|
|
|
|
2020-03-30 23:59:16 +13:00
|
|
|
setup_rmqr_grid(grid, h_size, v_size);
|
2019-11-26 08:08:25 +13:00
|
|
|
populate_grid(grid, h_size, v_size, fullstream, rmqr_total_codewords[version]);
|
|
|
|
|
|
|
|
/* apply bitmask */
|
|
|
|
for (i = 0; i < v_size; i++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
int r = i * h_size;
|
2019-11-26 08:08:25 +13:00
|
|
|
for (j = 0; j < h_size; j++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
if ((grid[r + j] & 0xf0) == 0) {
|
2019-11-26 08:08:25 +13:00
|
|
|
// This is a data module
|
|
|
|
if (((i / 2) + (j / 3)) % 2 == 0) { // < This is the data mask from section 7.8.2
|
|
|
|
// This module needs to be changed
|
2020-11-28 01:54:44 +13:00
|
|
|
if (grid[r + j] == 0x01) {
|
|
|
|
grid[r + j] = 0x00;
|
2019-11-26 08:08:25 +13:00
|
|
|
} else {
|
2020-11-28 01:54:44 +13:00
|
|
|
grid[r + j] = 0x01;
|
2019-11-26 08:08:25 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add format information */
|
|
|
|
format_data = version;
|
|
|
|
if (ecc_level == LEVEL_H) {
|
|
|
|
format_data += 32;
|
|
|
|
}
|
|
|
|
left_format_info = rmqr_format_info_left[format_data];
|
|
|
|
right_format_info = rmqr_format_info_right[format_data];
|
|
|
|
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
|
|
grid[(h_size * (i + 1)) + j + 8] = (left_format_info >> ((j * 5) + i)) & 0x01;
|
2021-06-20 00:11:23 +12:00
|
|
|
grid[(h_size * (v_size - 6)) + (h_size * i) + j + (h_size - 8)]
|
|
|
|
= (right_format_info >> ((j * 5) + i)) & 0x01;
|
2019-11-26 08:08:25 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
grid[(h_size * 1) + 11] = (left_format_info >> 15) & 0x01;
|
|
|
|
grid[(h_size * 2) + 11] = (left_format_info >> 16) & 0x01;
|
|
|
|
grid[(h_size * 3) + 11] = (left_format_info >> 17) & 0x01;
|
|
|
|
grid[(h_size * (v_size - 6)) + (h_size - 5)] = (right_format_info >> 15) & 0x01;
|
|
|
|
grid[(h_size * (v_size - 6)) + (h_size - 4)] = (right_format_info >> 16) & 0x01;
|
|
|
|
grid[(h_size * (v_size - 6)) + (h_size - 3)] = (right_format_info >> 17) & 0x01;
|
|
|
|
|
|
|
|
symbol->width = h_size;
|
|
|
|
symbol->rows = v_size;
|
|
|
|
|
|
|
|
for (i = 0; i < v_size; i++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
int r = i * h_size;
|
2019-11-26 08:08:25 +13:00
|
|
|
for (j = 0; j < h_size; j++) {
|
2020-11-28 01:54:44 +13:00
|
|
|
if (grid[r + j] & 0x01) {
|
2019-11-26 08:08:25 +13:00
|
|
|
set_module(symbol, i, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
symbol->row_height[i] = 1;
|
|
|
|
}
|
2021-06-20 00:11:23 +12:00
|
|
|
symbol->height = v_size;
|
2019-11-26 08:08:25 +13:00
|
|
|
|
|
|
|
return 0;
|
2019-12-05 02:45:01 +13:00
|
|
|
}
|