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