zint/backend/micqr.c

960 lines
33 KiB
C
Raw Normal View History

/* micqr.c - Handles Micro QR Code versions M1 - M4 */
/*
libzint - the open source barcode library
2009-04-27 10:04:01 +12:00
Copyright (C) 2009 Robin Stuart <robin@zint.org.uk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
2009-10-07 08:03:00 +13:00
#include <malloc.h>
#include "common.h"
#include "reedsol.h"
#include "micqr.h"
#define NUMERIC 1
#define ALPHANUM 2
#define BYTE 3
2009-02-22 12:19:31 +13:00
#define KANJI 4
2009-10-07 08:03:00 +13:00
#define RHODIUM "0123456789ABCDEFGHIJKLNMOPQRSTUVWXYZ $%*+-./:"
2009-10-07 08:03:00 +13:00
void qrnumeric_encode(char binary[], unsigned char source[], int length)
{ /* Encodes numeric data according to section 6.4.3 */
2009-10-07 08:03:00 +13:00
int blocks, remainder, i;
char block_binary[11];
int block_value;
2008-11-18 21:52:43 +13:00
block_value = 0;
blocks = length / 3;
remainder = length % 3;
for(i = 0; i < blocks; i++) {
block_value = ctoi(source[(i * 3)]) * 100;
block_value += ctoi(source[(i * 3) + 1]) * 10;
block_value += ctoi(source[(i * 3) + 2]);
strcpy(block_binary, "");
if(block_value & 0x200) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x100) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x80) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x40) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x20) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x10) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x08) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x04) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x02) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x01) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
concat(binary, block_binary);
}
if(remainder == 2) {
block_value = ctoi(source[(i * 3)]) * 10;
block_value += ctoi(source[(i * 3) + 1]);
}
if(remainder == 1) {
block_value = ctoi(source[(i * 3)]);
}
strcpy(block_binary, "");
switch(remainder) {
case 2:
if(block_value & 0x40) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x20) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x10) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
case 1:
if(block_value & 0x08) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x04) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x02) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x01) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
break;
}
concat(binary, block_binary);
return;
}
2009-10-07 08:03:00 +13:00
void qralpha_encode(char binary[], unsigned char source[], int length)
{ /* Encodes alphanumeric data according to 6.4.4 */
2009-10-07 08:03:00 +13:00
int blocks, remainder, i;
char block_binary[12];
int block_value;
blocks = length / 2;
remainder = length % 2;
for(i = 0; i < blocks; i++) {
2009-10-07 08:03:00 +13:00
block_value = posn(RHODIUM, source[i * 2]) * 45;
block_value += posn(RHODIUM, source[(i * 2) + 1]);
strcpy(block_binary, "");
if(block_value & 0x400) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x200) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x100) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x80) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x40) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x20) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x10) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x08) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x04) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x02) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x01) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
concat(binary, block_binary);
}
if(remainder == 1) {
2009-10-07 08:03:00 +13:00
block_value = posn(RHODIUM, source[i * 2]);
strcpy(block_binary, "");
if(block_value & 0x20) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x10) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x08) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x04) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x02) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
if(block_value & 0x01) { concat(block_binary, "1"); } else { concat(block_binary, "0"); }
concat(binary, block_binary);
}
return;
}
2009-10-07 08:03:00 +13:00
void qrbyte_encode(char binary[], unsigned char source[], int length)
{ /* Encodes byte mode data according to 6.4.5 */
2009-10-07 08:03:00 +13:00
int i;
for(i = 0; i < length; i++) {
if(source[i] & 0x80) { concat(binary, "1"); } else { concat(binary, "0"); }
if(source[i] & 0x40) { concat(binary, "1"); } else { concat(binary, "0"); }
if(source[i] & 0x20) { concat(binary, "1"); } else { concat(binary, "0"); }
if(source[i] & 0x10) { concat(binary, "1"); } else { concat(binary, "0"); }
if(source[i] & 0x08) { concat(binary, "1"); } else { concat(binary, "0"); }
if(source[i] & 0x04) { concat(binary, "1"); } else { concat(binary, "0"); }
if(source[i] & 0x02) { concat(binary, "1"); } else { concat(binary, "0"); }
if(source[i] & 0x01) { concat(binary, "1"); } else { concat(binary, "0"); }
}
return;
}
2009-10-07 08:03:00 +13:00
int qrkanji_encode(char binary[], unsigned char source[], int length)
2009-02-22 12:19:31 +13:00
{ /* Assumes input is in Shift-JIS format */
2009-10-07 08:03:00 +13:00
int i, h, val, count;
2009-02-22 12:19:31 +13:00
count = 0;
2009-10-07 08:03:00 +13:00
for(i = 0; i < length; i += 2) {
2009-02-22 12:19:31 +13:00
val = (source[i] << 8) | source[i+1];
if(val <= 0x9ffc) {
val -= 0x8140;
} else {
val -= 0xc140;
}
h = (val >> 8) * 0xc0;
val = (val & 0xff) + h;
if(val & 0x1000) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x800) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x400) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x200) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x100) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x80) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x40) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x20) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x10) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x08) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x04) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x02) { concat(binary, "1"); } else { concat(binary, "0"); }
if(val & 0x01) { concat(binary, "1"); } else { concat(binary, "0"); }
count++;
}
return count;
}
2009-10-07 08:03:00 +13:00
void versionm1(char binary_data[], unsigned char source[], int length)
{
2009-10-07 08:03:00 +13:00
int i, latch;
int bits_total, bits_left, remainder;
int data_codewords, ecc_codewords;
unsigned char data_blocks[4], ecc_blocks[3];
bits_total = 20;
latch = 0;
/* Character count indicator */
if(length & 0x04) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x02) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x01) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
2009-10-07 08:03:00 +13:00
qrnumeric_encode(binary_data, source, length);
/* Add terminator */
bits_left = bits_total - strlen(binary_data);
if(bits_left <= 3) {
for(i = 0; i < bits_left; i++) {
concat(binary_data, "0");
}
latch = 1;
} else {
concat(binary_data, "000");
}
if(latch == 0) {
/* Manage last (4-bit) block */
bits_left = bits_total - strlen(binary_data);
if(bits_left <= 4) {
for(i = 0; i < bits_left; i++) {
concat(binary_data, "0");
}
latch = 1;
}
}
if(latch == 0) {
/* Complete current byte */
remainder = 8 - (strlen(binary_data) % 8);
if(remainder == 8) { remainder = 0; }
for(i = 0; i < remainder; i++) {
concat(binary_data, "0");
}
/* Add padding */
bits_left = bits_total - strlen(binary_data);
if(bits_left > 4) {
remainder = (bits_left - 4) / 8;
for(i = 0; i < remainder; i++) {
if((i % 2) == 0) { concat(binary_data, "11101100"); }
if((i % 2) == 1) { concat(binary_data, "00010001"); }
}
}
concat(binary_data, "0000");
}
data_codewords = 3;
ecc_codewords = 2;
/* Copy data into codewords */
for(i = 0; i < (data_codewords - 1); i++) {
data_blocks[i] = 0;
if(binary_data[i * 8] == '1') { data_blocks[i] += 0x80; }
if(binary_data[(i * 8) + 1] == '1') { data_blocks[i] += 0x40; }
if(binary_data[(i * 8) + 2] == '1') { data_blocks[i] += 0x20; }
if(binary_data[(i * 8) + 3] == '1') { data_blocks[i] += 0x10; }
if(binary_data[(i * 8) + 4] == '1') { data_blocks[i] += 0x08; }
if(binary_data[(i * 8) + 5] == '1') { data_blocks[i] += 0x04; }
if(binary_data[(i * 8) + 6] == '1') { data_blocks[i] += 0x02; }
if(binary_data[(i * 8) + 7] == '1') { data_blocks[i] += 0x01; }
}
data_blocks[2] = 0;
if(binary_data[16] == '1') { data_blocks[2] += 0x08; }
if(binary_data[17] == '1') { data_blocks[2] += 0x04; }
if(binary_data[18] == '1') { data_blocks[2] += 0x02; }
if(binary_data[19] == '1') { data_blocks[2] += 0x01; }
/* Calculate Reed-Solomon error codewords */
rs_init_gf(0x11d);
rs_init_code(ecc_codewords, 1);
rs_encode(data_codewords,data_blocks,ecc_blocks);
rs_free();
/* Add Reed-Solomon codewords to binary data */
for(i = 0; i < ecc_codewords; i++) {
if(ecc_blocks[i] & 0x80) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x40) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x20) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x10) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x08) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x04) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x02) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x01) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
}
return;
}
2009-10-07 08:03:00 +13:00
void versionm2(char binary_data[], unsigned char source[], int length, int char_system, int ecc_mode)
{
2009-10-07 08:03:00 +13:00
int i, latch;
int bits_total, bits_left, remainder;
int data_codewords, ecc_codewords;
unsigned char data_blocks[6], ecc_blocks[7];
latch = 0;
if(ecc_mode == 1) { bits_total = 40; }
if(ecc_mode == 2) { bits_total = 32; }
/* Mode indicator */
if(char_system == NUMERIC) { concat(binary_data, "0"); }
if(char_system == ALPHANUM) { concat(binary_data, "1"); }
/* Character count indicator */
if(char_system == NUMERIC) {
if(length & 0x08) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
}
if(length & 0x04) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x02) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x01) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
2009-10-07 08:03:00 +13:00
if(char_system == NUMERIC) { qrnumeric_encode(binary_data, source, length); }
if(char_system == ALPHANUM) { qralpha_encode(binary_data, source, length); }
/* Add terminator */
bits_left = bits_total - strlen(binary_data);
if(bits_left <= 5) {
for(i = 0; i < bits_left; i++) {
concat(binary_data, "0");
}
latch = 1;
} else {
concat(binary_data, "00000");
}
if(latch == 0) {
/* Complete current byte */
remainder = 8 - (strlen(binary_data) % 8);
if(remainder == 8) { remainder = 0; }
for(i = 0; i < remainder; i++) {
concat(binary_data, "0");
}
/* Add padding */
bits_left = bits_total - strlen(binary_data);
remainder = bits_left / 8;
for(i = 0; i < remainder; i++) {
if((i % 2) == 0) { concat(binary_data, "11101100"); }
if((i % 2) == 1) { concat(binary_data, "00010001"); }
}
}
if(ecc_mode == 1) { data_codewords = 5; ecc_codewords = 5; }
if(ecc_mode == 2) { data_codewords = 4; ecc_codewords = 6; }
/* Copy data into codewords */
for(i = 0; i < data_codewords; i++) {
data_blocks[i] = 0;
if(binary_data[i * 8] == '1') { data_blocks[i] += 0x80; }
if(binary_data[(i * 8) + 1] == '1') { data_blocks[i] += 0x40; }
if(binary_data[(i * 8) + 2] == '1') { data_blocks[i] += 0x20; }
if(binary_data[(i * 8) + 3] == '1') { data_blocks[i] += 0x10; }
if(binary_data[(i * 8) + 4] == '1') { data_blocks[i] += 0x08; }
if(binary_data[(i * 8) + 5] == '1') { data_blocks[i] += 0x04; }
if(binary_data[(i * 8) + 6] == '1') { data_blocks[i] += 0x02; }
if(binary_data[(i * 8) + 7] == '1') { data_blocks[i] += 0x01; }
}
/* Calculate Reed-Solomon error codewords */
rs_init_gf(0x11d);
rs_init_code(ecc_codewords, 1);
rs_encode(data_codewords,data_blocks,ecc_blocks);
rs_free();
/* Add Reed-Solomon codewords to binary data */
for(i = 0; i < ecc_codewords; i++) {
if(ecc_blocks[i] & 0x80) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x40) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x20) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x10) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x08) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x04) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x02) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x01) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
}
return;
}
2009-10-07 08:03:00 +13:00
void versionm3(char binary_data[], unsigned char source[], int length, int char_system, int ecc_mode)
{
2009-10-07 08:03:00 +13:00
int i, latch;
int bits_total, bits_left, remainder;
int data_codewords, ecc_codewords;
unsigned char data_blocks[12], ecc_blocks[9];
2009-02-22 12:19:31 +13:00
int sjis_count;
latch = 0;
if(ecc_mode == 1) { bits_total = 84; }
if(ecc_mode == 2) { bits_total = 68; }
/* Mode indicator */
if(char_system == NUMERIC) { concat(binary_data, "00"); }
if(char_system == ALPHANUM) { concat(binary_data, "01"); }
if(char_system == BYTE) { concat(binary_data, "10"); }
2009-02-22 12:19:31 +13:00
if(char_system == KANJI) { concat(binary_data, "11"); }
/* Character count indicator */
2009-02-22 12:19:31 +13:00
if(char_system == KANJI) {
concat(binary_data, "XXX"); /* Place holder */
} else {
if(char_system == NUMERIC) {
if(length & 0x10) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
2009-02-22 12:19:31 +13:00
}
if(length & 0x08) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x04) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x02) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x01) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
}
2009-10-07 08:03:00 +13:00
if(char_system == NUMERIC) { qrnumeric_encode(binary_data, source, length); }
if(char_system == ALPHANUM) { qralpha_encode(binary_data, source, length); }
if(char_system == BYTE) { qrbyte_encode(binary_data, source, length); }
if(char_system == KANJI) { sjis_count = qrkanji_encode(binary_data, source, length); }
2009-02-22 12:19:31 +13:00
if(char_system == KANJI) {
if(sjis_count & 0x04) { binary_data[2] = '1'; } else { binary_data[2] = '0'; }
if(sjis_count & 0x02) { binary_data[3] = '1'; } else { binary_data[3] = '0'; }
if(sjis_count & 0x01) { binary_data[4] = '1'; } else { binary_data[4] = '0'; }
}
/* Add terminator */
bits_left = bits_total - strlen(binary_data);
if(bits_left <= 7) {
for(i = 0; i < bits_left; i++) {
concat(binary_data, "0");
}
latch = 1;
} else {
concat(binary_data, "0000000");
}
if(latch == 0) {
/* Manage last (4-bit) block */
bits_left = bits_total - strlen(binary_data);
if(bits_left <= 4) {
for(i = 0; i < bits_left; i++) {
concat(binary_data, "0");
}
latch = 1;
}
}
if(latch == 0) {
/* Complete current byte */
remainder = 8 - (strlen(binary_data) % 8);
if(remainder == 8) { remainder = 0; }
for(i = 0; i < remainder; i++) {
concat(binary_data, "0");
}
/* Add padding */
bits_left = bits_total - strlen(binary_data);
if(bits_left > 4) {
remainder = (bits_left - 4) / 8;
for(i = 0; i < remainder; i++) {
if((i % 2) == 0) { concat(binary_data, "11101100"); }
if((i % 2) == 1) { concat(binary_data, "00010001"); }
}
}
concat(binary_data, "0000");
}
if(ecc_mode == 1) { data_codewords = 11; ecc_codewords = 6; }
if(ecc_mode == 2) { data_codewords = 9; ecc_codewords = 8; }
/* Copy data into codewords */
for(i = 0; i < (data_codewords - 1); i++) {
data_blocks[i] = 0;
if(binary_data[i * 8] == '1') { data_blocks[i] += 0x80; }
if(binary_data[(i * 8) + 1] == '1') { data_blocks[i] += 0x40; }
if(binary_data[(i * 8) + 2] == '1') { data_blocks[i] += 0x20; }
if(binary_data[(i * 8) + 3] == '1') { data_blocks[i] += 0x10; }
if(binary_data[(i * 8) + 4] == '1') { data_blocks[i] += 0x08; }
if(binary_data[(i * 8) + 5] == '1') { data_blocks[i] += 0x04; }
if(binary_data[(i * 8) + 6] == '1') { data_blocks[i] += 0x02; }
if(binary_data[(i * 8) + 7] == '1') { data_blocks[i] += 0x01; }
}
if(ecc_mode == 1) {
data_blocks[11] = 0;
if(binary_data[80] == '1') { data_blocks[2] += 0x08; }
if(binary_data[81] == '1') { data_blocks[2] += 0x04; }
if(binary_data[82] == '1') { data_blocks[2] += 0x02; }
if(binary_data[83] == '1') { data_blocks[2] += 0x01; }
}
if(ecc_mode == 2) {
data_blocks[9] = 0;
if(binary_data[64] == '1') { data_blocks[2] += 0x08; }
if(binary_data[65] == '1') { data_blocks[2] += 0x04; }
if(binary_data[66] == '1') { data_blocks[2] += 0x02; }
if(binary_data[67] == '1') { data_blocks[2] += 0x01; }
}
/* Calculate Reed-Solomon error codewords */
rs_init_gf(0x11d);
rs_init_code(ecc_codewords, 1);
rs_encode(data_codewords,data_blocks,ecc_blocks);
rs_free();
/* Add Reed-Solomon codewords to binary data */
for(i = 0; i < ecc_codewords; i++) {
if(ecc_blocks[i] & 0x80) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x40) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x20) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x10) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x08) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x04) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x02) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x01) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
}
return;
}
2009-10-07 08:03:00 +13:00
void versionm4(char binary_data[], unsigned char source[], int length, int char_system, int ecc_mode)
{
2009-10-07 08:03:00 +13:00
int i, latch;
int bits_total, bits_left, remainder;
int data_codewords, ecc_codewords;
unsigned char data_blocks[17], ecc_blocks[15];
2009-02-22 12:19:31 +13:00
int sjis_count;
latch = 0;
if(ecc_mode == 1) { bits_total = 128; }
if(ecc_mode == 2) { bits_total = 112; }
if(ecc_mode == 3) { bits_total = 80; }
/* Mode indicator */
if(char_system == NUMERIC) { concat(binary_data, "000"); }
if(char_system == ALPHANUM) { concat(binary_data, "001"); }
if(char_system == BYTE) { concat(binary_data, "010"); }
2009-02-22 12:19:31 +13:00
if(char_system == KANJI) { concat(binary_data, "011"); }
/* Character count indicator */
2009-02-22 12:19:31 +13:00
if(char_system == KANJI) {
concat(binary_data, "XXXX"); /* Place holder */
} else {
if(char_system == NUMERIC) {
if(length & 0x20) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
2009-02-22 12:19:31 +13:00
}
if(length & 0x10) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x08) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x04) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x02) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(length & 0x01) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
}
2009-10-07 08:03:00 +13:00
if(char_system == NUMERIC) { qrnumeric_encode(binary_data, source, length); }
if(char_system == ALPHANUM) { qralpha_encode(binary_data, source, length); }
if(char_system == BYTE) { qrbyte_encode(binary_data, source, length); }
if(char_system == KANJI) { sjis_count = qrkanji_encode(binary_data, source, length); }
2009-02-22 12:19:31 +13:00
if(char_system == KANJI) {
if(sjis_count & 0x08) { binary_data[3] = '1'; } else { binary_data[3] = '0'; }
if(sjis_count & 0x04) { binary_data[4] = '1'; } else { binary_data[4] = '0'; }
if(sjis_count & 0x02) { binary_data[5] = '1'; } else { binary_data[5] = '0'; }
if(sjis_count & 0x01) { binary_data[6] = '1'; } else { binary_data[6] = '0'; }
}
/* Add terminator */
bits_left = bits_total - strlen(binary_data);
if(bits_left <= 9) {
for(i = 0; i < bits_left; i++) {
concat(binary_data, "0");
}
latch = 1;
} else {
concat(binary_data, "000000000");
}
if(latch == 0) {
/* Complete current byte */
remainder = 8 - (strlen(binary_data) % 8);
if(remainder == 8) { remainder = 0; }
for(i = 0; i < remainder; i++) {
concat(binary_data, "0");
}
/* Add padding */
bits_left = bits_total - strlen(binary_data);
remainder = bits_left / 8;
for(i = 0; i < remainder; i++) {
if((i % 2) == 0) { concat(binary_data, "11101100"); }
if((i % 2) == 1) { concat(binary_data, "00010001"); }
}
}
if(ecc_mode == 1) { data_codewords = 16; ecc_codewords = 8; }
if(ecc_mode == 2) { data_codewords = 14; ecc_codewords = 10; }
if(ecc_mode == 3) { data_codewords = 10; ecc_codewords = 14; }
/* Copy data into codewords */
for(i = 0; i < data_codewords; i++) {
data_blocks[i] = 0;
if(binary_data[i * 8] == '1') { data_blocks[i] += 0x80; }
if(binary_data[(i * 8) + 1] == '1') { data_blocks[i] += 0x40; }
if(binary_data[(i * 8) + 2] == '1') { data_blocks[i] += 0x20; }
if(binary_data[(i * 8) + 3] == '1') { data_blocks[i] += 0x10; }
if(binary_data[(i * 8) + 4] == '1') { data_blocks[i] += 0x08; }
if(binary_data[(i * 8) + 5] == '1') { data_blocks[i] += 0x04; }
if(binary_data[(i * 8) + 6] == '1') { data_blocks[i] += 0x02; }
if(binary_data[(i * 8) + 7] == '1') { data_blocks[i] += 0x01; }
}
/* Calculate Reed-Solomon error codewords */
rs_init_gf(0x11d);
rs_init_code(ecc_codewords, 1);
rs_encode(data_codewords,data_blocks,ecc_blocks);
rs_free();
/* Add Reed-Solomon codewords to binary data */
for(i = 0; i < ecc_codewords; i++) {
if(ecc_blocks[i] & 0x80) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x40) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x20) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x10) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x08) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x04) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x02) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
if(ecc_blocks[i] & 0x01) { concat(binary_data, "1"); } else { concat(binary_data, "0"); }
}
return;
}
int microqr(struct zint_symbol *symbol, unsigned char source[], int length)
{
int symbol_size;
int char_system;
char binary_data[200];
int latch;
char bitmask[17][17];
char imagemap[17][17];
char candidate[17][17];
char pattern_bit;
int width, i, j, pattern_no;
int sum1, sum2, evaluation[4], format, format_full, kanji;
char formatstr[16];
#ifndef _MSC_VER
2009-10-07 08:03:00 +13:00
unsigned char local_source[length + 1];
#else
2009-10-07 08:03:00 +13:00
unsigned char* local_source = (unsigned char*)_alloca(length + 1);
#endif
/* Analise input data and select encoding method - zint does not attempt to
optimise the symbol by switching encoding method part way through the symbol,
but merely chooses an encoding method for the whole symbol */
char_system = BYTE;
symbol_size = 0;
for(i = 0; i < length; i++) {
if(source[i] == '\0') {
strcpy(symbol->errtxt, "QR Code not yet able to handle NULL characters");
return ERROR_INVALID_DATA;
}
}
kanji = 0;
if(kanji) {
char_system = KANJI;
} else {
2009-10-07 08:03:00 +13:00
if(is_sane(RHODIUM, local_source, length) == 0) { char_system = ALPHANUM; }
if(is_sane(NEON, local_source, length) == 0) { char_system = NUMERIC; }
}
2008-11-18 21:52:43 +13:00
width = 0;
format = 0;
if(symbol->option_1 == 4) {
strcpy(symbol->errtxt, "Error correction level H not available for Micro QR symbols");
return ERROR_INVALID_OPTION;
}
if((symbol->option_1 < 1) || (symbol->option_1 > 4)) {
symbol->option_1 = 1;
}
/* Check that the data is not too long */
/* Note that there is no switching between error correction levels - this decision is left
to the user: invalid combinations fail */
latch = 0;
switch(symbol->option_1) {
case 1: /* ECC Level L */
switch(char_system) {
case NUMERIC: if(length > 35) latch = 1; break;
case ALPHANUM: if(length > 21) latch = 1; break;
case BYTE: if(length > 15) latch = 1; break;
case KANJI: if(length > 18) latch = 1; break;
}
break;
case 2: /* ECC Level M */
switch(char_system) {
case NUMERIC: if(length > 30) latch = 1; break;
case ALPHANUM: if(length > 18) latch = 1; break;
case BYTE: if(length > 13) latch = 1; break;
case KANJI: if(length > 16) latch = 1; break;
}
break;
case 3: /* ECC Level Q */
symbol_size = 4; /* Only size M4 supports level Q */
switch(char_system) {
case NUMERIC: if(length > 21) latch = 1; break;
case ALPHANUM: if(length > 13) latch = 1; break;
case BYTE: if(length > 9) latch = 1; break;
case KANJI: if(length > 10) latch = 1; break;
}
break;
}
if(latch == 1) {
strcpy(symbol->errtxt, "Input data too long");
return ERROR_TOO_LONG;
}
2009-04-27 10:04:01 +12:00
symbol_size = symbol->option_2;
if((symbol_size < 0) || (symbol_size > 4)) {
symbol_size = 0;
}
/* Decide symbol size */
if(symbol_size == 0) {
if(symbol->option_1 == 1) { /* ECC Level L */
switch(char_system) {
case NUMERIC:
symbol_size = 4;
if(length <= 23) { symbol_size = 3; }
if(length <= 10) { symbol_size = 2; }
if(length <= 5) { symbol_size = 1; }
break;
case ALPHANUM:
symbol_size = 4;
if(length <= 14) { symbol_size = 3; }
if(length <= 6) { symbol_size = 2; }
break;
case BYTE:
symbol_size = 4;
if(length <= 9) { symbol_size = 3; }
break;
2009-02-22 12:19:31 +13:00
case KANJI:
symbol_size = 4;
if(length <= 12) { symbol_size = 3; }
}
} else { /* ECC Level M */
switch(char_system) {
case NUMERIC:
symbol_size = 4;
if(length <= 18) { symbol_size = 3; }
if(length <= 8) { symbol_size = 2; }
break;
case ALPHANUM:
symbol_size = 4;
if(length <= 11) { symbol_size = 3; }
if(length <= 5) { symbol_size = 2; }
break;
case BYTE:
symbol_size = 4;
if(length <= 7) { symbol_size = 3; }
break;
2009-02-22 12:19:31 +13:00
case KANJI:
symbol_size = 4;
if(length <= 8) { symbol_size = 3; }
}
}
}
strcpy(binary_data, "");
switch(symbol_size) {
2009-10-07 08:03:00 +13:00
case 1: versionm1(binary_data, local_source, length); break;
case 2: versionm2(binary_data, local_source, length, char_system, symbol->option_1); break;
case 3: versionm3(binary_data, local_source, length, char_system, symbol->option_1); break;
case 4: versionm4(binary_data, local_source, length, char_system, symbol->option_1); break;
}
switch(symbol_size) {
case 1: width = 11; break;
case 2: width = 13; break;
case 3: width = 15; break;
case 4: width = 17; break;
}
for(i = 0; i < 17; i++) {
for(j = 0; j < 17; j++) {
bitmask[i][j] = '0';
imagemap[i][j] = '0';
candidate[i][j] = '0';
}
}
/* "bitmask" seperates data area */
for(i = 1; i < width; i++) {
for(j = 1; j < width; j++) {
bitmask[i][j] = '1';
}
}
for(i = 1; i < 9; i++) {
for(j = 1; j < 9; j++) {
bitmask[i][j] = '0';
}
}
/* Copy data into symbol grid */
for(i = 0; i < width; i++) {
for(j = 0; j < width; j++) {
if(bitmask[i][j] == '1') {
switch(symbol_size) {
case 1: imagemap[i][j] = binary_data[fig11m1[(i * width) + j]]; break;
case 2: imagemap[i][j] = binary_data[fig11m2[(i * width) + j]]; break;
case 3: imagemap[i][j] = binary_data[fig11m3[(i * width) + j]]; break;
case 4: imagemap[i][j] = binary_data[fig11m4[(i * width) + j]]; break;
}
}
}
}
/* XOR with data masks and evaluate */
for(pattern_no = 0; pattern_no < 4; pattern_no++) {
for(i = 0; i < width; i++) {
for(j = 0; j < width; j++) {
pattern_bit = '0';
candidate[i][j] = '0';
switch(pattern_no) {
case 0: if((i % 2) == 0) { pattern_bit = '1'; } break;
case 1: if((((i / 2) + (j / 3)) % 2) == 0) { pattern_bit = '1'; } break;
case 2: if((((i * j) % 2 + (i * j) % 3) % 2) == 0) { pattern_bit = '1'; } break;
case 3: if((((i + j) % 2 + (i * j) % 3) % 2) == 0) { pattern_bit = '1'; } break;
}
if(bitmask[i][j] == '1') {
if(pattern_bit != imagemap[i][j]) { candidate[i][j] = '1'; }
}
}
}
sum1 = 0;
sum2 = 0;
for(i = 1; i < width; i++) {
if(candidate[i][width - 1] == '1') { sum1++; }
if(candidate[width - 1][i] == '1') { sum2++; }
}
if(sum1 <= sum2) { evaluation[pattern_no] = (sum1 * 16) + sum2; } else { evaluation[pattern_no] = (sum2 * 16) + sum1; }
}
/* Choose best data mask */
j = evaluation[0];
pattern_no = 0;
for(i = 1; i < 4; i++) {
if(evaluation[i] > j) {
pattern_no = i;
j = evaluation[i];
}
}
/* Apply data mask */
for(i = 0; i < width; i++) {
for(j = 0; j < width; j++) {
pattern_bit = '0';
candidate[i][j] = '0';
switch(pattern_no) {
case 0: if((i % 2) == 0) { pattern_bit = '1'; } break;
case 1: if((((i / 2) + (j / 3)) % 2) == 0) { pattern_bit = '1'; } break;
case 2: if((((i * j) % 2 + (i * j) % 3) % 2) == 0) { pattern_bit = '1'; } break;
case 3: if((((i + j) % 2 + (i * j) % 3) % 2) == 0) { pattern_bit = '1'; } break;
}
if(bitmask[i][j] == '1') {
if(pattern_bit != imagemap[i][j]) { candidate[i][j] = '1'; }
}
}
}
/* Calculate format data */
switch(symbol_size) {
case 1: format = 0;
break;
case 2: switch(symbol->option_1) {
case 1: format = 1; break;
case 2: format = 2; break;
}
break;
case 3: switch(symbol->option_1) {
case 1: format = 3; break;
case 2: format = 4; break;
}
break;
case 4: switch(symbol->option_1) {
case 1: format = 5; break;
case 2: format = 6; break;
case 3: format = 7; break;
}
break;
}
format *= 4;
format += pattern_no;
format_full = tablec1[format];
strcpy(formatstr, "");
if(format_full & 0x2000) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x1000) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x800) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x400) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x200) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x100) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x80) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x80) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x40) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x20) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x10) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x08) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x04) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x02) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
if(format_full & 0x01) { concat(formatstr, "1"); } else { concat(formatstr, "0"); }
/* Add format data to symbol */
for(i = 0; i < 8; i++) {
candidate[i + 1][8] = formatstr[i];
}
for(i = 0; i < 7; i++) {
candidate[8][7 - i] = formatstr[i + 8];
}
/* Add timer pattern */
for(i = 0; i < width; i += 2) {
candidate[i][0] = '1';
candidate[0][i] = '1';
}
/* Add finder pattern */
for(i = 0; i < 7; i ++) {
for(j = 0; j < 7; j++) {
if(finder[(i * 7) + j] == 1) {
candidate[i][j] = '1';
}
}
}
/* Copy data into symbol */
for(i = 0; i < width; i++) {
for(j = 0; j < width; j++) {
if(candidate[i][j] == '1') { set_module(symbol, i, j); }
}
symbol->row_height[i] = 1;
}
symbol->rows = width;
symbol->width = width;
return 0;
}