File indexing completed on 2024-05-12 04:01:31

0001 /*
0002     SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "code128barcode_p.h"
0008 
0009 #include "barcodeutil_p.h"
0010 #include "bitvector_p.h"
0011 #include "prison_debug.h"
0012 
0013 #include <QImage>
0014 #include <QPainter>
0015 
0016 using namespace Prison;
0017 
0018 enum {
0019     SymbolSize = 11,
0020     StopPatternSize = 13,
0021     StopPattern = 108,
0022     QuietZone = 10,
0023 };
0024 
0025 enum CodeSet : uint8_t {
0026     CodeSetA = 0,
0027     CodeSetB = 1,
0028     CodeSetC = 2,
0029     CodeSetUnknown = 3,
0030 };
0031 
0032 enum CodeSetOp : uint8_t {
0033     None = 255,
0034     StartA = 103,
0035     StartB = 104,
0036     StartC = 105,
0037     Shift = 98,
0038     LatchA = 101,
0039     LatchB = 100,
0040     LatchC = 99,
0041 };
0042 
0043 Code128Barcode::Code128Barcode()
0044     : AbstractBarcodePrivate(Barcode::OneDimension)
0045 {
0046 }
0047 Code128Barcode::~Code128Barcode() = default;
0048 
0049 QImage Code128Barcode::paintImage()
0050 {
0051     const auto bits = encode(BarCodeUtil::asLatin1ByteArray(m_data));
0052     const auto width = bits.size() + 2 * QuietZone;
0053 
0054     QImage img(width, 1, QImage::Format_ARGB32);
0055     img.fill(m_background);
0056     QPainter p(&img);
0057     for (int i = 0; i < bits.size(); ++i) {
0058         if (bits.at(i)) {
0059             img.setPixel(QuietZone + i, 0, m_foreground.rgb());
0060         }
0061     }
0062 
0063     return img;
0064 }
0065 
0066 // Code 128 symbol table
0067 static const uint16_t code128_symbols[] = {
0068     0b11011001100, // 0
0069     0b11001101100,
0070     0b11001100110,
0071     0b10010011000,
0072     0b10010001100,
0073     0b10001001100,
0074     0b10011001000,
0075     0b10011000100,
0076     0b10001100100,
0077     0b11001001000,
0078     0b11001000100, // 10
0079     0b11000100100,
0080     0b10110011100,
0081     0b10011011100,
0082     0b10011001110,
0083     0b10111001100,
0084     0b10011101100,
0085     0b10011100110,
0086     0b11001110010,
0087     0b11001011100,
0088     0b11001001110, // 20
0089     0b11011100100,
0090     0b11001110100,
0091     0b11101101110,
0092     0b11101001100,
0093     0b11100101100,
0094     0b11100100110,
0095     0b11101100100,
0096     0b11100110100,
0097     0b11100110010,
0098     0b11011011000, // 30
0099     0b11011000110,
0100     0b11000110110,
0101     0b10100011000,
0102     0b10001011000,
0103     0b10001000110,
0104     0b10110001000,
0105     0b10001101000,
0106     0b10001100010,
0107     0b11010001000,
0108     0b11000101000, // 40
0109     0b11000100010,
0110     0b10110111000,
0111     0b10110001110,
0112     0b10001101110,
0113     0b10111011000,
0114     0b10111000110,
0115     0b10001110110,
0116     0b11101110110,
0117     0b11010001110,
0118     0b11000101110, // 50
0119     0b11011101000,
0120     0b11011100010,
0121     0b11011101110,
0122     0b11101011000,
0123     0b11101000110,
0124     0b11100010110,
0125     0b11101101000,
0126     0b11101100010,
0127     0b11100011010,
0128     0b11101111010, // 60
0129     0b11001000010,
0130     0b11110001010,
0131     0b10100110000,
0132     0b10100001100,
0133     0b10010110000,
0134     0b10010000110,
0135     0b10000101100,
0136     0b10000100110,
0137     0b10110010000,
0138     0b10110000100, // 70
0139     0b10011010000,
0140     0b10011000010,
0141     0b10000110100,
0142     0b10000110010,
0143     0b11000010010,
0144     0b11001010000,
0145     0b11110111010,
0146     0b11000010100,
0147     0b10001111010,
0148     0b10100111100, // 80
0149     0b10010111100,
0150     0b10010011110,
0151     0b10111100100,
0152     0b10011110100,
0153     0b10011110010,
0154     0b11110100100,
0155     0b11110010100,
0156     0b11110010010,
0157     0b11011011110,
0158     0b11011110110, // 90
0159     0b11110110110,
0160     0b10101111000,
0161     0b10100011110,
0162     0b10001011110,
0163     0b10111101000,
0164     0b10111100010,
0165     0b11110101000,
0166     0b11110100010,
0167     0b10111011110,
0168     0b10111101110, // 100
0169     0b11101011110,
0170     0b11110101110,
0171     0b11010000100,
0172     0b11010010000,
0173     0b11010011100,
0174     0b11000111010,
0175     0b11010111000,
0176     0b1100011101011,
0177 };
0178 
0179 static uint8_t symbolForCharacter(const QByteArray &data, int index, CodeSet set)
0180 {
0181     const auto c1 = data.at(index);
0182     switch (set) {
0183     case CodeSetA:
0184         return (c1 < ' ') ? c1 + 64 : c1 - ' ';
0185     case CodeSetB:
0186         return c1 - ' ';
0187     case CodeSetC: {
0188         const auto c2 = data.at(index + 1);
0189         return ((c1 - '0') * 10) + c2 - '0';
0190     }
0191     case CodeSetUnknown:
0192         Q_UNREACHABLE();
0193     }
0194 
0195     Q_UNREACHABLE();
0196     return {};
0197 }
0198 
0199 struct CodeSetChange {
0200     CodeSet set;
0201     CodeSetOp symbol;
0202 };
0203 
0204 static bool isInCodeSetA(char c)
0205 {
0206     return c <= 95;
0207 }
0208 
0209 static bool isInCodeSetB(char c)
0210 {
0211     // ### this does not consider FNC4 high byte encoding
0212     return c >= 32;
0213 }
0214 
0215 static CodeSetChange opForData(const QByteArray &data, int index, CodeSet currentSet)
0216 {
0217     // determine if Code C makes sense at this point
0218     int codeC = 0;
0219     for (int i = index; i < data.size(); ++i, ++codeC) {
0220         if (data.at(i) < '0' || data.at(i) > '9') {
0221             break;
0222         }
0223     }
0224     if (currentSet == CodeSetC && codeC >= 2) { // already in C
0225         return {CodeSetC, None};
0226     }
0227     if (codeC >= 6 // that's always good enough
0228         || (index == 0 && codeC >= 4) // beginning of data
0229         || (index + codeC == data.size() && codeC >= 4) // end of data
0230         || (codeC == data.size() && codeC == 2) // 2 ...
0231         || (codeC == data.size() && codeC == 4)) // ... or 4 as the entire data
0232     {
0233         return currentSet == CodeSetUnknown ? CodeSetChange{CodeSetC, StartC} : CodeSetChange{CodeSetC, LatchC};
0234     }
0235 
0236     // if we are in Code A or Code B, check if we need to switch for the next char
0237     // this is a shortcut to prevent the below more extensive search from making this O(n²) in the common case
0238     if ((currentSet == CodeSetA && isInCodeSetA(data.at(index))) || (currentSet == CodeSetB && isInCodeSetB(data.at(index)))) {
0239         return {currentSet, None};
0240     }
0241 
0242     // we need to switch to A or B, select which one, and select whether to use start, shift or latch
0243     const auto nextA = isInCodeSetA(data.at(index));
0244     const auto nextB = isInCodeSetB(data.at(index));
0245 
0246     // count how many following characters we could encode in A or B
0247     int countA = 0;
0248     for (int i = index + 1; i < data.size(); ++i, ++countA) {
0249         if (!isInCodeSetA(data.at(i))) {
0250             break;
0251         }
0252     }
0253     int countB = 0;
0254     for (int i = index + 1; i < data.size(); ++i, ++countB) {
0255         if (!isInCodeSetB(data.at(i))) {
0256             break;
0257         }
0258     }
0259 
0260     // select how we want to switch to Code A or Code B, biased to B as that's the more useful one in general
0261     switch (currentSet) {
0262     case CodeSetUnknown:
0263         // if we are at the start, take whichever code will get us further, or the only one that works
0264         if (nextA && nextB) {
0265             return countA > countB ? CodeSetChange{CodeSetA, StartA} : CodeSetChange{CodeSetB, StartB};
0266         }
0267         return nextA ? CodeSetChange{CodeSetA, StartA} : CodeSetChange{CodeSetB, StartB};
0268     case CodeSetC:
0269         // same for Code C
0270         if (nextA && nextB) {
0271             return countA > countB ? CodeSetChange{CodeSetA, LatchA} : CodeSetChange{CodeSetB, LatchB};
0272         }
0273         return nextA ? CodeSetChange{CodeSetA, LatchA} : CodeSetChange{CodeSetB, LatchB};
0274     case CodeSetA:
0275         // switch or latch to B?
0276         return CodeSetChange{CodeSetB, countB >= countA ? LatchB : Shift};
0277     case CodeSetB:
0278         // switch or latch to A?
0279         return CodeSetChange{CodeSetA, countA > countB ? LatchA : Shift};
0280     }
0281 
0282     Q_UNREACHABLE();
0283     return CodeSetChange{currentSet, None};
0284 }
0285 
0286 BitVector Code128Barcode::encode(const QByteArray &data) const
0287 {
0288     BitVector v;
0289     if (data.isEmpty()) {
0290         return v;
0291     }
0292 
0293     // determine code set for start
0294     const auto op = opForData(data, 0, CodeSetUnknown);
0295     auto currentSet = op.set;
0296 
0297     // write start code
0298     qCDebug(Log) << "start symbol:" << op.symbol << code128_symbols[op.symbol];
0299     v.appendMSB(code128_symbols[op.symbol], SymbolSize);
0300 
0301     uint32_t checksum = op.symbol;
0302     uint32_t checksumWeight = 1;
0303 
0304     for (int i = 0; i < data.size(); i += currentSet == CodeSetC ? 2 : 1) {
0305         if (static_cast<uint8_t>(data.at(i)) > 127) { // FNC4 encoding not implemented yet
0306             continue;
0307         }
0308 
0309         // perform code switch if needed
0310         const auto op = opForData(data, i, currentSet);
0311         if (op.symbol != None) {
0312             qCDebug(Log) << "op symbol:" << op.symbol << code128_symbols[op.symbol];
0313             v.appendMSB(code128_symbols[op.symbol], SymbolSize);
0314             checksum += op.symbol * checksumWeight++;
0315         }
0316 
0317         // encode current symbol
0318         const auto symbol = symbolForCharacter(data, i, op.set);
0319         qCDebug(Log) << "data symbol:" << symbol << code128_symbols[symbol];
0320         v.appendMSB(code128_symbols[symbol], SymbolSize);
0321         checksum += symbol * checksumWeight++;
0322 
0323         // update current code set
0324         if (op.symbol != Shift) {
0325             currentSet = op.set;
0326         }
0327     }
0328 
0329     // encode checksum
0330     qCDebug(Log) << "checksum:" << checksum << code128_symbols[checksum % 103];
0331     v.appendMSB(code128_symbols[checksum % 103], SymbolSize);
0332 
0333     // add stop pattern
0334     v.appendMSB(code128_symbols[StopPattern], StopPatternSize);
0335     return v;
0336 }