File indexing completed on 2024-05-12 15:49:08

0001 /*
0002     SPDX-FileCopyrightText: 2011 Geoffry Song <goffrie@gmail.com>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "code39barcode.h"
0008 #include "barcodeutil.h"
0009 #include <QChar>
0010 
0011 using namespace Prison;
0012 
0013 static QList<bool> sequenceForChar(ushort c)
0014 {
0015     switch (QChar::toUpper(c)) {
0016     case '0':
0017         return BarCodeUtil::barSequence("000110100");
0018     case '1':
0019         return BarCodeUtil::barSequence("100100001");
0020     case '2':
0021         return BarCodeUtil::barSequence("001100001");
0022     case '3':
0023         return BarCodeUtil::barSequence("101100000");
0024     case '4':
0025         return BarCodeUtil::barSequence("000110001");
0026     case '5':
0027         return BarCodeUtil::barSequence("100110000");
0028     case '6':
0029         return BarCodeUtil::barSequence("001110000");
0030     case '7':
0031         return BarCodeUtil::barSequence("000100101");
0032     case '8':
0033         return BarCodeUtil::barSequence("100100100");
0034     case '9':
0035         return BarCodeUtil::barSequence("001100100");
0036     case 'A':
0037         return BarCodeUtil::barSequence("100001001");
0038     case 'B':
0039         return BarCodeUtil::barSequence("001001001");
0040     case 'C':
0041         return BarCodeUtil::barSequence("101001000");
0042     case 'D':
0043         return BarCodeUtil::barSequence("000011001");
0044     case 'E':
0045         return BarCodeUtil::barSequence("100011000");
0046     case 'F':
0047         return BarCodeUtil::barSequence("001011000");
0048     case 'G':
0049         return BarCodeUtil::barSequence("000001101");
0050     case 'H':
0051         return BarCodeUtil::barSequence("100001100");
0052     case 'I':
0053         return BarCodeUtil::barSequence("001001100");
0054     case 'J':
0055         return BarCodeUtil::barSequence("000011100");
0056     case 'K':
0057         return BarCodeUtil::barSequence("100000011");
0058     case 'L':
0059         return BarCodeUtil::barSequence("001000011");
0060     case 'M':
0061         return BarCodeUtil::barSequence("101000010");
0062     case 'N':
0063         return BarCodeUtil::barSequence("000010011");
0064     case 'O':
0065         return BarCodeUtil::barSequence("100010010");
0066     case 'P':
0067         return BarCodeUtil::barSequence("001010010");
0068     case 'Q':
0069         return BarCodeUtil::barSequence("000000111");
0070     case 'R':
0071         return BarCodeUtil::barSequence("100000110");
0072     case 'S':
0073         return BarCodeUtil::barSequence("001000110");
0074     case 'T':
0075         return BarCodeUtil::barSequence("000010110");
0076     case 'U':
0077         return BarCodeUtil::barSequence("110000001");
0078     case 'V':
0079         return BarCodeUtil::barSequence("011000001");
0080     case 'W':
0081         return BarCodeUtil::barSequence("111000000");
0082     case 'X':
0083         return BarCodeUtil::barSequence("010010001");
0084     case 'Y':
0085         return BarCodeUtil::barSequence("110010000");
0086     case 'Z':
0087         return BarCodeUtil::barSequence("011010000");
0088     case '-':
0089         return BarCodeUtil::barSequence("010000101");
0090     case '.':
0091         return BarCodeUtil::barSequence("110000100");
0092     case ' ':
0093         return BarCodeUtil::barSequence("011000100");
0094     case '$':
0095         return BarCodeUtil::barSequence("010101000");
0096     case '/':
0097         return BarCodeUtil::barSequence("010100010");
0098     case '+':
0099         return BarCodeUtil::barSequence("010001010");
0100     case '%':
0101         return BarCodeUtil::barSequence("000101010");
0102     default:
0103         return QList<bool>(); // unknown character
0104     }
0105 }
0106 
0107 Code39Barcode::Code39Barcode()
0108     : AbstractBarcode(AbstractBarcode::OneDimension)
0109 {
0110 }
0111 Code39Barcode::~Code39Barcode() = default;
0112 
0113 QImage Code39Barcode::paintImage(const QSizeF &size)
0114 {
0115     Q_UNUSED(size);
0116     QList<bool> barcode;
0117     // convert text into sequences of wide/narrow bars
0118     {
0119         // the guard sequence that goes on each end
0120         const QList<bool> endSequence = BarCodeUtil::barSequence("010010100");
0121         barcode += endSequence;
0122         barcode += false;
0123         // translate the string
0124         const QString str = data().isEmpty() ? QString::fromLatin1(byteArrayData().constData(), byteArrayData().size()) : data();
0125         for (int i = 0; i < str.size(); i++) {
0126             QList<bool> b = sequenceForChar(str.at(i).unicode());
0127             if (!b.empty()) {
0128                 barcode += b;
0129                 barcode += false; // add a narrow space between each character
0130             }
0131         }
0132         // ending guard
0133         barcode += endSequence;
0134     }
0135 
0136     /*
0137      calculate integer bar widths that fit inside `size'
0138      each character has 6 narrow bars and 3 wide bars and there is a narrow bar between characters
0139      restrictions:
0140        *) smallWidth * 2 <= largeWidth <= smallWidth * 3
0141           - in other words, the ratio largeWidth:smallWidth is between 3:1 and 2:1
0142        *) wide * largeWidth + narrow * smallWidth <= size.width()
0143           - the barcode has to fit within the given size
0144      */
0145     const int wide = barcode.count(true);
0146     const int narrow = barcode.count(false);
0147     // wide bar width
0148     const int largeWidth = 2;
0149     // narrow bar width
0150     const int smallWidth = 1;
0151     Q_ASSERT(largeWidth > smallWidth);
0152 
0153     const int quietZoneWidth = 10 * smallWidth;
0154 
0155     // one line of the result image
0156     QVector<QRgb> line;
0157     line.reserve(wide * largeWidth + narrow * smallWidth + 2 * quietZoneWidth);
0158     line.insert(0, quietZoneWidth, backgroundColor().rgba());
0159     for (int i = 0; i < barcode.size(); i++) {
0160         const QRgb color = (((i & 1) == 0) ? foregroundColor() : backgroundColor()).rgba(); // alternate between foreground and background color
0161         const int width = barcode.at(i) ? largeWidth : smallWidth;
0162         for (int j = 0; j < width; j++) {
0163             line.append(color);
0164         }
0165     }
0166     line.insert(line.size(), quietZoneWidth, backgroundColor().rgba());
0167 
0168     // build the complete barcode
0169     QImage ret(line.size(), 1, QImage::Format_ARGB32);
0170     memcpy(ret.scanLine(0), line.data(), line.size() * sizeof(QRgb));
0171     return ret;
0172 }