]> git.sur5r.net Git - glabels/blob - src/bc-gnubarcode.c
Imported Upstream version 3.0.0
[glabels] / src / bc-gnubarcode.c
1 /*
2  *  bc-gnubarcode.c
3  *  Copyright (C) 2001-2009  Jim Evins <evins@snaught.com>.
4  *
5  *  This file is part of gLabels.
6  *
7  *  gLabels is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  gLabels is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with gLabels.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #ifdef HAVE_LIBBARCODE
24
25 #include "bc-gnubarcode.h"
26
27 #include <glib.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <barcode.h> /* GNU Barcode */
31
32 #include "debug.h"
33
34
35 /*========================================================*/
36 /* Private macros and constants.                          */
37 /*========================================================*/
38 #define SHRINK_AMOUNT 0.15        /* shrink bars to account for ink spreading */
39 #define FONT_SCALE    0.95        /* Shrink fonts just a hair */
40
41
42 /*===========================================*/
43 /* Local function prototypes                 */
44 /*===========================================*/
45 static lglBarcode *render_pass1     (struct Barcode_Item *bci,
46                                      gint                 flags);
47
48 static gboolean   is_length_valid  (const gchar         *digits,
49                                     gint                 n1,
50                                     gint                 n2);
51
52 static gboolean   is_length1_valid (const gchar         *digits,
53                                     gint                 n1,
54                                     gint                 n2);
55
56 static gboolean   is_length2_valid (const gchar         *digits,
57                                     gint                 n1,
58                                     gint                 n2);
59
60
61 /*****************************************************************************/
62 /* Generate intermediate representation of barcode.                          */
63 /*****************************************************************************/
64 lglBarcode *
65 gl_barcode_gnubarcode_new (const gchar    *id,
66                            gboolean        text_flag,
67                            gboolean        checksum_flag,
68                            gdouble         w,
69                            gdouble         h,
70                            const gchar    *digits)
71 {
72         lglBarcode          *gbc;
73         struct Barcode_Item *bci;
74         gint                 flags;
75
76         /* Assign type flag.  Pre-filter by length for subtypes. */
77         if (g_ascii_strcasecmp (id, "EAN") == 0)
78         {
79                 flags = BARCODE_EAN;
80         }
81         else if (g_ascii_strcasecmp (id, "EAN-8") == 0)
82         {
83                 if (!is_length_valid (digits, 7, 8))
84                 {
85                         return NULL;
86                 }
87                 flags = BARCODE_EAN;
88         }
89         else if (g_ascii_strcasecmp (id, "EAN-8+2") == 0)
90         {
91                 if (!is_length1_valid (digits, 7, 8) || !is_length2_valid (digits, 2, 2))
92                 {
93                         return NULL;
94                 }
95                 flags = BARCODE_EAN;
96         }
97         else if (g_ascii_strcasecmp (id, "EAN-8+5") == 0)
98         {
99                 if (!is_length1_valid (digits, 7, 8) || !is_length2_valid (digits, 5, 5))
100                 {
101                         return NULL;
102                 }
103                 flags = BARCODE_EAN;
104         }
105         else if (g_ascii_strcasecmp (id, "EAN-13") == 0)
106         {
107                 if (!is_length_valid (digits, 12, 13))
108                 {
109                         return NULL;
110                 }
111                 flags = BARCODE_EAN;
112         }
113         else if (g_ascii_strcasecmp (id, "EAN-13+2") == 0)
114         {
115                 if (!is_length1_valid (digits, 12,13) || !is_length2_valid (digits, 2,2))
116                 {
117                         return NULL;
118                 }
119                 flags = BARCODE_EAN;
120         }
121         else if (g_ascii_strcasecmp (id, "EAN-13+5") == 0)
122         {
123                 if (!is_length1_valid (digits, 12,13) || !is_length2_valid (digits, 5,5))
124                 {
125                         return NULL;
126                 }
127                 flags = BARCODE_EAN;
128         }
129         else if (g_ascii_strcasecmp (id, "UPC") == 0)
130         {
131                 flags = BARCODE_UPC;
132         }
133         else if (g_ascii_strcasecmp (id, "UPC-A") == 0)
134         {
135                 if (!is_length_valid (digits, 11, 12))
136                 {
137                         return NULL;
138                 }
139                 flags = BARCODE_UPC;
140         }
141         else if (g_ascii_strcasecmp (id, "UPC-A+2") == 0)
142         {
143                 if (!is_length1_valid (digits, 11,12) || !is_length2_valid (digits, 2,2))
144                 {
145                         return NULL;
146                 }
147                 flags = BARCODE_UPC;
148         }
149         else if (g_ascii_strcasecmp (id, "UPC-A+5") == 0)
150         {
151                 if (!is_length1_valid (digits, 11,12) || !is_length2_valid (digits, 5,5))
152                 {
153                         return NULL;
154                 }
155                 flags = BARCODE_UPC;
156         }
157         else if (g_ascii_strcasecmp (id, "UPC-E") == 0)
158         {
159                 if (!is_length_valid (digits, 6, 8))
160                 {
161                         return NULL;
162                 }
163                 flags = BARCODE_UPC;
164         }
165         else if (g_ascii_strcasecmp (id, "UPC-E+2") == 0)
166         {
167                 if (!is_length1_valid (digits, 6, 8) || !is_length2_valid (digits, 2,2))
168                 {
169                         return NULL;
170                 }
171                 flags = BARCODE_UPC;
172         }
173         else if (g_ascii_strcasecmp (id, "UPC-E+5") == 0)
174         {
175                 if (!is_length1_valid (digits, 6, 8) || !is_length2_valid (digits, 5,5))
176                 {
177                         return NULL;
178                 }
179                 flags = BARCODE_UPC;
180         }
181         else if (g_ascii_strcasecmp (id, "ISBN") == 0)
182         {
183                 if (!is_length_valid (digits, 9, 10))
184                 {
185                         return NULL;
186                 }
187                 flags = BARCODE_ISBN;
188         }
189         else if (g_ascii_strcasecmp (id, "ISBN+5") == 0)
190         {
191                 if (!is_length1_valid (digits, 9, 10) || !is_length2_valid (digits, 5,5))
192                 {
193                         return NULL;
194                 }
195                 flags = BARCODE_ISBN;
196         }
197         else if (g_ascii_strcasecmp (id, "Code39") == 0)
198         {
199                 flags = BARCODE_39;
200         }
201         else if (g_ascii_strcasecmp (id, "Code128") == 0)
202         {
203                 flags = BARCODE_128;
204         }
205         else if (g_ascii_strcasecmp (id, "Code128C") == 0)
206         {
207                 flags = BARCODE_128C;
208         }
209         else if (g_ascii_strcasecmp (id, "Code128B") == 0)
210         {
211                 flags = BARCODE_128B;
212         }
213         else if (g_ascii_strcasecmp (id, "I25") == 0)
214         {
215                 flags = BARCODE_I25;
216         }
217         else if (g_ascii_strcasecmp (id, "CBR") == 0)
218         {
219                 flags = BARCODE_CBR;
220         }
221         else if (g_ascii_strcasecmp (id, "MSI") == 0)
222         {
223                 flags = BARCODE_MSI;
224         }
225         else if (g_ascii_strcasecmp (id, "PLS") == 0)
226         {
227                 flags = BARCODE_PLS;
228         }
229         else if (g_ascii_strcasecmp (id, "Code93") == 0)
230         {
231                 flags = BARCODE_93;
232         }
233         else
234         {
235                 g_message( "Illegal barcode id %s", id );
236                 flags = BARCODE_ANY;
237         }
238
239
240         bci = Barcode_Create ((char *)digits);
241
242         /* First encode using GNU Barcode library */
243         if (!text_flag)
244         {
245                 flags |= BARCODE_NO_ASCII;
246         }
247         if (!checksum_flag)
248         {
249                 flags |= BARCODE_NO_CHECKSUM;
250         }
251
252         bci->scalef = 0.0;
253         bci->width  = w;
254         bci->height = h;
255
256         Barcode_Encode (bci, flags);
257         if (!bci->partial || !bci->textinfo)
258         {
259                 Barcode_Delete (bci);
260                 return NULL;
261         }
262
263         /* now render with our custom back-end,
264            to create appropriate intermdediate format */
265         gbc = render_pass1 (bci, flags);
266
267         Barcode_Delete (bci);
268         return gbc;
269 }
270
271
272 /*--------------------------------------------------------------------------
273  * PRIVATE.  Render to lglBarcode intermediate representation of barcode.
274  *
275  *  Some of this code is borrowed from the postscript renderer (ps.c)
276  *  from the GNU barcode library:
277  *
278  *     Copyright (C) 1999 Alessaandro Rubini (rubini@gnu.org)
279  *     Copyright (C) 1999 Prosa Srl. (prosa@prosa.it)
280  *
281  *--------------------------------------------------------------------------*/
282 static lglBarcode *
283 render_pass1 (struct Barcode_Item *bci,
284               gint                 flags)
285 {
286         gint                 validbits = BARCODE_NO_ASCII;
287         lglBarcode          *gbc;
288         gdouble              scalef = 1.0;
289         gdouble              x;
290         gint                 i, j, barlen;
291         gdouble              f1, f2;
292         gint                 mode = '-'; /* text below bars */
293         gdouble              x0, y0, yr;
294         gchar               *p, c;
295
296         if (bci->width > (2*bci->margin))
297         {
298                 bci->width -= 2*bci->margin;
299         }
300         if (bci->height > (2*bci->margin))
301         {
302                 bci->height -= 2*bci->margin;
303         }
304
305         /* If any flag is clear in "flags", inherit it from "bci->flags" */
306         if (!(flags & BARCODE_NO_ASCII))
307         {
308                 flags |= bci->flags & BARCODE_NO_ASCII;
309         }
310         flags = bci->flags = (flags & validbits) | (bci->flags & ~validbits);
311
312         /* First calculate barlen */
313         barlen = bci->partial[0] - '0';
314         for (p = bci->partial + 1; *p != 0; p++)
315         {
316                 if (isdigit (*p))
317                 {
318                         barlen += *p - '0';
319                 }
320                 else
321                 {
322                         if ((*p != '+') && (*p != '-'))
323                         {
324                                 barlen += *p - 'a' + 1;
325                         }
326                 }
327         }
328
329         /* The scale factor depends on bar length */
330         if (!bci->scalef)
331         {
332                 if (!bci->width)
333                 {
334                         bci->width = barlen; /* default */
335                 }
336                 scalef = bci->scalef = (double)bci->width / (double)barlen;
337                 if (scalef < 0.5)
338                 {
339                         scalef = 0.5;
340                 }
341         }
342
343         /* The width defaults to "just enough" */
344         bci->width = barlen * scalef + 1;
345
346         /* But it can be too small, in this case enlarge and center the area */
347         if (bci->width < barlen * scalef)
348         {
349                 int wid = barlen * scalef + 1;
350                 bci->xoff -= (wid - bci->width)/2 ;
351                 bci->width = wid;
352                 /* Can't extend too far on the left */
353                 if (bci->xoff < 0)
354                 {
355                         bci->width += -bci->xoff;
356                         bci->xoff = 0;
357                 }
358         }
359
360         /* The height defaults to 80 points (rescaled) */
361         if (!bci->height)
362         {
363                 bci->height = 80 * scalef;
364         }
365
366         /* If too small (5 + text), reduce the scale factor and center */
367         i = 5 + 10 * ((bci->flags & BARCODE_NO_ASCII)==0);
368         if (bci->height < i * scalef )
369         {
370                 bci->height = i * scalef;
371         }
372
373         gbc = lgl_barcode_new ();
374
375         /* Now traverse the code string and create a list of lines */
376         x = bci->margin + (bci->partial[0] - '0') * scalef;
377         for (p = bci->partial + 1, i = 1; *p != 0; p++, i++)
378         {
379                 /* special cases: '+' and '-' */
380                 if (*p == '+' || *p == '-')
381                 {
382                         mode = *p;        /* don't count it */
383                         i++;
384                         continue;
385                 }
386                 /* j is the width of this bar/space */
387                 if (isdigit (*p))
388                 {
389                         j = *p - '0';
390                 }
391                 else
392                 {
393                         j = *p - 'a' + 1;
394                 }
395                 if (i % 2)
396                 {
397                         /* bar */
398                         x0 = x + (j * scalef) / 2;
399                         y0 = bci->margin;
400                         yr = bci->height;
401                         if (!(bci->flags & BARCODE_NO_ASCII))
402                         {
403                                 /* leave space for text */
404                                 if (mode == '-')
405                                 {
406                                         /* text below bars: 10 or 5 points */
407                                         yr -= (isdigit (*p) ? 10 : 5) * scalef;
408                                 }
409                                 else
410                                 {
411                                         /* '+' */
412                                         /* above bars: 10 or 0 from bottom,
413                                            and 10 from top */
414                                         y0 += 10 * scalef;
415                                         yr -= (isdigit (*p) ? 20 : 10) * scalef;
416                                 }
417                         }
418                         lgl_barcode_add_line (gbc, x0, y0, yr, (j * scalef) - SHRINK_AMOUNT);
419                 }
420                 x += j * scalef;
421
422         }
423
424         /* Now the text */
425         mode = '-';                /* reinstantiate default */
426         if (!(bci->flags & BARCODE_NO_ASCII))
427         {
428                 for (p = bci->textinfo; p; p = strchr (p, ' '))
429                 {
430                         while (*p == ' ')
431                         {
432                                 p++;
433                         }
434                         if (!*p)
435                         {
436                                 break;
437                         }
438                         if (*p == '+' || *p == '-')
439                         {
440                                 mode = *p;
441                                 continue;
442                         }
443                         if (sscanf (p, "%lf:%lf:%c", &f1, &f2, &c) != 3)
444                         {
445                                 g_message ("impossible data: %s", p);
446                                 continue;
447                         }
448                         x0 = f1 * scalef + bci->margin;
449                         if (mode == '-')
450                         {
451                                 y0 = bci->margin + bci->height - 8 * scalef;
452                         }
453                         else
454                         {
455                                 y0 = bci->margin;
456                         }
457                         lgl_barcode_add_char (gbc, x0, y0, (f2 * FONT_SCALE * scalef), c);
458                 }
459         }
460
461         /* Fill in other info */
462         gbc->height = bci->height + 2.0 * bci->margin;
463         gbc->width = bci->width + 2.0 * bci->margin;
464
465         return gbc;
466 }
467
468
469 /*--------------------------------------------------------------------------*/
470 /* Validate specific length of string (for subtypes).                       */
471 /*--------------------------------------------------------------------------*/
472 static gboolean
473 is_length_valid (const gchar *digits,
474                  gint         n1,
475                  gint         n2)
476 {
477         gchar *p;
478         gint   i;
479
480         if (!digits)
481         {
482                 return FALSE;
483         }
484
485         for (p = (gchar *)digits, i=0; *p != 0; p++)
486         {
487                 if (g_ascii_isdigit (*p))
488                 {
489                         i++;
490                 }
491         }
492
493         return (i >= n1) && (i <= n2);
494 }
495
496
497 /*--------------------------------------------------------------------------*/
498 /* Validate specific length of string (for subtypes).                       */
499 /*--------------------------------------------------------------------------*/
500 static gboolean
501 is_length1_valid (const gchar *digits,
502                   gint         n1,
503                   gint         n2)
504 {
505         gchar *p;
506         gint   i;
507
508         if (!digits)
509         {
510                 return FALSE;
511         }
512
513         for (p = (gchar *)digits, i=0; !g_ascii_isspace (*p) && *p != 0; p++)
514         {
515                 if (g_ascii_isdigit (*p))
516                 {
517                         i++;
518                 }
519         }
520
521         return (i >= n1) && (i <= n2);
522 }
523
524
525 /*--------------------------------------------------------------------------*/
526 /* Validate specific length of second string (for subtypes).                */
527 /*--------------------------------------------------------------------------*/
528 static gboolean
529 is_length2_valid (const gchar *digits,
530                   gint         n1,
531                   gint         n2)
532 {
533         gchar *p;
534         gint   i;
535
536         if (!digits)
537         {
538                 return FALSE;
539         }
540
541         for (p = (gchar *)digits; !g_ascii_isspace (*p) && (*p != 0); p++)
542         {
543                 /* Skip over 1st string */
544         }
545
546         for (i=0; *p != 0; p++)
547         {
548                 if (g_ascii_isdigit (*p))
549                 {
550                         i++;
551                 }
552         }
553
554         return (i >= n1) && (i <= n2);
555 }
556
557 #endif /* HAVE_LIBBARCODE */
558
559
560
561 /*
562  * Local Variables:       -- emacs
563  * mode: C                -- emacs
564  * c-basic-offset: 8      -- emacs
565  * tab-width: 8           -- emacs
566  * indent-tabs-mode: nil  -- emacs
567  * End:                   -- emacs
568  */