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