Prison

code128barcode.cpp
1 /*
2  SPDX-FileCopyrightText: 2018 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: MIT
5 */
6 
7 #include "code128barcode.h"
8 #include "bitvector_p.h"
9 #include "prison_debug.h"
10 
11 #include <QImage>
12 #include <QPainter>
13 
14 using namespace Prison;
15 
16 enum {
17  SymbolSize = 11,
18  StopPatternSize = 13,
19  StopPattern = 108,
20  QuietZone = 10,
21 };
22 
23 enum CodeSet : uint8_t {
24  CodeSetA = 0,
25  CodeSetB = 1,
26  CodeSetC = 2,
27  CodeSetUnknown = 3,
28 };
29 
30 enum CodeSetOp : uint8_t {
31  None = 255,
32  StartA = 103,
33  StartB = 104,
34  StartC = 105,
35  Shift = 98,
36  LatchA = 101,
37  LatchB = 100,
38  LatchC = 99,
39 };
40 
41 Code128Barcode::Code128Barcode()
42  : AbstractBarcode(AbstractBarcode::OneDimension)
43 {
44 }
45 Code128Barcode::~Code128Barcode() = default;
46 
48 {
49  Q_UNUSED(size);
50 
51  const auto bits = encode(data().isEmpty() ? byteArrayData() : data().toLatin1());
52  const auto width = bits.size() + 2 * QuietZone;
53 
54  QImage img(width, 1, QImage::Format_ARGB32);
55  img.fill(backgroundColor());
56  QPainter p(&img);
57  for (int i = 0; i < bits.size(); ++i) {
58  if (bits.at(i)) {
59  img.setPixel(QuietZone + i, 0, foregroundColor().rgb());
60  }
61  }
62 
63  return img;
64 }
65 
66 // Code 128 symbol table
67 static const uint16_t code128_symbols[] = {
68  0b11011001100, // 0
69  0b11001101100,
70  0b11001100110,
71  0b10010011000,
72  0b10010001100,
73  0b10001001100,
74  0b10011001000,
75  0b10011000100,
76  0b10001100100,
77  0b11001001000,
78  0b11001000100, // 10
79  0b11000100100,
80  0b10110011100,
81  0b10011011100,
82  0b10011001110,
83  0b10111001100,
84  0b10011101100,
85  0b10011100110,
86  0b11001110010,
87  0b11001011100,
88  0b11001001110, // 20
89  0b11011100100,
90  0b11001110100,
91  0b11101101110,
92  0b11101001100,
93  0b11100101100,
94  0b11100100110,
95  0b11101100100,
96  0b11100110100,
97  0b11100110010,
98  0b11011011000, // 30
99  0b11011000110,
100  0b11000110110,
101  0b10100011000,
102  0b10001011000,
103  0b10001000110,
104  0b10110001000,
105  0b10001101000,
106  0b10001100010,
107  0b11010001000,
108  0b11000101000, // 40
109  0b11000100010,
110  0b10110111000,
111  0b10110001110,
112  0b10001101110,
113  0b10111011000,
114  0b10111000110,
115  0b10001110110,
116  0b11101110110,
117  0b11010001110,
118  0b11000101110, // 50
119  0b11011101000,
120  0b11011100010,
121  0b11011101110,
122  0b11101011000,
123  0b11101000110,
124  0b11100010110,
125  0b11101101000,
126  0b11101100010,
127  0b11100011010,
128  0b11101111010, // 60
129  0b11001000010,
130  0b11110001010,
131  0b10100110000,
132  0b10100001100,
133  0b10010110000,
134  0b10010000110,
135  0b10000101100,
136  0b10000100110,
137  0b10110010000,
138  0b10110000100, // 70
139  0b10011010000,
140  0b10011000010,
141  0b10000110100,
142  0b10000110010,
143  0b11000010010,
144  0b11001010000,
145  0b11110111010,
146  0b11000010100,
147  0b10001111010,
148  0b10100111100, // 80
149  0b10010111100,
150  0b10010011110,
151  0b10111100100,
152  0b10011110100,
153  0b10011110010,
154  0b11110100100,
155  0b11110010100,
156  0b11110010010,
157  0b11011011110,
158  0b11011110110, // 90
159  0b11110110110,
160  0b10101111000,
161  0b10100011110,
162  0b10001011110,
163  0b10111101000,
164  0b10111100010,
165  0b11110101000,
166  0b11110100010,
167  0b10111011110,
168  0b10111101110, // 100
169  0b11101011110,
170  0b11110101110,
171  0b11010000100,
172  0b11010010000,
173  0b11010011100,
174  0b11000111010,
175  0b11010111000,
176  0b1100011101011,
177 };
178 
179 static uint8_t symbolForCharacter(const QByteArray &data, int index, CodeSet set)
180 {
181  const auto c1 = data.at(index);
182  switch (set) {
183  case CodeSetA:
184  return (c1 < ' ') ? c1 + 64 : c1 - ' ';
185  case CodeSetB:
186  return c1 - ' ';
187  case CodeSetC: {
188  const auto c2 = data.at(index + 1);
189  return ((c1 - '0') * 10) + c2 - '0';
190  }
191  case CodeSetUnknown:
192  Q_UNREACHABLE();
193  }
194 
195  Q_UNREACHABLE();
196  return {};
197 }
198 
199 struct CodeSetChange {
200  CodeSet set;
201  CodeSetOp symbol;
202 };
203 
204 static bool isInCodeSetA(char c)
205 {
206  return c <= 95;
207 }
208 
209 static bool isInCodeSetB(char c)
210 {
211  // ### this does not consider FNC4 high byte encoding
212  return c >= 32;
213 }
214 
215 static CodeSetChange opForData(const QByteArray &data, int index, CodeSet currentSet)
216 {
217  // determine if Code C makes sense at this point
218  int codeC = 0;
219  for (int i = index; i < data.size(); ++i, ++codeC) {
220  if (data.at(i) < '0' || data.at(i) > '9') {
221  break;
222  }
223  }
224  if (currentSet == CodeSetC && codeC >= 2) { // already in C
225  return {CodeSetC, None};
226  }
227  if (codeC >= 6 // that's always good enough
228  || (index == 0 && codeC >= 4) // beginning of data
229  || (index + codeC == data.size() && codeC >= 4) // end of data
230  || (codeC == data.size() && codeC == 2) // 2 ...
231  || (codeC == data.size() && codeC == 4)) // ... or 4 as the entire data
232  {
233  return currentSet == CodeSetUnknown ? CodeSetChange{CodeSetC, StartC} : CodeSetChange{CodeSetC, LatchC};
234  }
235 
236  // if we are in Code A or Code B, check if we need to switch for the next char
237  // this is a shortcut to prevent the below more extensive search from making this O(n²) in the common case
238  if ((currentSet == CodeSetA && isInCodeSetA(data.at(index))) || (currentSet == CodeSetB && isInCodeSetB(data.at(index)))) {
239  return {currentSet, None};
240  }
241 
242  // we need to switch to A or B, select which one, and select whether to use start, shift or latch
243  const auto nextA = isInCodeSetA(data.at(index));
244  const auto nextB = isInCodeSetB(data.at(index));
245 
246  // count how many following characters we could encode in A or B
247  int countA = 0;
248  for (int i = index + 1; i < data.size(); ++i, ++countA) {
249  if (!isInCodeSetA(data.at(i))) {
250  break;
251  }
252  }
253  int countB = 0;
254  for (int i = index + 1; i < data.size(); ++i, ++countB) {
255  if (!isInCodeSetB(data.at(i))) {
256  break;
257  }
258  }
259 
260  // select how we want to switch to Code A or Code B, biased to B as that's the more useful one in general
261  switch (currentSet) {
262  case CodeSetUnknown:
263  // if we are at the start, take whichever code will get us further, or the only one that works
264  if (nextA && nextB) {
265  return countA > countB ? CodeSetChange{CodeSetA, StartA} : CodeSetChange{CodeSetB, StartB};
266  }
267  return nextA ? CodeSetChange{CodeSetA, StartA} : CodeSetChange{CodeSetB, StartB};
268  case CodeSetC:
269  // same for Code C
270  if (nextA && nextB) {
271  return countA > countB ? CodeSetChange{CodeSetA, LatchA} : CodeSetChange{CodeSetB, LatchB};
272  }
273  return nextA ? CodeSetChange{CodeSetA, LatchA} : CodeSetChange{CodeSetB, LatchB};
274  case CodeSetA:
275  // switch or latch to B?
276  return CodeSetChange{CodeSetB, countB >= countA ? LatchB : Shift};
277  case CodeSetB:
278  // switch or latch to A?
279  return CodeSetChange{CodeSetA, countA > countB ? LatchA : Shift};
280  }
281 
282  Q_UNREACHABLE();
283  return CodeSetChange{currentSet, None};
284 }
285 
286 BitVector Code128Barcode::encode(const QByteArray &data) const
287 {
288  BitVector v;
289  if (data.isEmpty()) {
290  return v;
291  }
292 
293  // determine code set for start
294  const auto op = opForData(data, 0, CodeSetUnknown);
295  auto currentSet = op.set;
296 
297  // write start code
298  qCDebug(Log) << "start symbol:" << op.symbol << code128_symbols[op.symbol];
299  v.appendMSB(code128_symbols[op.symbol], SymbolSize);
300 
301  uint32_t checksum = op.symbol;
302  uint32_t checksumWeight = 1;
303 
304  for (int i = 0; i < data.size(); i += currentSet == CodeSetC ? 2 : 1) {
305  if (static_cast<uint8_t>(data.at(i)) > 127) { // FNC4 encoding not implemented yet
306  continue;
307  }
308 
309  // perform code switch if needed
310  const auto op = opForData(data, i, currentSet);
311  if (op.symbol != None) {
312  qCDebug(Log) << "op symbol:" << op.symbol << code128_symbols[op.symbol];
313  v.appendMSB(code128_symbols[op.symbol], SymbolSize);
314  checksum += op.symbol * checksumWeight++;
315  }
316 
317  // encode current symbol
318  const auto symbol = symbolForCharacter(data, i, op.set);
319  qCDebug(Log) << "data symbol:" << symbol << code128_symbols[symbol];
320  v.appendMSB(code128_symbols[symbol], SymbolSize);
321  checksum += symbol * checksumWeight++;
322 
323  // update current code set
324  if (op.symbol != Shift) {
325  currentSet = op.set;
326  }
327  }
328 
329  // encode checksum
330  qCDebug(Log) << "checksum:" << checksum << code128_symbols[checksum % 103];
331  v.appendMSB(code128_symbols[checksum % 103], SymbolSize);
332 
333  // add stop pattern
334  v.appendMSB(code128_symbols[StopPattern], StopPatternSize);
335  return v;
336 }
int size() const const
void fill(uint pixelValue)
QImage paintImage(const QSizeF &size) override
Doing the actual painting of the image.
const QColor & backgroundColor() const
char at(int i) const const
void setPixel(int x, int y, uint index_or_rgb)
bool isEmpty() const const
base class for barcode generators To add your own barcode generator, subclass this class and reimplem...
QByteArray byteArrayData() const
Binary data encoded in this barcode.
const QChar at(int position) const const
int size() const const
const QColor & foregroundColor() const
QString data() const
Textual content encoded in this barcode.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Dec 1 2023 04:09:13 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.