#include <assert.h>
#include <stdio.h>
#include "common.h"
#include "aztec.h"
#include "reedsol.h"
#define AZTEC_MAX_CAPACITY 19968
#define AZTEC_BIN_CAPACITY 19932
#define AZTEC_MAP_SIZE 22801
#define AZTEC_MAP_POSN_MAX 20039
#define AZ_BIN_CAP_CWDS_S "1661"
static int az_count_doubles(const unsigned char source[], const int position, const int length) {
int i;
for (i = position; i + 1 < length && (source[i] == '.' || source[i] == ',') && source[i + 1] == ' '; i += 2);
return (i - position) >> 1;
}
static int az_count_dotcomma(const unsigned char source[], const int position, const int length) {
int i;
for (i = position; i < length && (source[i] == '.' || source[i] == ','); i++);
return i - position;
}
static int az_count_chr(const unsigned char source[], const int position, const int length, const unsigned char chr) {
int i;
for (i = position; i < length && source[i] == chr; i++);
return i - position;
}
static char az_get_next_mode(const char encode_mode[], const int length, int i) {
const char current_mode = encode_mode[i];
do {
i++;
} while (i < length && encode_mode[i] == current_mode);
if (i >= length) {
return 'E';
}
return encode_mode[i];
}
static int az_bin_append_posn(const int arg, const int length, char *binary, const int bin_posn) {
if (bin_posn + length > AZTEC_BIN_CAPACITY) {
return 0;
}
return z_bin_append_posn(arg, length, binary, bin_posn);
}
static int aztec_text_process(const unsigned char source[], int length, int bp, char binary_string[], const int gs1,
const int gs1_bp, const int eci, char *p_current_mode, int *data_length, const int debug_print) {
int i, j;
const char initial_mode = p_current_mode ? *p_current_mode : 'U';
char current_mode;
int count;
char next_mode;
int reduced_length;
char *encode_mode = (char *) z_alloca(length + 1);
unsigned char *reduced_source = (unsigned char *) z_alloca(length + 1);
char *reduced_encode_mode = (char *) z_alloca(length + 1);
for (i = 0; i < length; i++) {
if (!z_isascii(source[i])) {
encode_mode[i] = 'B';
} else if (gs1 && source[i] == '\x1D') {
encode_mode[i] = 'P';
} else {
encode_mode[i] = AztecModes[source[i]];
}
}
current_mode = initial_mode;
for (i = 0; i + 1 < length; i++) {
if (source[i] == 13 && source[i + 1] == 10) {
encode_mode[i] = 'P';
encode_mode[i + 1] = 'P';
} else if (source[i] == ':' && source[i + 1] == ' ') {
encode_mode[i + 1] = 'P';
} else if ((source[i] == '.' || source[i] == ',') && source[i + 1] == ' ' && encode_mode[i] == 'X') {
count = az_count_doubles(source, i, length);
next_mode = az_get_next_mode(encode_mode, length, i);
if (current_mode == 'U') {
if (next_mode == 'D' && count <= 5) {
memset(encode_mode + i, 'D', 2 * count);
}
} else if (current_mode == 'L') {
if (next_mode == 'D' && count <= 4) {
memset(encode_mode + i, 'D', 2 * count);
}
} else if (current_mode == 'M') {
if (next_mode == 'D' && count == 1) {
encode_mode[i] = 'D';
encode_mode[i + 1] = 'D';
}
} else if (current_mode == 'D') {
if (next_mode != 'D' && count <= 4) {
memset(encode_mode + i, 'D', 2 * count);
} else if (next_mode == 'D' && count <= 7) {
memset(encode_mode + i, 'D', 2 * count);
}
}
if (encode_mode[i] == 'X') {
encode_mode[i] = 'P';
encode_mode[i + 1] = 'P';
}
}
if (encode_mode[i] != 'X' && encode_mode[i] != 'B') {
current_mode = encode_mode[i];
}
}
if (debug_print) {
fputs("First Pass:\n", stdout);
printf("%.*s\n", length, encode_mode);
}
i = 0;
j = 0;
while (i < length) {
reduced_encode_mode[j] = encode_mode[i];
if (i + 1 < length) {
if (source[i] == 13 && source[i + 1] == 10) {
reduced_source[j] = 'a';
i += 2;
} else if (source[i] == '.' && source[i + 1] == ' ' && encode_mode[i] == 'P') {
reduced_source[j] = 'b';
i += 2;
} else if (source[i] == ',' && source[i + 1] == ' ' && encode_mode[i] == 'P') {
reduced_source[j] = 'c';
i += 2;
} else if (source[i] == ':' && source[i + 1] == ' ') {
reduced_source[j] = 'd';
i += 2;
} else {
reduced_source[j] = source[i++];
}
} else {
reduced_source[j] = source[i++];
}
j++;
}
reduced_length = j;
current_mode = initial_mode;
for (i = 0; i < reduced_length; i++) {
if (reduced_source[i] == 13) {
count = az_count_chr(reduced_source, i, reduced_length, 13);
next_mode = az_get_next_mode(reduced_encode_mode, reduced_length, i);
if (current_mode == 'U' && (next_mode == 'U' || next_mode == 'B') && count == 1) {
reduced_encode_mode[i] = 'P';
} else if (current_mode == 'L' && (next_mode == 'L' || next_mode == 'B') && count == 1) {
reduced_encode_mode[i] = 'P';
} else if (current_mode == 'P' || next_mode == 'P') {
reduced_encode_mode[i] = 'P';
}
if (current_mode == 'D') {
if ((next_mode == 'E' || next_mode == 'U' || next_mode == 'D' || next_mode == 'B') && count <= 2) {
memset(reduced_encode_mode + i, 'P', count);
} else if (next_mode == 'L' && count == 1) {
reduced_encode_mode[i] = 'P';
}
}
if (reduced_encode_mode[i] == 'X') {
reduced_encode_mode[i] = 'M';
}
} else if (reduced_source[i] == '.' || reduced_source[i] == ',') {
count = az_count_dotcomma(reduced_source, i, reduced_length);
next_mode = az_get_next_mode(reduced_encode_mode, reduced_length, i);
if (current_mode == 'U') {
if ((next_mode == 'U' || next_mode == 'L' || next_mode == 'M' || next_mode == 'B') && count == 1) {
reduced_encode_mode[i] = 'P';
}
} else if (current_mode == 'L') {
if (next_mode == 'L' && count <= 2) {
memset(reduced_encode_mode + i, 'P', count);
} else if ((next_mode == 'M' || next_mode == 'B') && count == 1) {
reduced_encode_mode[i] = 'P';
}
} else if (current_mode == 'M') {
if ((next_mode == 'E' || next_mode == 'U' || next_mode == 'L' || next_mode == 'M') && count <= 4) {
memset(reduced_encode_mode + i, 'P', count);
} else if (next_mode == 'B' && count <= 2) {
memset(reduced_encode_mode + i, 'P', count);
}
} else if (current_mode == 'P' && next_mode != 'D' && count <= 9) {
memset(reduced_encode_mode + i, 'P', count);
}
if (reduced_encode_mode[i] == 'X') {
reduced_encode_mode[i] = 'D';
}
} else if (reduced_source[i] == ' ') {
count = az_count_chr(reduced_source, i, reduced_length, ' ');
next_mode = az_get_next_mode(reduced_encode_mode, reduced_length, i);
if (current_mode == 'U') {
if (next_mode == 'E' && count <= 5) {
memset(reduced_encode_mode + i, 'U', count);
} else if ((next_mode == 'U' || next_mode == 'L' || next_mode == 'M' || next_mode == 'P'
|| next_mode == 'B') && count <= 9) {
memset(reduced_encode_mode + i, 'U', count);
}
} else if (current_mode == 'L') {
if (next_mode == 'E' && count <= 5) {
memset(reduced_encode_mode + i, 'L', count);
} else if (next_mode == 'U' && count == 1) {
reduced_encode_mode[i] = 'L';
} else if (next_mode == 'L' && count <= 14) {
memset(reduced_encode_mode + i, 'L', count);
} else if ((next_mode == 'M' || next_mode == 'P' || next_mode == 'B') && count <= 9) {
memset(reduced_encode_mode + i, 'L', count);
}
} else if (current_mode == 'M') {
if ((next_mode == 'E' || next_mode == 'U') && count <= 9) {
memset(reduced_encode_mode + i, 'M', count);
} else if ((next_mode == 'L' || next_mode == 'B') && count <= 14) {
memset(reduced_encode_mode + i, 'M', count);
} else if ((next_mode == 'M' || next_mode == 'P') && count <= 19) {
memset(reduced_encode_mode + i, 'M', count);
}
} else if (current_mode == 'P') {
if (next_mode == 'E' && count <= 5) {
memset(reduced_encode_mode + i, 'U', count);
} else if ((next_mode == 'U' || next_mode == 'L' || next_mode == 'M' || next_mode == 'P'
|| next_mode == 'B') && count <= 9) {
memset(reduced_encode_mode + i, 'U', count);
}
}
if (reduced_encode_mode[i] == 'X') {
reduced_encode_mode[i] = 'D';
}
}
if (reduced_encode_mode[i] != 'B') {
current_mode = reduced_encode_mode[i];
}
}
current_mode = initial_mode;
for (i = 0; i < reduced_length; i++) {
if (reduced_encode_mode[i] != current_mode) {
for (count = 0; i + count < reduced_length && reduced_encode_mode[i + count] == reduced_encode_mode[i];
count++);
next_mode = az_get_next_mode(reduced_encode_mode, reduced_length, i);
if (reduced_encode_mode[i] == 'P') {
if (current_mode == 'U' && count <= 2) {
memset(reduced_encode_mode + i, 'p', count);
} else if (current_mode == 'L' && next_mode != 'U' && count <= 2) {
memset(reduced_encode_mode + i, 'p', count);
} else if (current_mode == 'L' && next_mode == 'U' && count == 1) {
reduced_encode_mode[i] = 'p';
} else if (current_mode == 'M' && next_mode != 'M' && count == 1) {
reduced_encode_mode[i] = 'p';
} else if (current_mode == 'M' && next_mode == 'M' && count <= 2) {
memset(reduced_encode_mode + i, 'p', count);
} else if (current_mode == 'D' && next_mode != 'D' && count <= 3) {
memset(reduced_encode_mode + i, 'p', count);
} else if (current_mode == 'D' && next_mode == 'D' && count <= 6) {
memset(reduced_encode_mode + i, 'p', count);
}
} else if (reduced_encode_mode[i] == 'U') {
if (current_mode == 'L' && (next_mode == 'L' || next_mode == 'M') && count <= 2) {
memset(reduced_encode_mode + i, 'u', count);
} else if (current_mode == 'L' && (next_mode == 'E' || next_mode == 'D' || next_mode == 'B'
|| next_mode == 'P') && count == 1) {
reduced_encode_mode[i] = 'u';
} else if (current_mode == 'D' && next_mode == 'D' && count == 1) {
reduced_encode_mode[i] = 'u';
} else if (current_mode == 'D' && next_mode == 'P' && count <= 2) {
memset(reduced_encode_mode + i, 'u', count);
}
}
}
if (reduced_encode_mode[i] != 'p' && reduced_encode_mode[i] != 'u' && reduced_encode_mode[i] != 'B') {
current_mode = reduced_encode_mode[i];
}
}
if (debug_print) {
printf("%.*s\n", reduced_length, reduced_source);
printf("%.*s\n", reduced_length, reduced_encode_mode);
}
if (bp == gs1_bp && gs1) {
bp = z_bin_append_posn(0, 5, binary_string, bp);
bp = z_bin_append_posn(0, 5, binary_string, bp);
bp = z_bin_append_posn(0, 3, binary_string, bp);
}
if (eci != 0) {
bp = z_bin_append_posn(0, initial_mode == 'D' ? 4 : 5, binary_string, bp);
bp = z_bin_append_posn(0, 5, binary_string, bp);
if (eci <= 9) {
bp = z_bin_append_posn(1, 3, binary_string, bp);
bp = z_bin_append_posn(2 + eci, 4, binary_string, bp);
} else if (eci <= 99) {
bp = z_bin_append_posn(2, 3, binary_string, bp);
bp = z_bin_append_posn(2 + (eci / 10), 4, binary_string, bp);
bp = z_bin_append_posn(2 + (eci % 10), 4, binary_string, bp);
} else if (eci <= 999) {
bp = z_bin_append_posn(3, 3, binary_string, bp);
bp = z_bin_append_posn(2 + (eci / 100), 4, binary_string, bp);
bp = z_bin_append_posn(2 + ((eci % 100) / 10), 4, binary_string, bp);
bp = z_bin_append_posn(2 + (eci % 10), 4, binary_string, bp);
} else if (eci <= 9999) {
bp = z_bin_append_posn(4, 3, binary_string, bp);
bp = z_bin_append_posn(2 + (eci / 1000), 4, binary_string, bp);
bp = z_bin_append_posn(2 + ((eci % 1000) / 100), 4, binary_string, bp);
bp = z_bin_append_posn(2 + ((eci % 100) / 10), 4, binary_string, bp);
bp = z_bin_append_posn(2 + (eci % 10), 4, binary_string, bp);
} else if (eci <= 99999) {
bp = z_bin_append_posn(5, 3, binary_string, bp);
bp = z_bin_append_posn(2 + (eci / 10000), 4, binary_string, bp);
bp = z_bin_append_posn(2 + ((eci % 10000) / 1000), 4, binary_string, bp);
bp = z_bin_append_posn(2 + ((eci % 1000) / 100), 4, binary_string, bp);
bp = z_bin_append_posn(2 + ((eci % 100) / 10), 4, binary_string, bp);
bp = z_bin_append_posn(2 + (eci % 10), 4, binary_string, bp);
} else {
bp = z_bin_append_posn(6, 3, binary_string, bp);
bp = z_bin_append_posn(2 + (eci / 100000), 4, binary_string, bp);
bp = z_bin_append_posn(2 + ((eci % 100000) / 10000), 4, binary_string, bp);
bp = z_bin_append_posn(2 + ((eci % 10000) / 1000), 4, binary_string, bp);
bp = z_bin_append_posn(2 + ((eci % 1000) / 100), 4, binary_string, bp);
bp = z_bin_append_posn(2 + ((eci % 100) / 10), 4, binary_string, bp);
bp = z_bin_append_posn(2 + (eci % 10), 4, binary_string, bp);
}
}
current_mode = initial_mode;
for (i = 0; i < reduced_length; i++) {
if (reduced_encode_mode[i] != current_mode) {
if (current_mode == 'U') {
switch (reduced_encode_mode[i]) {
case 'L':
if (!(bp = az_bin_append_posn(28, 5, binary_string, bp))) return 0;
break;
case 'M':
if (!(bp = az_bin_append_posn(29, 5, binary_string, bp))) return 0;
break;
case 'P':
if (!(bp = az_bin_append_posn(29, 5, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(30, 5, binary_string, bp))) return 0;
break;
case 'p':
if (!(bp = az_bin_append_posn(0, 5, binary_string, bp))) return 0;
break;
case 'D':
if (!(bp = az_bin_append_posn(30, 5, binary_string, bp))) return 0;
break;
case 'B':
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
break;
}
} else if (current_mode == 'L') {
switch (reduced_encode_mode[i]) {
case 'U':
if (!(bp = az_bin_append_posn(30, 5, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(14, 4, binary_string, bp))) return 0;
break;
case 'u':
if (!(bp = az_bin_append_posn(28, 5, binary_string, bp))) return 0;
break;
case 'M':
if (!(bp = az_bin_append_posn(29, 5, binary_string, bp))) return 0;
break;
case 'P':
if (!(bp = az_bin_append_posn(29, 5, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(30, 5, binary_string, bp))) return 0;
break;
case 'p':
if (!(bp = az_bin_append_posn(0, 5, binary_string, bp))) return 0;
break;
case 'D':
if (!(bp = az_bin_append_posn(30, 5, binary_string, bp))) return 0;
break;
case 'B':
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
break;
}
} else if (current_mode == 'M') {
switch (reduced_encode_mode[i]) {
case 'U':
if (!(bp = az_bin_append_posn(29, 5, binary_string, bp))) return 0;
break;
case 'L':
if (!(bp = az_bin_append_posn(28, 5, binary_string, bp))) return 0;
break;
case 'P':
if (!(bp = az_bin_append_posn(30, 5, binary_string, bp))) return 0;
break;
case 'p':
if (!(bp = az_bin_append_posn(0, 5, binary_string, bp))) return 0;
break;
case 'D':
if (!(bp = az_bin_append_posn(29, 5, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(30, 5, binary_string, bp))) return 0;
break;
case 'B':
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
break;
}
} else if (current_mode == 'P') {
switch (reduced_encode_mode[i]) {
case 'U':
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
break;
case 'L':
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(28, 5, binary_string, bp))) return 0;
break;
case 'M':
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(29, 5, binary_string, bp))) return 0;
break;
case 'D':
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(30, 5, binary_string, bp))) return 0;
break;
case 'B':
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
current_mode = 'U';
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
break;
}
} else if (current_mode == 'D') {
switch (reduced_encode_mode[i]) {
case 'U':
if (!(bp = az_bin_append_posn(14, 4, binary_string, bp))) return 0;
break;
case 'u':
if (!(bp = az_bin_append_posn(15, 4, binary_string, bp))) return 0;
break;
case 'L':
if (!(bp = az_bin_append_posn(14, 4, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(28, 5, binary_string, bp))) return 0;
break;
case 'M':
if (!(bp = az_bin_append_posn(14, 4, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(29, 5, binary_string, bp))) return 0;
break;
case 'P':
if (!(bp = az_bin_append_posn(14, 4, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(29, 5, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(30, 5, binary_string, bp))) return 0;
break;
case 'p':
if (!(bp = az_bin_append_posn(0, 4, binary_string, bp))) return 0;
break;
case 'B':
if (!(bp = az_bin_append_posn(14, 4, binary_string, bp))) return 0;
current_mode = 'U';
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
break;
}
}
if (reduced_encode_mode[i] == 'B') {
int big_batch = 0;
for (count = 0; i + count < reduced_length && reduced_encode_mode[i + count] == 'B'; count++);
if (count > 2047 + 2078) {
return 0;
}
if (count > 2047) {
big_batch = count > 2078 ? 2078 : count;
if (!(bp = az_bin_append_posn(big_batch - 31, 16, binary_string, bp))) return 0;
for (j = 0; j < big_batch; j++) {
if (!(bp = az_bin_append_posn(reduced_source[i++], 8, binary_string, bp))) return 0;
}
count -= big_batch;
}
if (count) {
if (big_batch) {
if (!(bp = az_bin_append_posn(31, 5, binary_string, bp))) return 0;
}
if (count > 31) {
assert(count <= 2078);
if (!(bp = az_bin_append_posn(count - 31, 16, binary_string, bp))) return 0;
} else {
if (!(bp = az_bin_append_posn(count, 5, binary_string, bp))) return 0;
}
for (j = 0; j < count; j++) {
if (!(bp = az_bin_append_posn(reduced_source[i++], 8, binary_string, bp))) return 0;
}
}
i--;
continue;
}
if (reduced_encode_mode[i] != 'u' && reduced_encode_mode[i] != 'p') {
current_mode = reduced_encode_mode[i];
}
}
if (reduced_encode_mode[i] == 'U' || reduced_encode_mode[i] == 'u') {
if (reduced_source[i] == ' ') {
if (!(bp = az_bin_append_posn(1, 5, binary_string, bp))) return 0;
} else {
if (!(bp = az_bin_append_posn(AztecSymbolChar[reduced_source[i]], 5, binary_string, bp))) return 0;
}
} else if (reduced_encode_mode[i] == 'L') {
if (reduced_source[i] == ' ') {
if (!(bp = az_bin_append_posn(1, 5, binary_string, bp))) return 0;
} else {
if (!(bp = az_bin_append_posn(AztecSymbolChar[reduced_source[i]], 5, binary_string, bp))) return 0;
}
} else if (reduced_encode_mode[i] == 'M') {
if (reduced_source[i] == ' ') {
if (!(bp = az_bin_append_posn(1, 5, binary_string, bp))) return 0;
} else if (reduced_source[i] == 13) {
if (!(bp = az_bin_append_posn(14, 5, binary_string, bp))) return 0;
} else {
if (!(bp = az_bin_append_posn(AztecSymbolChar[reduced_source[i]], 5, binary_string, bp))) return 0;
}
} else if (reduced_encode_mode[i] == 'P' || reduced_encode_mode[i] == 'p') {
if (gs1 && reduced_source[i] == '\x1D') {
if (!(bp = az_bin_append_posn(0, 5, binary_string, bp))) return 0;
if (!(bp = az_bin_append_posn(0, 3, binary_string, bp))) return 0;
} else if (reduced_source[i] == 13) {
if (!(bp = az_bin_append_posn(1, 5, binary_string, bp))) return 0;
} else if (reduced_source[i] == 'a') {
if (!(bp = az_bin_append_posn(2, 5, binary_string, bp))) return 0;
} else if (reduced_source[i] == 'b') {
if (!(bp = az_bin_append_posn(3, 5, binary_string, bp))) return 0;
} else if (reduced_source[i] == 'c') {
if (!(bp = az_bin_append_posn(4, 5, binary_string, bp))) return 0;
} else if (reduced_source[i] == 'd') {
if (!(bp = az_bin_append_posn(5, 5, binary_string, bp))) return 0;
} else if (reduced_source[i] == ',') {
if (!(bp = az_bin_append_posn(17, 5, binary_string, bp))) return 0;
} else if (reduced_source[i] == '.') {
if (!(bp = az_bin_append_posn(19, 5, binary_string, bp))) return 0;
} else {
if (!(bp = az_bin_append_posn(AztecSymbolChar[reduced_source[i]], 5, binary_string, bp))) return 0;
}
} else if (reduced_encode_mode[i] == 'D') {
if (reduced_source[i] == ' ') {
if (!(bp = az_bin_append_posn(1, 4, binary_string, bp))) return 0;
} else if (reduced_source[i] == ',') {
if (!(bp = az_bin_append_posn(12, 4, binary_string, bp))) return 0;
} else if (reduced_source[i] == '.') {
if (!(bp = az_bin_append_posn(13, 4, binary_string, bp))) return 0;
} else {
if (!(bp = az_bin_append_posn(AztecSymbolChar[reduced_source[i]], 4, binary_string, bp))) return 0;
}
}
}
if (debug_print) {
printf("Binary String (%d): %.*s\n", bp, bp, binary_string);
}
*data_length = bp;
if (p_current_mode) {
*p_current_mode = current_mode;
}
return 1;
}
static int aztec_text_process_segs(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count, int bp,
char binary_string[], const int gs1, const int gs1_bp, int *data_length, const int debug_print) {
int i;
char current_mode = 'U';
const int content_segs = !gs1 && (symbol->output_options & BARCODE_CONTENT_SEGS);
for (i = 0; i < seg_count; i++) {
if (!aztec_text_process(segs[i].source, segs[i].length, bp, binary_string, gs1, gs1_bp, segs[i].eci,
¤t_mode, &bp, debug_print)) {
return ZINT_ERROR_TOO_LONG;
}
if (content_segs && segs[i].eci) {
z_ct_set_seg_eci(symbol, i, segs[i].eci);
}
}
*data_length = bp;
return 0;
}
static int az_avoidReferenceGrid(int output) {
if (output > 10) {
output += (output - 11) / 15 + 1;
}
return output;
}
static void az_populate_map(short AztecMap[], const int layers) {
int layer;
int x, y;
const int offset = AztecOffset[layers - 1];
const int endoffset = 151 - offset;
for (layer = 0; layer < layers; layer++) {
const int start = (112 * layer) + (16 * layer * layer) + 2;
const int length = 28 + (layer * 4) + (layer + 1) * 4;
int av0, av1;
int n = start, end;
x = 64 - (layer * 2);
y = 63 - (layer * 2);
av0 = az_avoidReferenceGrid(y) * 151;
av1 = az_avoidReferenceGrid(y - 1) * 151;
end = start + length;
while (n < end) {
const int avxi = az_avoidReferenceGrid(x++);
AztecMap[av0 + avxi] = n++;
AztecMap[av1 + avxi] = n++;
}
x = 78 + (layer * 2);
y = 64 - (layer * 2);
av0 = az_avoidReferenceGrid(x);
av1 = az_avoidReferenceGrid(x + 1);
end += length;
while (n < end) {
const int avyi = az_avoidReferenceGrid(y++) * 151;
AztecMap[avyi + av0] = n++;
AztecMap[avyi + av1] = n++;
}
x = 77 + (layer * 2);
y = 78 + (layer * 2);
av0 = az_avoidReferenceGrid(y) * 151;
av1 = az_avoidReferenceGrid(y + 1) * 151;
end += length;
while (n < end) {
const int avxi = az_avoidReferenceGrid(x--);
AztecMap[av0 + avxi] = n++;
AztecMap[av1 + avxi] = n++;
}
x = 63 - (layer * 2);
y = 77 + (layer * 2);
av0 = az_avoidReferenceGrid(x);
av1 = az_avoidReferenceGrid(x - 1);
end += length;
while (n < end) {
const int avyi = az_avoidReferenceGrid(y--) * 151;
AztecMap[avyi + av0] = n++;
AztecMap[avyi + av1] = n++;
}
}
for (y = 0; y < 15; y++) {
memcpy(AztecMap + (y + 68) * 151 + 68, AztecMapCore[y], sizeof(short) * 15);
}
for (y = offset <= 11 ? 11 : AztecMapGridYOffsets[(offset - 11) / 16]; y < endoffset; y += 16) {
for (x = offset; x < endoffset; x++) {
AztecMap[(x * 151) + y] = x & 1;
AztecMap[(y * 151) + x] = x & 1;
}
}
}
static int az_bitrun_stuff(const char *binary_string, const int data_length, const int codeword_size,
const int data_maxsize, char adjusted_string[AZTEC_MAX_CAPACITY]) {
int i, j = 0, count = 0;
for (i = 0; i < data_length; i++) {
if ((j + 1) % codeword_size == 0) {
if (count == 0 || count == codeword_size - 1) {
if (j > data_maxsize) {
return 0;
}
adjusted_string[j++] = count == 0 ? '1' : '0';
count = binary_string[i] == '1' ? 1 : 0;
} else {
count = 0;
}
} else if (binary_string[i] == '1') {
count++;
}
if (j > data_maxsize) {
return 0;
}
adjusted_string[j++] = binary_string[i];
}
return j;
}
static int az_add_padding(const int padbits, const int codeword_size, char adjusted_string[AZTEC_MAX_CAPACITY],
int adjusted_length) {
int i, count = 0;
for (i = 0; i < padbits; i++) {
adjusted_string[adjusted_length++] = '1';
}
for (i = (adjusted_length - codeword_size); i < adjusted_length; i++) {
count += adjusted_string[i] == '1';
}
if (count == codeword_size) {
adjusted_string[adjusted_length - 1] = '0';
}
return adjusted_length;
}
static int az_codeword_size(const int layers) {
int codeword_size;
if (layers <= 2) {
codeword_size = 6;
} else if (layers <= 8) {
codeword_size = 8;
} else if (layers <= 22) {
codeword_size = 10;
} else {
codeword_size = 12;
}
return codeword_size;
}
INTERNAL int zint_aztec(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
int x, y, i, p, data_blocks, ecc_blocks, layers, total_bits;
char bit_pattern[AZTEC_MAP_POSN_MAX + 1];
char *binary_string = bit_pattern;
char descriptor[42];
char adjusted_string[AZTEC_MAX_CAPACITY];
short AztecMap[AZTEC_MAP_SIZE];
unsigned char desc_data[4], desc_ecc[6];
int error_number;
int compact, data_length, data_maxsize, codeword_size, adjusted_length;
int remainder, padbits, adjustment_size;
int bp = 0;
int gs1_bp = 0;
const int gs1 = (symbol->input_mode & 0x07) == GS1_MODE;
const int reader_init = symbol->output_options & READER_INIT;
const int compact_loop_start = reader_init ? 1 : 4;
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
rs_t rs;
rs_uint_t rs_uint;
unsigned int *data_part;
unsigned int *ecc_part;
float ecc_ratio;
int dim;
if (gs1 && reader_init) {
return z_errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 501, "Cannot use Reader Initialisation in GS1 mode");
}
if (symbol->structapp.count) {
unsigned char sa_src[1 + sizeof(symbol->structapp.id) + 1 + 1 + 1 + 1] = {0};
int sa_len;
int id_len;
if (symbol->structapp.count < 2 || symbol->structapp.count > 26) {
return z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 701,
"Structured Append count '%d' out of range (2 to 26)", symbol->structapp.count);
}
if (symbol->structapp.index < 1 || symbol->structapp.index > symbol->structapp.count) {
return ZEXT z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 702,
"Structured Append index '%1$d' out of range (1 to count %2$d)",
symbol->structapp.index, symbol->structapp.count);
}
for (id_len = 0; id_len < 32 && symbol->structapp.id[id_len]; id_len++);
if (id_len && z_chr_cnt(ZCUCP(symbol->structapp.id), id_len, ' ')) {
return z_errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 703, "Structured Append ID cannot contain spaces");
}
bp = z_bin_append_posn(29, 5, binary_string, bp);
bp = z_bin_append_posn(29, 5, binary_string, bp);
sa_len = 0;
if (id_len) {
sa_src[sa_len++] = ' ';
memcpy(sa_src + sa_len, symbol->structapp.id, id_len);
sa_len += id_len;
sa_src[sa_len++] = ' ';
}
sa_src[sa_len++] = 'A' + symbol->structapp.index - 1;
sa_src[sa_len++] = 'A' + symbol->structapp.count - 1;
if (debug_print) {
printf("Structured Append Count: %d, Index: %d, ID: %.32s, String: %s\n",
symbol->structapp.count, symbol->structapp.index, symbol->structapp.id, sa_src);
}
(void) aztec_text_process(sa_src, sa_len, bp, binary_string, 0 , 0 , 0 ,
NULL , &bp, debug_print);
gs1_bp = bp;
}
if ((error_number = aztec_text_process_segs(symbol, segs, seg_count, bp, binary_string, gs1, gs1_bp, &data_length,
debug_print))) {
assert(error_number == ZINT_ERROR_TOO_LONG || error_number == ZINT_ERROR_MEMORY);
if (error_number == ZINT_ERROR_TOO_LONG) {
return z_errtxt(error_number, symbol, 502,
"Input too long, requires too many codewords (maximum " AZ_BIN_CAP_CWDS_S ")");
}
return error_number;
}
assert(data_length > 0);
if (symbol->option_1 < -1 || symbol->option_1 > 4) {
z_errtxtf(0, symbol, 503, "Error correction level '%d' out of range (1 to 4)", symbol->option_1);
if (symbol->warn_level == WARN_FAIL_ALL) {
return ZINT_ERROR_INVALID_OPTION;
}
error_number = z_errtxt_adj(ZINT_WARN_INVALID_OPTION, symbol, "%1$s%2$s", ", ignoring");
symbol->option_1 = -1;
}
data_maxsize = 0;
adjustment_size = 0;
if (symbol->option_2 == 0) {
int ecc_level = symbol->option_1;
if (ecc_level <= 0) {
ecc_level = 2;
}
do {
compact = 0;
layers = 0;
for (i = compact_loop_start; i > 0; i--) {
if (data_length + adjustment_size <= AztecCompactDataSizes[ecc_level - 1][i - 1]) {
layers = i;
compact = 1;
data_maxsize = AztecCompactDataSizes[ecc_level - 1][i - 1];
}
}
if (!compact) {
for (i = 32; i > 0; i--) {
if (data_length + adjustment_size <= AztecDataSizes[ecc_level - 1][i - 1]) {
layers = i;
compact = 0;
data_maxsize = AztecDataSizes[ecc_level - 1][i - 1];
}
}
}
if (layers == 0) {
if (adjustment_size == 0) {
return ZEXT z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 707,
"Input too long for ECC level %1$d, requires too many codewords (maximum %2$d)",
ecc_level, AztecDataSizes[ecc_level - 1][31] / 12);
}
return ZEXT z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 504,
"Input too long for ECC level %1$d, requires %2$d codewords (maximum %3$d)",
ecc_level, (data_length + adjustment_size + 11) / 12,
AztecDataSizes[ecc_level - 1][31] / 12);
}
codeword_size = az_codeword_size(layers);
adjusted_length = az_bitrun_stuff(binary_string, data_length, codeword_size,
adjustment_size ? data_maxsize : AZTEC_BIN_CAPACITY, adjusted_string);
if (adjusted_length == 0) {
return ZEXT z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 705,
"Input too long for ECC level %1$d, requires too many codewords (maximum %2$d)",
ecc_level, (adjustment_size ? data_maxsize : AZTEC_BIN_CAPACITY) / codeword_size);
}
adjustment_size = adjusted_length - data_length;
remainder = adjusted_length % codeword_size;
padbits = codeword_size - remainder;
if (padbits == codeword_size) {
padbits = 0;
}
if (debug_print) printf("Remainder: %d Pad bits: %d\n", remainder, padbits);
assert(adjusted_length <= AZTEC_BIN_CAPACITY);
adjusted_length = az_add_padding(padbits, codeword_size, adjusted_string, adjusted_length);
if (debug_print) printf("Adjusted Length: %d, Data Max Size %d\n", adjusted_length, data_maxsize);
} while (adjusted_length > data_maxsize);
symbol->option_2 = compact ? layers : layers + 4;
} else {
if (symbol->option_2 < 0 || symbol->option_2 > 36) {
return z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 510, "Version '%d' out of range (1 to 36)",
symbol->option_2);
}
if (reader_init) {
if (symbol->option_2 >= 2 && symbol->option_2 <= 4) {
symbol->option_2 = 5;
} else if (symbol->option_2 > 26) {
return z_errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 709,
"Version '%d' out of range for Reader Initialisation symbols (maximum 26)",
symbol->option_2);
}
}
if (symbol->option_2 <= 4) {
compact = 1;
layers = symbol->option_2;
} else {
compact = 0;
layers = symbol->option_2 - 4;
}
codeword_size = az_codeword_size(layers);
if (compact) {
data_maxsize = codeword_size * (AztecCompactSizes[layers - 1] - 3);
} else {
data_maxsize = codeword_size * (AztecSizes[layers - 1] - 3);
}
adjusted_length = az_bitrun_stuff(binary_string, data_length, codeword_size, data_maxsize, adjusted_string);
if (adjusted_length == 0) {
return ZEXT z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 704,
"Input too long for Version %1$d, requires too many codewords (maximum %2$d)",
symbol->option_2, data_maxsize / codeword_size);
}
remainder = adjusted_length % codeword_size;
padbits = codeword_size - remainder;
if (padbits == codeword_size) {
padbits = 0;
}
if (debug_print) printf("Remainder: %d Pad bits: %d\n", remainder, padbits);
if (adjusted_length + padbits > data_maxsize) {
return ZEXT z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 505,
"Input too long for Version %1$d, requires %2$d codewords (maximum %3$d)",
symbol->option_2, (adjusted_length + padbits) / codeword_size,
data_maxsize / codeword_size);
}
adjusted_length = az_add_padding(padbits, codeword_size, adjusted_string, adjusted_length);
if (debug_print) printf("Adjusted Length: %d\n", adjusted_length);
}
if (debug_print) {
printf("Codewords (%d):\n", adjusted_length / codeword_size);
for (i = 0; i < (adjusted_length / codeword_size); i++) {
printf(" %.*s", codeword_size, adjusted_string + i * codeword_size);
}
fputc('\n', stdout);
}
if (reader_init && layers > 22) {
return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 506,
"Input too long for Reader Initialisation, requires %d layers (maximum 22)", layers);
}
data_blocks = adjusted_length / codeword_size;
if (compact) {
ecc_blocks = AztecCompactSizes[layers - 1] - data_blocks;
if (layers == 4) {
ecc_blocks += 12;
}
} else {
ecc_blocks = AztecSizes[layers - 1] - data_blocks;
}
if (ecc_blocks == 3) {
ecc_ratio = 0.0f;
error_number = z_errtxt(ZINT_WARN_NONCOMPLIANT, symbol, 706, "Number of ECC codewords 3 at minimum");
symbol->option_1 = -1;
} else {
ecc_ratio = z_stripf((float) (ecc_blocks - 3) / (data_blocks + ecc_blocks));
if (ecc_ratio < 0.05f) {
error_number = ZEXT z_errtxtf(ZINT_WARN_NONCOMPLIANT, symbol, 708,
"Number of ECC codewords %1$d less than 5%% + 3 of data codewords %2$d",
ecc_blocks, data_blocks);
symbol->option_1 = 0;
} else {
symbol->option_1 = ecc_ratio < 0.165f ? 1 : ecc_ratio < 0.295f ? 2 : ecc_ratio < 0.43f ? 3 : 4;
}
symbol->option_1 |= ((int) z_stripf(ecc_ratio * 100.0f)) << 8;
}
if (debug_print) {
printf("Generating a %s symbol with %d layers\n", compact ? "compact" : "full-size", layers);
printf("Requires %d codewords of %d-bits\n", data_blocks + ecc_blocks, codeword_size);
printf(" (%d data words, %d ecc words, %.1f%%, output option_1 %d, option_2 %d)\n",
data_blocks, ecc_blocks, ecc_ratio * 100, symbol->option_1, symbol->option_2);
}
data_part = (unsigned int *) z_alloca(sizeof(unsigned int) * data_blocks);
ecc_part = (unsigned int *) z_alloca(sizeof(unsigned int) * ecc_blocks);
memset(data_part, 0, sizeof(unsigned int) * data_blocks);
memset(ecc_part, 0, sizeof(unsigned int) * ecc_blocks);
for (i = 0; i < data_blocks; i++) {
for (p = 0; p < codeword_size; p++) {
if (adjusted_string[i * codeword_size + p] == '1') {
data_part[i] |= 0x01 << (codeword_size - (p + 1));
}
}
}
switch (codeword_size) {
case 6:
zint_rs_init_gf(&rs, 0x43);
zint_rs_init_code(&rs, ecc_blocks, 1);
zint_rs_encode_uint(&rs, data_blocks, data_part, ecc_part);
break;
case 8:
zint_rs_init_gf(&rs, 0x12d);
zint_rs_init_code(&rs, ecc_blocks, 1);
zint_rs_encode_uint(&rs, data_blocks, data_part, ecc_part);
break;
case 10:
if (!zint_rs_uint_init_gf(&rs_uint, 0x409, 1023)) {
return z_errtxt(ZINT_ERROR_MEMORY, symbol, 500, "Insufficient memory for Reed-Solomon log tables");
}
zint_rs_uint_init_code(&rs_uint, ecc_blocks, 1);
zint_rs_uint_encode(&rs_uint, data_blocks, data_part, ecc_part);
zint_rs_uint_free(&rs_uint);
break;
case 12:
if (!zint_rs_uint_init_gf(&rs_uint, 0x1069, 4095)) {
return z_errtxt(ZINT_ERROR_MEMORY, symbol, 700, "Insufficient memory for Reed-Solomon log tables");
}
zint_rs_uint_init_code(&rs_uint, ecc_blocks, 1);
zint_rs_uint_encode(&rs_uint, data_blocks, data_part, ecc_part);
zint_rs_uint_free(&rs_uint);
break;
}
for (i = 0; i < ecc_blocks; i++) {
adjusted_length = z_bin_append_posn(ecc_part[i], codeword_size, adjusted_string, adjusted_length);
}
memset(bit_pattern, '0', AZTEC_MAP_POSN_MAX + 1);
total_bits = (data_blocks + ecc_blocks) * codeword_size;
for (i = 0; i < total_bits; i++) {
bit_pattern[i] = adjusted_string[total_bits - i - 1];
}
memset(desc_data, 0, 4);
memset(desc_ecc, 0, 6);
memset(descriptor, 0, 42);
if (compact) {
descriptor[0] = ((layers - 1) & 0x02) ? '1' : '0';
descriptor[1] = ((layers - 1) & 0x01) ? '1' : '0';
descriptor[2] = reader_init || ((data_blocks - 1) & 0x20) ? '1' : '0';
for (i = 3; i < 8; i++) {
descriptor[i] = ((data_blocks - 1) & (0x10 >> (i - 3))) ? '1' : '0';
}
if (debug_print) printf("Mode Message = %.8s\n", descriptor);
} else {
for (i = 0; i < 5; i++) {
descriptor[i] = ((layers - 1) & (0x10 >> i)) ? '1' : '0';
}
descriptor[5] = reader_init || ((data_blocks - 1) & 0x400) ? '1' : '0';
for (i = 6; i < 16; i++) {
descriptor[i] = ((data_blocks - 1) & (0x200 >> (i - 6))) ? '1' : '0';
}
if (debug_print) printf("Mode Message = %.16s\n", descriptor);
}
for (i = 0; i < 4; i++) {
desc_data[i] = ((descriptor[i * 4] == '1') << 3) | ((descriptor[(i * 4) + 1] == '1') << 2)
| ((descriptor[(i * 4) + 2] == '1') << 1) | (descriptor[(i * 4) + 3] == '1');
}
zint_rs_init_gf(&rs, 0x13);
if (compact) {
zint_rs_init_code(&rs, 5, 1);
zint_rs_encode(&rs, 2, desc_data, desc_ecc);
for (i = 0; i < 5; i++) {
descriptor[(i * 4) + 8] = (desc_ecc[i] & 0x08) ? '1' : '0';
descriptor[(i * 4) + 9] = (desc_ecc[i] & 0x04) ? '1' : '0';
descriptor[(i * 4) + 10] = (desc_ecc[i] & 0x02) ? '1' : '0';
descriptor[(i * 4) + 11] = (desc_ecc[i] & 0x01) ? '1' : '0';
}
} else {
zint_rs_init_code(&rs, 6, 1);
zint_rs_encode(&rs, 4, desc_data, desc_ecc);
for (i = 0; i < 6; i++) {
descriptor[(i * 4) + 16] = (desc_ecc[i] & 0x08) ? '1' : '0';
descriptor[(i * 4) + 17] = (desc_ecc[i] & 0x04) ? '1' : '0';
descriptor[(i * 4) + 18] = (desc_ecc[i] & 0x02) ? '1' : '0';
descriptor[(i * 4) + 19] = (desc_ecc[i] & 0x01) ? '1' : '0';
}
}
if (compact) {
memcpy(bit_pattern + 2000 - 2, descriptor, 40);
} else {
memcpy(bit_pattern + 20000 - 2, descriptor, 40);
}
if (compact) {
const int offset = AztecCompactOffset[layers - 1];
const int end_offset = 27 - offset;
for (y = offset; y < end_offset; y++) {
const int y_map = y * 27;
for (x = offset; x < end_offset; x++) {
const int map = AztecCompactMap[y_map + x];
if (map == 1 || (map >= 2 && bit_pattern[map - 2] == '1')) {
z_set_module(symbol, y - offset, x - offset);
}
}
symbol->row_height[y - offset] = 1;
}
dim = 27 - (2 * offset);
} else {
const int offset = AztecOffset[layers - 1];
const int end_offset = 151 - offset;
az_populate_map(AztecMap, layers);
for (y = offset; y < end_offset; y++) {
const int y_map = y * 151;
for (x = offset; x < end_offset; x++) {
const int map = AztecMap[y_map + x];
if (map == 1 || (map >= 2 && bit_pattern[map - 2] == '1')) {
z_set_module(symbol, y - offset, x - offset);
}
}
symbol->row_height[y - offset] = 1;
}
dim = 151 - (2 * offset);
}
symbol->height = dim;
symbol->rows = dim;
symbol->width = dim;
return error_number;
}
INTERNAL int zint_azrune(struct zint_symbol *symbol, unsigned char source[], int length) {
unsigned int input_value;
int i, y, x, r;
char binary_string[28];
unsigned char data_codewords[3], ecc_codewords[6];
int bp = 0;
rs_t rs;
const int content_segs = symbol->output_options & BARCODE_CONTENT_SEGS;
const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
if (length > 3) {
return z_errtxtf(ZINT_ERROR_TOO_LONG, symbol, 507, "Input length %d too long (maximum 3)", length);
}
if ((i = z_not_sane(NEON_F, source, length))) {
return z_errtxtf(ZINT_ERROR_INVALID_DATA, symbol, 508,
"Invalid character at position %d in input (digits only)", i);
}
input_value = z_to_int(source, length);
if (input_value > 255) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 509, "Input value out of range (0 to 255)");
}
bp = z_bin_append_posn(input_value, 8, binary_string, bp);
data_codewords[0] = (unsigned char) (input_value >> 4);
data_codewords[1] = (unsigned char) (input_value & 0xF);
zint_rs_init_gf(&rs, 0x13);
zint_rs_init_code(&rs, 5, 1);
zint_rs_encode(&rs, 2, data_codewords, ecc_codewords);
for (i = 0; i < 5; i++) {
bp = z_bin_append_posn(ecc_codewords[i], 4, binary_string, bp);
}
for (i = 0; i < 28; i += 2) {
binary_string[i] = '0' + (binary_string[i] != '1');
}
if (debug_print) {
printf("Binary String: %.28s\n", binary_string);
}
for (y = 8; y < 19; y++) {
r = y * 27;
for (x = 8; x < 19; x++) {
if (AztecCompactMap[r + x] == 1) {
z_set_module(symbol, y - 8, x - 8);
} else if (AztecCompactMap[r + x] && binary_string[AztecCompactMap[r + x] - 2000] == '1') {
z_set_module(symbol, y - 8, x - 8);
}
}
symbol->row_height[y - 8] = 1;
}
symbol->height = 11;
symbol->rows = 11;
symbol->width = 11;
if (content_segs && z_ct_printf_256(symbol, "%03d", input_value)) {
return ZINT_ERROR_MEMORY;
}
return 0;
}