KReport

code128paint.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 <QPainter>
28 #include <QPen>
29 #include <QBrush>
30 
31 #include "KReportRenderObjects.h"
32 #include "kreportplugin_debug.h"
33 
34 static const int SETA = 0;
35 static const int SETB = 1;
36 static const int SETC = 2;
37 
38 static const char FNC1 = (char)130;
39 static const char FNC2 = (char)131;
40 static const char FNC3 = (char)132;
41 static const char FNC4 = (char)133;
42 static const char SHIFT = (char)134;
43 static const char CODEA = (char)135;
44 static const char CODEB = (char)136;
45 static const char CODEC = (char)137;
46 static const char STARTA = (char)138;
47 static const char STARTB = (char)139;
48 static const char STARTC = (char)140;
49 
50 
51 struct code128 {
52  char codea;
53  char codeb;
54  char codec;
55 
56  int values[6];
57 
58  bool _null;
59 };
60 
61 static 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 
175 int 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 
186 void 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 }
typedef Alignment
void setPen(const QColor &color)
MiterJoin
void push_back(const T &value)
int width() const const
const QPen & pen() const const
void fillRect(const QRectF &rectangle, const QBrush &brush)
int left() const const
int top() const const
const T & at(int i) const const
bool isEmpty() const const
int length() const const
void setBrush(const QBrush &brush)
int height() const const
QColor color() const const
const QChar at(int position) const const
void restore()
int size() const const
void save()
char toLatin1() const const
QVector< V > values(const QMultiHash< K, V > &c)
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.