3 * IEC16022 bar code generation
4 * Adrian Kennard, Andrews & Arnold Ltd
5 * with help from Cliff Hones on the RS coding
7 * (c) 2004 Adrian Kennard, Andrews & Arnold Ltd
8 * (c) 2006 Stefan Schmidt <stefan@datenfreihafen.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 #define IEC16022_VERSION "0.2"
36 #include "iec16022ecc200.h"
38 // simple checked response malloc
39 void *safemalloc(int n)
43 fprintf(stderr, "Malloc(%d) failed\n", n);
49 // hex dump - bottom left pixel first
50 void dumphex(unsigned char *grid, int W, int H, unsigned char p)
53 for (y = 0; y < H; y++) {
54 int v = 0, x, b = 128;
55 for (x = 0; x < W; x++) {
60 printf("%02X", v ^ p);
67 printf("%02X", v ^ p);
81 int main(int argc, const char *argv[])
91 char *format = "Text";
94 int len = 0, maxlen = 0, ecclen = 0;
95 unsigned char *grid = 0;
96 poptContext optCon; // context for parsing command-line options
97 const struct poptOption optionsTable[] = {
99 "size", 's', POPT_ARG_STRING, &size, 0, "Size", "WxH"},
101 "barcode", 'c', POPT_ARG_STRING, &barcode, 0, "Barcode",
104 "ecc", 0, POPT_ARG_STRING, &eccstr, 0, "ECC",
105 "000/050/080/100/140/200"},
107 "infile", 'i', POPT_ARG_STRING, &infile, 0, "Barcode file",
110 "outfile", 'o', POPT_ARG_STRING, &outfile, 0,
114 "encoding", 'e', POPT_ARG_STRING, &encoding, 0,
116 "[CTXEAB]* for ecc200 or 11/27/41/37/128/256"},
118 "format", 'f', POPT_ARGFLAG_SHOW_DEFAULT | POPT_ARG_STRING,
120 "Output format", "Text/EPS/PNG/Bin/Hex/Stamp"},
124 optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
125 poptSetOtherOptionHelp(optCon, "[barcode]");
126 if ((c = poptGetNextOpt(optCon)) < -1) {
127 /* an error occurred during option processing */
128 fprintf(stderr, "%s: %s\n", poptBadOption(optCon,
129 POPT_BADOPTION_NOALIAS),
134 if (poptPeekArg(optCon) && !barcode && !infile)
135 barcode = (char *)poptGetArg(optCon);
136 if (poptPeekArg(optCon) || !barcode && !infile || barcode && infile) {
137 fprintf(stderr, "Version: %s\n", IEC16022_VERSION);
138 poptPrintUsage(optCon, stderr, 0);
141 if (outfile && !freopen(outfile, "w", stdout)) {
146 if (infile) { // read from file
147 FILE *f = fopen(infile, "rb");
148 barcode = safemalloc(4001);
153 barcodelen = fread(barcode, 1, 4000, f);
154 if (barcodelen < 0) {
158 barcode[barcodelen] = 0; // null terminate anyway
161 barcodelen = strlen(barcode);
164 char *x = strchr(size, 'x');
173 if (W & 1) { // odd size
174 if (W != H || W < 9 || W > 49) {
175 fprintf(stderr, "Invalid size %dx%d\n", W, H);
188 if (ecc && ecc != 50 && ecc != 80 && ecc != 100 && ecc != 140 ||
189 ecc == 50 && W < 11 || ecc == 80 && W < 13 || ecc == 100
190 && W < 13 || ecc == 140 && W < 17) {
191 fprintf(stderr, "ECC%03d invalid for %dx%d\n", ecc, W,
196 } else if (W) { // even size
205 fprintf(stderr, "ECC%03d invalid for %dx%d\n", ecc, W,
213 // default is even sizes only unless explicit ecc set to force odd
218 if (tolower(*format) == 's') { // special stamp format checks & defaults
221 if (ecc != 200 || W != 32 || H != 32)
222 fprintf(stderr, "Stamps must be 32x32\n");
224 fprintf(stderr, "Stamps should use auto encoding\n");
227 for (n = 0; n < barcodelen && (barcode[n] == ' ' ||
229 || isupper(barcode[n]));
233 "Has invalid characters for a stamp\n");
235 // Generate simplistic encoding rules as used by the windows app
236 // TBA - does not always match the windows app...
238 encoding = safemalloc(barcodelen + 1);
239 while (n < barcodelen) {
243 || n + 3 <= barcodelen
244 && (!isdigit(barcode[n])
251 && isdigit(barcode[n - 1])
252 && isdigit(barcode[n]))
258 while (n + r < barcodelen
260 isdigit(barcode[n + r]))
262 if (n + 3 > barcodelen
271 //fprintf (stderr, "%s\n%s\n", barcode, encoding);
276 if ((W & 1) || ecc < 200) { // odd sizes
277 fprintf(stderr, "Not done odd sizes yet, sorry\n");
278 } else { // even sizes
280 iec16022ecc200(&W, &H, &encoding, barcodelen, barcode, &len,
286 fprintf(stderr, "No barcode produced\n");
289 switch (tolower(*format)) {
291 printf("Size : %dx%d\n", W, H);
292 printf("Encoded : %d of %d bytes with %d bytes of ecc\n", len,
294 printf("Barcode : %s\n", barcode);
295 printf("Encoding: %s\n", encoding);
298 dumphex(grid, W, H, 0);
303 for (y = 0; y < H; y++) {
304 int v = 0, x, b = 128;
305 for (x = 0; x < W; x++) {
323 for (y = H - 1; y >= 0; y--) {
325 for (x = 0; x < W; x++)
327 grid[W * y + x] ? '*' : ' ');
333 printf("%%!PS-Adobe-3.0 EPSF-3.0\n"
334 "%%%%Creator: IEC16022 barcode/stamp generator\n"
335 "%%%%BarcodeData: %s\n" "%%%%BarcodeSize: %dx%d\n"
336 "%%%%BarcodeFormat: ECC200\n"
337 "%%%%DocumentData: Clean7Bit\n" "%%%%LanguageLevel: 1\n"
338 "%%%%Pages: 1\n" "%%%%BoundingBox: 0 0 %d %d\n"
339 "%%%%EndComments\n" "%%%%Page: 1 1\n"
340 "%d %d 1[1 0 0 1 -1 -1]{<\n", barcode, W, H, W + 2,
342 dumphex(grid, W, H, 0xFF);
353 if (barcodelen < 74) {
355 "Does not look like a stamp barcode\n");
358 memcpy(temp, barcode, 74);
361 t.tm_year = atoi(temp + 54) + 100;
365 now += 86400 * (atoi(temp + 51) - 1);
369 printf("%%!PS-Adobe-3.0 EPSF-3.0\n"
370 "%%%%Creator: IEC16022 barcode/stamp generator\n"
371 "%%%%BarcodeData: %s\n" "%%%%BarcodeSize: %dx%d\n" "%%%%DocumentData: Clean7Bit\n" "%%%%LanguageLevel: 1\n" "%%%%Pages: 1\n" "%%%%BoundingBox: 0 0 190 80\n" "%%%%EndComments\n" "%%%%Page: 1 1\n" "10 dict begin/f{findfont exch scalefont \
372 setfont}bind def/rm/rmoveto load def/m/moveto load \
373 def/rl/rlineto load def\n" "/l/lineto load def/cp/closepath load def/c{dup stringwidth \
374 pop -2 div 0 rmoveto show}bind def\n" "gsave 72 25.4 div dup scale 0 0 m 67 0 rl 0 28 rl -67 0 rl \
375 cp clip 1 setgray fill 0 setgray 0.5 0 translate 0.3 \
376 setlinewidth\n" "32 32 1[2 0 0 2 0 -11]{<\n", barcode, W, H);
377 dumphex(grid, W, H, 0xFF);
379 "3.25/Helvetica-Bold f 8 25.3 m(\\243%d.%02d)c\n"
380 "2.6/Helvetica f 8 22.3 m(%.4s %.4s)c\n"
381 "1.5/Helvetica f 8 3.3 m(POST BY)c\n"
382 "3.3/Helvetica f 8 0.25 m(%02d.%02d.%02d)c\n",
383 v / 100, v % 100, temp + 6, temp + 10, t.tm_mday,
384 t.tm_mon + 1, t.tm_year % 100);
385 if (c == '1' || c == '2' || c == 'A' || c == 'S') {
388 ("42 0 m 10 0 rl 0 28 rl -10 0 rl cp 57 0 m 5 0 rl 0 \
392 ("42 0 m 5 0 rl 0 28 rl -5 0 rl cp 52 0 m 10 0 rl 0 \
395 (" 21 0 m 16 0 rl 0 28 rl -16 0 rl cp fill\n"
396 "21.3 0.3 m 15.4 0 rl 0 13 rl -15.4 0 rl cp 1 setgray \
397 fill gsave 21.3 0.3 15.4 27.4 rectclip newpath\n");
401 ("27/Helvetica-Bold f 27 8.7 m(1)show grestore 0 setgray \
402 1.5/Helvetica-Bold f 22 3.3 m(POSTAGE PAID GB)show \
403 1.7/Helvetica f 29 1.5 m(DumbStamp.co.uk)c\n");
407 ("21/Helvetica-Bold f 23.5 13 m(2)1.25 1 scale show grestore \
408 0 setgray 1.5/Helvetica-Bold f 22 3.3 \
409 m(POSTAGE PAID GB)show 1.7/Helvetica f 29 1.5 \
410 m(DumbStamp.co.uk)c\n");
414 ("16/Helvetica-Bold f 29 14.75 m 1.1 1 scale(A)c grestore 0 \
415 setgray 1.5/Helvetica-Bold f 22 3.3 m(POSTAGE PAID GB)show \
416 1.7/Helvetica f 22 1.5 m(Par Avion)show\n");
420 ("10/Helvetica-Bold f 29 17 m(SU)c grestore 0 setgray \
421 1.5/Helvetica-Bold f 22 1.5 m(POSTAGE PAID GB)show\n");
425 ("2.3/Helvetica-Bold f 29 10 m(LOYAL MAIL)c\n");
426 } else if (c == 'P') { // Standard Parcels
427 printf("21 0 m 41 0 rl 0 28 rl -41 0 rl cp fill\n" "37.7 0.3 m 24 0 rl 0 27.4 rl -24 0 rl cp 1 setgray fill \
428 gsave 21.3 0.3 16.4 27.4 rectclip newpath\n" "22.5/Helvetica-Bold f 37.75 -1.25 m 90 rotate(SP)show \
429 grestore 0 setgray\n"
430 "3.5/Helvetica-Bold f 49.7 21.5 m(LOYAL MAIL)c\n" "2.3/Helvetica-Bold f 49.7 7 m(POSTAGE PAID GB)c\n" "2.6/Helveica f 49.7 4.25 m(DumbStamp.co.uk)c\n");
432 printf("21.15 0.15 40.7 27.7 rectstroke\n"
433 "21 0 m 41 0 rl 0 5 rl -41 0 rl cp fill\n"
434 "0 1 2{0 1 18{dup 1.525 mul 22.9 add 24 3 index 1.525 mul \
435 add 3 -1 roll 9 add 29 div 0 360 arc fill}for pop}for\n" "50.5 23.07 m 11.5 0 rl 0 5 rl -11.5 0 rl cp fill\n"
436 "5.85/Helvetica f 23.7 15.6 m(Loyal Mail)show\n" "4.75/Helvetica-Bold f 24 11 m(special)show 4.9/Helvetica \
437 f(delivery)show\n" "gsave 1 setgray 3.2/Helvetica-Bold f 24 1.6 \
438 m(next day)show 26 10.15 m 2 0 rl stroke grestore\n" "21.15 9.9 m 53.8 9.9 l stroke 53.8 9.9 0.4 0 360 \
440 printf("end grestore\n");
446 Image *i = ImageNew(W + 2, H + 2, 2);
447 i->Colour[0] = 0xFFFFFF;
449 for (y = 0; y < H; y++)
450 for (x = 0; x < W; x++)
452 ImagePixel(i, x + 1, H - y) = 1;
453 ImageWritePNG(i, fileno(stdout), 0, -1, barcode);
458 fprintf(stderr, "Unknown output format %s\n", format);