#include "ODTelepenReader.h"
#include "BarcodeData.h"
#include "SymbologyIdentifier.h"
#include "ZXAlgorithms.h"
#include <cstdint>
#include <ranges>
#ifndef PRINT_DEBUG
#define printf(...){}
#endif
namespace ZXing::OneD {
static std::string DecodeNumeric(std::string_view encoded)
{
std::string decoded;
decoded.reserve(encoded.size() * 2);
for (uint8_t codeword : encoded) {
if (codeword < 16 || codeword == 127)
decoded += codeword; else if (17 <= codeword && codeword < 27)
(decoded += ToDigit(codeword - 17)) += 'X';
else if (27 <= codeword && codeword < 127)
decoded += ToString(codeword - 27, 2);
else return {};
}
return decoded;
}
BarcodeData TelepenReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr<RowReader::DecodingState>&) const
{
constexpr int minCharCount = 1; constexpr int minQuietZone = 5; constexpr int minCharLength = 16 / 3;
constexpr FixedPattern<12, 16> startPatterns[3] = {
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3}, {1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3}, {1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3} };
constexpr FixedPattern<6, 6> prefixPattern = {1, 1, 1, 1, 1, 1};
constexpr FixedPattern<11, 15> endPatterns[3] = {
{3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {3, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1}, {3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1} };
#if 0#else
next = FindLeftGuard<true>(next, 2 * 12 + minCharCount * minCharLength, prefixPattern, minQuietZone);
#endif
if (!next.isValid())
return {};
int startChar = 1;
for (; startChar < 4; ++startChar)
if (IsPattern<true>(next, startPatterns[startChar - 1], next.spaceInFront(), minQuietZone))
break;
if (startChar == 4 || (startChar > 1 && !readNumeric))
return {};
const auto &startPattern = startPatterns[startChar - 1];
const auto &endPattern = endPatterns[startChar - 1];
int xStart = next.pixelsInFront();
next = next.subView(0, startPattern.size());
auto threshold = NarrowWideThreshold(next);
if (!threshold.isValid())
return {};
next = next.subView(startPattern.size(), endPattern.size());
std::string raw;
while (next.isValid() && !IsRightGuard<true>(next, endPattern, minQuietZone)) {
BitArray ba;
bool inBlock = false;
BarAndSpace<int> wSum, wNum, nSum, nNum;
while (next.isValid() && ba.size() < 8) {
if (next[0] > threshold[0] * 3 || next[1] > threshold[1] * 3)
return {};
BarAndSpace<bool> wide = {next[0] > threshold[0], next[1] > threshold[1]};
if (!wide.bar && !wide.space) ba.appendBit(1);
else if (!wide.bar && wide.space) ba.appendBits(std::exchange(inBlock, !inBlock) ? 0b10 : 0b01, 2);
else if (wide.bar && !wide.space) ba.appendBits(0b00, 2);
else if (wide.bar && wide.space) ba.appendBits(0b010, 3);
for (int i = 0; i < 2; ++i) {
if (next[i] > threshold[i]) {
wSum[i] += next[i];
++wNum[i];
} else {
nSum[i] += next[i];
++nNum[i];
}
}
next.skipPair();
}
if (ba.size() != 8 || std::ranges::count(ba, false) % 2 != 0) return {};
ba.reverse();
raw += ToInt<char>(ba) & 0x7f;
printf("line: %d, threshold: %2d, %2d, wSum: %2d, %2d, wNum: %d, %d, nSum: %2d, %2d, nNum: %d, %d -> %c ends at: %d\n", rowNumber,
threshold[0], threshold[1], wSum[0], wSum[1], wNum[0], wNum[1], nSum[0], nSum[1], nNum[0], nNum[1],
raw.back(), next.pixelsInFront());
for (int i = 0; i < 2; ++i)
threshold[i] = wNum[i] && nNum[i] ? (wSum[i] / wNum[i] + nSum[i] / nNum[i]) / 2
: nNum[i] ? 2 * nSum[i] / nNum[i]
: threshold[i];
}
if (raw.size() < minCharCount + 1 || !IsRightGuard<true>(next, endPattern, minQuietZone))
return {};
auto txt = raw.substr(0, raw.size() - 1); auto checkSum = (127 - (Reduce(txt, 0) % 127)) % 127;
char modifier = 0;
Error error;
if (checkSum != raw.back()) {
error = Error::Checksum;
} else {
if (startChar == 1 && readNumeric) { if (!readAlpha
|| (std::ranges::count(txt, '\x10') <= 1 && txt.front() != '\x10'
&& std::ranges::any_of(txt, [](uint8_t c) { return c < 32; })
&& std::ranges::none_of(txt, [](uint8_t c) { return c < 16; })))
startChar = 2;
}
const auto dle = IndexOf(txt, '\x10'); if (startChar == 1) {
modifier = '0'; } else if (startChar == 2 && dle != 0) {
auto num = dle == -1 ? DecodeNumeric(txt) : DecodeNumeric(txt.substr(0, dle));
auto alpha = dle == -1 ? std::string() : txt.substr(dle + 1);
if (num.empty())
error = Error::Format; else
txt = num + alpha;
modifier = dle == -1 ? '1' : '2'; } else if (startChar == 3 && dle != 0) {
auto alpha = dle == -1 ? txt : txt.substr(0, dle);
auto num = dle == -1 ? std::string() : DecodeNumeric(txt.substr(dle + 1));
if (dle != -1 && num.empty())
error = Error::Format; else
txt = alpha + num;
modifier = dle == -1 ? '0' : '4'; } else
error = Error::Format; }
int xStop = next.pixelsTillEnd();
SymbologyIdentifier symbologyIdentifier = {'B', modifier};
auto format = modifier == '1' ? BarcodeFormat::TelepenNumeric : BarcodeFormat::TelepenAlpha;
printf("line: %d, raw: %s, txt: %s, checksum: %d\n", rowNumber, raw.c_str(), txt.c_str(), checkSum);
return LinearBarcode(format, txt, rowNumber, xStart, xStop, symbologyIdentifier, error);
}
}