From e6e82548ab53b14d322f780c7b8ccd35c2b5c25b Mon Sep 17 00:00:00 2001 From: favoritas37 Date: Sun, 14 Jun 2015 20:51:23 +0300 Subject: [PATCH] progressing port of QR Encoder --- source/zxing/zxing/qrcode/encoder/Encoder.cpp | 1059 +++++++++-------- source/zxing/zxing/qrcode/encoder/Encoder.h | 221 ++-- 2 files changed, 651 insertions(+), 629 deletions(-) diff --git a/source/zxing/zxing/qrcode/encoder/Encoder.cpp b/source/zxing/zxing/qrcode/encoder/Encoder.cpp index 4b58280..5fd9edc 100644 --- a/source/zxing/zxing/qrcode/encoder/Encoder.cpp +++ b/source/zxing/zxing/qrcode/encoder/Encoder.cpp @@ -1,545 +1,582 @@ -//#include "Encoder.h" -//#include +#include "Encoder.h" +#include "MaskUtil.h" -//namespace zxing { -//namespace qrcode { +//import com.google.zxing.EncodeHintType; +//import com.google.zxing.WriterException; +//import com.google.zxing.common.BitArray; +//import com.google.zxing.common.CharacterSetECI; +//import com.google.zxing.common.reedsolomon.GenericGF; +//import com.google.zxing.common.reedsolomon.ReedSolomonEncoder; +//import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +//import com.google.zxing.qrcode.decoder.Mode; +//import com.google.zxing.qrcode.decoder.Version; -//// The original table is defined in the table 5 of JISX0510:2004 (p.19). -//const int Encoder::ALPHANUMERIC_TABLE[] = { -// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f -// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f -// 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f -// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f -// -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f -// 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f -//}; +//import java.io.UnsupportedEncodingException; +//import java.util.ArrayList; +//import java.util.Collection; +//import java.util.Map; -//const std::string DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1"; +#include +#include +#include +#include +#include "BlockPair.h" +#include +#include -////static int Encoder::calculateMaskPenalty(ByteMatrix matrix) { -//// return MaskUtil.applyMaskPenaltyRule1(matrix) -//// + MaskUtil.applyMaskPenaltyRule2(matrix) -//// + MaskUtil.applyMaskPenaltyRule3(matrix) -//// + MaskUtil.applyMaskPenaltyRule4(matrix); -////} +namespace zxing { +namespace qrcode { -//// /** -//// * Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen -//// * internally by chooseMode(). On success, store the result in "qrCode". -//// * -//// * We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for -//// * "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very -//// * strong error correction for this purpose. -//// * -//// * Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode() -//// * with which clients can specify the encoding mode. For now, we don't need the functionality. -//// */ -//// public static QRCode encode(String content, ErrorCorrectionLevel ecLevel) throws WriterException { -//// return encode(content, ecLevel, null); -//// } +const int Encoder::ALPHANUMERIC_TABLE_SIZE = 96; +// The original table is defined in the table 5 of JISX0510:2004 (p.19). +const int Encoder::ALPHANUMERIC_TABLE[Encoder::ALPHANUMERIC_TABLE_SIZE] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f +}; -//// public static QRCode encode(String content, -//// ErrorCorrectionLevel ecLevel, -//// Map hints) throws WriterException { +const QString DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1"; -//// // Determine what character encoding has been specified by the caller, if any -//// String encoding = hints == null ? null : (String) hints.get(EncodeHintType.CHARACTER_SET); -//// if (encoding == null) { -//// encoding = DEFAULT_BYTE_MODE_ENCODING; -//// } +int Encoder::calculateMaskPenalty(const ByteMatrix& matrix) +{ + return MaskUtil::applyMaskPenaltyRule1(matrix) + + MaskUtil::applyMaskPenaltyRule2(matrix) + + MaskUtil::applyMaskPenaltyRule3(matrix) + + MaskUtil::applyMaskPenaltyRule4(matrix); +} -//// // Pick an encoding mode appropriate for the content. Note that this will not attempt to use -//// // multiple modes / segments even if that were more efficient. Twould be nice. -//// Mode mode = chooseMode(content, encoding); +QRCode* Encoder::encode(const QString& content, const ErrorCorrectionLevel *ecLevel) +{ + return encode(content, ecLevel, NULL); +} -//// // This will store the header information, like mode and -//// // length, as well as "header" segments like an ECI segment. -//// BitArray headerBits = new BitArray(); +QRCode* Encoder::encode(const QString& content, const ErrorCorrectionLevel* ecLevel, const EncodeHint* hints) +{ + // // Determine what character encoding has been specified by the caller, if any + // QString encoding = hints == NULL ? "" : hints->get_character_set(); + // if (encoding == "") + // encoding = DEFAULT_BYTE_MODE_ENCODING; -//// // Append ECI segment if applicable -//// if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) { -//// CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding); -//// if (eci != null) { -//// appendECI(eci, headerBits); -//// } -//// } + // // Pick an encoding mode appropriate for the content. Note that this will not attempt to use + // // multiple modes / segments even if that were more efficient. Twould be nice. + // Mode* mode = chooseMode(content, encoding); -//// // (With ECI in place,) Write the mode marker -//// appendModeInfo(mode, headerBits); + // // This will store the header information, like mode and + // // length, as well as "header" segments like an ECI segment. + // BitArray headerBits; -//// // Collect data within the main segment, separately, to count its size if needed. Don't add it to -//// // main payload yet. -//// BitArray dataBits = new BitArray(); -//// appendBytes(content, mode, dataBits, encoding); + // // Append ECI segment if applicable + // if (mode == &Mode::BYTE && DEFAULT_BYTE_MODE_ENCODING != encoding) { + // zxing::common::CharacterSetECI* eci = zxing::common::CharacterSetECI::getCharacterSetECIByName(encoding); + // if (eci != NULL) { + // appendECI(*eci, headerBits); + // } + // } -//// // Hard part: need to know version to know how many bits length takes. But need to know how many -//// // bits it takes to know version. First we take a guess at version by assuming version will be -//// // the minimum, 1: + // // (With ECI in place,) Write the mode marker + // appendModeInfo(*mode, headerBits); -//// int provisionalBitsNeeded = headerBits.getSize() -//// + mode.getCharacterCountBits(Version.getVersionForNumber(1)) -//// + dataBits.getSize(); -//// Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel); + // // Collect data within the main segment, separately, to count its size if needed. Don't add it to + // // main payload yet. + // BitArray dataBits; + // appendBytes(content, *mode, dataBits, encoding); -//// // Use that guess to calculate the right version. I am still not sure this works in 100% of cases. + // // Hard part: need to know version to know how many bits length takes. But need to know how many + // // bits it takes to know version. First we take a guess at version by assuming version will be + // // the minimum, 1: -//// int bitsNeeded = headerBits.getSize() -//// + mode.getCharacterCountBits(provisionalVersion) -//// + dataBits.getSize(); -//// Version version = chooseVersion(bitsNeeded, ecLevel); + // int provisionalBitsNeeded = headerBits.getSize() + // + mode.getCharacterCountBits(Version.getVersionForNumber(1)) + // + dataBits.getSize(); + // Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel); -//// BitArray headerAndDataBits = new BitArray(); -//// headerAndDataBits.appendBitArray(headerBits); -//// // Find "length" of main segment and write it -//// int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length(); -//// appendLengthInfo(numLetters, version, mode, headerAndDataBits); -//// // Put data together into the overall payload -//// headerAndDataBits.appendBitArray(dataBits); + // // Use that guess to calculate the right version. I am still not sure this works in 100% of cases. -//// Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); -//// int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords(); + // int bitsNeeded = headerBits.getSize() + // + mode.getCharacterCountBits(provisionalVersion) + // + dataBits.getSize(); + // Version version = chooseVersion(bitsNeeded, ecLevel); -//// // Terminate the bits properly. -//// terminateBits(numDataBytes, headerAndDataBits); + // BitArray headerAndDataBits = new BitArray(); + // headerAndDataBits.appendBitArray(headerBits); + // // Find "length" of main segment and write it + // int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length(); + // appendLengthInfo(numLetters, version, mode, headerAndDataBits); + // // Put data together into the overall payload + // headerAndDataBits.appendBitArray(dataBits); -//// // Interleave data bits with error correction code. -//// BitArray finalBits = interleaveWithECBytes(headerAndDataBits, -//// version.getTotalCodewords(), -//// numDataBytes, -//// ecBlocks.getNumBlocks()); + // Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); + // int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords(); -//// QRCode qrCode = new QRCode(); + // // Terminate the bits properly. + // terminateBits(numDataBytes, headerAndDataBits); -//// qrCode.setECLevel(ecLevel); -//// qrCode.setMode(mode); -//// qrCode.setVersion(version); + // // Interleave data bits with error correction code. + // BitArray finalBits = interleaveWithECBytes(headerAndDataBits, + // version.getTotalCodewords(), + // numDataBytes, + // ecBlocks.getNumBlocks()); -//// // Choose the mask pattern and set to "qrCode". -//// int dimension = version.getDimensionForVersion(); -//// ByteMatrix matrix = new ByteMatrix(dimension, dimension); -//// int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix); -//// qrCode.setMaskPattern(maskPattern); + // QRCode qrCode = new QRCode(); -//// // Build the matrix and set it to "qrCode". -//// MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix); -//// qrCode.setMatrix(matrix); + // qrCode.setECLevel(ecLevel); + // qrCode.setMode(mode); + // qrCode.setVersion(version); -//// return qrCode; -//// } + // // Choose the mask pattern and set to "qrCode". + // int dimension = version.getDimensionForVersion(); + // ByteMatrix matrix = new ByteMatrix(dimension, dimension); + // int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix); + // qrCode.setMaskPattern(maskPattern); -//// /** -//// * @return the code point of the table used in alphanumeric mode or -//// * -1 if there is no corresponding code in the table. -//// */ -//// static int getAlphanumericCode(int code) { -//// if (code < ALPHANUMERIC_TABLE.length) { -//// return ALPHANUMERIC_TABLE[code]; -//// } -//// return -1; -//// } + // // Build the matrix and set it to "qrCode". + // MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix); + // qrCode.setMatrix(matrix); -//// public static Mode chooseMode(String content) { -//// return chooseMode(content, null); -//// } + // return qrCode; -//// /** -//// * Choose the best mode by examining the content. Note that 'encoding' is used as a hint; -//// * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. -//// */ -//// private static Mode chooseMode(String content, String encoding) { -//// if ("Shift_JIS".equals(encoding)) { -//// // Choose Kanji mode if all input are double-byte characters -//// return isOnlyDoubleByteKanji(content) ? Mode.KANJI : Mode.BYTE; -//// } -//// boolean hasNumeric = false; -//// boolean hasAlphanumeric = false; -//// for (int i = 0; i < content.length(); ++i) { -//// char c = content.charAt(i); -//// if (c >= '0' && c <= '9') { -//// hasNumeric = true; -//// } else if (getAlphanumericCode(c) != -1) { -//// hasAlphanumeric = true; -//// } else { -//// return Mode.BYTE; -//// } -//// } -//// if (hasAlphanumeric) { -//// return Mode.ALPHANUMERIC; -//// } -//// if (hasNumeric) { -//// return Mode.NUMERIC; -//// } -//// return Mode.BYTE; -//// } + return NULL; +} -//// private static boolean isOnlyDoubleByteKanji(String content) { -//// byte[] bytes; -//// try { -//// bytes = content.getBytes("Shift_JIS"); -//// } catch (UnsupportedEncodingException ignored) { -//// return false; -//// } -//// int length = bytes.length; -//// if (length % 2 != 0) { -//// return false; -//// } -//// for (int i = 0; i < length; i += 2) { -//// int byte1 = bytes[i] & 0xFF; -//// if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) { -//// return false; -//// } -//// } -//// return true; -//// } +/** + * @return the code point of the table used in alphanumeric mode or + * -1 if there is no corresponding code in the table. + */ +int Encoder::getAlphanumericCode(int code) +{ + if (code < ALPHANUMERIC_TABLE_SIZE) { + return ALPHANUMERIC_TABLE[code]; + } + return -1; +} -//// private static int chooseMaskPattern(BitArray bits, -//// ErrorCorrectionLevel ecLevel, -//// Version version, -//// ByteMatrix matrix) throws WriterException { +Mode* Encoder::chooseMode(const QString& content) +{ + return chooseMode(content, ""); +} -//// int minPenalty = Integer.MAX_VALUE; // Lower penalty is better. -//// int bestMaskPattern = -1; -//// // We try all mask patterns to choose the best one. -//// for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) { -//// MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix); -//// int penalty = calculateMaskPenalty(matrix); -//// if (penalty < minPenalty) { -//// minPenalty = penalty; -//// bestMaskPattern = maskPattern; -//// } -//// } -//// return bestMaskPattern; -//// } +/** + * Choose the best mode by examining the content. Note that 'encoding' is used as a hint; + * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. + */ +Mode* Encoder::chooseMode(const QString& content, const QString& encoding) +{ + if (encoding == "Shift_JIS") + return &(Mode::BYTE); -//// private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) throws WriterException { -//// // In the following comments, we use numbers of Version 7-H. -//// for (int versionNum = 1; versionNum <= 40; versionNum++) { -//// Version version = Version.getVersionForNumber(versionNum); -//// // numBytes = 196 -//// int numBytes = version.getTotalCodewords(); -//// // getNumECBytes = 130 -//// Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); -//// int numEcBytes = ecBlocks.getTotalECCodewords(); -//// // getNumDataBytes = 196 - 130 = 66 -//// int numDataBytes = numBytes - numEcBytes; -//// int totalInputBytes = (numInputBits + 7) / 8; -//// if (numDataBytes >= totalInputBytes) { -//// return version; -//// } -//// } -//// throw new WriterException("Data too big"); -//// } - -//// /** -//// * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). -//// */ -//// static void terminateBits(int numDataBytes, BitArray bits) throws WriterException { -//// int capacity = numDataBytes << 3; -//// if (bits.getSize() > capacity) { -//// throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " + -//// capacity); -//// } -//// for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) { -//// bits.appendBit(false); -//// } -//// // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details. -//// // If the last byte isn't 8-bit aligned, we'll add padding bits. -//// int numBitsInLastByte = bits.getSize() & 0x07; -//// if (numBitsInLastByte > 0) { -//// for (int i = numBitsInLastByte; i < 8; i++) { -//// bits.appendBit(false); -//// } -//// } -//// // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24). -//// int numPaddingBytes = numDataBytes - bits.getSizeInBytes(); -//// for (int i = 0; i < numPaddingBytes; ++i) { -//// bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8); -//// } -//// if (bits.getSize() != capacity) { -//// throw new WriterException("Bits size does not equal capacity"); -//// } -//// } - -//// /** -//// * Get number of data bytes and number of error correction bytes for block id "blockID". Store -//// * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of -//// * JISX0510:2004 (p.30) -//// */ -//// static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, -//// int numDataBytes, -//// int numRSBlocks, -//// int blockID, -//// int[] numDataBytesInBlock, -//// int[] numECBytesInBlock) throws WriterException { -//// if (blockID >= numRSBlocks) { -//// throw new WriterException("Block ID too large"); -//// } -//// // numRsBlocksInGroup2 = 196 % 5 = 1 -//// int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks; -//// // numRsBlocksInGroup1 = 5 - 1 = 4 -//// int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2; -//// // numTotalBytesInGroup1 = 196 / 5 = 39 -//// int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks; -//// // numTotalBytesInGroup2 = 39 + 1 = 40 -//// int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1; -//// // numDataBytesInGroup1 = 66 / 5 = 13 -//// int numDataBytesInGroup1 = numDataBytes / numRSBlocks; -//// // numDataBytesInGroup2 = 13 + 1 = 14 -//// int numDataBytesInGroup2 = numDataBytesInGroup1 + 1; -//// // numEcBytesInGroup1 = 39 - 13 = 26 -//// int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1; -//// // numEcBytesInGroup2 = 40 - 14 = 26 -//// int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2; -//// // Sanity checks. -//// // 26 = 26 -//// if (numEcBytesInGroup1 != numEcBytesInGroup2) { -//// throw new WriterException("EC bytes mismatch"); -//// } -//// // 5 = 4 + 1. -//// if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) { -//// throw new WriterException("RS blocks mismatch"); -//// } -//// // 196 = (13 + 26) * 4 + (14 + 26) * 1 -//// if (numTotalBytes != -//// ((numDataBytesInGroup1 + numEcBytesInGroup1) * -//// numRsBlocksInGroup1) + -//// ((numDataBytesInGroup2 + numEcBytesInGroup2) * -//// numRsBlocksInGroup2)) { -//// throw new WriterException("Total bytes mismatch"); -//// } - -//// if (blockID < numRsBlocksInGroup1) { -//// numDataBytesInBlock[0] = numDataBytesInGroup1; -//// numECBytesInBlock[0] = numEcBytesInGroup1; -//// } else { -//// numDataBytesInBlock[0] = numDataBytesInGroup2; -//// numECBytesInBlock[0] = numEcBytesInGroup2; -//// } -//// } - -//// /** -//// * Interleave "bits" with corresponding error correction bytes. On success, store the result in -//// * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details. -//// */ -//// static BitArray interleaveWithECBytes(BitArray bits, -//// int numTotalBytes, -//// int numDataBytes, -//// int numRSBlocks) throws WriterException { - -//// // "bits" must have "getNumDataBytes" bytes of data. -//// if (bits.getSizeInBytes() != numDataBytes) { -//// throw new WriterException("Number of bits and data bytes does not match"); -//// } - -//// // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll -//// // store the divided data bytes blocks and error correction bytes blocks into "blocks". -//// int dataBytesOffset = 0; -//// int maxNumDataBytes = 0; -//// int maxNumEcBytes = 0; - -//// // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number. -//// Collection blocks = new ArrayList<>(numRSBlocks); - -//// for (int i = 0; i < numRSBlocks; ++i) { -//// int[] numDataBytesInBlock = new int[1]; -//// int[] numEcBytesInBlock = new int[1]; -//// getNumDataBytesAndNumECBytesForBlockID( -//// numTotalBytes, numDataBytes, numRSBlocks, i, -//// numDataBytesInBlock, numEcBytesInBlock); - -//// int size = numDataBytesInBlock[0]; -//// byte[] dataBytes = new byte[size]; -//// bits.toBytes(8*dataBytesOffset, dataBytes, 0, size); -//// byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]); -//// blocks.add(new BlockPair(dataBytes, ecBytes)); - -//// maxNumDataBytes = Math.max(maxNumDataBytes, size); -//// maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length); -//// dataBytesOffset += numDataBytesInBlock[0]; -//// } -//// if (numDataBytes != dataBytesOffset) { -//// throw new WriterException("Data bytes does not match offset"); -//// } - -//// BitArray result = new BitArray(); - -//// // First, place data blocks. -//// for (int i = 0; i < maxNumDataBytes; ++i) { -//// for (BlockPair block : blocks) { -//// byte[] dataBytes = block.getDataBytes(); -//// if (i < dataBytes.length) { -//// result.appendBits(dataBytes[i], 8); -//// } -//// } -//// } -//// // Then, place error correction blocks. -//// for (int i = 0; i < maxNumEcBytes; ++i) { -//// for (BlockPair block : blocks) { -//// byte[] ecBytes = block.getErrorCorrectionBytes(); -//// if (i < ecBytes.length) { -//// result.appendBits(ecBytes[i], 8); -//// } -//// } -//// } -//// if (numTotalBytes != result.getSizeInBytes()) { // Should be same. -//// throw new WriterException("Interleaving error: " + numTotalBytes + " and " + -//// result.getSizeInBytes() + " differ."); -//// } - -//// return result; -//// } - -//// static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) { -//// int numDataBytes = dataBytes.length; -//// int[] toEncode = new int[numDataBytes + numEcBytesInBlock]; -//// for (int i = 0; i < numDataBytes; i++) { -//// toEncode[i] = dataBytes[i] & 0xFF; -//// } -//// new ReedSolomonEncoder(GenericGF.QR_CODE_FIELD_256).encode(toEncode, numEcBytesInBlock); - -//// byte[] ecBytes = new byte[numEcBytesInBlock]; -//// for (int i = 0; i < numEcBytesInBlock; i++) { -//// ecBytes[i] = (byte) toEncode[numDataBytes + i]; -//// } -//// return ecBytes; -//// } - -//// /** -//// * Append mode info. On success, store the result in "bits". -//// */ -//// static void appendModeInfo(Mode mode, BitArray bits) { -//// bits.appendBits(mode.getBits(), 4); -//// } - - -//// /** -//// * Append length info. On success, store the result in "bits". -//// */ -//// static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits) throws WriterException { -//// int numBits = mode.getCharacterCountBits(version); -//// if (numLetters >= (1 << numBits)) { -//// throw new WriterException(numLetters + " is bigger than " + ((1 << numBits) - 1)); -//// } -//// bits.appendBits(numLetters, numBits); -//// } - -//// /** -//// * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". -//// */ -//// static void appendBytes(String content, -//// Mode mode, -//// BitArray bits, -//// String encoding) throws WriterException { -//// switch (mode) { -//// case NUMERIC: -//// appendNumericBytes(content, bits); -//// break; -//// case ALPHANUMERIC: -//// appendAlphanumericBytes(content, bits); -//// break; -//// case BYTE: -//// append8BitBytes(content, bits, encoding); -//// break; -//// case KANJI: -//// appendKanjiBytes(content, bits); -//// break; -//// default: -//// throw new WriterException("Invalid mode: " + mode); -//// } -//// } - -//// static void appendNumericBytes(CharSequence content, BitArray bits) { -//// int length = content.length(); -//// int i = 0; -//// while (i < length) { -//// int num1 = content.charAt(i) - '0'; -//// if (i + 2 < length) { -//// // Encode three numeric letters in ten bits. -//// int num2 = content.charAt(i + 1) - '0'; -//// int num3 = content.charAt(i + 2) - '0'; -//// bits.appendBits(num1 * 100 + num2 * 10 + num3, 10); -//// i += 3; -//// } else if (i + 1 < length) { -//// // Encode two numeric letters in seven bits. -//// int num2 = content.charAt(i + 1) - '0'; -//// bits.appendBits(num1 * 10 + num2, 7); -//// i += 2; -//// } else { -//// // Encode one numeric letter in four bits. -//// bits.appendBits(num1, 4); -//// i++; -//// } -//// } -//// } - -//// static void appendAlphanumericBytes(CharSequence content, BitArray bits) throws WriterException { -//// int length = content.length(); -//// int i = 0; -//// while (i < length) { -//// int code1 = getAlphanumericCode(content.charAt(i)); -//// if (code1 == -1) { -//// throw new WriterException(); -//// } -//// if (i + 1 < length) { -//// int code2 = getAlphanumericCode(content.charAt(i + 1)); -//// if (code2 == -1) { -//// throw new WriterException(); -//// } -//// // Encode two alphanumeric letters in 11 bits. -//// bits.appendBits(code1 * 45 + code2, 11); -//// i += 2; -//// } else { -//// // Encode one alphanumeric letter in six bits. -//// bits.appendBits(code1, 6); -//// i++; -//// } -//// } -//// } - -//// static void append8BitBytes(String content, BitArray bits, String encoding) -//// throws WriterException { -//// byte[] bytes; -//// try { -//// bytes = content.getBytes(encoding); -//// } catch (UnsupportedEncodingException uee) { -//// throw new WriterException(uee); -//// } -//// for (byte b : bytes) { -//// bits.appendBits(b, 8); -//// } -//// } - -//// static void appendKanjiBytes(String content, BitArray bits) throws WriterException { -//// byte[] bytes; -//// try { -//// bytes = content.getBytes("Shift_JIS"); -//// } catch (UnsupportedEncodingException uee) { -//// throw new WriterException(uee); -//// } -//// int length = bytes.length; -//// for (int i = 0; i < length; i += 2) { -//// int byte1 = bytes[i] & 0xFF; -//// int byte2 = bytes[i + 1] & 0xFF; -//// int code = (byte1 << 8) | byte2; -//// int subtracted = -1; -//// if (code >= 0x8140 && code <= 0x9ffc) { -//// subtracted = code - 0x8140; -//// } else if (code >= 0xe040 && code <= 0xebbf) { -//// subtracted = code - 0xc140; -//// } -//// if (subtracted == -1) { -//// throw new WriterException("Invalid byte sequence"); -//// } -//// int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff); -//// bits.appendBits(encoded, 13); -//// } -//// } - -//// private static void appendECI(CharacterSetECI eci, BitArray bits) { -//// bits.appendBits(Mode.ECI.getBits(), 4); -//// // This is correct for values up to 127, which is all we need now. -//// bits.appendBits(eci.getValue(), 8); -//// } + bool hasNumeric = false; + bool hasAlphanumeric = false; + for (int i = 0; i < content.size(); ++i) { + char c = content.at(i).toLatin1(); + if (c >= '0' && c <= '9') { + hasNumeric = true; + } else if (getAlphanumericCode(c) != -1) { + hasAlphanumeric = true; + } else { + return &Mode::BYTE; + } + } + if (hasAlphanumeric) { + return &Mode::ALPHANUMERIC; + } + if (hasNumeric) { + return &Mode::NUMERIC; + } + return &Mode::BYTE; +} +//bool Encoder::isOnlyDoubleByteKanji(const QString& content) +//{ +// std::vector bytes; +// try { +// bytes = content.getBytes("Shift_JIS"); +// } catch (UnsupportedEncodingException ignored) { +// return false; +// } +// int length = bytes.length; +// if (length % 2 != 0) { +// return false; +// } +// for (int i = 0; i < length; i += 2) { +// int byte1 = bytes[i] & 0xFF; +// if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) { +// return false; +// } +// } +// return true; //} -//} + +// private static int chooseMaskPattern(BitArray bits, +// ErrorCorrectionLevel ecLevel, +// Version version, +// ByteMatrix matrix) throws WriterException { + +// int minPenalty = Integer.MAX_VALUE; // Lower penalty is better. +// int bestMaskPattern = -1; +// // We try all mask patterns to choose the best one. +// for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) { +// MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix); +// int penalty = calculateMaskPenalty(matrix); +// if (penalty < minPenalty) { +// minPenalty = penalty; +// bestMaskPattern = maskPattern; +// } +// } +// return bestMaskPattern; +// } + +Version Encoder::chooseVersion(int numInputBits, ErrorCorrectionLevel &ecLevel) +{ + // In the following comments, we use numbers of Version 7-H. + for (int versionNum = 1; versionNum <= 40; versionNum++) { + Version& version = *Version::getVersionForNumber(versionNum); + // numBytes = 196 + int numBytes = version.getTotalCodewords(); + // getNumECBytes = 130 + ECBlocks& ecBlocks = version.getECBlocksForLevel(ecLevel); + int numEcBytes = ecBlocks.getECCodewords(); + // getNumDataBytes = 196 - 130 = 66 + int numDataBytes = numBytes - numEcBytes; + int totalInputBytes = (numInputBits + 7) / 8; + if (numDataBytes >= totalInputBytes) { + return version; + } + } + throw new WriterException("Data too big"); +} + +/** + * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). + */ +void Encoder::terminateBits(int numDataBytes, BitArray& bits) +{ + int capacity = numDataBytes << 3; + if (bits.getSize() > capacity) { + QString message("data bits cannot fit in the QR Code"); + message += QString::number(bits.getSize()); + message += " > "; + message += QString::number(capacity); + throw new WriterException(message.toStdString().c_str()); + } + for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) { + bits.appendBit(false); + } + // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details. + // If the last byte isn't 8-bit aligned, we'll add padding bits. + int numBitsInLastByte = bits.getSize() & 0x07; + if (numBitsInLastByte > 0) { + for (int i = numBitsInLastByte; i < 8; i++) { + bits.appendBit(false); + } + } + // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24). + int numPaddingBytes = numDataBytes - bits.getSize();//bits.getSizeInBytes(); + for (int i = 0; i < numPaddingBytes; ++i) { + bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8); + } + if (bits.getSize() != capacity) { + throw new WriterException("Bits size does not equal capacity"); + } +} + +/** + * Get number of data bytes and number of error correction bytes for block id "blockID". Store + * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of + * JISX0510:2004 (p.30) + */ +void Encoder::getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, + int numDataBytes, + int numRSBlocks, + int blockID, + std::vector& numDataBytesInBlock, + std::vector& numECBytesInBlock) +{ + if (blockID >= numRSBlocks) { + throw new WriterException("Block ID too large"); + } + // numRsBlocksInGroup2 = 196 % 5 = 1 + int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks; + // numRsBlocksInGroup1 = 5 - 1 = 4 + int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2; + // numTotalBytesInGroup1 = 196 / 5 = 39 + int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks; + // numTotalBytesInGroup2 = 39 + 1 = 40 + int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1; + // numDataBytesInGroup1 = 66 / 5 = 13 + int numDataBytesInGroup1 = numDataBytes / numRSBlocks; + // numDataBytesInGroup2 = 13 + 1 = 14 + int numDataBytesInGroup2 = numDataBytesInGroup1 + 1; + // numEcBytesInGroup1 = 39 - 13 = 26 + int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1; + // numEcBytesInGroup2 = 40 - 14 = 26 + int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2; + // Sanity checks. + // 26 = 26 + if (numEcBytesInGroup1 != numEcBytesInGroup2) { + throw new WriterException("EC bytes mismatch"); + } + // 5 = 4 + 1. + if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) { + throw new WriterException("RS blocks mismatch"); + } + // 196 = (13 + 26) * 4 + (14 + 26) * 1 + if (numTotalBytes != + ((numDataBytesInGroup1 + numEcBytesInGroup1) * + numRsBlocksInGroup1) + + ((numDataBytesInGroup2 + numEcBytesInGroup2) * + numRsBlocksInGroup2)) { + throw new WriterException("Total bytes mismatch"); + } + + if (numDataBytesInBlock.size() < 1 ) + numDataBytesInBlock.resize(1); + + if (numECBytesInBlock.size() < 1 ) + numECBytesInBlock.resize(1); + + if (blockID < numRsBlocksInGroup1) { + numDataBytesInBlock[0] = numDataBytesInGroup1; + numECBytesInBlock[0] = numEcBytesInGroup1; + } else { + numDataBytesInBlock[0] = numDataBytesInGroup2; + numECBytesInBlock[0] = numEcBytesInGroup2; + } +} + +/** + * Interleave "bits" with corresponding error correction bytes. On success, store the result in + * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details. + */ +BitArray* Encoder::interleaveWithECBytes(const BitArray& bits, + int numTotalBytes, + int numDataBytes, + int numRSBlocks) +{ + + // "bits" must have "getNumDataBytes" bytes of data. + if (bits.getSize() != numDataBytes) { + throw new WriterException("Number of bits and data bytes does not match"); + } + + // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll + // store the divided data bytes blocks and error correction bytes blocks into "blocks". + int dataBytesOffset = 0; + int maxNumDataBytes = 0; + int maxNumEcBytes = 0; + + // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number. + QList< BlockPair > blocks; + + for (int i = 0; i < numRSBlocks; ++i) { + std::vector numDataBytesInBlock; + std::vector numEcBytesInBlock; + getNumDataBytesAndNumECBytesForBlockID( + numTotalBytes, numDataBytes, numRSBlocks, i, + numDataBytesInBlock, numEcBytesInBlock); + + int size = numDataBytesInBlock[0]; + std::vector dataBytes; + dataBytes.resize(size); + bits.toBytes(8*dataBytesOffset, dataBytes, 0, size); + ArrayRef ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]); + blocks.push_back(BlockPair(ArrayRef(dataBytes.data(), dataBytes.size()),ecBytes)); //?? please revisit + + maxNumDataBytes = max(maxNumDataBytes, size); + maxNumEcBytes = max(maxNumEcBytes, (int)ecBytes->size()); + dataBytesOffset += numDataBytesInBlock[0]; + } + if (numDataBytes != dataBytesOffset) { + throw new WriterException("Data bytes does not match offset"); + } + + BitArray* result = new BitArray; + + // First, place data blocks. + for (int i = 0; i < maxNumDataBytes; ++i) { + for (QList< BlockPair >::iterator it=blocks.begin(); it != blocks.end(); ++it) { + ArrayRef dataBytes = it->getDataBytes(); + if (i < dataBytes.array_->size()) { + result->appendBits(dataBytes[i], 8); ///????? are we sure? + } + } + } + // Then, place error correction blocks. + for (int i = 0; i < maxNumEcBytes; ++i) { + for (QList< BlockPair >::iterator it=blocks.begin(); it != blocks.end(); ++it) { + ArrayRef ecBytes = it->getErrorCorrectionBytes(); + if (i < ecBytes.array_->size()) { + result->appendBits(ecBytes[i], 8); + } + } + } + if (numTotalBytes != result->getSize()) { // Should be same. + QString message("Interleaving error: "); + message += QString::number(numTotalBytes); + message += " and "; + message += QString(result->getSize()); + message += " differ."; + throw new WriterException(message.toStdString().c_str()); + } + + return result; +} + +ArrayRef Encoder::generateECBytes(const std::vector& dataBytes, int numEcBytesInBlock) +{ + int numDataBytes = dataBytes.size(); + std::vector toEncode; + for (int i = 0; i < numDataBytes; i++) + toEncode.push_back(dataBytes[i] & 0xFF); + + zxing::ReedSolomonEncoder encoder(GenericGF::QR_CODE_FIELD_256); + encoder.encode(toEncode, numEcBytesInBlock); + + ArrayRef ecBytes(numEcBytesInBlock); + for (int i = 0; i < numEcBytesInBlock; i++) { + ecBytes[i] = (char) toEncode[numDataBytes + i]; + } + return ecBytes; +} + +// /** +// * Append mode info. On success, store the result in "bits". +// */ +// static void appendModeInfo(Mode mode, BitArray bits) { +// bits.appendBits(mode.getBits(), 4); +// } + + +// /** +// * Append length info. On success, store the result in "bits". +// */ +// static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits) throws WriterException { +// int numBits = mode.getCharacterCountBits(version); +// if (numLetters >= (1 << numBits)) { +// throw new WriterException(numLetters + " is bigger than " + ((1 << numBits) - 1)); +// } +// bits.appendBits(numLetters, numBits); +// } + +// /** +// * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". +// */ +// static void appendBytes(String content, +// Mode mode, +// BitArray bits, +// String encoding) throws WriterException { +// switch (mode) { +// case NUMERIC: +// appendNumericBytes(content, bits); +// break; +// case ALPHANUMERIC: +// appendAlphanumericBytes(content, bits); +// break; +// case BYTE: +// append8BitBytes(content, bits, encoding); +// break; +// case KANJI: +// appendKanjiBytes(content, bits); +// break; +// default: +// throw new WriterException("Invalid mode: " + mode); +// } +// } + +// static void appendNumericBytes(CharSequence content, BitArray bits) { +// int length = content.length(); +// int i = 0; +// while (i < length) { +// int num1 = content.charAt(i) - '0'; +// if (i + 2 < length) { +// // Encode three numeric letters in ten bits. +// int num2 = content.charAt(i + 1) - '0'; +// int num3 = content.charAt(i + 2) - '0'; +// bits.appendBits(num1 * 100 + num2 * 10 + num3, 10); +// i += 3; +// } else if (i + 1 < length) { +// // Encode two numeric letters in seven bits. +// int num2 = content.charAt(i + 1) - '0'; +// bits.appendBits(num1 * 10 + num2, 7); +// i += 2; +// } else { +// // Encode one numeric letter in four bits. +// bits.appendBits(num1, 4); +// i++; +// } +// } +// } + +// static void appendAlphanumericBytes(CharSequence content, BitArray bits) throws WriterException { +// int length = content.length(); +// int i = 0; +// while (i < length) { +// int code1 = getAlphanumericCode(content.charAt(i)); +// if (code1 == -1) { +// throw new WriterException(); +// } +// if (i + 1 < length) { +// int code2 = getAlphanumericCode(content.charAt(i + 1)); +// if (code2 == -1) { +// throw new WriterException(); +// } +// // Encode two alphanumeric letters in 11 bits. +// bits.appendBits(code1 * 45 + code2, 11); +// i += 2; +// } else { +// // Encode one alphanumeric letter in six bits. +// bits.appendBits(code1, 6); +// i++; +// } +// } +// } + +// static void append8BitBytes(String content, BitArray bits, String encoding) +// throws WriterException { +// byte[] bytes; +// try { +// bytes = content.getBytes(encoding); +// } catch (UnsupportedEncodingException uee) { +// throw new WriterException(uee); +// } +// for (byte b : bytes) { +// bits.appendBits(b, 8); +// } +// } + +// static void appendKanjiBytes(String content, BitArray bits) throws WriterException { +// byte[] bytes; +// try { +// bytes = content.getBytes("Shift_JIS"); +// } catch (UnsupportedEncodingException uee) { +// throw new WriterException(uee); +// } +// int length = bytes.length; +// for (int i = 0; i < length; i += 2) { +// int byte1 = bytes[i] & 0xFF; +// int byte2 = bytes[i + 1] & 0xFF; +// int code = (byte1 << 8) | byte2; +// int subtracted = -1; +// if (code >= 0x8140 && code <= 0x9ffc) { +// subtracted = code - 0x8140; +// } else if (code >= 0xe040 && code <= 0xebbf) { +// subtracted = code - 0xc140; +// } +// if (subtracted == -1) { +// throw new WriterException("Invalid byte sequence"); +// } +// int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff); +// bits.appendBits(encoded, 13); +// } +// } + +// private static void appendECI(CharacterSetECI eci, BitArray bits) { +// bits.appendBits(Mode.ECI.getBits(), 4); +// // This is correct for values up to 127, which is all we need now. +// bits.appendBits(eci.getValue(), 8); +// } + +} +} diff --git a/source/zxing/zxing/qrcode/encoder/Encoder.h b/source/zxing/zxing/qrcode/encoder/Encoder.h index 4dab2cd..bc32360 100644 --- a/source/zxing/zxing/qrcode/encoder/Encoder.h +++ b/source/zxing/zxing/qrcode/encoder/Encoder.h @@ -1,148 +1,133 @@ -//#ifndef ENCODER_H -//#define ENCODER_H -////package com.google.zxing.qrcode.encoder; +#ifndef ENCODER_H +#define ENCODER_H -////import com.google.zxing.EncodeHintType; -////import com.google.zxing.WriterException; -////import com.google.zxing.common.BitArray; -////import com.google.zxing.common.CharacterSetECI; -////import com.google.zxing.common.reedsolomon.GenericGF; -////import com.google.zxing.common.reedsolomon.ReedSolomonEncoder; -////import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; -////import com.google.zxing.qrcode.decoder.Mode; -////import com.google.zxing.qrcode.decoder.Version; +#include +#include +#include +#include +#include -////import java.io.UnsupportedEncodingException; -////import java.util.ArrayList; -////import java.util.Collection; -////import java.util.Map; +#include +#include -//#include -//#include -//#include -//#include -//#include +namespace zxing { +namespace qrcode { -//namespace zxing { -//namespace qrcode { +class Encoder { -//class Encoder { +private: + static const int ALPHANUMERIC_TABLE_SIZE; + static const int ALPHANUMERIC_TABLE[]; + static const QString DEFAULT_BYTE_MODE_ENCODING; -//private: -// static const int ALPHANUMERIC_TABLE[]; -// static const std::string DEFAULT_BYTE_MODE_ENCODING; + /** + * The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details. + * Basically it applies four rules and summate all penalties. + */ + static int calculateMaskPenalty(const ByteMatrix& matrix); -// Encoder() {} + /** + * Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen + * internally by chooseMode(). On success, store the result in "qrCode". + * + * We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for + * "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very + * strong error correction for this purpose. + * + * Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode() + * with which clients can specify the encoding mode. For now, we don't need the functionality. + */ +public: + static QRCode* encode(const QString& content, const ErrorCorrectionLevel* ecLevel); -// /** -// * The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details. -// * Basically it applies four rules and summate all penalties. -// */ -// static int calculateMaskPenalty(ByteMatrix matrix); + static QRCode* encode(const QString& content, const ErrorCorrectionLevel* ecLevel, const EncodeHint* hints); -// /** -// * Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen -// * internally by chooseMode(). On success, store the result in "qrCode". -// * -// * We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for -// * "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very -// * strong error correction for this purpose. -// * -// * Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode() -// * with which clients can specify the encoding mode. For now, we don't need the functionality. -// */ -//public: -// static QRCode encode(std::string content, ErrorCorrectionLevel ecLevel); + /** + * @return the code point of the table used in alphanumeric mode or + * -1 if there is no corresponding code in the table. + */ + static int getAlphanumericCode(int code); -// static QRCode encode(String content, ErrorCorrectionLevel ecLevel/*, -// Map hints*/); + static Mode* chooseMode(const QString& content); -// /** -// * @return the code point of the table used in alphanumeric mode or -// * -1 if there is no corresponding code in the table. -// */ -// static int getAlphanumericCode(int code); + /** + * Choose the best mode by examining the content. Note that 'encoding' is used as a hint; + * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. + */ +private: + static Mode* chooseMode(const QString& content, const QString& encoding); -// static Mode chooseMode(String content); + //static bool isOnlyDoubleByteKanji(const QString& content); -// /** -// * Choose the best mode by examining the content. Note that 'encoding' is used as a hint; -// * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. -// */ -//private: -// static Mode chooseMode(std::string content, std::string encoding); + static int chooseMaskPattern(BitArray bits, + ErrorCorrectionLevel ecLevel, + Version version, + ByteMatrix matrix); -// static boolean isOnlyDoubleByteKanji(std::string content); + static Version chooseVersion(int numInputBits, ErrorCorrectionLevel &ecLevel) ; -// static int chooseMaskPattern(BitArray bits, -// ErrorCorrectionLevel ecLevel, -// Version version, -// ByteMatrix matrix); + /** + * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). + */ +protected: + static void terminateBits(int numDataBytes, BitArray& bits); -// static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) ; + /** + * Get number of data bytes and number of error correction bytes for block id "blockID". Store + * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of + * JISX0510:2004 (p.30) + */ + static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, + int numDataBytes, + int numRSBlocks, + int blockID, + std::vector& numDataBytesInBlock, + std::vector& numECBytesInBlock); -// /** -// * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). -// */ -//protected: -// static void terminateBits(int numDataBytes, BitArray bits); + /** + * Interleave "bits" with corresponding error correction bytes. On success, store the result in + * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details. + */ + static BitArray* interleaveWithECBytes(const BitArray& bits, + int numTotalBytes, + int numDataBytes, + int numRSBlocks); -// /** -// * Get number of data bytes and number of error correction bytes for block id "blockID". Store -// * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of -// * JISX0510:2004 (p.30) -// */ -// static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, -// int numDataBytes, -// int numRSBlocks, -// int blockID, -// int numDataBytesInBlock[], -// int numECBytesInBlock[]); + static ArrayRef generateECBytes(const std::vector& dataBytes, int numEcBytesInBlock); -// /** -// * Interleave "bits" with corresponding error correction bytes. On success, store the result in -// * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details. -// */ -// static BitArray interleaveWithECBytes(BitArray bits, -// int numTotalBytes, -// int numDataBytes, -// int numRSBlocks); - -// static std::vector generateECBytes(const std::vector& dataBytes[], int numEcBytesInBlock); - -// /** -// * Append mode info. On success, store the result in "bits". -// */ -// static void appendModeInfo(Mode mode, BitArray bits); + /** + * Append mode info. On success, store the result in "bits". + */ + static void appendModeInfo(const Mode& mode, BitArray& bits); -// /** -// * Append length info. On success, store the result in "bits". -// */ -// static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits); + /** + * Append length info. On success, store the result in "bits". + */ + static void appendLengthInfo(int numLetters, const Version& version, const Mode& mode, BitArray& bits); -// /** -// * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". -// */ -// static void appendBytes(std::string content, -// Mode mode, -// BitArray bits, -// std::string encoding); + /** + * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". + */ + static void appendBytes(const QString& content, + const Mode& mode, + BitArray& bits, + const QString& encoding); -// static void appendNumericBytes( const std::vector& content, BitArray bits); + static void appendNumericBytes( const std::vector& content, BitArray& bits); -// static void appendAlphanumericBytes(const std::vector& content, BitArray bits); + static void appendAlphanumericBytes(const std::vector& content, BitArray& bits); -// static void append8BitBytes(std::string content, BitArray bits, std::string encoding); + static void append8BitBytes(const QString& content, BitArray& bits, const QString& encoding); -// static void appendKanjiBytes(std::string content, BitArray bits); + static void appendKanjiBytes(const QString& content, BitArray& bits); -//private: -// static void appendECI(zxing::common::CharacterSetECI eci, BitArray bits); +private: + static void appendECI(const zxing::common::CharacterSetECI& eci, BitArray& bits); -//} +}; -//} -//} +} +} -//#endif // ENCODER_H +#endif // ENCODER_H