#include "ODCode93Reader.h"
#include "ODCode93Patterns.h"
#include "BarcodeData.h"
#include "SymbologyIdentifier.h"
#include "ZXAlgorithms.h"
#include <array>
#include <string>
namespace ZXing::OneD {
static constexpr char ALPHABET[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd*";
static_assert(Size(ALPHABET) == Size(Code93::CODE_PATTERNS), "table size mismatch");
static const int ASTERISK_ENCODING = 0x660;
static bool
CheckOneChecksum(const std::string& result, int checkPosition, int weightMax)
{
int weight = 1;
int checksum = 0;
for (int i = checkPosition - 1; i >= 0; i--) {
checksum += weight * IndexOf(ALPHABET, result[i]);
if (++weight > weightMax) {
weight = 1;
}
}
return result[checkPosition] == ALPHABET[checksum % 47];
}
static bool
CheckChecksums(const std::string& result)
{
int length = Size(result);
return CheckOneChecksum(result, length - 2, 20) && CheckOneChecksum(result, length - 1, 15);
}
std::string DecodeCode39AndCode93FullASCII(std::string encoded, const char ctrl[4]);
constexpr int CHAR_LEN = 6;
constexpr int CHAR_MODS = 9;
constexpr float QUIET_ZONE_SCALE = 0.5f;
static auto E2E_PATTERNS = [ ] {
std::array<int, 48> res;
for (int i = 0; i < Size(res); ++i) {
const auto& a = Code93::CODE_PATTERNS[i];
std::array<int, 4> e2e;
for (int j = 0; j < 4; j++)
e2e[j] = a[j] + a[j + 1];
res[i] = ToInt(e2e);
}
return res;
}();
static bool IsStartGuard(const PatternView& window, int spaceInPixel)
{
return IsPattern(window, FixedPattern<4, 4>{1, 1, 1, 1}, spaceInPixel, QUIET_ZONE_SCALE * 12) &&
window[4] > 3 * window[5] - 2 &&
ToInt(NormalizedE2EPattern<CHAR_LEN>(window, CHAR_MODS)) == ASTERISK_ENCODING;
}
BarcodeData Code93Reader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr<DecodingState>&) const
{
int minCharCount = 5;
next = FindLeftGuard<CHAR_LEN>(next, minCharCount * CHAR_LEN, IsStartGuard);
if (!next.isValid())
return {};
int xStart = next.pixelsInFront();
std::string txt;
txt.reserve(20);
do {
if (!next.skipSymbol())
return {};
txt += LookupBitPattern(ToInt(NormalizedE2EPattern<CHAR_LEN>(next, CHAR_MODS)), E2E_PATTERNS, ALPHABET);
if (txt.back() == 0)
return {};
} while (txt.back() != '*');
txt.pop_back();
if (Size(txt) < minCharCount - 2)
return {};
next = next.subView(0, CHAR_LEN + 1);
if (!next.isValid() || next[CHAR_LEN] > next.sum(CHAR_LEN) / 4 || !next.hasQuietZoneAfter(QUIET_ZONE_SCALE))
return {};
Error error;
if (!CheckChecksums(txt))
error = ChecksumError();
txt.resize(txt.size() - 2);
if (!error && (txt = DecodeCode39AndCode93FullASCII(txt, "abcd")).empty())
error = FormatError("ASCII decoding of Code93 failed");
SymbologyIdentifier symbologyIdentifier = {'G', '0'};
int xStop = next.pixelsTillEnd();
return LinearBarcode(BarcodeFormat::Code93, txt, rowNumber, xStart, xStop, symbologyIdentifier, error);
}
}