#include "Encoder.h"
#include "MaskUtil.h"
#include <zxing/common/CharacterSetECI.h>
#include <zxing/UnsupportedEncodingException.h>
#include <zxing/WriterException.h>
#include <zxing/common/reedsolomon/ReedSolomonEncoder.h>
#include "BlockPair.h"
#include <QList>
#include <math.h>
#include <limits>
#include "MatrixUtil.h"
#include <string>
#include "zxing/common/StringUtils.h"
namespace zxing {
namespace qrcode {
const int Encoder::ALPHANUMERIC_TABLE_SIZE = 96;
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,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
};
const std::string Encoder::DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
int Encoder::calculateMaskPenalty(const ByteMatrix& matrix)
{
return MaskUtil::applyMaskPenaltyRule1(matrix)
+ MaskUtil::applyMaskPenaltyRule2(matrix)
+ MaskUtil::applyMaskPenaltyRule3(matrix)
+ MaskUtil::applyMaskPenaltyRule4(matrix);
}
Ref<QRCode> Encoder::encode(const std::string& content, ErrorCorrectionLevel &ecLevel)
{
return encode(content, ecLevel, NULL);
}
Ref<QRCode> Encoder::encode(const std::string& content, ErrorCorrectionLevel &ecLevel, const EncodeHint* hints)
{
std::string encoding = hints == NULL ? "" : hints->getCharacterSet();
if (encoding == "")
encoding = DEFAULT_BYTE_MODE_ENCODING;
Mode mode = chooseMode(content, encoding);
BitArray headerBits;
if (mode == Mode::BYTE && DEFAULT_BYTE_MODE_ENCODING != encoding) {
zxing::common::CharacterSetECI const * eci =
zxing::common::CharacterSetECI::getCharacterSetECIByName(encoding);
if (eci != NULL) {
appendECI(*eci, headerBits);
}
}
appendModeInfo(mode, headerBits);
BitArray dataBits;
appendBytes(content, mode, dataBits, encoding);
Ref<Version> version;
if (hints != NULL) {
version = Version::getVersionForNumber(1);
int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version);
if (!willFit(bitsNeeded, version, ecLevel)) {
throw new WriterException("Data too big for requested version");
}
} else {
version = recommendVersion(ecLevel, mode, headerBits, dataBits);
}
BitArray headerAndDataBits;
headerAndDataBits.appendBitArray(headerBits);
int numLetters = (mode == Mode::BYTE) ? dataBits.getSizeInBytes() : content.length();
appendLengthInfo(numLetters, *version, mode, headerAndDataBits);
headerAndDataBits.appendBitArray(dataBits);
zxing::qrcode::ECBlocks &ecBlocks = version->getECBlocksForLevel(ecLevel);
int numDataBytes = version->getTotalCodewords() - ecBlocks.getTotalECCodewords();
terminateBits(numDataBytes, headerAndDataBits);
Ref<BitArray> finalBits(interleaveWithECBytes(headerAndDataBits,
version->getTotalCodewords(),
numDataBytes,
ecBlocks.getECBlocks().size()));
Ref<QRCode> qrCode(new QRCode);
qrCode->setECLevel(Ref<ErrorCorrectionLevel>(new ErrorCorrectionLevel(ecLevel)));
qrCode->setMode(mode);
qrCode->setVersion(version);
int dimension = version->getDimensionForVersion();
Ref<ByteMatrix> matrix(new ByteMatrix(dimension, dimension));
int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix);
qrCode->setMaskPattern(maskPattern);
MatrixUtil::buildMatrix(*finalBits, ecLevel, *version, maskPattern, *matrix);
qrCode->setMatrix(matrix);
return qrCode;
}
bool Encoder::willFit(int numInputBits, Ref<Version> version, const ErrorCorrectionLevel &ecLevel) {
int numBytes = version->getTotalCodewords();
ECBlocks& ecBlocks = version->getECBlocksForLevel(ecLevel);
int numEcBytes = ecBlocks.getTotalECCodewords();
int numDataBytes = numBytes - numEcBytes;
int totalInputBytes = (numInputBits + 7) / 8;
return numDataBytes >= totalInputBytes;
}
int Encoder::getAlphanumericCode(int code)
{
if (code < ALPHANUMERIC_TABLE_SIZE) {
return ALPHANUMERIC_TABLE[code];
}
return -1;
}
Mode Encoder::chooseMode(const std::string& content, const std::string& encoding)
{
if (encoding == "Shift_JIS")
{
std::cout << "DEBUG: Shift_JIS detected...be aware!" << std::endl;
return Mode::BYTE;
}
bool hasNumeric = false;
bool hasAlphanumeric = false;
for (int i = 0; i < content.size(); i++) {
char c = content.at(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;
}
int Encoder::chooseMaskPattern(Ref<BitArray> bits,
ErrorCorrectionLevel& ecLevel,
Ref<Version> version,
Ref<ByteMatrix> matrix)
{
int minPenalty = std::numeric_limits<int>::max();
int bestMaskPattern = -1;
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;
}
std::cout << std::string("i: ") << maskPattern << std::string(", penantly: ") << penalty << std::endl;
}
return bestMaskPattern;
}
Ref<Version> Encoder::chooseVersion(int numInputBits, const ErrorCorrectionLevel &ecLevel)
{
for (int versionNum = 1; versionNum <= 40; versionNum++) {
Ref<Version> version = Version::getVersionForNumber(versionNum);
if (willFit(numInputBits, version, ecLevel)) {
return version;
}
}
throw WriterException("Data too big");
}
void Encoder::terminateBits(int numDataBytes, BitArray& bits)
{
int capacity = numDataBytes << 3;
if (bits.getSize() > capacity) {
std::string message("data bits cannot fit in the QR Code");
message += zxing::common::StringUtils::intToStr(bits.getSize());
message += " > ";
message += zxing::common::StringUtils::intToStr(capacity);
throw WriterException(message.c_str());
}
for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) {
bits.appendBit(false);
}
int numBitsInLastByte = bits.getSize() & 7;
if (numBitsInLastByte > 0) {
for (int i = numBitsInLastByte; i < 8; i++) {
bits.appendBit(false);
}
}
int bitSizeInBytes = bits.getSizeInBytes();
int numPaddingBytes = numDataBytes - bitSizeInBytes;
for (int i = 0; i < numPaddingBytes; i++) {
bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8);
}
if (bits.getSize() != capacity) {
throw WriterException("Bits size does not equal capacity");
}
}
void Encoder::getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes,
int numDataBytes,
int numRSBlocks,
int blockID,
std::vector<int>& numDataBytesInBlock,
std::vector<int>& numECBytesInBlock)
{
if (blockID >= numRSBlocks) {
throw WriterException("Block ID too large");
}
int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;
int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;
int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;
int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;
int numDataBytesInGroup1 = numDataBytes / numRSBlocks;
int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;
int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;
int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;
if (numEcBytesInGroup1 != numEcBytesInGroup2) {
throw WriterException("EC bytes mismatch");
}
if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) {
throw WriterException("RS blocks mismatch");
}
if (numTotalBytes !=
((numDataBytesInGroup1 + numEcBytesInGroup1) *
numRsBlocksInGroup1) +
((numDataBytesInGroup2 + numEcBytesInGroup2) *
numRsBlocksInGroup2)) {
throw 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;
}
}
BitArray* Encoder::interleaveWithECBytes(const BitArray& bits,
int numTotalBytes,
int numDataBytes,
int numRSBlocks)
{
if (bits.getSizeInBytes() != numDataBytes) {
std::string message("Encoder::interleaveWithECBytes: Number of bits [");
message += zxing::common::StringUtils::intToStr(bits.getSizeInBytes());
message += "] and data bytes [";
message += zxing::common::StringUtils::intToStr(numDataBytes);
message += "] does not match";
throw WriterException( message.c_str());
}
int dataBytesOffset = 0;
int maxNumDataBytes = 0;
int maxNumEcBytes = 0;
std::vector< BlockPair > blocks;
for (int i = 0; i < numRSBlocks; i++) {
std::vector<int> numDataBytesInBlock;
std::vector<int> numEcBytesInBlock;
getNumDataBytesAndNumECBytesForBlockID(
numTotalBytes, numDataBytes, numRSBlocks, i,
numDataBytesInBlock, numEcBytesInBlock);
int size = numDataBytesInBlock[0];
std::vector<byte> dataBytes;
dataBytes.resize(size);
bits.toBytes(8*dataBytesOffset, dataBytes, 0, size);
ArrayRef<byte> ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);
blocks.push_back(BlockPair(ArrayRef<byte>(dataBytes.data(), dataBytes.size()),ecBytes));
maxNumDataBytes = max(maxNumDataBytes, size);
maxNumEcBytes = max(maxNumEcBytes, (int)ecBytes->size());
dataBytesOffset += numDataBytesInBlock[0];
}
if (numDataBytes != dataBytesOffset) {
throw WriterException("Data bytes does not match offset");
}
BitArray* result = new BitArray;
for (int i = 0; i < maxNumDataBytes; i++) {
for (std::vector< BlockPair >::iterator it=blocks.begin(); it != blocks.end(); it++) {
ArrayRef<byte> dataBytes = it->getDataBytes();
if (i < dataBytes.array_->size()) {
result->appendBits(dataBytes[i], 8);
}
}
}
for (int i = 0; i < maxNumEcBytes; i++) {
for (std::vector< BlockPair >::iterator it=blocks.begin(); it != blocks.end(); it++) {
ArrayRef<byte> ecBytes = it->getErrorCorrectionBytes();
if (i < ecBytes.array_->size()) {
result->appendBits(ecBytes[i], 8);
}
}
}
if (numTotalBytes != result->getSizeInBytes()) {
std::string message("Interleaving error: ");
message += zxing::common::StringUtils::intToStr(numTotalBytes);
message += " and ";
message += zxing::common::StringUtils::intToStr(result->getSizeInBytes());
message += " differ.";
throw WriterException(message.c_str());
}
return result;
}
ArrayRef<byte> Encoder::generateECBytes(const std::vector<byte>& dataBytes, int numEcBytesInBlock)
{
int numDataBytes = dataBytes.size();
std::vector<byte> dataBytesCopy(dataBytes);
zxing::ReedSolomonEncoder encoder(GenericGF::QR_CODE_FIELD_256);
encoder.encode(dataBytesCopy, numEcBytesInBlock);
ArrayRef<byte> ecBytes(numEcBytesInBlock);
for (int i = 0; i < numEcBytesInBlock; i++) {
ecBytes[i] = dataBytesCopy[numDataBytes + i];
}
return ecBytes;
}
void Encoder::appendModeInfo(const Mode& mode, BitArray& bits)
{
bits.appendBits(mode.getBits(), 4);
}
void Encoder::appendLengthInfo(int numLetters, const Version& version, const Mode& mode, BitArray& bits)
{
int numBits = mode.getCharacterCountBits(&version);
if (numLetters >= (1 << numBits)) {
std::string message = zxing::common::StringUtils::intToStr(numLetters);
message += " is bigger than ";
message += zxing::common::StringUtils::intToStr((1 << numBits) - 1);
throw WriterException(message.c_str());
}
bits.appendBits(numLetters, numBits);
}
void Encoder::appendBytes(const std::string& content,
Mode& mode,
BitArray& bits,
const std::string& encoding)
{
if (mode == Mode::NUMERIC)
appendNumericBytes(content, bits);
else if (mode == Mode::ALPHANUMERIC)
appendAlphanumericBytes(content, bits);
else if (mode == Mode::BYTE)
append8BitBytes(content, bits, encoding);
else if (mode == Mode::KANJI)
appendKanjiBytes(content, bits);
else {
std::string message("Invalid mode: ");
message += mode.getName();
throw WriterException(message.c_str());
}
}
void Encoder::appendNumericBytes( const std::string& content, BitArray& bits)
{
int length = content.size();
int i = 0;
while (i < length) {
int num1 = content.at(i) - '0';
if (i + 2 < length) {
int num2 = content.at(i + 1) - '0';
int num3 = content.at(i + 2) - '0';
bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);
i += 3;
} else if (i + 1 < length) {
int num2 = content.at(i + 1) - '0';
bits.appendBits(num1 * 10 + num2, 7);
i += 2;
} else {
bits.appendBits(num1, 4);
i++;
}
}
}
void Encoder::appendAlphanumericBytes(const std::string& content, BitArray& bits)
{
int length = content.length();
int i = 0;
while (i < length) {
int code1 = getAlphanumericCode(content.at(i));
if (code1 == -1) {
throw WriterException();
}
if (i + 1 < length) {
int code2 = getAlphanumericCode(content.at(i + 1));
if (code2 == -1) {
throw WriterException();
}
bits.appendBits(code1 * 45 + code2, 11);
i += 2;
} else {
bits.appendBits(code1, 6);
i++;
}
}
}
void Encoder::append8BitBytes(const std::string& content, BitArray& bits, const std::string& )
{
for (int i=0; i<content.size(); i++) {
bits.appendBits(content.at(i), 8);
}
}
void Encoder::appendKanjiBytes(const std::string& content, BitArray& bits)
{
int length = content.size();
for (int i = 0; i < length; i += 2) {
int byte1 = content.at(i) & 0xFF;
int byte2 = content.at(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 WriterException("Invalid byte sequence");
}
int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
bits.appendBits(encoded, 13);
}
}
void Encoder::appendECI(const zxing::common::CharacterSetECI& eci, BitArray& bits) {
bits.appendBits(Mode::ECI.getBits(), 4);
bits.appendBits(eci.getValue(), 8);
}
int Encoder::calculateBitsNeeded(const Mode &mode, const BitArray &headerBits, const BitArray &dataBits, const
Ref<Version> version)
{
return headerBits.getSize() + mode.getCharacterCountBits(&(*version)) + dataBits.getSize();
}
Ref<Version> Encoder::recommendVersion(ErrorCorrectionLevel &ecLevel,
Mode &mode,
BitArray &headerBits,
BitArray &dataBits)
{
int provisionalBitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, Version::getVersionForNumber(1));
Ref<Version> provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel);
int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, provisionalVersion);
return chooseVersion(bitsNeeded, ecLevel);
}
}
}