File indexing completed on 2024-05-12 04:43:18

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2001-2007 by OpenMFG, LLC (info@openmfg.com)
0003  * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk)
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Lesser General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2.1 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013  * Lesser General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Lesser General Public
0016  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0017  */
0018 
0019 /*
0020  *     This file contains the implementation of the Code 128 barcode renderer.
0021  * All this code assumes a 100dpi rendering surface for it's calculations.
0022  */
0023 
0024 #include <QString>
0025 #include <QVector>
0026 #include <QRect>
0027 #include <QPainter>
0028 #include <QPen>
0029 #include <QBrush>
0030 
0031 #include "KReportRenderObjects.h"
0032 #include "kreportplugin_debug.h"
0033 
0034 static const int SETA = 0;
0035 static const int SETB = 1;
0036 static const int SETC = 2;
0037 
0038 static const char FNC1   = (char)130;
0039 static const char FNC2   = (char)131;
0040 static const char FNC3   = (char)132;
0041 static const char FNC4   = (char)133;
0042 static const char SHIFT  = (char)134;
0043 static const char CODEA  = (char)135;
0044 static const char CODEB  = (char)136;
0045 static const char CODEC  = (char)137;
0046 static const char STARTA = (char)138;
0047 static const char STARTB = (char)139;
0048 static const char STARTC = (char)140;
0049 
0050 
0051 struct code128 {
0052     char codea;
0053     char codeb;
0054     char codec;
0055 
0056     int values[6];
0057 
0058     bool _null;
0059 };
0060 
0061 static const struct code128 _128codes[] = {
0062     // A ,     B ,     C ,    { B  S  B  S  B  S }, NULL? },
0063     { ' ',    ' ',      0,    { 2, 1, 2, 2, 2, 2 }, false },
0064     { '!',    '!',      1,    { 2, 2, 2, 1, 2, 2 }, false },
0065     { '"',    '"',      2,    { 2, 2, 2, 2, 2, 1 }, false },
0066     { '#',    '#',      3,    { 1, 2, 1, 2, 2, 3 }, false },
0067     { '$',    '$',      4,    { 1, 2, 1, 3, 2, 2 }, false },
0068     { '%',    '%',      5,    { 1, 3, 1, 2, 2, 2 }, false },
0069     { '&',    '&',      6,    { 1, 2, 2, 2, 1, 3 }, false },
0070     { '\'',   '\'',     7,    { 1, 2, 2, 3, 1, 2 }, false },
0071     { '(',    '(',      8,    { 1, 3, 2, 2, 1, 2 }, false },
0072     { ')',    ')',      9,    { 2, 2, 1, 2, 1, 3 }, false },
0073     { '*',    '*',     10,    { 2, 2, 1, 3, 1, 2 }, false },
0074     { '+',    '+',     11,    { 2, 3, 1, 2, 1, 2 }, false },
0075     { ',',    ',',     12,    { 1, 1, 2, 2, 3, 2 }, false },
0076     { '-',    '-',     13,    { 1, 2, 2, 1, 3, 2 }, false },
0077     { '.',    '.',     14,    { 1, 2, 2, 2, 3, 1 }, false },
0078     { '/',    '/',     15,    { 1, 1, 3, 2, 2, 2 }, false },
0079     { '0',    '0',     16,    { 1, 2, 3, 1, 2, 2 }, false },
0080     { '1',    '1',     17,    { 1, 2, 3, 2, 2, 1 }, false },
0081     { '2',    '2',     18,    { 2, 2, 3, 2, 1, 1 }, false },
0082     { '3',    '3',     19,    { 2, 2, 1, 1, 3, 2 }, false },
0083     { '4',    '4',     20,    { 2, 2, 1, 2, 3, 1 }, false },
0084     { '5',    '5',     21,    { 2, 1, 3, 2, 1, 2 }, false },
0085     { '6',    '6',     22,    { 2, 2, 3, 1, 1, 2 }, false },
0086     { '7',    '7',     23,    { 3, 1, 2, 1, 3, 1 }, false },
0087     { '8',    '8',     24,    { 3, 1, 1, 2, 2, 2 }, false },
0088     { '9',    '9',     25,    { 3, 2, 1, 1, 2, 2 }, false },
0089     { ':',    ':',     26,    { 3, 2, 1, 2, 2, 1 }, false },
0090     { ';',    ';',     27,    { 3, 1, 2, 2, 1, 2 }, false },
0091     { '<',    '<',     28,    { 3, 2, 2, 1, 1, 2 }, false },
0092     { '=',    '=',     29,    { 3, 2, 2, 2, 1, 1 }, false },
0093     { '>',    '>',     30,    { 2, 1, 2, 1, 2, 3 }, false },
0094     { '?',    '?',     31,    { 2, 1, 2, 3, 2, 1 }, false },
0095     { '@',    '@',     32,    { 2, 3, 2, 1, 2, 1 }, false },
0096     { 'A',    'A',     33,    { 1, 1, 1, 3, 2, 3 }, false },
0097     { 'B',    'B',     34,    { 1, 3, 1, 1, 2, 3 }, false },
0098     { 'C',    'C',     35,    { 1, 3, 1, 3, 2, 1 }, false },
0099     { 'D',    'D',     36,    { 1, 1, 2, 3, 1, 3 }, false },
0100     { 'E',    'E',     37,    { 1, 3, 2, 1, 1, 3 }, false },
0101     { 'F',    'F',     38,    { 1, 3, 2, 3, 1, 1 }, false },
0102     { 'G',    'G',     39,    { 2, 1, 1, 3, 1, 3 }, false },
0103     { 'H',    'H',     40,    { 2, 3, 1, 1, 1, 3 }, false },
0104     { 'I',    'I',     41,    { 2, 3, 1, 3, 1, 1 }, false },
0105     { 'J',    'J',     42,    { 1, 1, 2, 1, 3, 3 }, false },
0106     { 'K',    'K',     43,    { 1, 1, 2, 3, 3, 1 }, false },
0107     { 'L',    'L',     44,    { 1, 3, 2, 1, 3, 1 }, false },
0108     { 'M',    'M',     45,    { 1, 1, 3, 1, 2, 3 }, false },
0109     { 'N',    'N',     46,    { 1, 1, 3, 3, 2, 1 }, false },
0110     { 'O',    'O',     47,    { 1, 3, 3, 1, 2, 1 }, false },
0111     { 'P',    'P',     48,    { 3, 1, 3, 1, 2, 1 }, false },
0112     { 'Q',    'Q',     49,    { 2, 1, 1, 3, 3, 1 }, false },
0113     { 'R',    'R',     50,    { 2, 3, 1, 1, 3, 1 }, false },
0114     { 'S',    'S',     51,    { 2, 1, 3, 1, 1, 3 }, false },
0115     { 'T',    'T',     52,    { 2, 1, 3, 3, 1, 1 }, false },
0116     { 'U',    'U',     53,    { 2, 1, 3, 1, 3, 1 }, false },
0117     { 'V',    'V',     54,    { 3, 1, 1, 1, 2, 3 }, false },
0118     { 'W',    'W',     55,    { 3, 1, 1, 3, 2, 1 }, false },
0119     { 'X',    'X',     56,    { 3, 3, 1, 1, 2, 1 }, false },
0120     { 'Y',    'Y',     57,    { 3, 1, 2, 1, 1, 3 }, false },
0121     { 'Z',    'Z',     58,    { 3, 1, 2, 3, 1, 1 }, false },
0122     { '[',    '[',     59,    { 3, 3, 2, 1, 1, 1 }, false },
0123     { '\\',   '\\',    60,    { 3, 1, 4, 1, 1, 1 }, false },
0124     { ']',    ']',     61,    { 2, 2, 1, 4, 1, 1 }, false },
0125     { '^',    '^',     62,    { 4, 3, 1, 1, 1, 1 }, false },
0126     { '_',    '_',     63,    { 1, 1, 1, 2, 2, 4 }, false },
0127     { 0x00,   '`',     64,    { 1, 1, 1, 4, 2, 2 }, false }, // NUL
0128     { 0x01,   'a',     65,    { 1, 2, 1, 1, 2, 4 }, false }, // SOH
0129     { 0x02,   'b',     66,    { 1, 2, 1, 4, 2, 1 }, false }, // STX
0130     { 0x03,   'c',     67,    { 1, 4, 1, 1, 2, 2 }, false }, // ETX
0131     { 0x04,   'd',     68,    { 1, 4, 1, 2, 2, 1 }, false }, // EOT
0132     { 0x05,   'e',     69,    { 1, 1, 2, 2, 1, 4 }, false }, // ENQ
0133     { 0x06,   'f',     70,    { 1, 1, 2, 4, 1, 2 }, false }, // ACK
0134     { 0x07,   'g',     71,    { 1, 2, 2, 1, 1, 4 }, false }, // BEL
0135     { 0x08,   'h',     72,    { 1, 2, 2, 4, 1, 1 }, false }, // BS
0136     { 0x09,   'i',     73,    { 1, 4, 2, 1, 1, 2 }, false }, // HT
0137     { 0x0A,   'j',     74,    { 1, 4, 2, 2, 1, 1 }, false }, // LF
0138     { 0x0B,   'k',     75,    { 2, 4, 1, 2, 1, 1 }, false }, // VT
0139     { 0x0C,   'l',     76,    { 2, 2, 1, 1, 1, 4 }, false }, // FF
0140     { 0x0D,   'm',     77,    { 4, 1, 3, 1, 1, 1 }, false }, // CR
0141     { 0x0E,   'n',     78,    { 2, 4, 1, 1, 1, 2 }, false }, // SO
0142     { 0x0F,   'o',     79,    { 1, 3, 4, 1, 1, 1 }, false }, // SI
0143     { 0x10,   'p',     80,    { 1, 1, 1, 2, 4, 2 }, false }, // DLE
0144     { 0x11,   'q',     81,    { 1, 2, 1, 1, 4, 2 }, false }, // DC1
0145     { 0x12,   'r',     82,    { 1, 2, 1, 2, 4, 1 }, false }, // DC2
0146     { 0x13,   's',     83,    { 1, 1, 4, 2, 1, 2 }, false }, // DC3
0147     { 0x14,   't',     84,    { 1, 2, 4, 1, 1, 2 }, false }, // DC4
0148     { 0x15,   'u',     85,    { 1, 2, 4, 2, 1, 1 }, false }, // NAK
0149     { 0x16,   'v',     86,    { 4, 1, 1, 2, 1, 2 }, false }, // SYN
0150     { 0x17,   'w',     87,    { 4, 2, 1, 1, 1, 2 }, false }, // ETB
0151     { 0x18,   'x',     88,    { 4, 2, 1, 2, 1, 1 }, false }, // CAN
0152     { 0x19,   'y',     89,    { 2, 1, 2, 1, 4, 1 }, false }, // EM
0153     { 0x1A,   'z',     90,    { 2, 1, 4, 1, 2, 1 }, false }, // SUB
0154     { 0x1B,   '{',     91,    { 4, 1, 2, 1, 2, 1 }, false }, // ESC
0155     { 0x1C,   '|',     92,    { 1, 1, 1, 1, 4, 3 }, false }, // FS
0156     { 0x1D,   '}',     93,    { 1, 1, 1, 3, 4, 1 }, false }, // GS
0157     { 0x1E,   '~',     94,    { 1, 3, 1, 1, 4, 1 }, false }, // RS
0158     { 0x1F,   0x7F,    95,    { 1, 1, 4, 1, 1, 3 }, false }, // US    DEL
0159     { FNC3,   FNC3,    96,    { 1, 1, 4, 3, 1, 1 }, false }, // FNC3  FNC3
0160     { FNC2,   FNC2,    97,    { 4, 1, 1, 1, 1, 3 }, false }, // FNC2  FNC2
0161     { SHIFT,  SHIFT,   98,    { 4, 1, 1, 3, 1, 1 }, false }, // SHIFT SHIFT
0162     { CODEC,  CODEC,   99,    { 1, 1, 3, 1, 4, 1 }, false }, // CODEC CODEC
0163     { CODEB,  FNC4,   CODEB,  { 1, 1, 4, 1, 3, 1 }, false }, // CODEB FNC4  CODEB
0164     { FNC4,   CODEA,  CODEA,  { 3, 1, 1, 1, 4, 1 }, false }, // FNC4  CODEA CODEA
0165     { FNC1,   FNC1,   FNC1,   { 4, 1, 1, 1, 3, 1 }, false }, // FNC1  FNC1  FNC1
0166     { STARTA, STARTA, STARTA, { 2, 1, 1, 4, 1, 2 }, false }, // STARTA
0167     { STARTB, STARTB, STARTB, { 2, 1, 1, 2, 1, 4 }, false }, // STARTB
0168     { STARTC, STARTC, STARTC, { 2, 1, 1, 2, 3, 2 }, false }, // STARTC
0169 
0170     { '\0', '\0', '\0', { 0, 0, 0, 0, 0, 0 }, true } // null termininator of list
0171 };
0172 
0173 // STOP CHARACTER { 2 3 3 1 1 1 2 }
0174 
0175 int code128IndexP(QChar code, int set)
0176 {
0177     const char latin1Code = code.toLatin1();
0178     for (int idx = 0; _128codes[idx]._null == false; idx++) {
0179         if (set == SETA && _128codes[idx].codea == latin1Code) return idx;
0180         if (set == SETB && _128codes[idx].codeb == latin1Code) return idx;
0181         if (set == SETC && _128codes[idx].codec == latin1Code) return idx;
0182     }
0183     return -1;  // couldn't find it
0184 }
0185 
0186 void renderCode128(const QRect & r, const QString & _str, Qt::Alignment align, QPainter * pPainter)
0187 {
0188     QVector<int> str;
0189 
0190     // create the list.. if the list is empty then just set a start code and move on
0191     if (_str.isEmpty()) {
0192         str.push_back(104);
0193     } else {
0194         int rank_a = 0;
0195         int rank_b = 0;
0196         int rank_c = 0;
0197 
0198         QChar c;
0199         for (int i = 0; i < _str.length(); ++i) {
0200             c = _str.at(i);
0201             rank_a += (code128IndexP(c, SETA) != -1 ? 1 : 0);
0202             rank_b += (code128IndexP(c, SETB) != -1 ? 1 : 0);
0203             rank_c += (c >= QLatin1Char('0') && c <= QLatin1Char('9') ? 1 : 0);
0204         }
0205         if (rank_c == _str.length() && ((rank_c % 2) == 0 || rank_c > 4)) {
0206             // every value in the is a digit so we are going to go with mode C
0207             // and we have an even number or we have more than 4 values
0208             int i;
0209             if ((rank_c % 2) == 1) {
0210                 str.push_back(104); // START B
0211                 c = _str.at(0);
0212                 str.push_back(code128IndexP(c, SETB));
0213                 str.push_back(99); // MODE C
0214                 i = 1;
0215             } else {
0216                 str.push_back(105); // START C
0217                 i = 0;
0218             }
0219             for (; i < _str.length(); i += 2) {
0220                 char a, b;
0221                 c = _str.at(i);
0222                 a = c.toLatin1();
0223                 a -= 48;
0224                 c = _str.at(i + 1);
0225                 b = c.toLatin1();
0226                 b -= 48;
0227                 str.push_back(int((a * 10) + b));
0228             }
0229         } else {
0230             // start in the mode that had the higher number of hits and then
0231             // just shift into the opposite mode as needed
0232             int set = (rank_a > rank_b ? SETA : SETB);
0233             str.push_back((rank_a > rank_b ? 103 : 104));
0234             for (int i = 0; i < _str.length(); ++i) {
0235                 c = _str.at(i);
0236                 int v = code128IndexP(c, set);
0237                 if (v == -1) {
0238                     v = code128IndexP(c, (set == SETA ? SETB : SETA));
0239                     if (v != -1) {
0240                         str.push_back(98); // SHIFT
0241                         str.push_back(v);
0242                     }
0243                 } else {
0244                     str.push_back(v);
0245                 }
0246             }
0247         }
0248     }
0249 
0250     // calculate and append the checksum value to the list
0251     int checksum = str.at(0);
0252     for (int i = 1; i < str.size(); ++i) {
0253         checksum += (str.at(i) * i);
0254     }
0255     checksum = checksum % 103;
0256     str.push_back(checksum);
0257 
0258     // lets determine some core attributes about this barcode
0259     int bar_width = 1; // the width of the base unit bar
0260 
0261     // this is are mandatory minimum quiet zone
0262     int quiet_zone = bar_width * 10;
0263     //if (quiet_zone < 10) quiet_zone = 10;
0264 
0265     // what kind of area do we have to work with
0266     int draw_width = r.width();
0267     int draw_height = r.height();
0268 
0269     // how long is the value we need to encode?
0270     int val_length = str.size() - 2; // we include start and checksum in are list so
0271     // subtract them out for our calculations
0272 
0273     // L = (11C + 35)X
0274     // L length of barcode (excluding quite zone) in units same as X and I
0275     // C the number of characters in the value excluding the start/stop and checksum characters
0276     // X the width of a bar (pixels in our case)
0277     int L;
0278 
0279     int C = val_length;
0280     int X = bar_width;
0281 
0282     L = (((11 * C) + 35) * X);
0283 
0284     // now we have the actual width the barcode will be so can determine the actual
0285     // size of the quiet zone (we assume we center the barcode in the given area
0286     // what should we do if the area is too small????
0287     // At the moment the way the code is written is we will always start at the minimum
0288     // required quiet zone if we don't have enough space.... I guess we'll just have over-run
0289     // to the right
0290     //
0291     // calculate the starting position based on the alignment option
0292     // for left align we don't need to do anything as the values are already setup for it
0293     if (align == Qt::AlignHCenter) {
0294         int nqz = (draw_width - L) / 2;
0295         if (nqz > quiet_zone) quiet_zone = nqz;
0296     } else if (align == Qt::AlignRight) {
0297         quiet_zone = draw_width - (L + quiet_zone);
0298     }
0299     // left : do nothing
0300 
0301     int pos = r.left() + quiet_zone;
0302     int top = r.top();
0303 
0304     if (pPainter) {
0305         pPainter->save();
0306 
0307         QPen oneWide(pPainter->pen());
0308         oneWide.setWidth(1);
0309 #ifndef Q_OS_WIN32
0310         oneWide.setJoinStyle(Qt::MiterJoin);
0311 #endif
0312         pPainter->setPen(oneWide);
0313         pPainter->setBrush(pPainter->pen().color());
0314     }
0315 
0316 
0317     bool space = false;
0318     for (int i = 0; i < str.size(); ++i) {
0319         // loop through each value and render the barcode
0320         int idx = str.at(i);
0321         if (idx < 0 || idx > 105) {
0322             kreportpluginWarning() << "Encountered a non-compliant element while rendering a 3of9 barcode -- skipping";
0323             continue;
0324         }
0325         space = false;
0326         for (int b = 0; b < 6; b++, space = !space) {
0327             int w = _128codes[idx].values[b] * bar_width;
0328             if (!space && pPainter) {
0329                 pPainter->fillRect(pos, top, w, draw_height, pPainter->pen().color());
0330             }
0331             pos += w;
0332         }
0333     }
0334 
0335     // we have to do the stop character separately like this because it has
0336     // 7 elements in it's bar sequence rather than 6 like the others
0337     int STOP_CHARACTER[] = { 2, 3, 3, 1, 1, 1, 2 };
0338     space = false;
0339     for (int b = 0; b < 7; b++, space = !space) {
0340         int w = STOP_CHARACTER[b] * bar_width;
0341         if (!space && pPainter) {
0342             pPainter->fillRect(pos, top, w, draw_height, pPainter->pen().color());
0343         }
0344         pos += w;
0345     }
0346 
0347     if (pPainter) {
0348         pPainter->restore();
0349     }
0350 }