KReport

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

KDE's Doxygen guidelines are available online.