KReport

code128paint.cpp
1/* This file is part of the KDE project
2 * Copyright (C) 2001-2007 by OpenMFG, LLC (info@openmfg.com)
3 * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/*
20 * This file contains the implementation of the Code 128 barcode renderer.
21 * All this code assumes a 100dpi rendering surface for it's calculations.
22 */
23
24#include <QString>
25#include <QVector>
26#include <QRect>
27#include <QPainter>
28#include <QPen>
29#include <QBrush>
30
31#include "KReportRenderObjects.h"
32#include "kreportplugin_debug.h"
33
34static const int SETA = 0;
35static const int SETB = 1;
36static const int SETC = 2;
37
38static const char FNC1 = (char)130;
39static const char FNC2 = (char)131;
40static const char FNC3 = (char)132;
41static const char FNC4 = (char)133;
42static const char SHIFT = (char)134;
43static const char CODEA = (char)135;
44static const char CODEB = (char)136;
45static const char CODEC = (char)137;
46static const char STARTA = (char)138;
47static const char STARTB = (char)139;
48static const char STARTC = (char)140;
49
50
51struct code128 {
52 char codea;
53 char codeb;
54 char codec;
55
56 int values[6];
57
58 bool _null;
59};
60
61static const struct code128 _128codes[] = {
62 // A , B , C , { B S B S B S }, NULL? },
63 { ' ', ' ', 0, { 2, 1, 2, 2, 2, 2 }, false },
64 { '!', '!', 1, { 2, 2, 2, 1, 2, 2 }, false },
65 { '"', '"', 2, { 2, 2, 2, 2, 2, 1 }, false },
66 { '#', '#', 3, { 1, 2, 1, 2, 2, 3 }, false },
67 { '$', '$', 4, { 1, 2, 1, 3, 2, 2 }, false },
68 { '%', '%', 5, { 1, 3, 1, 2, 2, 2 }, false },
69 { '&', '&', 6, { 1, 2, 2, 2, 1, 3 }, false },
70 { '\'', '\'', 7, { 1, 2, 2, 3, 1, 2 }, false },
71 { '(', '(', 8, { 1, 3, 2, 2, 1, 2 }, false },
72 { ')', ')', 9, { 2, 2, 1, 2, 1, 3 }, false },
73 { '*', '*', 10, { 2, 2, 1, 3, 1, 2 }, false },
74 { '+', '+', 11, { 2, 3, 1, 2, 1, 2 }, false },
75 { ',', ',', 12, { 1, 1, 2, 2, 3, 2 }, false },
76 { '-', '-', 13, { 1, 2, 2, 1, 3, 2 }, false },
77 { '.', '.', 14, { 1, 2, 2, 2, 3, 1 }, false },
78 { '/', '/', 15, { 1, 1, 3, 2, 2, 2 }, false },
79 { '0', '0', 16, { 1, 2, 3, 1, 2, 2 }, false },
80 { '1', '1', 17, { 1, 2, 3, 2, 2, 1 }, false },
81 { '2', '2', 18, { 2, 2, 3, 2, 1, 1 }, false },
82 { '3', '3', 19, { 2, 2, 1, 1, 3, 2 }, false },
83 { '4', '4', 20, { 2, 2, 1, 2, 3, 1 }, false },
84 { '5', '5', 21, { 2, 1, 3, 2, 1, 2 }, false },
85 { '6', '6', 22, { 2, 2, 3, 1, 1, 2 }, false },
86 { '7', '7', 23, { 3, 1, 2, 1, 3, 1 }, false },
87 { '8', '8', 24, { 3, 1, 1, 2, 2, 2 }, false },
88 { '9', '9', 25, { 3, 2, 1, 1, 2, 2 }, false },
89 { ':', ':', 26, { 3, 2, 1, 2, 2, 1 }, false },
90 { ';', ';', 27, { 3, 1, 2, 2, 1, 2 }, false },
91 { '<', '<', 28, { 3, 2, 2, 1, 1, 2 }, false },
92 { '=', '=', 29, { 3, 2, 2, 2, 1, 1 }, false },
93 { '>', '>', 30, { 2, 1, 2, 1, 2, 3 }, false },
94 { '?', '?', 31, { 2, 1, 2, 3, 2, 1 }, false },
95 { '@', '@', 32, { 2, 3, 2, 1, 2, 1 }, false },
96 { 'A', 'A', 33, { 1, 1, 1, 3, 2, 3 }, false },
97 { 'B', 'B', 34, { 1, 3, 1, 1, 2, 3 }, false },
98 { 'C', 'C', 35, { 1, 3, 1, 3, 2, 1 }, false },
99 { 'D', 'D', 36, { 1, 1, 2, 3, 1, 3 }, false },
100 { 'E', 'E', 37, { 1, 3, 2, 1, 1, 3 }, false },
101 { 'F', 'F', 38, { 1, 3, 2, 3, 1, 1 }, false },
102 { 'G', 'G', 39, { 2, 1, 1, 3, 1, 3 }, false },
103 { 'H', 'H', 40, { 2, 3, 1, 1, 1, 3 }, false },
104 { 'I', 'I', 41, { 2, 3, 1, 3, 1, 1 }, false },
105 { 'J', 'J', 42, { 1, 1, 2, 1, 3, 3 }, false },
106 { 'K', 'K', 43, { 1, 1, 2, 3, 3, 1 }, false },
107 { 'L', 'L', 44, { 1, 3, 2, 1, 3, 1 }, false },
108 { 'M', 'M', 45, { 1, 1, 3, 1, 2, 3 }, false },
109 { 'N', 'N', 46, { 1, 1, 3, 3, 2, 1 }, false },
110 { 'O', 'O', 47, { 1, 3, 3, 1, 2, 1 }, false },
111 { 'P', 'P', 48, { 3, 1, 3, 1, 2, 1 }, false },
112 { 'Q', 'Q', 49, { 2, 1, 1, 3, 3, 1 }, false },
113 { 'R', 'R', 50, { 2, 3, 1, 1, 3, 1 }, false },
114 { 'S', 'S', 51, { 2, 1, 3, 1, 1, 3 }, false },
115 { 'T', 'T', 52, { 2, 1, 3, 3, 1, 1 }, false },
116 { 'U', 'U', 53, { 2, 1, 3, 1, 3, 1 }, false },
117 { 'V', 'V', 54, { 3, 1, 1, 1, 2, 3 }, false },
118 { 'W', 'W', 55, { 3, 1, 1, 3, 2, 1 }, false },
119 { 'X', 'X', 56, { 3, 3, 1, 1, 2, 1 }, false },
120 { 'Y', 'Y', 57, { 3, 1, 2, 1, 1, 3 }, false },
121 { 'Z', 'Z', 58, { 3, 1, 2, 3, 1, 1 }, false },
122 { '[', '[', 59, { 3, 3, 2, 1, 1, 1 }, false },
123 { '\\', '\\', 60, { 3, 1, 4, 1, 1, 1 }, false },
124 { ']', ']', 61, { 2, 2, 1, 4, 1, 1 }, false },
125 { '^', '^', 62, { 4, 3, 1, 1, 1, 1 }, false },
126 { '_', '_', 63, { 1, 1, 1, 2, 2, 4 }, false },
127 { 0x00, '`', 64, { 1, 1, 1, 4, 2, 2 }, false }, // NUL
128 { 0x01, 'a', 65, { 1, 2, 1, 1, 2, 4 }, false }, // SOH
129 { 0x02, 'b', 66, { 1, 2, 1, 4, 2, 1 }, false }, // STX
130 { 0x03, 'c', 67, { 1, 4, 1, 1, 2, 2 }, false }, // ETX
131 { 0x04, 'd', 68, { 1, 4, 1, 2, 2, 1 }, false }, // EOT
132 { 0x05, 'e', 69, { 1, 1, 2, 2, 1, 4 }, false }, // ENQ
133 { 0x06, 'f', 70, { 1, 1, 2, 4, 1, 2 }, false }, // ACK
134 { 0x07, 'g', 71, { 1, 2, 2, 1, 1, 4 }, false }, // BEL
135 { 0x08, 'h', 72, { 1, 2, 2, 4, 1, 1 }, false }, // BS
136 { 0x09, 'i', 73, { 1, 4, 2, 1, 1, 2 }, false }, // HT
137 { 0x0A, 'j', 74, { 1, 4, 2, 2, 1, 1 }, false }, // LF
138 { 0x0B, 'k', 75, { 2, 4, 1, 2, 1, 1 }, false }, // VT
139 { 0x0C, 'l', 76, { 2, 2, 1, 1, 1, 4 }, false }, // FF
140 { 0x0D, 'm', 77, { 4, 1, 3, 1, 1, 1 }, false }, // CR
141 { 0x0E, 'n', 78, { 2, 4, 1, 1, 1, 2 }, false }, // SO
142 { 0x0F, 'o', 79, { 1, 3, 4, 1, 1, 1 }, false }, // SI
143 { 0x10, 'p', 80, { 1, 1, 1, 2, 4, 2 }, false }, // DLE
144 { 0x11, 'q', 81, { 1, 2, 1, 1, 4, 2 }, false }, // DC1
145 { 0x12, 'r', 82, { 1, 2, 1, 2, 4, 1 }, false }, // DC2
146 { 0x13, 's', 83, { 1, 1, 4, 2, 1, 2 }, false }, // DC3
147 { 0x14, 't', 84, { 1, 2, 4, 1, 1, 2 }, false }, // DC4
148 { 0x15, 'u', 85, { 1, 2, 4, 2, 1, 1 }, false }, // NAK
149 { 0x16, 'v', 86, { 4, 1, 1, 2, 1, 2 }, false }, // SYN
150 { 0x17, 'w', 87, { 4, 2, 1, 1, 1, 2 }, false }, // ETB
151 { 0x18, 'x', 88, { 4, 2, 1, 2, 1, 1 }, false }, // CAN
152 { 0x19, 'y', 89, { 2, 1, 2, 1, 4, 1 }, false }, // EM
153 { 0x1A, 'z', 90, { 2, 1, 4, 1, 2, 1 }, false }, // SUB
154 { 0x1B, '{', 91, { 4, 1, 2, 1, 2, 1 }, false }, // ESC
155 { 0x1C, '|', 92, { 1, 1, 1, 1, 4, 3 }, false }, // FS
156 { 0x1D, '}', 93, { 1, 1, 1, 3, 4, 1 }, false }, // GS
157 { 0x1E, '~', 94, { 1, 3, 1, 1, 4, 1 }, false }, // RS
158 { 0x1F, 0x7F, 95, { 1, 1, 4, 1, 1, 3 }, false }, // US DEL
159 { FNC3, FNC3, 96, { 1, 1, 4, 3, 1, 1 }, false }, // FNC3 FNC3
160 { FNC2, FNC2, 97, { 4, 1, 1, 1, 1, 3 }, false }, // FNC2 FNC2
161 { SHIFT, SHIFT, 98, { 4, 1, 1, 3, 1, 1 }, false }, // SHIFT SHIFT
162 { CODEC, CODEC, 99, { 1, 1, 3, 1, 4, 1 }, false }, // CODEC CODEC
163 { CODEB, FNC4, CODEB, { 1, 1, 4, 1, 3, 1 }, false }, // CODEB FNC4 CODEB
164 { FNC4, CODEA, CODEA, { 3, 1, 1, 1, 4, 1 }, false }, // FNC4 CODEA CODEA
165 { FNC1, FNC1, FNC1, { 4, 1, 1, 1, 3, 1 }, false }, // FNC1 FNC1 FNC1
166 { STARTA, STARTA, STARTA, { 2, 1, 1, 4, 1, 2 }, false }, // STARTA
167 { STARTB, STARTB, STARTB, { 2, 1, 1, 2, 1, 4 }, false }, // STARTB
168 { STARTC, STARTC, STARTC, { 2, 1, 1, 2, 3, 2 }, false }, // STARTC
169
170 { '\0', '\0', '\0', { 0, 0, 0, 0, 0, 0 }, true } // null termininator of list
171};
172
173// STOP CHARACTER { 2 3 3 1 1 1 2 }
174
175int code128IndexP(QChar code, int set)
176{
177 const char latin1Code = code.toLatin1();
178 for (int idx = 0; _128codes[idx]._null == false; idx++) {
179 if (set == SETA && _128codes[idx].codea == latin1Code) return idx;
180 if (set == SETB && _128codes[idx].codeb == latin1Code) return idx;
181 if (set == SETC && _128codes[idx].codec == latin1Code) return idx;
182 }
183 return -1; // couldn't find it
184}
185
186void renderCode128(const QRect & r, const QString & _str, Qt::Alignment align, QPainter * pPainter)
187{
188 QVector<int> str;
189
190 // create the list.. if the list is empty then just set a start code and move on
191 if (_str.isEmpty()) {
192 str.push_back(104);
193 } else {
194 int rank_a = 0;
195 int rank_b = 0;
196 int rank_c = 0;
197
198 QChar c;
199 for (int i = 0; i < _str.length(); ++i) {
200 c = _str.at(i);
201 rank_a += (code128IndexP(c, SETA) != -1 ? 1 : 0);
202 rank_b += (code128IndexP(c, SETB) != -1 ? 1 : 0);
203 rank_c += (c >= QLatin1Char('0') && c <= QLatin1Char('9') ? 1 : 0);
204 }
205 if (rank_c == _str.length() && ((rank_c % 2) == 0 || rank_c > 4)) {
206 // every value in the is a digit so we are going to go with mode C
207 // and we have an even number or we have more than 4 values
208 int i;
209 if ((rank_c % 2) == 1) {
210 str.push_back(104); // START B
211 c = _str.at(0);
212 str.push_back(code128IndexP(c, SETB));
213 str.push_back(99); // MODE C
214 i = 1;
215 } else {
216 str.push_back(105); // START C
217 i = 0;
218 }
219 for (; i < _str.length(); i += 2) {
220 char a, b;
221 c = _str.at(i);
222 a = c.toLatin1();
223 a -= 48;
224 c = _str.at(i + 1);
225 b = c.toLatin1();
226 b -= 48;
227 str.push_back(int((a * 10) + b));
228 }
229 } else {
230 // start in the mode that had the higher number of hits and then
231 // just shift into the opposite mode as needed
232 int set = (rank_a > rank_b ? SETA : SETB);
233 str.push_back((rank_a > rank_b ? 103 : 104));
234 for (int i = 0; i < _str.length(); ++i) {
235 c = _str.at(i);
236 int v = code128IndexP(c, set);
237 if (v == -1) {
238 v = code128IndexP(c, (set == SETA ? SETB : SETA));
239 if (v != -1) {
240 str.push_back(98); // SHIFT
241 str.push_back(v);
242 }
243 } else {
244 str.push_back(v);
245 }
246 }
247 }
248 }
249
250 // calculate and append the checksum value to the list
251 int checksum = str.at(0);
252 for (int i = 1; i < str.size(); ++i) {
253 checksum += (str.at(i) * i);
254 }
255 checksum = checksum % 103;
256 str.push_back(checksum);
257
258 // lets determine some core attributes about this barcode
259 int bar_width = 1; // the width of the base unit bar
260
261 // this is are mandatory minimum quiet zone
262 int quiet_zone = bar_width * 10;
263 //if (quiet_zone < 10) quiet_zone = 10;
264
265 // what kind of area do we have to work with
266 int draw_width = r.width();
267 int draw_height = r.height();
268
269 // how long is the value we need to encode?
270 int val_length = str.size() - 2; // we include start and checksum in are list so
271 // subtract them out for our calculations
272
273 // L = (11C + 35)X
274 // L length of barcode (excluding quite zone) in units same as X and I
275 // C the number of characters in the value excluding the start/stop and checksum characters
276 // X the width of a bar (pixels in our case)
277 int L;
278
279 int C = val_length;
280 int X = bar_width;
281
282 L = (((11 * C) + 35) * X);
283
284 // now we have the actual width the barcode will be so can determine the actual
285 // size of the quiet zone (we assume we center the barcode in the given area
286 // what should we do if the area is too small????
287 // At the moment the way the code is written is we will always start at the minimum
288 // required quiet zone if we don't have enough space.... I guess we'll just have over-run
289 // to the right
290 //
291 // calculate the starting position based on the alignment option
292 // for left align we don't need to do anything as the values are already setup for it
293 if (align == Qt::AlignHCenter) {
294 int nqz = (draw_width - L) / 2;
295 if (nqz > quiet_zone) quiet_zone = nqz;
296 } else if (align == Qt::AlignRight) {
297 quiet_zone = draw_width - (L + quiet_zone);
298 }
299 // left : do nothing
300
301 int pos = r.left() + quiet_zone;
302 int top = r.top();
303
304 if (pPainter) {
305 pPainter->save();
306
307 QPen oneWide(pPainter->pen());
308 oneWide.setWidth(1);
309#ifndef Q_OS_WIN32
310 oneWide.setJoinStyle(Qt::MiterJoin);
311#endif
312 pPainter->setPen(oneWide);
313 pPainter->setBrush(pPainter->pen().color());
314 }
315
316
317 bool space = false;
318 for (int i = 0; i < str.size(); ++i) {
319 // loop through each value and render the barcode
320 int idx = str.at(i);
321 if (idx < 0 || idx > 105) {
322 kreportpluginWarning() << "Encountered a non-compliant element while rendering a 3of9 barcode -- skipping";
323 continue;
324 }
325 space = false;
326 for (int b = 0; b < 6; b++, space = !space) {
327 int w = _128codes[idx].values[b] * bar_width;
328 if (!space && pPainter) {
329 pPainter->fillRect(pos, top, w, draw_height, pPainter->pen().color());
330 }
331 pos += w;
332 }
333 }
334
335 // we have to do the stop character separately like this because it has
336 // 7 elements in it's bar sequence rather than 6 like the others
337 int STOP_CHARACTER[] = { 2, 3, 3, 1, 1, 1, 2 };
338 space = false;
339 for (int b = 0; b < 7; b++, space = !space) {
340 int w = STOP_CHARACTER[b] * bar_width;
341 if (!space && pPainter) {
342 pPainter->fillRect(pos, top, w, draw_height, pPainter->pen().color());
343 }
344 pos += w;
345 }
346
347 if (pPainter) {
348 pPainter->restore();
349 }
350}
char toLatin1() const const
const_reference at(qsizetype i) const const
void push_back(parameter_type value)
qsizetype size() const const
void fillRect(const QRect &rectangle, QGradient::Preset preset)
const QPen & pen() const const
void restore()
void save()
void setBrush(Qt::BrushStyle style)
void setPen(Qt::PenStyle style)
QColor color() const const
int height() const const
int left() const const
int top() const const
int width() const const
const QChar at(qsizetype position) const const
bool isEmpty() const const
qsizetype length() const const
typedef Alignment
MiterJoin
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:21:31 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.