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 3of9 barcode renderer.
0021  * All this code assumes a 100dpi rendering surface for it's calculations.
0022  */
0023 
0024 #include <QString>
0025 #include <QPainter>
0026 #include <QPen>
0027 #include <QBrush>
0028 
0029 #include "kreportplugin_debug.h"
0030 
0031 struct code3of9 {
0032     char code;
0033     int values[9];
0034 };
0035 
0036 const struct code3of9 _3of9codes[] = {
0037     { '0', { 0, 0, 0, 1, 1, 0, 1, 0, 0 } },
0038     { '1', { 1, 0, 0, 1, 0, 0, 0, 0, 1 } },
0039     { '2', { 0, 0, 1, 1, 0, 0, 0, 0, 1 } },
0040     { '3', { 1, 0, 1, 1, 0, 0, 0, 0, 0 } },
0041     { '4', { 0, 0, 0, 1, 1, 0, 0, 0, 1 } },
0042     { '5', { 1, 0, 0, 1, 1, 0, 0, 0, 0 } },
0043     { '6', { 0, 0, 1, 1, 1, 0, 0, 0, 0 } },
0044     { '7', { 0, 0, 0, 1, 0, 0, 1, 0, 1 } },
0045     { '8', { 1, 0, 0, 1, 0, 0, 1, 0, 0 } },
0046     { '9', { 0, 0, 1, 1, 0, 0, 1, 0, 0 } },
0047 
0048     { 'A', { 1, 0, 0, 0, 0, 1, 0, 0, 1 } },
0049     { 'B', { 0, 0, 1, 0, 0, 1, 0, 0, 1 } },
0050     { 'C', { 1, 0, 1, 0, 0, 1, 0, 0, 0 } },
0051     { 'D', { 0, 0, 0, 0, 1, 1, 0, 0, 1 } },
0052     { 'E', { 1, 0, 0, 0, 1, 1, 0, 0, 0 } },
0053     { 'F', { 0, 0, 1, 0, 1, 1, 0, 0, 0 } },
0054     { 'G', { 0, 0, 0, 0, 0, 1, 1, 0, 1 } },
0055     { 'H', { 1, 0, 0, 0, 0, 1, 1, 0, 0 } },
0056     { 'I', { 0, 0, 1, 0, 0, 1, 1, 0, 0 } },
0057     { 'J', { 0, 0, 0, 0, 1, 1, 1, 0, 0 } },
0058     { 'K', { 1, 0, 0, 0, 0, 0, 0, 1, 1 } },
0059     { 'L', { 0, 0, 1, 0, 0, 0, 0, 1, 1 } },
0060     { 'M', { 1, 0, 1, 0, 0, 0, 0, 1, 0 } },
0061     { 'N', { 0, 0, 0, 0, 1, 0, 0, 1, 1 } },
0062     { 'O', { 1, 0, 0, 0, 1, 0, 0, 1, 0 } },
0063     { 'P', { 0, 0, 1, 0, 1, 0, 0, 1, 0 } },
0064     { 'Q', { 0, 0, 0, 0, 0, 0, 1, 1, 1 } },
0065     { 'R', { 1, 0, 0, 0, 0, 0, 1, 1, 0 } },
0066     { 'S', { 0, 0, 1, 0, 0, 0, 1, 1, 0 } },
0067     { 'T', { 0, 0, 0, 0, 1, 0, 1, 1, 0 } },
0068     { 'U', { 1, 1, 0, 0, 0, 0, 0, 0, 1 } },
0069     { 'V', { 0, 1, 1, 0, 0, 0, 0, 0, 1 } },
0070     { 'W', { 1, 1, 1, 0, 0, 0, 0, 0, 0 } },
0071     { 'X', { 0, 1, 0, 0, 1, 0, 0, 0, 1 } },
0072     { 'Y', { 1, 1, 0, 0, 1, 0, 0, 0, 0 } },
0073     { 'Z', { 0, 1, 1, 0, 1, 0, 0, 0, 0 } },
0074 
0075     { '-', { 0, 1, 0, 0, 0, 0, 1, 0, 1 } },
0076     { '.', { 1, 1, 0, 0, 0, 0, 1, 0, 0 } },
0077     { ' ', { 0, 1, 1, 0, 0, 0, 1, 0, 0 } },
0078     { '$', { 0, 1, 0, 1, 0, 1, 0, 0, 0 } },
0079     { '/', { 0, 1, 0, 1, 0, 0, 0, 1, 0 } },
0080     { '+', { 0, 1, 0, 0, 0, 1, 0, 1, 0 } },
0081     { '%', { 0, 0, 0, 1, 0, 1, 0, 1, 0 } },
0082 
0083     { '*', { 0, 1, 0, 0, 1, 0, 1, 0, 0 } }, // this is a special start/stop character
0084 
0085     { '\0', { 0, 0, 0, 0, 0, 0, 0, 0, 0 } } // null termininator of list
0086 };
0087 
0088 int codeIndexP(QChar code)
0089 {
0090     // we are a case insensitive search
0091     const char latin1Code = code.toUpper().toLatin1();
0092     for (int idx = 0; _3of9codes[idx].code != '\0'; idx++) {
0093         if (_3of9codes[idx].code == latin1Code) return idx;
0094     }
0095     return -1;  // couldn't find it
0096 }
0097 
0098 void render3of9(const QRect & r, const QString & _str, Qt::Alignment align, QPainter * pPainter)
0099 {
0100     QString str = _str;
0101     // lets determine some core attributes about this barcode
0102     int narrow_bar = 1; // a narrow bar is 1px wide
0103     int interchange_gap = narrow_bar; // the space between each 'set' of bars
0104     int bar_width_mult = 2; // the wide bar width multiple of the narrow bar
0105 
0106     // this is are mandatory minimum quiet zone
0107     int quiet_zone = narrow_bar * 10;
0108     //if (quiet_zone < 10) quiet_zone = 10;
0109 
0110     // what kind of area do we have to work with
0111     int draw_width = r.width();
0112     int draw_height = r.height();
0113 
0114     // how long is the value we need to encode?
0115     int val_length = str.length();
0116 
0117     // L = (C + 2)(3N + 6)X + (C + 1)I
0118     // L length of barcode (excluding quite zone) in units same as X and I
0119     // C the number of characters in the value excluding the start/stop
0120     // N the bar width multiple for wide bars
0121     // X the width of a bar (pixels in our case)
0122     // I the interchange gap in the same units as X (value is same as X for our case)
0123     int L;
0124 
0125     int C = val_length;
0126     int N = bar_width_mult;
0127     int X = narrow_bar;
0128     int I = interchange_gap;
0129 
0130     L = ((C + 2) * (3 * N + 6) * X) + ((C + 1) * I);
0131 
0132     // now we have the actual width the barcode will be so can determine the actual
0133     // size of the quiet zone (we assume we center the barcode in the given area
0134     // what should we do if the area is too small????
0135     // At the moment the way the code is written is we will always start at the minimum
0136     // required quiet zone if we don't have enough space.... I guess we'll just have over-run
0137     // to the right
0138     //
0139     // calculate the starting position based on the alignment option
0140     // for left align we don't need to do anything as the values are already setup for it
0141     if (align == Qt::AlignHCenter) {
0142         int nqz = (draw_width - L) / 2;
0143         if (nqz > quiet_zone) quiet_zone = nqz;
0144     } else if (align == Qt::AlignRight) {
0145         quiet_zone = draw_width - (L + quiet_zone);
0146     }
0147     // left : do nothing
0148 
0149     int pos = r.left() + quiet_zone;
0150     int top = r.top();
0151 
0152     // ok we need to prepend and append the str with a *
0153     str = QLatin1Char('*') + str + QLatin1Char('*');
0154 
0155     if (pPainter) {
0156         pPainter->save();
0157 
0158         QPen oneWide(pPainter->pen());
0159         oneWide.setWidth(1);
0160 #ifndef Q_OS_WIN32
0161         oneWide.setJoinStyle(Qt::MiterJoin);
0162 #endif
0163         pPainter->setPen(oneWide);
0164         pPainter->setBrush(pPainter->pen().color());
0165     }
0166     for (int i = 0; i < str.length(); i++) {
0167         // loop through each char and render the barcode
0168         QChar c = str.at(i);
0169         int idx = codeIndexP(c);
0170         if (idx == -1) {
0171             kreportpluginWarning() << "Encountered a non-compliant character while rendering a 3of9 barcode -- skipping";
0172             continue;
0173         }
0174 
0175         bool space = false;
0176         for (int b = 0; b < 9; b++, space = !space) {
0177             int w = (_3of9codes[idx].values[b] == 1 ? narrow_bar * bar_width_mult : narrow_bar);
0178             if (!space && pPainter) {
0179                 pPainter->fillRect(pos, top, w, draw_height, pPainter->pen().color());
0180             }
0181             pos += w;
0182         }
0183         pos += interchange_gap;
0184     }
0185     if (pPainter) {
0186         pPainter->restore();
0187     }
0188 }