/* * Copyright 2016 Huy Cuong Nguyen * Copyright 2016 ZXing authors */ // SPDX-License-Identifier: Apache-2.0 #include "ODCode93Writer.h" #include "ODWriterHelper.h" #include "Utf.h" #include "ZXAlgorithms.h" #include "ZXTestSupport.h" #include namespace ZXing::OneD { static const char ALPHABET[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd*"; /** * These represent the encodings of characters, as patterns of wide and narrow bars. * The 9 least-significant bits of each int correspond to the pattern of wide and narrow. */ static const int CHARACTER_ENCODINGS[] = { 0x114, 0x148, 0x144, 0x142, 0x128, 0x124, 0x122, 0x150, 0x112, 0x10A, // 0-9 0x1A8, 0x1A4, 0x1A2, 0x194, 0x192, 0x18A, 0x168, 0x164, 0x162, 0x134, // A-J 0x11A, 0x158, 0x14C, 0x146, 0x12C, 0x116, 0x1B4, 0x1B2, 0x1AC, 0x1A6, // K-T 0x196, 0x19A, 0x16C, 0x166, 0x136, 0x13A, // U-Z 0x12E, 0x1D4, 0x1D2, 0x1CA, 0x16E, 0x176, 0x1AE, // - - % 0x126, 0x1DA, 0x1D6, 0x132, 0x15E, // Control chars? $-* }; static_assert(Size(ALPHABET) - 1 == Size(CHARACTER_ENCODINGS), "table size mismatch"); static const int ASTERISK_ENCODING = CHARACTER_ENCODINGS[47]; static int AppendPattern(std::vector& target, int pos, int a) { for (int i = 0; i < 9; i++) { int temp = a & (1 << (8 - i)); target[pos + i] = temp != 0; } return 9; } static int ComputeChecksumIndex(const std::string& contents, int maxWeight) { int weight = 1; int total = 0; for (int i = Size(contents) - 1; i >= 0; i--) { int indexInString = IndexOf(ALPHABET, contents[i]); total += indexInString * weight; if (++weight > maxWeight) { weight = 1; } } return total % 47; } ZXING_EXPORT_TEST_ONLY std::string Code93ConvertToExtended(const std::wstring& contents) { size_t length = contents.length(); std::string extendedContent; extendedContent.reserve(length * 2); for (size_t i = 0; i < length; i++) { int character = contents[i]; // ($)=a, (%)=b, (/)=c, (+)=d. see Code93Reader.ALPHABET if (character == 0) { // NUL: (%)U extendedContent.append("bU"); } else if (character <= 26) { // SOH - SUB: ($)A - ($)Z extendedContent.push_back('a'); extendedContent.push_back((char)('A' + character - 1)); } else if (character <= 31) { // ESC - US: (%)A - (%)E extendedContent.push_back('b'); extendedContent.push_back((char)('A' + character - 27)); } else if (character == ' ' || character == '$' || character == '%' || character == '+') { // space $ % + extendedContent.push_back(character); } else if (character <= ',') { // ! " # & ' ( ) * ,: (/)A - (/)L extendedContent.push_back('c'); extendedContent.push_back((char)('A' + character - '!')); } else if (character <= '9') { extendedContent.push_back(character); } else if (character == ':') { // :: (/)Z extendedContent.append("cZ"); } else if (character <= '?') { // ; - ?: (%)F - (%)J extendedContent.push_back('b'); extendedContent.push_back((char)('F' + character - ';')); } else if (character == '@') { // @: (%)V extendedContent.append("bV"); } else if (character <= 'Z') { // A - Z extendedContent.push_back(character); } else if (character <= '_') { // [ - _: (%)K - (%)O extendedContent.push_back('b'); extendedContent.push_back((char)('K' + character - '[')); } else if (character == '`') { // `: (%)W extendedContent.append("bW"); } else if (character <= 'z') { // a - z: (*)A - (*)Z extendedContent.push_back('d'); extendedContent.push_back((char)('A' + character - 'a')); } else if (character <= 127) { // { - DEL: (%)P - (%)T extendedContent.push_back('b'); extendedContent.push_back((char)('P' + character - '{')); } else { throw std::invalid_argument(std::string("Requested content contains a non-encodable character: '") + (char)character + "'"); } } return extendedContent; } BitMatrix Code93Writer::encode(const std::wstring& contents_, int width, int height) const { std::string contents = Code93ConvertToExtended(contents_); size_t length = contents.length(); if (length == 0) { throw std::invalid_argument("Found empty contents"); } if (length > 80) { throw std::invalid_argument("Requested contents should be less than 80 digits long after converting to extended encoding"); } //length of code + 2 start/stop characters + 2 checksums, each of 9 bits, plus a termination bar size_t codeWidth = (contents.length() + 2 + 2) * 9 + 1; std::vector result(codeWidth, false); //start character (*) int pos = AppendPattern(result, 0, ASTERISK_ENCODING); for (size_t i = 0; i < length; i++) { int indexInString = IndexOf(ALPHABET, contents[i]); pos += AppendPattern(result, pos, CHARACTER_ENCODINGS[indexInString]); } //add two checksums int check1 = ComputeChecksumIndex(contents, 20); pos += AppendPattern(result, pos, CHARACTER_ENCODINGS[check1]); //append the contents to reflect the first checksum added contents += static_cast(ALPHABET[check1]); int check2 = ComputeChecksumIndex(contents, 15); pos += AppendPattern(result, pos, CHARACTER_ENCODINGS[check2]); //end character (*) pos += AppendPattern(result, pos, ASTERISK_ENCODING); //termination bar (single black bar) result[pos] = true; return WriterHelper::RenderResult(result, width, height, _sidesMargin >= 0 ? _sidesMargin : 10); } BitMatrix Code93Writer::encode(const std::string& contents, int width, int height) const { return encode(FromUtf8(contents), width, height); } } // namespace ZXing::OneD