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() { } } }