KReport

code128.cpp
1 /* This file is part of the KDE project
2  * Copyright (C) 2001-2007 by OpenMFG, LLC ([email protected])
3  * Copyright (C) 2007-2008 by Adam Pigg ([email protected])
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 
33 static const int SETA = 0;
34 static const int SETB = 1;
35 static const int SETC = 2;
36 
37 static const char FNC1 = (char)130;
38 static const char FNC2 = (char)131;
39 static const char FNC3 = (char)132;
40 static const char FNC4 = (char)133;
41 static const char SHIFT = (char)134;
42 static const char CODEA = (char)135;
43 static const char CODEB = (char)136;
44 static const char CODEC = (char)137;
45 static const char STARTA = (char)138;
46 static const char STARTB = (char)139;
47 static const char STARTC = (char)140;
48 
49 
50 struct code128 {
51  char codea;
52  char codeb;
53  char codec;
54 
55  int values[6];
56 
57  bool _null;
58 };
59 
60 static 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 
174 int 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 
187 void 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 }
qreal left() const const
typedef Alignment
void push_back(const T &value)
Represents a single page in a document and may contain zero or more OROPrimitive objects all of which...
Defines a rectangle.
const T & at(int i) const const
bool isEmpty() const const
int length() const const
qreal top() const const
qreal width() const const
const QChar at(int position) const const
int size() const const
char toLatin1() const const
QVector< V > values(const QMultiHash< K, V > &c)
qreal height() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Feb 7 2023 04:17:37 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.