initial implementation of Qr Encoder. Not tested

This commit is contained in:
favoritas37 2015-06-19 01:56:26 +03:00
parent dbc47abff2
commit f6c5917c38
2 changed files with 244 additions and 230 deletions

View File

@ -23,6 +23,9 @@
#include "BlockPair.h" #include "BlockPair.h"
#include <QList> #include <QList>
#include <math.h> #include <math.h>
#include <limits>
#include "MatrixUtil.h"
#include <string>
namespace zxing { namespace zxing {
namespace qrcode { namespace qrcode {
@ -38,7 +41,7 @@ const int Encoder::ALPHANUMERIC_TABLE[Encoder::ALPHANUMERIC_TABLE_SIZE] = {
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
}; };
const QString DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1"; const QString Encoder::DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
int Encoder::calculateMaskPenalty(const ByteMatrix& matrix) int Encoder::calculateMaskPenalty(const ByteMatrix& matrix)
{ {
@ -48,97 +51,98 @@ int Encoder::calculateMaskPenalty(const ByteMatrix& matrix)
+ MaskUtil::applyMaskPenaltyRule4(matrix); + MaskUtil::applyMaskPenaltyRule4(matrix);
} }
QRCode* Encoder::encode(const QString& content, const ErrorCorrectionLevel *ecLevel) Ref<QRCode> Encoder::encode(const QString& content, Ref<ErrorCorrectionLevel> ecLevel)
{ {
return encode(content, ecLevel, NULL); return encode(content, ecLevel, NULL);
} }
QRCode* Encoder::encode(const QString& content, const ErrorCorrectionLevel* ecLevel, const EncodeHint* hints) Ref<QRCode> Encoder::encode(const QString& content, Ref<ErrorCorrectionLevel> ecLevel, const EncodeHint* hints)
{ {
// // Determine what character encoding has been specified by the caller, if any // Determine what character encoding has been specified by the caller, if any
// QString encoding = hints == NULL ? "" : hints->get_character_set(); QString encoding = hints == NULL ? "" : QString(hints->getCharacterSet().c_str());
// if (encoding == "") if (encoding == "")
// encoding = DEFAULT_BYTE_MODE_ENCODING; encoding = DEFAULT_BYTE_MODE_ENCODING;
// // Pick an encoding mode appropriate for the content. Note that this will not attempt to use // 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. // multiple modes / segments even if that were more efficient. Twould be nice.
// Mode* mode = chooseMode(content, encoding); Mode* mode = chooseMode(content, encoding);
// // This will store the header information, like mode and // This will store the header information, like mode and
// // length, as well as "header" segments like an ECI segment. // length, as well as "header" segments like an ECI segment.
// BitArray headerBits; BitArray headerBits;
// // Append ECI segment if applicable // Append ECI segment if applicable
// if (mode == &Mode::BYTE && DEFAULT_BYTE_MODE_ENCODING != encoding) { if (mode == &Mode::BYTE && DEFAULT_BYTE_MODE_ENCODING != encoding) {
// zxing::common::CharacterSetECI* eci = zxing::common::CharacterSetECI::getCharacterSetECIByName(encoding); zxing::common::CharacterSetECI* eci =
// if (eci != NULL) { zxing::common::CharacterSetECI::getCharacterSetECIByName(encoding.toStdString().c_str());
// appendECI(*eci, headerBits); if (eci != NULL) {
// } appendECI(*eci, headerBits);
// } }
}
// // (With ECI in place,) Write the mode marker // (With ECI in place,) Write the mode marker
// appendModeInfo(*mode, headerBits); appendModeInfo(*mode, headerBits);
// // Collect data within the main segment, separately, to count its size if needed. Don't add it to // Collect data within the main segment, separately, to count its size if needed. Don't add it to
// // main payload yet. // main payload yet.
// BitArray dataBits; BitArray dataBits;
// appendBytes(content, *mode, dataBits, encoding); appendBytes(content, *mode, dataBits, encoding);
// // Hard part: need to know version to know how many bits length takes. But need to know how many // 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 // bits it takes to know version. First we take a guess at version by assuming version will be
// // the minimum, 1: // the minimum, 1:
// int provisionalBitsNeeded = headerBits.getSize() int provisionalBitsNeeded = headerBits.getSize()
// + mode.getCharacterCountBits(Version.getVersionForNumber(1)) + mode->getCharacterCountBits(Version::getVersionForNumber(1))
// + dataBits.getSize(); + dataBits.getSize();
// Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel); Ref<Version> provisionalVersion = chooseVersion(provisionalBitsNeeded, *ecLevel);
// // Use that guess to calculate the right version. I am still not sure this works in 100% of cases. // Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
// int bitsNeeded = headerBits.getSize() int bitsNeeded = headerBits.getSize()
// + mode.getCharacterCountBits(provisionalVersion) + mode->getCharacterCountBits(provisionalVersion)
// + dataBits.getSize(); + dataBits.getSize();
// Version version = chooseVersion(bitsNeeded, ecLevel); Ref<Version> version = chooseVersion(bitsNeeded, *ecLevel);
// BitArray headerAndDataBits = new BitArray(); BitArray headerAndDataBits;
// headerAndDataBits.appendBitArray(headerBits); headerAndDataBits.appendBitArray(headerBits);
// // Find "length" of main segment and write it // Find "length" of main segment and write it
// int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length(); int numLetters = (*mode == Mode::BYTE) ? dataBits.getSize() : content.length();
// appendLengthInfo(numLetters, version, mode, headerAndDataBits); appendLengthInfo(numLetters, *version, *mode, headerAndDataBits);
// // Put data together into the overall payload // Put data together into the overall payload
// headerAndDataBits.appendBitArray(dataBits); headerAndDataBits.appendBitArray(dataBits);
// Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); zxing::qrcode::ECBlocks ecBlocks = version->getECBlocksForLevel(*ecLevel);
// int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords(); int numDataBytes = version->getTotalCodewords() - ecBlocks.getTotalECCodewords();
// // Terminate the bits properly. // Terminate the bits properly.
// terminateBits(numDataBytes, headerAndDataBits); terminateBits(numDataBytes, headerAndDataBits);
// // Interleave data bits with error correction code. // Interleave data bits with error correction code.
// BitArray finalBits = interleaveWithECBytes(headerAndDataBits, Ref<BitArray> finalBits(interleaveWithECBytes(headerAndDataBits,
// version.getTotalCodewords(), version->getTotalCodewords(),
// numDataBytes, numDataBytes,
// ecBlocks.getNumBlocks()); 1));//ecBlocks->getNumBlocks());
// QRCode qrCode = new QRCode(); Ref<QRCode> qrCode(new QRCode);
// qrCode.setECLevel(ecLevel); qrCode->setECLevel(ecLevel);
// qrCode.setMode(mode); qrCode->setMode(Ref<Mode>(mode));
// qrCode.setVersion(version); qrCode->setVersion(version);
// // Choose the mask pattern and set to "qrCode". // Choose the mask pattern and set to "qrCode".
// int dimension = version.getDimensionForVersion(); int dimension = version->getDimensionForVersion();
// ByteMatrix matrix = new ByteMatrix(dimension, dimension); Ref<ByteMatrix> matrix(new ByteMatrix(dimension, dimension));
// int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix); int maskPattern = chooseMaskPattern(finalBits, *ecLevel, version, matrix);
// qrCode.setMaskPattern(maskPattern); qrCode->setMaskPattern(maskPattern);
// // Build the matrix and set it to "qrCode". // Build the matrix and set it to "qrCode".
// MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix); MatrixUtil::buildMatrix(*finalBits, *ecLevel, *version, maskPattern, *matrix);
// qrCode.setMatrix(matrix); qrCode->setMatrix(matrix);
// return qrCode; return qrCode;
return NULL; //return NULL;
} }
/** /**
@ -209,35 +213,36 @@ Mode* Encoder::chooseMode(const QString& content, const QString& encoding)
// return true; // return true;
//} //}
// private static int chooseMaskPattern(BitArray bits, int Encoder::chooseMaskPattern(Ref<BitArray> bits,
// ErrorCorrectionLevel ecLevel, ErrorCorrectionLevel ecLevel,
// Version version, Ref<Version> version,
// ByteMatrix matrix) throws WriterException { Ref<ByteMatrix> matrix)
{
// int minPenalty = Integer.MAX_VALUE; // Lower penalty is better. int minPenalty = std::numeric_limits<int>::max(); // Lower penalty is better.
// int bestMaskPattern = -1; int bestMaskPattern = -1;
// // We try all mask patterns to choose the best one. // We try all mask patterns to choose the best one.
// for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) { for (int maskPattern = 0; maskPattern < QRCode::NUM_MASK_PATTERNS; maskPattern++) {
// MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix); MatrixUtil::buildMatrix(*bits, ecLevel, *version, maskPattern, *matrix);
// int penalty = calculateMaskPenalty(matrix); int penalty = calculateMaskPenalty(*matrix);
// if (penalty < minPenalty) { if (penalty < minPenalty) {
// minPenalty = penalty; minPenalty = penalty;
// bestMaskPattern = maskPattern; bestMaskPattern = maskPattern;
// } }
// } }
// return bestMaskPattern; return bestMaskPattern;
// } }
Version Encoder::chooseVersion(int numInputBits, ErrorCorrectionLevel &ecLevel) Ref<Version> Encoder::chooseVersion(int numInputBits, const ErrorCorrectionLevel &ecLevel)
{ {
// In the following comments, we use numbers of Version 7-H. // In the following comments, we use numbers of Version 7-H.
for (int versionNum = 1; versionNum <= 40; versionNum++) { for (int versionNum = 1; versionNum <= 40; versionNum++) {
Version& version = *Version::getVersionForNumber(versionNum); Ref<Version> version = Version::getVersionForNumber(versionNum);
// numBytes = 196 // numBytes = 196
int numBytes = version.getTotalCodewords(); int numBytes = version->getTotalCodewords();
// getNumECBytes = 130 // getNumECBytes = 130
ECBlocks& ecBlocks = version.getECBlocksForLevel(ecLevel); ECBlocks& ecBlocks = version->getECBlocksForLevel(ecLevel);
int numEcBytes = ecBlocks.getECCodewords(); int numEcBytes = ecBlocks.getTotalECCodewords();
// getNumDataBytes = 196 - 130 = 66 // getNumDataBytes = 196 - 130 = 66
int numDataBytes = numBytes - numEcBytes; int numDataBytes = numBytes - numEcBytes;
int totalInputBytes = (numInputBits + 7) / 8; int totalInputBytes = (numInputBits + 7) / 8;
@ -357,9 +362,8 @@ BitArray* Encoder::interleaveWithECBytes(const BitArray& bits,
{ {
// "bits" must have "getNumDataBytes" bytes of data. // "bits" must have "getNumDataBytes" bytes of data.
if (bits.getSize() != numDataBytes) { if (bits.getSize() != numDataBytes)
throw new WriterException("Number of bits and data bytes does not match"); 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 // 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". // store the divided data bytes blocks and error correction bytes blocks into "blocks".
@ -441,142 +445,152 @@ ArrayRef<char> Encoder::generateECBytes(const std::vector<char>& dataBytes, int
return ecBytes; return ecBytes;
} }
// /** /**
// * Append mode info. On success, store the result in "bits". * Append mode info. On success, store the result in "bits".
// */ */
// static void appendModeInfo(Mode mode, BitArray bits) { void Encoder::appendModeInfo(const Mode& mode, BitArray& bits)
// bits.appendBits(mode.getBits(), 4); {
// } 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". * Append length info. On success, store the result in "bits".
// */ */
// static void appendBytes(String content, void Encoder::appendLengthInfo(int numLetters, const Version& version, const Mode& mode, BitArray& bits)
// Mode mode, {
// BitArray bits, int numBits = mode.getCharacterCountBits(&version);
// String encoding) throws WriterException { if (numLetters >= (1 << numBits)) {
// switch (mode) { QString message = QString::number(numLetters);
// case NUMERIC: message += " is bigger than ";
// appendNumericBytes(content, bits); message += QString::number((1 << numBits) - 1);
// 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) { throw new WriterException(message.toStdString().c_str());
// int length = content.length(); }
// int i = 0; bits.appendBits(numLetters, numBits);
// 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(); * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
// int i = 0; */
// while (i < length) { void Encoder::appendBytes(const QString& content,
// int code1 = getAlphanumericCode(content.charAt(i)); Mode& mode,
// if (code1 == -1) { BitArray& bits,
// throw new WriterException(); const QString& encoding)
// } {
// if (i + 1 < length) { if (mode == Mode::NUMERIC)
// int code2 = getAlphanumericCode(content.charAt(i + 1)); appendNumericBytes(content, bits);
// if (code2 == -1) { else if (mode == Mode::ALPHANUMERIC)
// throw new WriterException(); appendAlphanumericBytes(content, bits);
// } else if (mode == Mode::BYTE)
// // Encode two alphanumeric letters in 11 bits. append8BitBytes(content, bits, encoding);
// bits.appendBits(code1 * 45 + code2, 11); else if (mode == Mode::KANJI)
// i += 2; appendKanjiBytes(content, bits);
// } else { else {
// // Encode one alphanumeric letter in six bits. QString message("Invalid mode: ");
// bits.appendBits(code1, 6); message += QString::fromStdString(mode.getName());
// i++; throw new WriterException(message.toStdString().c_str());
// } }
// } }
// }
// static void append8BitBytes(String content, BitArray bits, String encoding) void Encoder::appendNumericBytes( const QString& content, BitArray& bits)
// throws WriterException { {
// byte[] bytes; int length = content.size();
// try { int i = 0;
// bytes = content.getBytes(encoding); while (i < length) {
// } catch (UnsupportedEncodingException uee) { int num1 = content.at(i).toLatin1() - '0';
// throw new WriterException(uee); if (i + 2 < length) {
// } // Encode three numeric letters in ten bits.
// for (byte b : bytes) { int num2 = content.at(i + 1).toLatin1() - '0';
// bits.appendBits(b, 8); int num3 = content.at(i + 2).toLatin1() - '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.at(i + 1).toLatin1() - '0';
bits.appendBits(num1 * 10 + num2, 7);
i += 2;
} else {
// Encode one numeric letter in four bits.
bits.appendBits(num1, 4);
i++;
}
}
}
// static void appendKanjiBytes(String content, BitArray bits) throws WriterException { void Encoder::appendAlphanumericBytes(const QString& content, BitArray& bits)
// byte[] bytes; {
// try { int length = content.length();
// bytes = content.getBytes("Shift_JIS"); int i = 0;
// } catch (UnsupportedEncodingException uee) { while (i < length) {
// throw new WriterException(uee); int code1 = getAlphanumericCode(content.at(i).toLatin1());
// } if (code1 == -1) {
// int length = bytes.length; throw new WriterException();
// for (int i = 0; i < length; i += 2) { }
// int byte1 = bytes[i] & 0xFF; if (i + 1 < length) {
// int byte2 = bytes[i + 1] & 0xFF; int code2 = getAlphanumericCode(content.at(i + 1).toLatin1());
// int code = (byte1 << 8) | byte2; if (code2 == -1) {
// int subtracted = -1; throw new WriterException();
// if (code >= 0x8140 && code <= 0x9ffc) { }
// subtracted = code - 0x8140; // Encode two alphanumeric letters in 11 bits.
// } else if (code >= 0xe040 && code <= 0xebbf) { bits.appendBits(code1 * 45 + code2, 11);
// subtracted = code - 0xc140; i += 2;
// } } else {
// if (subtracted == -1) { // Encode one alphanumeric letter in six bits.
// throw new WriterException("Invalid byte sequence"); bits.appendBits(code1, 6);
// } i++;
// int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff); }
// bits.appendBits(encoded, 13); }
// } }
// }
// private static void appendECI(CharacterSetECI eci, BitArray bits) { void Encoder::append8BitBytes(const QString& content, BitArray& bits, const QString& /*encoding*/)
// bits.appendBits(Mode.ECI.getBits(), 4); {
// // This is correct for values up to 127, which is all we need now. // For now we will suppose that all the encoding has been handled by QString class.
// bits.appendBits(eci.getValue(), 8); // byte[] bytes;
// } // try {
// bytes = content.getBytes(encoding);
// } catch (UnsupportedEncodingException uee) {
// throw new WriterException(uee);
// }
for (int i=0; i<content.size(); ++i) {
bits.appendBits(content.at(i).toLatin1(), 8);
}
}
void Encoder::appendKanjiBytes(const QString& content, BitArray& bits)
{
// For now we will suppose that all the encoding has been handled by QString class.
// try {
// bytes = content.getBytes("Shift_JIS");
// } catch (UnsupportedEncodingException uee) {
// throw new WriterException(uee);
// }
int length = content.size();
for (int i = 0; i < length; i += 2) {
int byte1 = content.at(i).toLatin1() & 0xFF;
int byte2 = content.at(i + 1).toLatin1() & 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);
}
}
void Encoder::appendECI(const zxing::common::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);
}
} }
} }

View File

@ -38,9 +38,9 @@ private:
* with which clients can specify the encoding mode. For now, we don't need the functionality. * with which clients can specify the encoding mode. For now, we don't need the functionality.
*/ */
public: public:
static QRCode* encode(const QString& content, const ErrorCorrectionLevel* ecLevel); static Ref<QRCode> encode(const QString& content, Ref<ErrorCorrectionLevel> ecLevel);
static QRCode* encode(const QString& content, const ErrorCorrectionLevel* ecLevel, const EncodeHint* hints); static Ref<QRCode> encode(const QString& content, Ref<ErrorCorrectionLevel> ecLevel, const EncodeHint* hints);
/** /**
* @return the code point of the table used in alphanumeric mode or * @return the code point of the table used in alphanumeric mode or
@ -59,12 +59,12 @@ private:
//static bool isOnlyDoubleByteKanji(const QString& content); //static bool isOnlyDoubleByteKanji(const QString& content);
static int chooseMaskPattern(BitArray bits, static int chooseMaskPattern(Ref<BitArray> bits,
ErrorCorrectionLevel ecLevel, ErrorCorrectionLevel ecLevel,
Version version, Ref<Version> version,
ByteMatrix matrix); Ref<ByteMatrix> matrix);
static Version chooseVersion(int numInputBits, ErrorCorrectionLevel &ecLevel) ; static Ref<Version> chooseVersion(int numInputBits, const ErrorCorrectionLevel &ecLevel) ;
/** /**
* Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
@ -98,7 +98,7 @@ protected:
/** /**
* Append mode info. On success, store the result in "bits". * Append mode info. On success, store the result in "bits".
*/ */
static void appendModeInfo(const Mode& mode, BitArray& bits); static void appendModeInfo(const Mode& mode, BitArray &bits);
/** /**
@ -110,13 +110,13 @@ protected:
* Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
*/ */
static void appendBytes(const QString& content, static void appendBytes(const QString& content,
const Mode& mode, Mode& mode,
BitArray& bits, BitArray& bits,
const QString& encoding); const QString& encoding);
static void appendNumericBytes( const std::vector<char>& content, BitArray& bits); static void appendNumericBytes(const QString& content, BitArray& bits);
static void appendAlphanumericBytes(const std::vector<char>& content, BitArray& bits); static void appendAlphanumericBytes(const QString& content, BitArray& bits);
static void append8BitBytes(const QString& content, BitArray& bits, const QString& encoding); static void append8BitBytes(const QString& content, BitArray& bits, const QString& encoding);