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