#include "DMDecoder.h"
#include "Barcode.h"
#include "BitMatrix.h"
#include "BitSource.h"
#include "ByteArray.h"
#include "DMBitLayout.h"
#include "DMDataBlock.h"
#include "DMVersion.h"
#include "DecoderResult.h"
#include "GenericGF.h"
#include "ReedSolomonDecoder.h"
#include "ZXAlgorithms.h"
#include "ZXTestSupport.h"
#include <algorithm>
#include <array>
#include <optional>
#include <string>
#include <utility>
#include <vector>
namespace ZXing::DataMatrix {
namespace DecodedBitStreamParser {
static constexpr std::array C40_BASIC_SET_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'
};
static constexpr std::array C40_SHIFT2_SET_CHARS = {
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', (char)29 };
static constexpr std::array TEXT_BASIC_SET_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'
};
#define TEXT_SHIFT2_SET_CHARS C40_SHIFT2_SET_CHARS
static constexpr std::array TEXT_SHIFT3_SET_CHARS = {
'`', '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', '{', '|', '}', '~', (char)127
};
struct Shift128
{
bool set = false;
char operator()(int val) { return static_cast<char>(val + std::exchange(set, false) * 128); }
};
static ECI ParseECIValue(BitSource& bits)
{
int firstByte = bits.readBits(8);
if (firstByte <= 127)
return ECI(firstByte - 1);
int secondByte = bits.readBits(8);
if (firstByte <= 191)
return ECI((firstByte - 128) * 254 + 127 + secondByte - 1);
int thirdByte = bits.readBits(8);
return ECI((firstByte - 192) * 64516 + 16383 + (secondByte - 1) * 254 + thirdByte - 1);
}
static void ParseStructuredAppend(BitSource& bits, StructuredAppendInfo& sai)
{
int symbolSequenceIndicator = bits.readBits(8);
sai.index = symbolSequenceIndicator >> 4;
sai.count = 17 - (symbolSequenceIndicator & 0x0F);
if (sai.count == 17 || sai.count <= sai.index) sai.count = 0;
int fileId1 = bits.readBits(8); int fileId2 = bits.readBits(8);
sai.id = std::to_string((fileId1 << 8) | fileId2);
}
std::optional<std::array<int, 3>> DecodeNextTriple(BitSource& bits)
{
if (bits.available() < 16)
return {};
int firstByte = bits.readBits(8);
if (firstByte == 254) return {};
int fullBitValue = (firstByte << 8) + bits.readBits(8) - 1;
int a = fullBitValue / 1600;
fullBitValue -= a * 1600;
int b = fullBitValue / 40;
int c = fullBitValue - b * 40;
return {{a, b, c}};
}
enum class Mode {C40, TEXT};
static void DecodeC40OrTextSegment(BitSource& bits, Content& result, Mode mode)
{
Shift128 upperShift;
int shift = 0;
auto& BASIC_SET_CHARS = mode == Mode::C40 ? C40_BASIC_SET_CHARS : TEXT_BASIC_SET_CHARS;
auto& SHIFT_SET_CHARS = mode == Mode::C40 ? C40_SHIFT2_SET_CHARS : TEXT_SHIFT2_SET_CHARS;
while (auto triple = DecodeNextTriple(bits)) {
for (int cValue : *triple) {
switch (std::exchange(shift, 0)) {
case 0:
if (cValue < 3)
shift = cValue + 1;
else if (cValue < Size(BASIC_SET_CHARS))
result.push_back(upperShift(BASIC_SET_CHARS[cValue]));
else
throw FormatError("invalid value in C40 or Text segment");
break;
case 1: result.push_back(upperShift(cValue)); break;
case 2:
if (cValue < Size(SHIFT_SET_CHARS))
result.push_back(upperShift(SHIFT_SET_CHARS[cValue]));
else if (cValue == 30) upperShift.set = true;
else
throw FormatError("invalid value in C40 or Text segment");
break;
case 3:
if (mode == Mode::C40)
result.push_back(upperShift(cValue + 96));
else if (cValue < Size(TEXT_SHIFT3_SET_CHARS))
result.push_back(upperShift(TEXT_SHIFT3_SET_CHARS[cValue]));
else
throw FormatError("invalid value in C40 or Text segment");
break;
default: throw FormatError("invalid value in C40 or Text segment"); ;
}
}
}
}
static void DecodeAnsiX12Segment(BitSource& bits, Content& result)
{
while (auto triple = DecodeNextTriple(bits)) {
for (int cValue : *triple) {
static const char segChars[4] = {'\r', '*', '>', ' '};
if (cValue < 0)
throw FormatError("invalid value in AnsiX12 segment");
else if (cValue < 4)
result.push_back(segChars[cValue]);
else if (cValue < 14) result.push_back((char)(cValue + 44));
else if (cValue < 40) result.push_back((char)(cValue + 51));
else
throw FormatError("invalid value in AnsiX12 segment");
}
}
}
static void DecodeEdifactSegment(BitSource& bits, Content& result)
{
while (bits.available() >= 24) {
for (int i = 0; i < 4; i++) {
char edifactValue = bits.readBits(6);
if (edifactValue == 0x1F) { if (bits.bitOffset())
bits.readBits(8 - bits.bitOffset());
return;
}
if ((edifactValue & 0x20) == 0) edifactValue |= 0x40; result.push_back(edifactValue);
}
}
}
static int Unrandomize255State(int randomizedBase256Codeword, int base256CodewordPosition)
{
int pseudoRandomNumber = ((149 * base256CodewordPosition) % 255) + 1;
int tempVariable = randomizedBase256Codeword - pseudoRandomNumber;
return tempVariable >= 0 ? tempVariable : tempVariable + 256;
}
static void DecodeBase256Segment(BitSource& bits, Content& result)
{
int codewordPosition = 1 + bits.byteOffset(); int d1 = Unrandomize255State(bits.readBits(8), codewordPosition++);
int count;
if (d1 == 0) count = bits.available() / 8;
else if (d1 < 250)
count = d1;
else
count = 250 * (d1 - 249) + Unrandomize255State(bits.readBits(8), codewordPosition++);
if (count < 0)
throw FormatError("invalid count in Base256 segment");
result.reserve(count);
for (int i = 0; i < count; i++) {
result.push_back(Unrandomize255State(bits.readBits(8), codewordPosition++));
}
}
ZXING_EXPORT_TEST_ONLY
DecoderResult Decode(ByteArray&& bytes, const bool isDMRE)
{
BitSource bits(bytes);
Content result;
Error error;
result.symbology = {'d', '1', 3}; std::string resultTrailer;
struct StructuredAppendInfo sai;
bool readerInit = false;
bool firstCodeword = true;
bool done = false;
int firstFNC1Position = 1;
Shift128 upperShift;
auto setError = [&error](Error&& e) {
if (!error)
error = std::move(e);
};
try {
while (!done && bits.available() >= 8) {
int oneByte = bits.readBits(8);
switch (oneByte) {
case 0: setError(FormatError("invalid 0 code word")); break;
case 129: done = true; break; case 230: DecodeC40OrTextSegment(bits, result, Mode::C40); break;
case 231: DecodeBase256Segment(bits, result); break;
case 232: if (bits.byteOffset() == firstFNC1Position)
result.symbology.modifier = '2'; else if (bits.byteOffset() == firstFNC1Position + 1)
result.symbology.modifier = '3'; else
result.push_back((char)29); break;
case 233: if (!firstCodeword) setError(FormatError("structured append tag must be first code word"));
ParseStructuredAppend(bits, sai);
firstFNC1Position = 5;
break;
case 234: if (!firstCodeword) setError(FormatError("reader programming tag must be first code word"));
readerInit = true;
break;
case 235: upperShift.set = true; break; case 236: result.append("[)>\x1E" "05\x1D");
resultTrailer.insert(0, "\x1E\x04");
break;
case 237: result.append("[)>\x1E" "06\x1D");
resultTrailer.insert(0, "\x1E\x04");
break;
case 238: DecodeAnsiX12Segment(bits, result); break;
case 239: DecodeC40OrTextSegment(bits, result, Mode::TEXT); break;
case 240: DecodeEdifactSegment(bits, result); break;
case 241: result.switchEncoding(ParseECIValue(bits)); break;
default:
if (oneByte <= 128) { result.push_back(upperShift(oneByte) - 1);
} else if (oneByte <= 229) { result.append(ToString(oneByte - 130, 2));
} else if (oneByte >= 242) { if (oneByte == 254 && bits.available() == 0)
break;
setError(FormatError("invalid code word"));
break;
}
}
firstCodeword = false;
}
} catch (Error e) {
setError(std::move(e));
}
result.append(resultTrailer);
result.symbology.aiFlag = result.symbology.modifier == '2' ? AIFlag::GS1 : AIFlag::None;
result.symbology.modifier += isDMRE * 6;
return DecoderResult(std::move(result))
.setError(std::move(error))
.setStructuredAppend(sai)
.setReaderInit(readerInit);
}
}
static bool
CorrectErrors(ByteArray& codewordBytes, int numDataCodewords)
{
std::vector<int> codewordsInts(codewordBytes.begin(), codewordBytes.end());
int numECCodewords = Size(codewordBytes) - numDataCodewords;
if (!ReedSolomonDecode(GenericGF::DataMatrixField256(), codewordsInts, numECCodewords))
return false;
std::copy_n(codewordsInts.begin(), numDataCodewords, codewordBytes.begin());
return true;
}
static DecoderResult DoDecode(const BitMatrix& bits)
{
const Version* version = VersionForDimensionsOf(bits);
if (version == nullptr)
return FormatError("Invalid matrix dimension");
ByteArray codewords = CodewordsFromBitMatrix(bits, *version);
if (codewords.empty())
return FormatError("Invalid number of code words");
bool fix259 = false; retry:
std::vector<DataBlock> dataBlocks = GetDataBlocks(codewords, *version, fix259);
if (dataBlocks.empty())
return FormatError("Invalid number of data blocks");
ByteArray resultBytes(TransformReduce(dataBlocks, 0, [](const auto& db) { return db.numDataCodewords; }));
const int dataBlocksCount = Size(dataBlocks);
for (int j = 0; j < dataBlocksCount; j++) {
auto& [numDataCodewords, codewords] = dataBlocks[j];
if (!CorrectErrors(codewords, numDataCodewords)) {
if(version->versionNumber == 24 && !fix259) {
fix259 = true;
goto retry;
}
return ChecksumError();
}
for (int i = 0; i < numDataCodewords; i++) {
resultBytes[i * dataBlocksCount + j] = codewords[i];
}
}
#ifdef PRINT_DEBUG
if (fix259)
printf("-> needed retry with fix259 for 144x144 symbol\n");
#endif
return DecodedBitStreamParser::Decode(std::move(resultBytes), version->isDMRE())
.setVersionNumber(version->versionNumber)
.addExtra(BarcodeExtra::Version, std::to_string(version->symbolHeight) + 'x' + std::to_string(version->symbolWidth));
}
static BitMatrix FlippedL(const BitMatrix& bits)
{
BitMatrix res(bits.height(), bits.width());
for (int y = 0; y < res.height(); ++y)
for (int x = 0; x < res.width(); ++x)
res.set(x, y, bits.get(bits.width() - 1 - y, bits.height() - 1 - x));
return res;
}
DecoderResult Decode(const BitMatrix& bits)
{
auto res = DoDecode(bits);
if (res.isValid())
return res;
if (auto mirroredRes = DoDecode(FlippedL(bits)); mirroredRes.error().type() != Error::Checksum) {
mirroredRes.setIsMirrored(true);
return mirroredRes;
}
return res;
}
}