]> git.sur5r.net Git - iec16022/blob - iec16022.c
Merge branch 'upstream'
[iec16022] / iec16022.c
1 /** 
2  *
3  * IEC16022 bar code generation
4  * Adrian Kennard, Andrews & Arnold Ltd
5  * with help from Cliff Hones on the RS coding
6  *
7  * (c) 2004 Adrian Kennard, Andrews & Arnold Ltd
8  * (c) 2006 Stefan Schmidt <stefan@datenfreihafen.org>
9  * 
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.
14  *
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.
19  *
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
23  *
24  */ 
25
26 #define IEC16022_VERSION "0.2"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <time.h>
33 #include <popt.h>
34 #include <malloc.h>
35 #include "image.h"
36 #include "iec16022ecc200.h"
37
38  // simple checked response malloc
39 void * safemalloc (int n)
40 {
41    void *p = malloc (n);
42    if (!p)
43    {
44       fprintf (stderr, "Malloc(%d) failed\n", n);
45       exit (1);
46    }
47    return p;
48 }
49
50 // hex dump - bottom left pixel first
51 void dumphex (unsigned char *grid, int W, int H, unsigned char p)
52 {
53    int c = 0,
54       y;
55    for (y = 0; y < H; y++)
56    {
57       int v = 0,
58          x,
59          b = 128;
60       for (x = 0; x < W; x++)
61       {
62          if (grid[y * W + x])
63             v |= b;
64          b >>= 1;
65          if (!b)
66          {
67             printf ("%02X", v ^ p);
68             v = 0;
69             b = 128;
70             c++;
71          }
72       }
73       if (b != 128)
74       {
75          printf ("%02X", v ^ p);
76          c++;
77       }
78       printf (" ");
79       c++;
80       if (c >= 80)
81       {
82          printf ("\n");
83          c = 0;
84       }
85    }
86    if (c)
87       printf ("\n");
88 }
89
90 int main (int argc, const char *argv[])
91 {
92    char c;
93    int W = 0,
94       H = 0;
95    int ecc = 0;
96    int barcodelen = 0;
97    char *encoding = 0;
98    char *outfile = 0;
99    char *infile = 0;
100    char *barcode = 0;
101    char *format = "Text";
102    char *size = 0;
103    char *eccstr = 0;
104    int len = 0,
105       maxlen = 0,
106       ecclen = 0;
107    unsigned char *grid = 0;
108    poptContext optCon;          // context for parsing command-line options
109    const struct poptOption optionsTable[] = {
110       {
111        "size", 's', POPT_ARG_STRING, &size, 0, "Size", "WxH"},
112       {
113        "barcode", 'c', POPT_ARG_STRING, &barcode, 0, "Barcode", "text"},
114       {
115        "ecc", 0, POPT_ARG_STRING, &eccstr, 0, "ECC", "000/050/080/100/140/200"},
116       {
117        "infile", 'i', POPT_ARG_STRING, &infile, 0, "Barcode file", "filename"},
118       {
119        "outfile", 'o', POPT_ARG_STRING, &outfile, 0, "Output filename", \
120                                                 "filename"},
121       {
122        "encoding", 'e', POPT_ARG_STRING, &encoding, 0, "Encoding template", \
123                                                 "[CTXEAB]* for ecc200 or 11/27/41/37/128/256"},
124       {
125        "format", 'f', POPT_ARGFLAG_SHOW_DEFAULT | POPT_ARG_STRING, &format, 0, \
126                                                 "Output format", "Text/EPS/PNG/Bin/Hex/Stamp"},
127           POPT_AUTOHELP {
128                      NULL, 0, 0, NULL, 0}
129    };
130    optCon = poptGetContext (NULL, argc, argv, optionsTable, 0);
131    poptSetOtherOptionHelp (optCon, "[barcode]");
132    if ((c = poptGetNextOpt (optCon)) < -1)
133    {
134       /* an error occurred during option processing */
135       fprintf (stderr, "%s: %s\n", poptBadOption (optCon, \
136                                         POPT_BADOPTION_NOALIAS), poptStrerror (c));
137       return 1;
138    }
139
140    if (poptPeekArg (optCon) && !barcode && !infile)
141       barcode = (char *) poptGetArg (optCon);
142    if (poptPeekArg (optCon) || !barcode && !infile || barcode && infile)
143    {
144       fprintf (stderr, "Version: %s\n", IEC16022_VERSION);
145           poptPrintUsage (optCon, stderr, 0);
146       return -1;
147    }
148    if (outfile && !freopen (outfile, "w", stdout))
149    {
150       perror (outfile);
151       return 1;
152    }
153
154    if (infile)
155    {                            // read from file
156       FILE *f = fopen (infile, "rb");
157       barcode = safemalloc (4001);
158       if (!f)
159       {
160          perror (infile);
161          return 1;
162       }
163       barcodelen = fread (barcode, 1, 4000, f);
164       if (barcodelen < 0)
165       {
166          perror (infile);
167          return 1;
168       }
169       barcode[barcodelen] = 0;  // null terminate anyway
170       close (f);
171    } else
172       barcodelen = strlen (barcode);
173    // check parameters
174    if (size)
175    {
176       char *x = strchr (size, 'x');
177       W = atoi (size);
178       if (x)
179          H = atoi (x + 1);
180       if (!H)
181          W = H;
182    }
183    if (eccstr)
184       ecc = atoi (eccstr);
185    if (W & 1)
186    {                            // odd size
187       if (W != H || W < 9 || W > 49)
188       {
189          fprintf (stderr, "Invalid size %dx%d\n", W, H);
190          return 1;
191       }
192       if (!eccstr)
193       {
194          if (W >= 17)
195             ecc = 140;
196          else if (W >= 13)
197             ecc = 100;
198          else if (W >= 11)
199             ecc = 80;
200          else
201             ecc = 0;
202       }
203       if (ecc && ecc != 50 && ecc != 80 && ecc != 100 && ecc != 140 || \
204                                 ecc == 50 && W < 11 || ecc == 80 && W < 13 || ecc == 100 \
205                                 && W < 13 || ecc == 140 && W < 17)
206       {
207          fprintf (stderr, "ECC%03d invalid for %dx%d\n", ecc, W, H);
208          return 1;
209       }
210
211    } else if (W)
212    {                            // even size
213       if (W < H)
214       {
215          int t = W;
216          W = H;
217          H = t;
218       }
219       if (!eccstr)
220          ecc = 200;
221       if (ecc != 200)
222       {
223          fprintf (stderr, "ECC%03d invalid for %dx%d\n", ecc, W, H);
224          return 1;
225       }
226    }
227
228    else
229    {                            // auto size
230       if (!eccstr)
231          // default is even sizes only unless explicit ecc set to force odd
232                  // sizes
233                   ecc = 200;
234    }
235
236    if (tolower (*format) == 's')
237    {                            // special stamp format checks & defaults
238       if (!W)
239          W = H = 32;
240       if (ecc != 200 || W != 32 || H != 32)
241          fprintf (stderr, "Stamps must be 32x32\n");
242       if (encoding)
243          fprintf (stderr, "Stamps should use auto encoding\n");
244       else
245       {
246          int n;
247          for (n = 0; n < barcodelen && (barcode[n] == ' ' || \
248                                                 isdigit (barcode[n]) || isupper (barcode[n])); n++);
249          if (n < barcodelen)
250             fprintf (stderr, "Has invalid characters for a stamp\n");
251          else
252          {                      
253                         // Generate simplistic encoding rules as used by the windows app
254             // TBA - does not always match the windows app...
255             n = 0;
256             encoding = safemalloc (barcodelen + 1);
257             while (n < barcodelen)
258             {
259                // ASCII
260                while (1)
261                {
262                   if (n == barcodelen || n + 3 <= barcodelen && (!isdigit \
263                                                           (barcode[n]) || !isdigit (barcode[n + 1])))
264                      break;
265                   encoding[n++] = 'A';
266                   if (n < barcodelen && isdigit (barcode[n - 1]) && isdigit \
267                                                         (barcode[n]))
268                      encoding[n++] = 'A';
269                }
270                // C40
271                while (1)
272                {
273                   int r = 0;
274                   while (n + r < barcodelen && isdigit (barcode[n + r]))
275                      r++;
276                   if (n + 3 > barcodelen || r >= 6)
277                      break;
278                   encoding[n++] = 'C';
279                   encoding[n++] = 'C';
280                   encoding[n++] = 'C';
281                }
282             }
283             encoding[n] = 0;
284             //fprintf (stderr, "%s\n%s\n", barcode, encoding);
285          }
286       }
287    }
288    // processing stamps
289    if ((W & 1) || ecc < 200)
290    {                            // odd sizes
291       fprintf (stderr, "Not done odd sizes yet, sorry\n");
292    } else
293    {                            // even sizes
294       grid = iec16022ecc200 (&W, &H, &encoding, barcodelen, barcode, &len, \
295                                                         &maxlen, &ecclen);
296    }
297
298    // output
299    if (!grid || !W)
300    {
301       fprintf (stderr, "No barcode produced\n");
302       return 1;
303    }
304    switch (tolower (*format))
305    {
306    case 'i':                   // info
307       printf ("Size    : %dx%d\n", W, H);
308       printf ("Encoded : %d of %d bytes with %d bytes of ecc\n", len, maxlen, \
309                           ecclen);
310       printf ("Barcode : %s\n", barcode);
311       printf ("Encoding: %s\n", encoding);
312       break;
313    case 'h':                   // hex
314       dumphex (grid, W, H, 0);
315       break;
316    case 'b':                   // bin
317       {
318          int y;
319          for (y = 0; y < H; y++)
320          {
321             int v = 0,
322                x,
323                b = 128;
324             for (x = 0; x < W; x++)
325             {
326                if (grid[y * W + x])
327                   v |= b;
328                b >>= 1;
329                if (!b)
330                {
331                   putchar (v);
332                   v = 0;
333                   b = 128;
334                }
335             }
336             if (b != 128)
337                putchar (v);
338          }
339       }
340       break;
341    case 't':                   // text
342       {
343          int y;
344          for (y = H - 1; y >= 0; y--)
345          {
346             int x;
347             for (x = 0; x < W; x++)
348                printf ("%c", grid[W * y + x] ? '*' : ' ');
349             printf ("\n");
350          }
351       }
352       break;
353    case 'e':                   // EPS
354       printf ("%%!PS-Adobe-3.0 EPSF-3.0\n" 
355                           "%%%%Creator: IEC16022 barcode/stamp generator\n" 
356                           "%%%%BarcodeData: %s\n" "%%%%BarcodeSize: %dx%d\n" 
357                           "%%%%BarcodeFormat: ECC200\n" "%%%%DocumentData: Clean7Bit\n" 
358                           "%%%%LanguageLevel: 1\n" "%%%%Pages: 1\n" 
359                           "%%%%BoundingBox: 0 0 %d %d\n" "%%%%EndComments\n" 
360                           "%%%%Page: 1 1\n" "%d %d 1[1 0 0 1 -1 -1]{<\n",
361               barcode, W, H, W + 2, H + 2, W, H);
362       dumphex (grid, W, H, 0xFF);
363       printf (">}image\n");
364       break;
365    case 's':                   // Stamp
366       {
367          char temp[74],
368            c;
369          time_t now;
370          struct tm t = {
371             0
372          };
373          int v;
374          if (barcodelen < 74)
375          {
376             fprintf (stderr, "Does not look like a stamp barcode\n");
377             return 1;
378          }
379          memcpy (temp, barcode, 74);
380          c = temp[5];
381          temp[56] = 0;
382          t.tm_year = atoi (temp + 54) + 100;
383          t.tm_mday = 1;
384          now = mktime (&t);
385          temp[54] = 0;
386          now += 86400 * (atoi (temp + 51) - 1);
387          t = *gmtime (&now);
388          temp[46] = 0;
389          v = atoi (temp + 36);
390          printf ("%%!PS-Adobe-3.0 EPSF-3.0\n" 
391                                  "%%%%Creator: IEC16022 barcode/stamp generator\n" 
392                                  "%%%%BarcodeData: %s\n" "%%%%BarcodeSize: %dx%d\n" 
393                                  "%%%%DocumentData: Clean7Bit\n" "%%%%LanguageLevel: 1\n"
394                  "%%%%Pages: 1\n" "%%%%BoundingBox: 0 0 190 80\n" 
395                                  "%%%%EndComments\n" "%%%%Page: 1 1\n"
396                  "10 dict begin/f{findfont exch scalefont \
397                                  setfont}bind def/rm/rmoveto load def/m/moveto load \
398                                  def/rl/rlineto load def\n"
399                  "/l/lineto load def/cp/closepath load def/c{dup stringwidth \
400                                  pop -2 div 0 rmoveto show}bind def\n"
401                  "gsave 72 25.4 div dup scale 0 0 m 67 0 rl 0 28 rl -67 0 rl \
402                                  cp clip 1 setgray fill 0 setgray 0.5 0 translate 0.3 \
403                                  setlinewidth\n" "32 32 1[2 0 0 2 0 -11]{<\n", barcode, W, H);
404          dumphex (grid, W, H, 0xFF);
405          printf (">}image\n"
406                  "3.25/Helvetica-Bold f 8 25.3 m(\\243%d.%02d)c\n"
407                  "2.6/Helvetica f 8 22.3 m(%.4s %.4s)c\n"
408                  "1.5/Helvetica f 8 3.3 m(POST BY)c\n"
409                  "3.3/Helvetica f 8 0.25 m(%02d.%02d.%02d)c\n",
410                  v / 100, v % 100, temp + 6, temp + 10, t.tm_mday, t.tm_mon + \
411                                  1, t.tm_year % 100);
412          if (c == '1' || c == '2' || c == 'A' || c == 'S')
413          {
414             if (c == '2')
415                printf ("42 0 m 10 0 rl 0 28 rl -10 0 rl cp 57 0 m 5 0 rl 0 \
416                                            28 rl -5 0 rl cp");
417             else
418                printf ("42 0 m 5 0 rl 0 28 rl -5 0 rl cp 52 0 m 10 0 rl 0 \
419                                            28 rl -10 0 rl cp");
420             printf (" 21 0 m 16 0 rl 0 28 rl -16 0 rl cp fill\n"
421                     "21.3 0.3 m 15.4 0 rl 0 13 rl -15.4 0 rl cp 1 setgray \
422                                         fill gsave 21.3 0.3 15.4 27.4 rectclip newpath\n");
423             switch (c)
424             {
425             case '1':
426                printf
427                   ("27/Helvetica-Bold f 27 8.7 m(1)show grestore 0 setgray \
428                                    1.5/Helvetica-Bold f 22 3.3 m(POSTAGE PAID GB)show \
429                                    1.7/Helvetica f 29 1.5 m(DumbStamp.co.uk)c\n");
430                break;
431             case '2':
432                printf
433                   ("21/Helvetica-Bold f 23.5 13 m(2)1.25 1 scale show grestore \
434                                    0 setgray 1.5/Helvetica-Bold f 22 3.3 \
435                                    m(POSTAGE PAID GB)show 1.7/Helvetica f 29 1.5 \
436                                    m(DumbStamp.co.uk)c\n");
437                break;
438             case 'A':
439                printf
440                   ("16/Helvetica-Bold f 29 14.75 m 1.1 1 scale(A)c grestore 0 \
441                                    setgray 1.5/Helvetica-Bold f 22 3.3 m(POSTAGE PAID GB)show \
442                                    1.7/Helvetica f 22 1.5 m(Par Avion)show\n");
443                break;
444             case 'S':
445                printf ("10/Helvetica-Bold f 29 17 m(SU)c grestore 0 setgray \
446                                            1.5/Helvetica-Bold f 22 1.5 m(POSTAGE PAID GB)show\n");
447                break;
448             }
449             printf ("2.3/Helvetica-Bold f 29 10 m(LOYAL MAIL)c\n");
450          } else if (c == 'P')
451          {                      // Standard Parcels
452             printf ("21 0 m 41 0 rl 0 28 rl -41 0 rl cp fill\n"
453                     "37.7 0.3 m 24 0 rl 0 27.4 rl -24 0 rl cp 1 setgray fill \
454                                         gsave 21.3 0.3 16.4 27.4 rectclip newpath\n"
455                     "22.5/Helvetica-Bold f 37.75 -1.25 m 90 rotate(SP)show \
456                                         grestore 0 setgray\n"
457                     "3.5/Helvetica-Bold f 49.7 21.5 m(LOYAL MAIL)c\n"
458                     "2.3/Helvetica-Bold f 49.7 7 m(POSTAGE PAID GB)c\n" \
459                                         "2.6/Helveica f 49.7 4.25 m(DumbStamp.co.uk)c\n");
460          } else if (c == '3')
461             printf ("21.15 0.15 40.7 27.7 rectstroke\n"
462                     "21 0 m 41 0 rl 0 5 rl -41 0 rl cp fill\n"
463                     "0 1 2{0 1 18{dup 1.525 mul 22.9 add 24 3 index 1.525 mul \
464                                         add 3 -1 roll 9 add 29 div 0 360 arc fill}for pop}for\n"
465                     "50.5 23.07 m 11.5 0 rl 0 5 rl -11.5 0 rl cp fill\n"
466                     "5.85/Helvetica f 23.7 15.6 m(Loyal Mail)show\n"
467                     "4.75/Helvetica-Bold f 24 11 m(special)show 4.9/Helvetica \
468                                         f(delivery)show\n"
469                     "gsave 1 setgray 3.2/Helvetica-Bold f 24 1.6 \
470                                         m(next day)show 26 10.15 m 2 0 rl stroke grestore\n"
471                     "21.15 9.9 m 53.8 9.9 l stroke 53.8 9.9 0.4 0 360 \
472                                         arc fill\n");
473          printf ("end grestore\n");
474       }
475       break;
476    case 'p':                   // png
477       {
478          int x,
479            y;
480          Image *i = ImageNew (W + 2, H + 2, 2);
481          i->Colour[0] = 0xFFFFFF;
482          i->Colour[1] = 0;
483          for (y = 0; y < H; y++)
484             for (x = 0; x < W; x++)
485                if (grid[y * W + x])
486                   ImagePixel (i, x + 1, H - y) = 1;
487          ImageWritePNG (i, fileno (stdout), 0, -1, barcode);
488          ImageFree (i);
489       }
490       break;
491    default:
492       fprintf (stderr, "Unknown output format %s\n", format);
493       break;
494    }
495    return 0;
496 }