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