package cn.besteam.modules.system.util;
|
|
import uk.org.okapibarcode.backend.OkapiException;
|
import uk.org.okapibarcode.backend.ReedSolomon;
|
import uk.org.okapibarcode.backend.Symbol;
|
import uk.org.okapibarcode.util.Arrays;
|
import uk.org.okapibarcode.util.EciMode;
|
|
import java.nio.CharBuffer;
|
import java.nio.charset.Charset;
|
|
public class MyQrCode extends Symbol {
|
|
private static final char[] RHODIUM = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':'};
|
|
private static final int[] QR_DATA_CODEWORDS_L = new int[]{19, 34, 55, 80, 108, 136, 156, 194, 232, 274, 324, 370, 428, 461, 523, 589, 647, 721, 795, 861, 932, 1006, 1094, 1174, 1276, 1370, 1468, 1531, 1631, 1735, 1843, 1955, 2071, 2191, 2306, 2434, 2566, 2702, 2812, 2956};
|
|
private static final int[] QR_DATA_CODEWORDS_M = new int[]{16, 28, 44, 64, 86, 108, 124, 154, 182, 216, 254, 290, 334, 365, 415, 453, 507, 563, 627, 669, 714, 782, 860, 914, 1000, 1062, 1128, 1193, 1267, 1373, 1455, 1541, 1631, 1725, 1812, 1914, 1992, 2102, 2216, 2334};
|
|
private static final int[] QR_DATA_CODEWORDS_Q = new int[]{13, 22, 34, 48, 62, 76, 88, 110, 132, 154, 180, 206, 244, 261, 295, 325, 367, 397, 445, 485, 512, 568, 614, 664, 718, 754, 808, 871, 911, 985, 1033, 1115, 1171, 1231, 1286, 1354, 1426, 1502, 1582, 1666};
|
|
private static final int[] QR_DATA_CODEWORDS_H = new int[]{9, 16, 26, 36, 46, 60, 66, 86, 100, 122, 140, 158, 180, 197, 223, 253, 283, 313, 341, 385, 406, 442, 464, 514, 538, 596, 628, 661, 701, 745, 793, 845, 901, 961, 986, 1054, 1096, 1142, 1222, 1276};
|
|
private static final int[] QR_BLOCKS_L = new int[]{1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25};
|
|
private static final int[] QR_BLOCKS_M = new int[]{1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49};
|
|
private static final int[] QR_BLOCKS_Q = new int[]{1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68};
|
|
private static final int[] QR_BLOCKS_H = new int[]{1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81};
|
|
private static final int[] QR_TOTAL_CODEWORDS = new int[]{26, 44, 70, 100, 134, 172, 196, 242, 292, 346, 404, 466, 532, 581, 655, 733, 815, 901, 991, 1085, 1156, 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, 2185, 2323, 2465, 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706};
|
|
private static final int[] QR_SIZES = new int[]{21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177};
|
|
private static final int[] QR_ALIGN_LOOPSIZE = new int[]{0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7};
|
|
private static final int[] QR_TABLE_E1 = new int[]{6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0, 0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0, 0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54, 80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56, 82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132, 158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170};
|
|
private static final int[] QR_ANNEX_C = new int[]{21522, 20773, 24188, 23371, 17913, 16590, 20375, 19104, 30660, 29427, 32170, 30877, 26159, 25368, 27713, 26998, 5769, 5054, 7399, 6608, 1890, 597, 3340, 2107, 13663, 12392, 16177, 14854, 9396, 8579, 11994, 11245};
|
|
private static final long[] QR_ANNEX_D = new long[]{31892L, 34236L, 39577L, 42195L, 48118L, 51042L, 55367L, 58893L, 63784L, 68472L, 70749L, 76311L, 79154L, 84390L, 87683L, 92361L, 96236L, 102084L, 102881L, 110507L, 110734L, 117786L, 119615L, 126325L, 127568L, 133589L, 136944L, 141498L, 145311L, 150283L, 152622L, 158308L, 161089L, 167017L};
|
|
private int preferredVersion;
|
|
private MyQrCode.EccLevel preferredEccLevel;
|
|
public MyQrCode() {
|
|
this.preferredEccLevel = MyQrCode.EccLevel.L;
|
}
|
|
public void setPreferredVersion(int version) {
|
|
this.preferredVersion = version;
|
}
|
|
public int getPreferredVersion() {
|
|
return this.preferredVersion;
|
}
|
|
public void setPreferredEccLevel(MyQrCode.EccLevel preferredEccLevel) {
|
|
this.preferredEccLevel = preferredEccLevel;
|
}
|
|
public MyQrCode.EccLevel getPreferredEccLevel() {
|
|
return this.preferredEccLevel;
|
}
|
|
protected boolean gs1Supported() {
|
|
return true;
|
}
|
|
@Override
|
protected void encode() {
|
|
boolean gs1 = this.inputDataType == DataType.GS1;
|
EciMode eci = EciMode.of(this.content, "ISO8859_1", 3).or(this.content, "ISO8859_2", 4).or(this.content, "ISO8859_3", 5).or(this.content, "ISO8859_4", 6).or(this.content, "ISO8859_5", 7).or(this.content, "ISO8859_6", 8).or(this.content, "ISO8859_7", 9).or(this.content, "ISO8859_8", 10).or(this.content, "ISO8859_9", 11).or(this.content, "ISO8859_10", 12).or(this.content, "ISO8859_11", 13).or(this.content, "ISO8859_13", 15).or(this.content, "ISO8859_14", 16).or(this.content, "ISO8859_15", 17).or(this.content, "ISO8859_16", 18).or(this.content, "Windows_1250", 21).or(this.content, "Windows_1251", 22).or(this.content, "Windows_1252", 23).or(this.content, "Windows_1256", 24).or(this.content, "UTF8", 26);
|
if (EciMode.NONE.equals(eci)) {
|
throw new OkapiException("Unable to determine ECI mode.");
|
} else {
|
this.eciMode = eci.mode;
|
this.inputData = toBytes(this.content, eci.charset);
|
this.encodeInfo = this.encodeInfo + "ECI Mode: " + eci.mode + "\n";
|
this.encodeInfo = this.encodeInfo + "ECI Charset: " + eci.charset.name() + "\n";
|
}
|
int i;
|
int proposedBinLen;
|
if (this.eciMode == 20) {
|
Charset c = Charset.forName("Shift_JIS");
|
this.inputData = new int[this.content.length()];
|
for (i = 0; i < this.inputData.length; ++i) {
|
CharBuffer buffer = CharBuffer.wrap(this.content, i, i + 1);
|
byte[] bytes = c.encode(buffer).array();
|
proposedBinLen = bytes.length == 2 ? (bytes[0] & 255) << 8 | bytes[1] & 255 : bytes[0];
|
this.inputData[i] = proposedBinLen;
|
}
|
}
|
MyQrCode.QrMode[] inputMode = new MyQrCode.QrMode[this.inputData.length];
|
defineMode(inputMode, this.inputData);
|
int est_binlen = getBinaryLength(40, inputMode, this.inputData, gs1, this.eciMode);
|
MyQrCode.EccLevel ecc_level = this.preferredEccLevel;
|
short max_cw;
|
switch (this.preferredEccLevel) {
|
case L:
|
default:
|
max_cw = 2956;
|
break;
|
case M:
|
max_cw = 2334;
|
break;
|
case Q:
|
max_cw = 1666;
|
break;
|
case H:
|
max_cw = 1276;
|
}
|
if (est_binlen > 8 * max_cw) {
|
throw new OkapiException("Input too long for selected error correction level");
|
} else {
|
int version = 40;
|
int[] datastream;
|
for (i = 39; i >= 0; --i) {
|
switch (ecc_level) {
|
case L:
|
default:
|
datastream = QR_DATA_CODEWORDS_L;
|
break;
|
case M:
|
datastream = QR_DATA_CODEWORDS_M;
|
break;
|
case Q:
|
datastream = QR_DATA_CODEWORDS_Q;
|
break;
|
case H:
|
datastream = QR_DATA_CODEWORDS_H;
|
}
|
int proposedVersion = i + 1;
|
proposedBinLen = getBinaryLength(proposedVersion, inputMode, this.inputData, gs1, this.eciMode);
|
if (8 * datastream[i] >= proposedBinLen) {
|
version = proposedVersion;
|
est_binlen = proposedBinLen;
|
}
|
}
|
inputMode = applyOptimisation(version, inputMode);
|
if (this.preferredVersion >= 1 && this.preferredVersion <= 40) {
|
if (this.preferredVersion > version) {
|
version = this.preferredVersion;
|
est_binlen = getBinaryLength(this.preferredVersion, inputMode, this.inputData, gs1, this.eciMode);
|
inputMode = applyOptimisation(version, inputMode);
|
}
|
if (this.preferredVersion < version) {
|
throw new OkapiException("Input too long for selected symbol size");
|
}
|
}
|
if (est_binlen <= QR_DATA_CODEWORDS_M[version - 1] * 8) {
|
ecc_level = MyQrCode.EccLevel.M;
|
}
|
if (est_binlen <= QR_DATA_CODEWORDS_Q[version - 1] * 8) {
|
ecc_level = MyQrCode.EccLevel.Q;
|
}
|
if (est_binlen <= QR_DATA_CODEWORDS_H[version - 1] * 8) {
|
ecc_level = MyQrCode.EccLevel.H;
|
}
|
int targetCwCount = QR_DATA_CODEWORDS_L[version - 1];
|
int blocks = QR_BLOCKS_L[version - 1];
|
switch (ecc_level) {
|
case M:
|
targetCwCount = QR_DATA_CODEWORDS_M[version - 1];
|
blocks = QR_BLOCKS_M[version - 1];
|
break;
|
case Q:
|
targetCwCount = QR_DATA_CODEWORDS_Q[version - 1];
|
blocks = QR_BLOCKS_Q[version - 1];
|
break;
|
case H:
|
targetCwCount = QR_DATA_CODEWORDS_H[version - 1];
|
blocks = QR_BLOCKS_H[version - 1];
|
}
|
datastream = new int[targetCwCount + 1];
|
int[] fullstream = new int[QR_TOTAL_CODEWORDS[version - 1] + 1];
|
this.qrBinary(datastream, version, targetCwCount, inputMode, this.inputData, gs1, this.eciMode, est_binlen);
|
addEcc(fullstream, datastream, version, targetCwCount, blocks);
|
int size = QR_SIZES[version - 1];
|
byte[] grid = new byte[size * size];
|
this.encodeInfo = this.encodeInfo + "Version: " + version + "\n";
|
this.encodeInfo = this.encodeInfo + "ECC Level: " + ecc_level.name() + "\n";
|
setupGrid(grid, size, version);
|
populateGrid(grid, size, fullstream, QR_TOTAL_CODEWORDS[version - 1]);
|
if (version >= 7) {
|
addVersionInfo(grid, size, version);
|
}
|
int bitmask = applyBitmask(grid, size, ecc_level);
|
this.encodeInfo = this.encodeInfo + "Mask Pattern: " + Integer.toBinaryString(bitmask) + "\n";
|
addFormatInfo(grid, size, ecc_level, bitmask);
|
this.readable = "";
|
this.pattern = new String[size];
|
this.row_count = size;
|
this.row_height = new int[size];
|
for (i = 0; i < size; ++i) {
|
StringBuilder bin = new StringBuilder(size);
|
for (int j = 0; j < size; ++j) {
|
if ((grid[i * size + j] & 1) != 0) {
|
bin.append('1');
|
} else {
|
bin.append('0');
|
}
|
}
|
this.pattern[i] = bin2pat(bin.toString());
|
this.row_height[i] = 1;
|
}
|
}
|
}
|
|
private static void defineMode(MyQrCode.QrMode[] inputMode, int[] inputData) {
|
|
for (int i = 0; i < inputData.length; ++i) {
|
if (inputData[i] > 255) {
|
inputMode[i] = MyQrCode.QrMode.KANJI;
|
} else {
|
inputMode[i] = MyQrCode.QrMode.BINARY;
|
if (isAlpha(inputData[i])) {
|
inputMode[i] = MyQrCode.QrMode.ALPHANUM;
|
}
|
if (inputData[i] == -1) {
|
inputMode[i] = MyQrCode.QrMode.ALPHANUM;
|
}
|
if (isNumeric(inputData[i])) {
|
inputMode[i] = MyQrCode.QrMode.NUMERIC;
|
}
|
}
|
}
|
}
|
|
private static int getBinaryLength(int version, MyQrCode.QrMode[] inputModeUnoptimized, int[] inputData, boolean gs1, int eciMode) {
|
|
int inputLength = inputModeUnoptimized.length;
|
int count = 0;
|
int percent = 0;
|
MyQrCode.QrMode[] inputMode = applyOptimisation(version, inputModeUnoptimized);
|
MyQrCode.QrMode currentMode = MyQrCode.QrMode.NULL;
|
if (gs1) {
|
count += 4;
|
}
|
if (eciMode != 3) {
|
count += 12;
|
}
|
for (int i = 0; i < inputLength; ++i) {
|
if (inputMode[i] != currentMode) {
|
count += 4;
|
int j;
|
label67:
|
switch (inputMode[i]) {
|
case KANJI:
|
count += tribus(version, 8, 10, 12);
|
count += blockLength(i, inputMode) * 13;
|
break;
|
case BINARY:
|
count += tribus(version, 8, 16, 16);
|
j = i;
|
while (true) {
|
if (j >= i + blockLength(i, inputMode)) {
|
break label67;
|
}
|
if (inputData[j] > 255) {
|
count += 16;
|
} else {
|
count += 8;
|
}
|
++j;
|
}
|
case ALPHANUM:
|
count += tribus(version, 9, 11, 13);
|
int alphaLength = blockLength(i, inputMode);
|
if (gs1) {
|
for (j = i; j < i + alphaLength; ++j) {
|
if (inputData[j] == 37) {
|
++percent;
|
}
|
}
|
}
|
alphaLength += percent;
|
switch (alphaLength % 2) {
|
case 0:
|
count += alphaLength / 2 * 11;
|
break label67;
|
case 1:
|
count += (alphaLength - 1) / 2 * 11;
|
count += 6;
|
default:
|
break label67;
|
}
|
case NUMERIC:
|
count += tribus(version, 10, 12, 14);
|
switch (blockLength(i, inputMode) % 3) {
|
case 0:
|
count += blockLength(i, inputMode) / 3 * 10;
|
break;
|
case 1:
|
count += (blockLength(i, inputMode) - 1) / 3 * 10;
|
count += 4;
|
break;
|
case 2:
|
count += (blockLength(i, inputMode) - 2) / 3 * 10;
|
count += 7;
|
}
|
}
|
currentMode = inputMode[i];
|
}
|
}
|
return count;
|
}
|
|
private static MyQrCode.QrMode[] applyOptimisation(int version, MyQrCode.QrMode[] inputMode) {
|
|
int inputLength = inputMode.length;
|
int blockCount = 0;
|
MyQrCode.QrMode currentMode = MyQrCode.QrMode.NULL;
|
int i;
|
for (i = 0; i < inputLength; ++i) {
|
if (inputMode[i] != currentMode) {
|
currentMode = inputMode[i];
|
++blockCount;
|
}
|
}
|
int[] blockLength = new int[blockCount];
|
MyQrCode.QrMode[] blockMode = new MyQrCode.QrMode[blockCount];
|
int j = -1;
|
currentMode = MyQrCode.QrMode.NULL;
|
for (i = 0; i < inputLength; ++i) {
|
if (inputMode[i] != currentMode) {
|
++j;
|
blockLength[j] = 1;
|
blockMode[j] = inputMode[i];
|
currentMode = inputMode[i];
|
} else {
|
int var10002 = blockLength[j]++;
|
}
|
}
|
if (blockCount > 1) {
|
for (i = 0; i <= blockCount - 2; ++i) {
|
if (blockMode[i] == MyQrCode.QrMode.BINARY) {
|
switch (blockMode[i + 1]) {
|
case KANJI:
|
if (blockLength[i + 1] < tribus(version, 4, 5, 6)) {
|
blockMode[i + 1] = MyQrCode.QrMode.BINARY;
|
}
|
case BINARY:
|
default:
|
break;
|
case ALPHANUM:
|
if (blockLength[i + 1] < tribus(version, 7, 8, 9)) {
|
blockMode[i + 1] = MyQrCode.QrMode.BINARY;
|
}
|
break;
|
case NUMERIC:
|
if (blockLength[i + 1] < tribus(version, 3, 4, 5)) {
|
blockMode[i + 1] = MyQrCode.QrMode.BINARY;
|
}
|
}
|
}
|
if (blockMode[i] == MyQrCode.QrMode.ALPHANUM && blockMode[i + 1] == MyQrCode.QrMode.NUMERIC && blockLength[i + 1] < tribus(version, 6, 8, 10)) {
|
blockMode[i + 1] = MyQrCode.QrMode.ALPHANUM;
|
}
|
}
|
for (i = blockCount - 1; i > 0; --i) {
|
if (blockMode[i] == MyQrCode.QrMode.BINARY) {
|
switch (blockMode[i - 1]) {
|
case KANJI:
|
if (blockLength[i - 1] < tribus(version, 4, 5, 6)) {
|
blockMode[i - 1] = MyQrCode.QrMode.BINARY;
|
}
|
case BINARY:
|
default:
|
break;
|
case ALPHANUM:
|
if (blockLength[i - 1] < tribus(version, 7, 8, 9)) {
|
blockMode[i - 1] = MyQrCode.QrMode.BINARY;
|
}
|
break;
|
case NUMERIC:
|
if (blockLength[i - 1] < tribus(version, 3, 4, 5)) {
|
blockMode[i - 1] = MyQrCode.QrMode.BINARY;
|
}
|
}
|
}
|
if (blockMode[i] == MyQrCode.QrMode.ALPHANUM && blockMode[i - 1] == MyQrCode.QrMode.NUMERIC && blockLength[i - 1] < tribus(version, 6, 8, 10)) {
|
blockMode[i - 1] = MyQrCode.QrMode.ALPHANUM;
|
}
|
}
|
}
|
MyQrCode.QrMode[] optimized = new MyQrCode.QrMode[inputMode.length];
|
j = 0;
|
for (int block = 0; block < blockCount; ++block) {
|
currentMode = blockMode[block];
|
for (i = 0; i < blockLength[block]; ++i) {
|
optimized[j] = currentMode;
|
++j;
|
}
|
}
|
return optimized;
|
}
|
|
private static int blockLength(int start, MyQrCode.QrMode[] inputMode) {
|
|
MyQrCode.QrMode mode = inputMode[start];
|
int count = 0;
|
int i = start;
|
do {
|
++count;
|
} while (i + count < inputMode.length && inputMode[i + count] == mode);
|
return count;
|
}
|
|
private static int tribus(int version, int a, int b, int c) {
|
|
if (version < 10) {
|
return a;
|
} else {
|
return version >= 10 && version <= 26 ? b : c;
|
}
|
}
|
|
private static boolean isAlpha(int c) {
|
|
return c >= 48 && c <= 57 || c >= 65 && c <= 90 || c == 32 || c == 36 || c == 37 || c == 42 || c == 43 || c == 45 || c == 46 || c == 47 || c == 58;
|
}
|
|
private static boolean isNumeric(int c) {
|
|
return c >= 48 && c <= 57;
|
}
|
|
private void qrBinary(int[] datastream, int version, int target_binlen, MyQrCode.QrMode[] inputMode, int[] inputData, boolean gs1, int eciMode, int est_binlen) {
|
|
int position = 0;
|
StringBuilder binary = new StringBuilder(est_binlen + 12);
|
if (gs1) {
|
binary.append("0101");
|
}
|
if (eciMode != 3) {
|
binary.append("0111");
|
if (eciMode <= 127) {
|
binaryAppend(eciMode, 8, binary);
|
} else if (eciMode <= 16383) {
|
binaryAppend('耀' + eciMode, 16, binary);
|
} else {
|
binaryAppend(12582912 + eciMode, 24, binary);
|
}
|
}
|
this.encodeInfo = this.encodeInfo + "Encoding: ";
|
int i;
|
int p;
|
do {
|
MyQrCode.QrMode data_block = inputMode[position];
|
int short_data_block_length = 0;
|
do {
|
++short_data_block_length;
|
} while (short_data_block_length + position < inputMode.length && inputMode[position + short_data_block_length] == data_block);
|
int first;
|
int prod;
|
label170:
|
switch (data_block) {
|
case KANJI:
|
binary.append("1000");
|
binaryAppend(short_data_block_length, tribus(version, 8, 10, 12), binary);
|
this.encodeInfo = this.encodeInfo + "KNJI ";
|
for (i = 0; i < short_data_block_length; ++i) {
|
p = inputData[position + i];
|
if (p >= 33088 && p <= 40956) {
|
p -= 33088;
|
} else if (p >= 57408 && p <= 60351) {
|
p -= 49472;
|
}
|
prod = (p >> 8) * 192 + (p & 255);
|
binaryAppend(prod, 13, binary);
|
this.encodeInfo = this.encodeInfo + Integer.toString(prod) + " ";
|
}
|
break;
|
case BINARY:
|
binary.append("0100");
|
binaryAppend(short_data_block_length, tribus(version, 8, 16, 16), binary);
|
this.encodeInfo = this.encodeInfo + "BYTE ";
|
i = 0;
|
while (true) {
|
if (i >= short_data_block_length) {
|
break label170;
|
}
|
p = inputData[position + i];
|
if (p == -1) {
|
p = 29;
|
}
|
binaryAppend(p, 8, binary);
|
this.encodeInfo = this.encodeInfo + Integer.toString(p) + " ";
|
++i;
|
}
|
case ALPHANUM:
|
binary.append("0010");
|
p = 0;
|
if (gs1) {
|
for (i = 0; i < short_data_block_length; ++i) {
|
if (inputData[position + i] == 37) {
|
++p;
|
}
|
}
|
}
|
int[] inputExpanded = new int[short_data_block_length + p];
|
p = 0;
|
for (i = 0; i < short_data_block_length; ++i) {
|
first = inputData[position + i];
|
if (first == -1) {
|
inputExpanded[i + p] = 37;
|
} else {
|
inputExpanded[i + p] = first;
|
if (gs1 && first == 37) {
|
++p;
|
inputExpanded[i + p] = first;
|
}
|
}
|
}
|
binaryAppend(inputExpanded.length, tribus(version, 9, 11, 13), binary);
|
this.encodeInfo = this.encodeInfo + "ALPH ";
|
for (i = 0; i + 1 < inputExpanded.length; i += 2) {
|
first = Arrays.positionOf((char) inputExpanded[i], RHODIUM);
|
int second = Arrays.positionOf((char) inputExpanded[i + 1], RHODIUM);
|
prod = first * 45 + second;
|
int count = 2;
|
binaryAppend(prod, 1 + 5 * count, binary);
|
this.encodeInfo = this.encodeInfo + Integer.toString(prod) + " ";
|
}
|
if (inputExpanded.length % 2 != 0) {
|
first = Arrays.positionOf((char) inputExpanded[inputExpanded.length - 1], RHODIUM);
|
int count = 1;
|
binaryAppend(first, 1 + 5 * count, binary);
|
this.encodeInfo = this.encodeInfo + Integer.toString(first) + " ";
|
}
|
break;
|
case NUMERIC:
|
binary.append("0001");
|
binaryAppend(short_data_block_length, tribus(version, 10, 12, 14), binary);
|
this.encodeInfo = this.encodeInfo + "NUMB ";
|
byte count;
|
for (i = 0; i < short_data_block_length; i += count) {
|
first = Character.getNumericValue(inputData[position + i]);
|
count = 1;
|
prod = first;
|
if (i + 1 < short_data_block_length) {
|
int second = Character.getNumericValue(inputData[position + i + 1]);
|
count = 2;
|
prod = first * 10 + second;
|
if (i + 2 < short_data_block_length) {
|
int third = Character.getNumericValue(inputData[position + i + 2]);
|
count = 3;
|
prod = prod * 10 + third;
|
}
|
}
|
binaryAppend(prod, 1 + 3 * count, binary);
|
this.encodeInfo = this.encodeInfo + Integer.toString(prod) + " ";
|
}
|
}
|
position += short_data_block_length;
|
} while (position < inputMode.length);
|
this.encodeInfo = this.encodeInfo + "\n";
|
binary.append("0000");
|
int current_binlen = binary.length();
|
int padbits = 8 - current_binlen % 8;
|
if (padbits == 8) {
|
padbits = 0;
|
}
|
int current_bytes = (current_binlen + padbits) / 8;
|
for (i = 0; i < padbits; ++i) {
|
binary.append('0');
|
}
|
for (i = 0; i < current_bytes; ++i) {
|
datastream[i] = 0;
|
for (p = 0; p < 8; ++p) {
|
if (binary.charAt(i * 8 + p) == '1') {
|
datastream[i] += 128 >> p;
|
}
|
}
|
}
|
boolean toggle = false;
|
for (i = current_bytes; i < target_binlen; ++i) {
|
if (!toggle) {
|
datastream[i] = 236;
|
toggle = true;
|
} else {
|
datastream[i] = 17;
|
toggle = false;
|
}
|
}
|
this.encodeInfo = this.encodeInfo + "Codewords: ";
|
for (i = 0; i < target_binlen; ++i) {
|
this.encodeInfo = this.encodeInfo + Integer.toString(datastream[i]) + " ";
|
}
|
this.encodeInfo = this.encodeInfo + "\n";
|
}
|
|
private static void binaryAppend(int value, int length, StringBuilder binary) {
|
|
int start = 1 << length - 1;
|
for (int i = 0; i < length; ++i) {
|
if ((value & start >> i) != 0) {
|
binary.append('1');
|
} else {
|
binary.append('0');
|
}
|
}
|
}
|
|
private static void addEcc(int[] fullstream, int[] datastream, int version, int data_cw, int blocks) {
|
|
int ecc_cw = QR_TOTAL_CODEWORDS[version - 1] - data_cw;
|
int short_data_block_length = data_cw / blocks;
|
int qty_long_blocks = data_cw % blocks;
|
int qty_short_blocks = blocks - qty_long_blocks;
|
int ecc_block_length = ecc_cw / blocks;
|
int[] data_block = new int[short_data_block_length + 2];
|
int[] ecc_block = new int[ecc_block_length + 2];
|
int[] interleaved_data = new int[data_cw + 2];
|
int[] interleaved_ecc = new int[ecc_cw + 2];
|
int posn = 0;
|
int j;
|
for (int i = 0; i < blocks; ++i) {
|
int length_this_block;
|
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;
|
}
|
for (j = 0; j < length_this_block; ++j) {
|
data_block[j] = datastream[posn + j];
|
}
|
ReedSolomon rs = new ReedSolomon();
|
rs.init_gf(285);
|
rs.init_code(ecc_block_length, 0);
|
rs.encode(length_this_block, data_block);
|
for (j = 0; j < ecc_block_length; ++j) {
|
ecc_block[j] = rs.getResult(j);
|
}
|
for (j = 0; j < short_data_block_length; ++j) {
|
interleaved_data[j * blocks + i] = data_block[j];
|
}
|
if (i >= qty_short_blocks) {
|
interleaved_data[short_data_block_length * blocks + (i - qty_short_blocks)] = data_block[short_data_block_length];
|
}
|
for (j = 0; j < ecc_block_length; ++j) {
|
interleaved_ecc[j * blocks + i] = ecc_block[ecc_block_length - j - 1];
|
}
|
posn += length_this_block;
|
}
|
for (j = 0; j < data_cw; ++j) {
|
fullstream[j] = interleaved_data[j];
|
}
|
for (j = 0; j < ecc_cw; ++j) {
|
fullstream[j + data_cw] = interleaved_ecc[j];
|
}
|
}
|
|
private static void setupGrid(byte[] grid, int size, int version) {
|
|
boolean toggle = true;
|
int i;
|
for (i = 0; i < size; ++i) {
|
if (toggle) {
|
grid[6 * size + i] = 33;
|
grid[i * size + 6] = 33;
|
toggle = false;
|
} else {
|
grid[6 * size + i] = 32;
|
grid[i * size + 6] = 32;
|
toggle = true;
|
}
|
}
|
placeFinder(grid, size, 0, 0);
|
placeFinder(grid, size, 0, size - 7);
|
placeFinder(grid, size, size - 7, 0);
|
for (i = 0; i < 7; ++i) {
|
grid[7 * size + i] = 16;
|
grid[i * size + 7] = 16;
|
grid[7 * size + (size - 1 - i)] = 16;
|
grid[i * size + (size - 8)] = 16;
|
grid[(size - 8) * size + i] = 16;
|
grid[(size - 1 - i) * size + 7] = 16;
|
}
|
grid[7 * size + 7] = 16;
|
grid[7 * size + (size - 8)] = 16;
|
grid[(size - 8) * size + 7] = 16;
|
if (version != 1) {
|
int loopsize = QR_ALIGN_LOOPSIZE[version - 1];
|
for (int x = 0; x < loopsize; ++x) {
|
for (int y = 0; y < loopsize; ++y) {
|
int xcoord = QR_TABLE_E1[(version - 2) * 7 + x];
|
int ycoord = QR_TABLE_E1[(version - 2) * 7 + y];
|
if ((grid[ycoord * size + xcoord] & 16) == 0) {
|
placeAlign(grid, size, xcoord, ycoord);
|
}
|
}
|
}
|
}
|
for (i = 0; i < 8; ++i) {
|
grid[8 * size + i] = (byte) (grid[8 * size + i] + 32);
|
grid[i * size + 8] = (byte) (grid[i * size + 8] + 32);
|
grid[8 * size + (size - 1 - i)] = 32;
|
grid[(size - 1 - i) * size + 8] = 32;
|
}
|
grid[8 * size + 8] = (byte) (grid[8 * size + 8] + 32);
|
grid[(size - 1 - 7) * size + 8] = 33;
|
if (version >= 7) {
|
for (i = 0; i < 6; ++i) {
|
grid[(size - 9) * size + i] = 32;
|
grid[(size - 10) * size + i] = 32;
|
grid[(size - 11) * size + i] = 32;
|
grid[i * size + (size - 9)] = 32;
|
grid[i * size + (size - 10)] = 32;
|
grid[i * size + (size - 11)] = 32;
|
}
|
}
|
}
|
|
private static void placeFinder(byte[] grid, int size, int x, int y) {
|
|
int[] finder = new int[]{1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};
|
for (int xp = 0; xp < 7; ++xp) {
|
for (int yp = 0; yp < 7; ++yp) {
|
if (finder[xp + 7 * yp] == 1) {
|
grid[(yp + y) * size + xp + x] = 17;
|
} else {
|
grid[(yp + y) * size + xp + x] = 16;
|
}
|
}
|
}
|
}
|
|
private static void placeAlign(byte[] grid, int size, int x, int y) {
|
|
int[] alignment = new int[]{1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1};
|
x -= 2;
|
y -= 2;
|
for (int xp = 0; xp < 5; ++xp) {
|
for (int yp = 0; yp < 5; ++yp) {
|
if (alignment[xp + 5 * yp] == 1) {
|
grid[(yp + y) * size + xp + x] = 17;
|
} else {
|
grid[(yp + y) * size + xp + x] = 16;
|
}
|
}
|
}
|
}
|
|
private static void populateGrid(byte[] grid, int size, int[] fullstream, int cw) {
|
|
boolean goingUp = true;
|
int row = 0;
|
int n = cw * 8;
|
int y = size - 1;
|
int i = 0;
|
do {
|
int x = size - 2 - row * 2;
|
if (x < 6) {
|
--x;
|
}
|
if ((grid[y * size + x + 1] & 240) == 0) {
|
if (cwbit(fullstream, i)) {
|
grid[y * size + x + 1] = 1;
|
} else {
|
grid[y * size + x + 1] = 0;
|
}
|
++i;
|
}
|
if (i < n && (grid[y * size + x] & 240) == 0) {
|
if (cwbit(fullstream, i)) {
|
grid[y * size + x] = 1;
|
} else {
|
grid[y * size + x] = 0;
|
}
|
++i;
|
}
|
if (goingUp) {
|
--y;
|
} else {
|
++y;
|
}
|
if (y == -1) {
|
++row;
|
y = 0;
|
goingUp = false;
|
}
|
if (y == size) {
|
++row;
|
y = size - 1;
|
goingUp = true;
|
}
|
} while (i < n);
|
}
|
|
private static boolean cwbit(int[] fullstream, int i) {
|
|
return (fullstream[i / 8] & 128 >> i % 8) != 0;
|
}
|
|
private static int applyBitmask(byte[] grid, int size, MyQrCode.EccLevel ecc_level) {
|
|
int[] penalty = new int[8];
|
byte[] mask = new byte[size * size];
|
byte[] eval = new byte[size * size];
|
int x;
|
int y;
|
for (x = 0; x < size; ++x) {
|
for (y = 0; y < size; ++y) {
|
mask[y * size + x] = 0;
|
if ((grid[y * size + x] & 240) == 0) {
|
if ((y + x & 1) == 0) {
|
++mask[y * size + x];
|
}
|
if ((y & 1) == 0) {
|
mask[y * size + x] = (byte) (mask[y * size + x] + 2);
|
}
|
if (x % 3 == 0) {
|
mask[y * size + x] = (byte) (mask[y * size + x] + 4);
|
}
|
if ((y + x) % 3 == 0) {
|
mask[y * size + x] = (byte) (mask[y * size + x] + 8);
|
}
|
if ((y / 2 + x / 3 & 1) == 0) {
|
mask[y * size + x] = (byte) (mask[y * size + x] + 16);
|
}
|
if ((y * x & 1) + y * x % 3 == 0) {
|
mask[y * size + x] = (byte) (mask[y * size + x] + 32);
|
}
|
if (((y * x & 1) + y * x % 3 & 1) == 0) {
|
mask[y * size + x] = (byte) (mask[y * size + x] + 64);
|
}
|
if (((y + x & 1) + y * x % 3 & 1) == 0) {
|
mask[y * size + x] = (byte) (mask[y * size + x] + 128);
|
}
|
}
|
}
|
}
|
for (x = 0; x < size; ++x) {
|
for (y = 0; y < size; ++y) {
|
short p;
|
if ((grid[y * size + x] & 1) != 0) {
|
p = 255;
|
} else {
|
p = 0;
|
}
|
eval[y * size + x] = (byte) (mask[y * size + x] ^ p);
|
}
|
}
|
int pattern;
|
for (pattern = 0; pattern < 8; ++pattern) {
|
addFormatInfoEval(eval, size, ecc_level, pattern);
|
penalty[pattern] = evaluate(eval, size, pattern);
|
}
|
int best_pattern = 0;
|
int best_val = penalty[0];
|
for (pattern = 1; pattern < 8; ++pattern) {
|
if (penalty[pattern] < best_val) {
|
best_pattern = pattern;
|
best_val = penalty[pattern];
|
}
|
}
|
for (x = 0; x < size; ++x) {
|
for (y = 0; y < size; ++y) {
|
if ((mask[y * size + x] & 1 << best_pattern) != 0) {
|
if ((grid[y * size + x] & 1) != 0) {
|
grid[y * size + x] = 0;
|
} else {
|
grid[y * size + x] = 1;
|
}
|
}
|
}
|
}
|
return best_pattern;
|
}
|
|
private static void addFormatInfoEval(byte[] eval, int size, MyQrCode.EccLevel ecc_level, int pattern) {
|
|
int format = pattern;
|
switch (ecc_level) {
|
case L:
|
format = pattern + 8;
|
case M:
|
default:
|
break;
|
case Q:
|
format = pattern + 24;
|
break;
|
case H:
|
format = pattern + 16;
|
}
|
int seq = QR_ANNEX_C[format];
|
int i;
|
for (i = 0; i < 6; ++i) {
|
eval[i * size + 8] = (byte) ((seq >> i & 1) != 0 ? 1 >> pattern : 0);
|
}
|
for (i = 0; i < 8; ++i) {
|
eval[8 * size + (size - i - 1)] = (byte) ((seq >> i & 1) != 0 ? 1 >> pattern : 0);
|
}
|
for (i = 0; i < 6; ++i) {
|
eval[8 * size + (5 - i)] = (byte) ((seq >> i + 9 & 1) != 0 ? 1 >> pattern : 0);
|
}
|
for (i = 0; i < 7; ++i) {
|
eval[(size - 7 + i) * size + 8] = (byte) ((seq >> i + 8 & 1) != 0 ? 1 >> pattern : 0);
|
}
|
eval[7 * size + 8] = (byte) ((seq >> 6 & 1) != 0 ? 1 >> pattern : 0);
|
eval[8 * size + 8] = (byte) ((seq >> 7 & 1) != 0 ? 1 >> pattern : 0);
|
eval[8 * size + 7] = (byte) ((seq >> 8 & 1) != 0 ? 1 >> pattern : 0);
|
}
|
|
private static int evaluate(byte[] eval, int size, int pattern) {
|
|
int result = 0;
|
byte[] local = new byte[size * size];
|
int x;
|
int y;
|
for (x = 0; x < size; ++x) {
|
for (y = 0; y < size; ++y) {
|
if ((eval[y * size + x] & 1 << pattern) != 0) {
|
local[y * size + x] = 49;
|
} else {
|
local[y * size + x] = 48;
|
}
|
}
|
}
|
int block;
|
byte state;
|
for (x = 0; x < size; ++x) {
|
state = local[x];
|
block = 0;
|
for (y = 0; y < size; ++y) {
|
if (local[y * size + x] == state) {
|
++block;
|
} else {
|
if (block > 5) {
|
result += 3 + (block - 5);
|
}
|
block = 0;
|
state = local[y * size + x];
|
}
|
}
|
if (block > 5) {
|
result += 3 + (block - 5);
|
}
|
}
|
for (y = 0; y < size; ++y) {
|
state = local[y * size];
|
block = 0;
|
for (x = 0; x < size; ++x) {
|
if (local[y * size + x] == state) {
|
++block;
|
} else {
|
if (block > 5) {
|
result += 3 + (block - 5);
|
}
|
block = 0;
|
state = local[y * size + x];
|
}
|
}
|
if (block > 5) {
|
result += 3 + (block - 5);
|
}
|
}
|
for (x = 0; x < size - 1; ++x) {
|
for (y = 0; y < size - 1; ++y) {
|
if (local[y * size + x] == local[(y + 1) * size + x] && local[y * size + x] == local[y * size + x + 1] && local[y * size + x] == local[(y + 1) * size + x + 1]) {
|
result += 3;
|
}
|
}
|
}
|
int weight;
|
int p;
|
int a;
|
int b;
|
int afterCount;
|
int beforeCount;
|
for (x = 0; x < size; ++x) {
|
for (y = 0; y < size - 7; ++y) {
|
p = 0;
|
for (weight = 0; weight < 7; ++weight) {
|
if (local[(y + weight) * size + x] == 49) {
|
p += 64 >> weight;
|
}
|
}
|
if (p == 93) {
|
beforeCount = 0;
|
for (b = y - 4; b < y; ++b) {
|
if (b < 0) {
|
++beforeCount;
|
} else if (local[b * size + x] == 48) {
|
++beforeCount;
|
} else {
|
beforeCount = 0;
|
}
|
}
|
afterCount = 0;
|
for (a = y + 7; a <= y + 10; ++a) {
|
if (a >= size) {
|
++afterCount;
|
} else if (local[a * size + x] == 48) {
|
++afterCount;
|
} else {
|
afterCount = 0;
|
}
|
}
|
if (beforeCount == 4 || afterCount == 4) {
|
result += 40;
|
}
|
}
|
}
|
}
|
for (y = 0; y < size; ++y) {
|
for (x = 0; x < size - 7; ++x) {
|
p = 0;
|
for (weight = 0; weight < 7; ++weight) {
|
if (local[y * size + x + weight] == 49) {
|
p += 64 >> weight;
|
}
|
}
|
if (p == 93) {
|
beforeCount = 0;
|
for (b = x - 4; b < x; ++b) {
|
if (b < 0) {
|
++beforeCount;
|
} else if (local[y * size + b] == 48) {
|
++beforeCount;
|
} else {
|
beforeCount = 0;
|
}
|
}
|
afterCount = 0;
|
for (a = x + 7; a <= x + 10; ++a) {
|
if (a >= size) {
|
++afterCount;
|
} else if (local[y * size + a] == 48) {
|
++afterCount;
|
} else {
|
afterCount = 0;
|
}
|
}
|
if (beforeCount == 4 || afterCount == 4) {
|
result += 40;
|
}
|
}
|
}
|
}
|
int dark_mods = 0;
|
for (x = 0; x < size; ++x) {
|
for (y = 0; y < size; ++y) {
|
if (local[y * size + x] == 49) {
|
++dark_mods;
|
}
|
}
|
}
|
int percentage = 100 * (dark_mods / (size * size));
|
int k;
|
if (percentage <= 50) {
|
k = (100 - percentage - 50) / 5;
|
} else {
|
k = (percentage - 50) / 5;
|
}
|
result += 10 * k;
|
return result;
|
}
|
|
private static void addFormatInfo(byte[] grid, int size, MyQrCode.EccLevel ecc_level, int pattern) {
|
|
int format = pattern;
|
switch (ecc_level) {
|
case L:
|
format = pattern + 8;
|
case M:
|
default:
|
break;
|
case Q:
|
format = pattern + 24;
|
break;
|
case H:
|
format = pattern + 16;
|
}
|
int seq = QR_ANNEX_C[format];
|
int i;
|
for (i = 0; i < 6; ++i) {
|
grid[i * size + 8] = (byte) (grid[i * size + 8] + (seq >> i & 1));
|
}
|
for (i = 0; i < 8; ++i) {
|
grid[8 * size + (size - i - 1)] = (byte) (grid[8 * size + (size - i - 1)] + (seq >> i & 1));
|
}
|
for (i = 0; i < 6; ++i) {
|
grid[8 * size + (5 - i)] = (byte) (grid[8 * size + (5 - i)] + (seq >> i + 9 & 1));
|
}
|
for (i = 0; i < 7; ++i) {
|
grid[(size - 7 + i) * size + 8] = (byte) (grid[(size - 7 + i) * size + 8] + (seq >> i + 8 & 1));
|
}
|
grid[7 * size + 8] = (byte) (grid[7 * size + 8] + (seq >> 6 & 1));
|
grid[8 * size + 8] = (byte) (grid[8 * size + 8] + (seq >> 7 & 1));
|
grid[8 * size + 7] = (byte) (grid[8 * size + 7] + (seq >> 8 & 1));
|
}
|
|
private static void addVersionInfo(byte[] grid, int size, int version) {
|
|
long version_data = QR_ANNEX_D[version - 7];
|
for (int i = 0; i < 6; ++i) {
|
grid[(size - 11) * size + i] = (byte) ((int) ((long) grid[(size - 11) * size + i] + (version_data >> i * 3 & 1L)));
|
grid[(size - 10) * size + i] = (byte) ((int) ((long) grid[(size - 10) * size + i] + (version_data >> i * 3 + 1 & 1L)));
|
grid[(size - 9) * size + i] = (byte) ((int) ((long) grid[(size - 9) * size + i] + (version_data >> i * 3 + 2 & 1L)));
|
grid[i * size + (size - 11)] = (byte) ((int) ((long) grid[i * size + (size - 11)] + (version_data >> i * 3 & 1L)));
|
grid[i * size + (size - 10)] = (byte) ((int) ((long) grid[i * size + (size - 10)] + (version_data >> i * 3 + 1 & 1L)));
|
grid[i * size + (size - 9)] = (byte) ((int) ((long) grid[i * size + (size - 9)] + (version_data >> i * 3 + 2 & 1L)));
|
}
|
}
|
|
private static enum QrMode {
|
NULL, KANJI, BINARY, ALPHANUM, NUMERIC;
|
|
private QrMode() {
|
|
}
|
}
|
|
public static enum EccLevel {
|
L, M, Q, H;
|
|
private EccLevel() {
|
|
}
|
|
}
|
|
}
|