Files
ANSLibs/QRCode/oned/ODDataBarExpandedBitDecoder.cpp

249 lines
6.0 KiB
C++

/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "ODDataBarExpandedBitDecoder.h"
#include "BitArray.h"
#include "Error.h"
#include "GTIN.h"
namespace ZXing::OneD::DataBar {
constexpr char GS = 29; // FNC1
static std::string DecodeGeneralPurposeBits(BitArrayView& bits)
{
enum State { NUMERIC, ALPHA, ISO_IEC_646 };
State state = NUMERIC;
std::string res;
auto decode5Bits = [](State& state, std::string& res, BitArrayView& bits) {
int v = bits.readBits(5);
if (v == 4) {
state = state == ALPHA ? ISO_IEC_646 : ALPHA;
} else if (v == 15) { // FNC1 + latch to Numeric
res.push_back(GS);
state = NUMERIC;
// Allow for some generators incorrectly placing a numeric latch "000" after an FNC1
if (bits.size() >= 7 && bits.peakBits(7) < 8)
bits.skipBits(3);
} else {
res.push_back(v + 43);
}
};
auto isPadding = [](State state, BitArrayView& bits) {
bool res = (state == NUMERIC) ? bits.size() < 4
: bits.size() < 5 && (0b00100 >> (5 - bits.size()) == bits.peakBits(bits.size()));
if (res)
bits.skipBits(bits.size());
return res;
};
while(bits.size() >= 3) {
switch(state) {
case NUMERIC:
if (isPadding(state, bits))
break;
if (bits.size() < 7) {
int v = bits.readBits(4);
if (v > 0)
res.push_back(ToDigit(v - 1));
} else if (bits.peakBits(4) == 0) {
bits.skipBits(4);
state = ALPHA;
} else {
int v = bits.readBits(7);
for (int digit : {(v - 8) / 11, (v - 8) % 11})
res.push_back(digit == 10 ? GS : ToDigit(digit));
}
break;
case ALPHA:
if (isPadding(state, bits))
break;
if (bits.peakBits(1) == 1) {
constexpr char const* lut58to62 = R"(*,-./)";
int v = bits.readBits(6);
if (v < 58)
res.push_back(v + 33);
else if (v < 63)
res.push_back(lut58to62[v - 58]);
else
throw FormatError();
} else if (bits.peakBits(3) == 0) {
bits.skipBits(3);
state = NUMERIC;
} else {
decode5Bits(state, res, bits);
}
break;
case ISO_IEC_646:
if (isPadding(state, bits))
break;
if (bits.peakBits(3) == 0) {
bits.skipBits(3);
state = NUMERIC;
} else {
int v = bits.peakBits(5);
if (v < 16) {
decode5Bits(state, res, bits);
} else if (v < 29) {
v = bits.readBits(7);
res.push_back(v < 90 ? v + 1 : v + 7);
} else {
constexpr char const* lut232to252 = R"(!"%&'()*+,-./:;<=>?_ )";
v = bits.readBits(8);
if (v < 232 || 252 < v)
throw FormatError();
res.push_back(lut232to252[v - 232]);
}
}
break;
}
}
// in NUMERIC encodation there might be a trailing FNC1 that needs to be ignored
if (res.size() && res.back() == GS)
res.pop_back();
return res;
}
static std::string DecodeCompressedGTIN(std::string prefix, BitArrayView& bits)
{
for (int i = 0; i < 4; ++i)
prefix.append(ToString(bits.readBits(10), 3));
prefix.push_back(GTIN::ComputeCheckDigit(prefix.substr(2)));
return prefix;
}
static std::string DecodeAI01GTIN(BitArrayView& bits)
{
return DecodeCompressedGTIN("019", bits);
}
static std::string DecodeAI01AndOtherAIs(BitArrayView& bits)
{
bits.skipBits(2); // Variable length symbol bit field
auto header = DecodeCompressedGTIN("01" + std::to_string(bits.readBits(4)), bits);
auto trailer = DecodeGeneralPurposeBits(bits);
return header + trailer;
}
static std::string DecodeAnyAI(BitArrayView& bits)
{
bits.skipBits(2); // Variable length symbol bit field
return DecodeGeneralPurposeBits(bits);
}
static std::string DecodeAI013103(BitArrayView& bits)
{
std::string buffer = DecodeAI01GTIN(bits);
buffer.append("3103");
buffer.append(ToString(bits.readBits(15), 6));
return buffer;
}
static std::string DecodeAI01320x(BitArrayView& bits)
{
std::string buffer = DecodeAI01GTIN(bits);
int weight = bits.readBits(15);
buffer.append(weight < 10000 ? "3202" : "3203");
buffer.append(ToString(weight < 10000 ? weight : weight - 10000, 6));
return buffer;
}
static std::string DecodeAI0139yx(BitArrayView& bits, char y)
{
bits.skipBits(2); // Variable length symbol bit field
std::string buffer = DecodeAI01GTIN(bits);
buffer.append("39");
buffer.push_back(y);
buffer.append(std::to_string(bits.readBits(2)));
if (y == '3')
buffer.append(ToString(bits.readBits(10), 3));
auto trailer = DecodeGeneralPurposeBits(bits);
if (trailer.empty())
return {};
return buffer + trailer;
}
static std::string DecodeAI013x0x1x(BitArrayView& bits, const char* aiPrefix, const char* dateCode)
{
std::string buffer = DecodeAI01GTIN(bits);
buffer.append(aiPrefix);
int weight = bits.readBits(20);
buffer.append(std::to_string(weight / 100000));
buffer.append(ToString(weight % 100000, 6));
int date = bits.readBits(16);
if (date != 38400) {
buffer.append(dateCode);
int day = date % 32;
date /= 32;
int month = date % 12 + 1;
date /= 12;
int year = date;
buffer.append(ToString(year, 2));
buffer.append(ToString(month, 2));
buffer.append(ToString(day, 2));
}
return buffer;
}
std::string DecodeExpandedBits(const BitArray& _bits)
{
auto bits = BitArrayView(_bits);
bits.readBits(1); // skip linkage bit
if (bits.peakBits(1) == 1)
return DecodeAI01AndOtherAIs(bits.skipBits(1));
if (bits.peakBits(2) == 0)
return DecodeAnyAI(bits.skipBits(2));
switch (bits.peakBits(4)) {
case 4: return DecodeAI013103(bits.skipBits(4));
case 5: return DecodeAI01320x(bits.skipBits(4));
}
switch (bits.peakBits(5)) {
case 12: return DecodeAI0139yx(bits.skipBits(5), '2');
case 13: return DecodeAI0139yx(bits.skipBits(5), '3');
}
switch (bits.readBits(7)) {
case 56: return DecodeAI013x0x1x(bits, "310", "11");
case 57: return DecodeAI013x0x1x(bits, "320", "11");
case 58: return DecodeAI013x0x1x(bits, "310", "13");
case 59: return DecodeAI013x0x1x(bits, "320", "13");
case 60: return DecodeAI013x0x1x(bits, "310", "15");
case 61: return DecodeAI013x0x1x(bits, "320", "15");
case 62: return DecodeAI013x0x1x(bits, "310", "17");
case 63: return DecodeAI013x0x1x(bits, "320", "17");
}
return {};
}
} // namespace ZXing::OneD::DataBar