#include "QRDecoder.h"
#include "Barcode.h"
#include "BitMatrix.h"
#include "BitSource.h"
#include "ByteArray.h"
#include "CharacterSet.h"
#include "DecoderResult.h"
#include "GenericGF.h"
#include "QRBitMatrixParser.h"
#include "QRCodecMode.h"
#include "QRDataBlock.h"
#include "QRFormatInformation.h"
#include "QRVersion.h"
#include "ReedSolomonDecoder.h"
#include "StructuredAppend.h"
#include "ZXAlgorithms.h"
#include "ZXTestSupport.h"
#include <algorithm>
#include <stdexcept>
#include <utility>
#include <vector>
namespace ZXing::QRCode {
static bool CorrectErrors(ByteArray& codewordBytes, int numDataCodewords)
{
std::vector<int> codewordsInts(codewordBytes.begin(), codewordBytes.end());
int numECCodewords = Size(codewordBytes) - numDataCodewords;
if (!ReedSolomonDecode(GenericGF::QRCodeField256(), codewordsInts, numECCodewords))
return false;
std::copy_n(codewordsInts.begin(), numDataCodewords, codewordBytes.begin());
return true;
}
static void DecodeHanziSegment(BitSource& bits, int count, Content& result)
{
result.switchEncoding(CharacterSet::GB18030);
result.reserve(2 * count);
while (count > 0) {
int twoBytes = bits.readBits(13);
int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
if (assembledTwoBytes < 0x00A00) {
assembledTwoBytes += 0x0A1A1;
} else {
assembledTwoBytes += 0x0A6A1;
}
result.push_back((assembledTwoBytes >> 8) & 0xFF);
result.push_back(assembledTwoBytes & 0xFF);
count--;
}
}
static void DecodeKanjiSegment(BitSource& bits, int count, Content& result)
{
result.switchEncoding(CharacterSet::Shift_JIS);
result.reserve(2 * count);
while (count > 0) {
int twoBytes = bits.readBits(13);
int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
if (assembledTwoBytes < 0x01F00) {
assembledTwoBytes += 0x08140;
} else {
assembledTwoBytes += 0x0C140;
}
result.push_back(assembledTwoBytes >> 8);
result.push_back(assembledTwoBytes);
count--;
}
}
static void DecodeByteSegment(BitSource& bits, int count, Content& result)
{
result.switchEncoding(CharacterSet::Unknown);
result.reserve(count);
for (int i = 0; i < count; i++)
result.push_back(bits.readBits(8));
}
static char ToAlphaNumericChar(int value)
{
constexpr std::array ALPHANUMERIC_CHARS = {
'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',
' ', '$', '%', '*', '+', '-', '.', '/', ':'
};
return ALPHANUMERIC_CHARS.at(value);
}
static void DecodeAlphanumericSegment(BitSource& bits, int count, Content& result)
{
std::string buffer;
while (count > 1) {
int nextTwoCharsBits = bits.readBits(11);
buffer += ToAlphaNumericChar(nextTwoCharsBits / 45);
buffer += ToAlphaNumericChar(nextTwoCharsBits % 45);
count -= 2;
}
if (count == 1) {
buffer += ToAlphaNumericChar(bits.readBits(6));
}
if (result.symbology.aiFlag != AIFlag::None) {
for (auto i = buffer.begin(); i != buffer.end(); i++) {
if (*i == '%') {
if (i + 1 != buffer.end() && *(i + 1) == '%') {
i = buffer.erase(i);
} else {
*i = static_cast<char>(0x1D);
}
}
}
}
result.switchEncoding(CharacterSet::ISO8859_1);
result.append(buffer);
}
static void DecodeNumericSegment(BitSource& bits, int count, Content& result)
{
result.switchEncoding(CharacterSet::ISO8859_1);
result.reserve(count);
while (count) {
int n = std::min(count, 3);
int nDigits = bits.readBits(1 + 3 * n); result.append(ZXing::ToString(nDigits, n));
count -= n;
}
}
static ECI ParseECIValue(BitSource& bits)
{
int firstByte = bits.readBits(8);
if ((firstByte & 0x80) == 0) {
return ECI(firstByte & 0x7F);
}
if ((firstByte & 0xC0) == 0x80) {
int secondByte = bits.readBits(8);
return ECI(((firstByte & 0x3F) << 8) | secondByte);
}
if ((firstByte & 0xE0) == 0xC0) {
int secondThirdBytes = bits.readBits(16);
return ECI(((firstByte & 0x1F) << 16) | secondThirdBytes);
}
throw FormatError("ParseECIValue: invalid value");
}
bool IsEndOfStream(const BitSource& bits, const Version& version)
{
const int bitsRequired = TerminatorBitsLength(version);
const int bitsAvailable = std::min(bits.available(), bitsRequired);
return bitsAvailable == 0 || bits.peekBits(bitsAvailable) == 0;
}
ZXING_EXPORT_TEST_ONLY
DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel)
{
BitSource bits(bytes);
Content result;
Error error;
result.symbology = {'Q', version.isModel1() ? '0' : '1', 1};
StructuredAppendInfo structuredAppend;
const int modeBitLength = CodecModeBitsLength(version);
if (version.isModel1())
bits.readBits(4);
try
{
while(!IsEndOfStream(bits, version)) {
CodecMode mode;
if (modeBitLength == 0)
mode = CodecMode::NUMERIC; else
mode = CodecModeForBits(bits.readBits(modeBitLength), version.type());
switch (mode) {
case CodecMode::FNC1_FIRST_POSITION:
result.symbology.modifier = '3';
result.symbology.aiFlag = AIFlag::GS1; break;
case CodecMode::FNC1_SECOND_POSITION:
if (!result.empty())
throw FormatError("AIM Application Indicator (FNC1 in second position) at illegal position");
result.symbology.modifier = '5'; if (int appInd = bits.readBits(8); appInd < 100) result.append(ZXing::ToString(appInd, 2));
else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222)) result.push_back(appInd - 100);
else
throw FormatError("Invalid AIM Application Indicator");
result.symbology.aiFlag = AIFlag::AIM; break;
case CodecMode::STRUCTURED_APPEND:
structuredAppend.index = bits.readBits(4);
structuredAppend.count = bits.readBits(4) + 1;
structuredAppend.id = std::to_string(bits.readBits(8));
break;
case CodecMode::ECI:
if (version.isModel1())
throw FormatError("QRCode Model 1 does not support ECI");
result.switchEncoding(ParseECIValue(bits));
break;
case CodecMode::HANZI: {
if (int subset = bits.readBits(4); subset != 1) throw FormatError("Unsupported HANZI subset");
int count = bits.readBits(CharacterCountBits(mode, version));
DecodeHanziSegment(bits, count, result);
break;
}
default: {
int count = bits.readBits(CharacterCountBits(mode, version));
switch (mode) {
case CodecMode::NUMERIC: DecodeNumericSegment(bits, count, result); break;
case CodecMode::ALPHANUMERIC: DecodeAlphanumericSegment(bits, count, result); break;
case CodecMode::BYTE: DecodeByteSegment(bits, count, result); break;
case CodecMode::KANJI: DecodeKanjiSegment(bits, count, result); break;
default: throw FormatError("Invalid CodecMode");
}
break;
}
}
}
} catch (std::out_of_range&) { error = FormatError("Truncated bit stream");
} catch (Error e) {
error = std::move(e);
}
return DecoderResult(std::move(result))
.setError(std::move(error))
.setEcLevel(ToString(ecLevel))
.setVersionNumber(version.versionNumber())
.setStructuredAppend(structuredAppend);
}
DecoderResult Decode(const BitMatrix& bits)
{
if (!Version::HasValidSize(bits))
return FormatError("Invalid symbol size");
auto formatInfo = ReadFormatInformation(bits);
if (!formatInfo.isValid())
return FormatError("Invalid format information");
const Version* pversion = ReadVersion(bits, formatInfo.type());
if (!pversion)
return FormatError("Invalid version");
const Version& version = *pversion;
ByteArray codewords = ReadCodewords(bits, version, formatInfo);
if (codewords.empty())
return FormatError("Failed to read codewords");
std::vector<DataBlock> dataBlocks = DataBlock::GetDataBlocks(codewords, version, formatInfo.ecLevel);
if (dataBlocks.empty())
return FormatError("Failed to get data blocks");
const auto op = [](auto totalBytes, const auto& dataBlock){ return totalBytes + dataBlock.numDataCodewords();};
const auto totalBytes = Reduce(dataBlocks, int{}, op);
ByteArray resultBytes(totalBytes);
auto resultIterator = resultBytes.begin();
Error error;
for (auto& dataBlock : dataBlocks)
{
ByteArray& codewordBytes = dataBlock.codewords();
int numDataCodewords = dataBlock.numDataCodewords();
if (!CorrectErrors(codewordBytes, numDataCodewords))
error = ChecksumError();
resultIterator = std::copy_n(codewordBytes.begin(), numDataCodewords, resultIterator);
}
auto versionStr = version.isRMQR() ? "R" + ToString(Version::SymbolSize(version.versionNumber(), version.type()), true)
: (version.isMicro() ? "M" : "") + std::to_string(version.versionNumber());
auto ret = DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel)
.setIsMirrored(formatInfo.isMirrored)
.addExtra(BarcodeExtra::DataMask, formatInfo.dataMask, uint8_t(255))
.addExtra(BarcodeExtra::Version, versionStr)
;
if (error)
ret.setError(error);
return ret;
}
}