]> git.sur5r.net Git - glabels/blob - src/bc-gnubarcode.c
88b9e280834ad7846d06d13b710d0b7988a2099e
[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                 flags = BARCODE_EAN;
79         } else if (g_ascii_strcasecmp (id, "EAN-8") == 0) {
80                 if (!is_length_valid (digits, 7, 8)) {
81                         return NULL;
82                 }
83                 flags = BARCODE_EAN;
84         } else if (g_ascii_strcasecmp (id, "EAN-8+2") == 0) {
85                 if (!is_length1_valid (digits, 7, 8) || !is_length2_valid (digits, 2, 2)) {
86                         return NULL;
87                 }
88                 flags = BARCODE_EAN;
89         } else if (g_ascii_strcasecmp (id, "EAN-8+5") == 0) {
90                 if (!is_length1_valid (digits, 7, 8) || !is_length2_valid (digits, 5, 5)) {
91                         return NULL;
92                 }
93                 flags = BARCODE_EAN;
94         } else if (g_ascii_strcasecmp (id, "EAN-13") == 0) {
95                 if (!is_length_valid (digits, 12, 13)) {
96                         return NULL;
97                 }
98                 flags = BARCODE_EAN;
99         } else if (g_ascii_strcasecmp (id, "EAN-13+2") == 0) {
100                 if (!is_length1_valid (digits, 12,13) || !is_length2_valid (digits, 2,2)) {
101                         return NULL;
102                 }
103                 flags = BARCODE_EAN;
104         } else if (g_ascii_strcasecmp (id, "EAN-13+5") == 0) {
105                 if (!is_length1_valid (digits, 12,13) || !is_length2_valid (digits, 5,5)) {
106                         return NULL;
107                 }
108                 flags = BARCODE_EAN;
109         } else if (g_ascii_strcasecmp (id, "UPC") == 0) {
110                 flags = BARCODE_UPC;
111         } else if (g_ascii_strcasecmp (id, "UPC-A") == 0) {
112                 if (!is_length_valid (digits, 11, 12)) {
113                         return NULL;
114                 }
115                 flags = BARCODE_UPC;
116         } else if (g_ascii_strcasecmp (id, "UPC-A+2") == 0) {
117                 if (!is_length1_valid (digits, 11,12) || !is_length2_valid (digits, 2,2)) {
118                         return NULL;
119                 }
120                 flags = BARCODE_UPC;
121         } else if (g_ascii_strcasecmp (id, "UPC-A+5") == 0) {
122                 if (!is_length1_valid (digits, 11,12) || !is_length2_valid (digits, 5,5)) {
123                         return NULL;
124                 }
125                 flags = BARCODE_UPC;
126         } else if (g_ascii_strcasecmp (id, "UPC-E") == 0) {
127                 if (!is_length_valid (digits, 6, 8)) {
128                         return NULL;
129                 }
130                 flags = BARCODE_UPC;
131         } else if (g_ascii_strcasecmp (id, "UPC-E+2") == 0) {
132                 if (!is_length1_valid (digits, 6, 8) || !is_length2_valid (digits, 2,2)) {
133                         return NULL;
134                 }
135                 flags = BARCODE_UPC;
136         } else if (g_ascii_strcasecmp (id, "UPC-E+5") == 0) {
137                 if (!is_length1_valid (digits, 6, 8) || !is_length2_valid (digits, 5,5)) {
138                         return NULL;
139                 }
140                 flags = BARCODE_UPC;
141         } else if (g_ascii_strcasecmp (id, "ISBN") == 0) {
142                 if (!is_length_valid (digits, 9, 10)) {
143                         return NULL;
144                 }
145                 flags = BARCODE_ISBN;
146         } else if (g_ascii_strcasecmp (id, "ISBN+5") == 0) {
147                 if (!is_length1_valid (digits, 9, 10) || !is_length2_valid (digits, 5,5)) {
148                         return NULL;
149                 }
150                 flags = BARCODE_ISBN;
151         } else if (g_ascii_strcasecmp (id, "Code39") == 0) {
152                 flags = BARCODE_39;
153         } else if (g_ascii_strcasecmp (id, "Code128") == 0) {
154                 flags = BARCODE_128;
155         } else if (g_ascii_strcasecmp (id, "Code128C") == 0) {
156                 flags = BARCODE_128C;
157         } else if (g_ascii_strcasecmp (id, "Code128B") == 0) {
158                 flags = BARCODE_128B;
159         } else if (g_ascii_strcasecmp (id, "I25") == 0) {
160                 flags = BARCODE_I25;
161         } else if (g_ascii_strcasecmp (id, "CBR") == 0) {
162                 flags = BARCODE_CBR;
163         } else if (g_ascii_strcasecmp (id, "MSI") == 0) {
164                 flags = BARCODE_MSI;
165         } else if (g_ascii_strcasecmp (id, "PLS") == 0) {
166                 flags = BARCODE_PLS;
167         } else if (g_ascii_strcasecmp (id, "Code93") == 0) {
168                 flags = BARCODE_93;
169         } else {
170                 g_message( "Illegal barcode id %s", id );
171                 flags = BARCODE_ANY;
172         }
173
174
175         bci = Barcode_Create ((char *)digits);
176
177         /* First encode using GNU Barcode library */
178         if (!text_flag) {
179                 flags |= BARCODE_NO_ASCII;
180         }
181         if (!checksum_flag) {
182                 flags |= BARCODE_NO_CHECKSUM;
183         }
184
185         bci->scalef = 0.0;
186         bci->width  = w;
187         bci->height = h;
188
189         Barcode_Encode (bci, flags);
190         if (!bci->partial || !bci->textinfo) {
191                 Barcode_Delete (bci);
192                 return NULL;
193         }
194
195         /* now render with our custom back-end,
196            to create appropriate intermdediate format */
197         gbc = render_pass1 (bci, flags);
198
199         Barcode_Delete (bci);
200         return gbc;
201 }
202
203
204 /*--------------------------------------------------------------------------
205  * PRIVATE.  Render to lglBarcode intermediate representation of barcode.
206  *
207  *  Some of this code is borrowed from the postscript renderer (ps.c)
208  *  from the GNU barcode library:
209  *
210  *     Copyright (C) 1999 Alessaandro Rubini (rubini@gnu.org)
211  *     Copyright (C) 1999 Prosa Srl. (prosa@prosa.it)
212  *
213  *--------------------------------------------------------------------------*/
214 static lglBarcode *
215 render_pass1 (struct Barcode_Item *bci,
216               gint                 flags)
217 {
218         gint                 validbits = BARCODE_NO_ASCII;
219         lglBarcode          *gbc;
220         gdouble              scalef = 1.0;
221         gdouble              x;
222         gint                 i, j, barlen;
223         gdouble              f1, f2;
224         gint                 mode = '-'; /* text below bars */
225         gdouble              x0, y0, yr;
226         gchar               *p, c;
227
228         if (bci->width > (2*bci->margin)) {
229                 bci->width -= 2*bci->margin;
230         }
231         if (bci->height > (2*bci->margin)) {
232                 bci->height -= 2*bci->margin;
233         }
234
235         /* If any flag is clear in "flags", inherit it from "bci->flags" */
236         if (!(flags & BARCODE_NO_ASCII)) {
237                 flags |= bci->flags & BARCODE_NO_ASCII;
238         }
239         flags = bci->flags = (flags & validbits) | (bci->flags & ~validbits);
240
241         /* First calculate barlen */
242         barlen = bci->partial[0] - '0';
243         for (p = bci->partial + 1; *p != 0; p++) {
244                 if (isdigit (*p)) {
245                         barlen += *p - '0';
246                 } else {
247                         if ((*p != '+') && (*p != '-')) {
248                                 barlen += *p - 'a' + 1;
249                         }
250                 }
251         }
252
253         /* The scale factor depends on bar length */
254         if (!bci->scalef) {
255                 if (!bci->width) bci->width = barlen; /* default */
256                 scalef = bci->scalef = (double)bci->width / (double)barlen;
257                 if (scalef < 0.5) scalef = 0.5;
258         }
259
260         /* The width defaults to "just enough" */
261         bci->width = barlen * scalef + 1;
262
263         /* But it can be too small, in this case enlarge and center the area */
264         if (bci->width < barlen * scalef) {
265                 int wid = barlen * scalef + 1;
266                 bci->xoff -= (wid - bci->width)/2 ;
267                 bci->width = wid;
268                 /* Can't extend too far on the left */
269                 if (bci->xoff < 0) {
270                         bci->width += -bci->xoff;
271                         bci->xoff = 0;
272                 }
273         }
274
275         /* The height defaults to 80 points (rescaled) */
276         if (!bci->height)
277                 bci->height = 80 * scalef;
278
279         /* If too small (5 + text), reduce the scale factor and center */
280         i = 5 + 10 * ((bci->flags & BARCODE_NO_ASCII)==0);
281         if (bci->height < i * scalef ) {
282                 bci->height = i * scalef;
283         }
284
285         gbc = lgl_barcode_new ();
286
287         /* Now traverse the code string and create a list of lines */
288         x = bci->margin + (bci->partial[0] - '0') * scalef;
289         for (p = bci->partial + 1, i = 1; *p != 0; p++, i++) {
290                 /* special cases: '+' and '-' */
291                 if (*p == '+' || *p == '-') {
292                         mode = *p;      /* don't count it */
293                         i++;
294                         continue;
295                 }
296                 /* j is the width of this bar/space */
297                 if (isdigit (*p))
298                         j = *p - '0';
299                 else
300                         j = *p - 'a' + 1;
301                 if (i % 2) {    /* bar */
302                         x0 = x + (j * scalef) / 2;
303                         y0 = bci->margin;
304                         yr = bci->height;
305                         if (!(bci->flags & BARCODE_NO_ASCII)) { /* leave space for text */
306                                 if (mode == '-') {
307                                         /* text below bars: 10 or 5 points */
308                                         yr -= (isdigit (*p) ? 10 : 5) * scalef;
309                                 } else {        /* '+' */
310                                         /* above bars: 10 or 0 from bottom,
311                                            and 10 from top */
312                                         y0 += 10 * scalef;
313                                         yr -= (isdigit (*p) ? 20 : 10) * scalef;
314                                 }
315                         }
316                         lgl_barcode_add_line (gbc, x0, y0, yr, (j * scalef) - SHRINK_AMOUNT);
317                 }
318                 x += j * scalef;
319
320         }
321
322         /* Now the text */
323         mode = '-';             /* reinstantiate default */
324         if (!(bci->flags & BARCODE_NO_ASCII)) {
325                 for (p = bci->textinfo; p; p = strchr (p, ' ')) {
326                         while (*p == ' ')
327                                 p++;
328                         if (!*p)
329                                 break;
330                         if (*p == '+' || *p == '-') {
331                                 mode = *p;
332                                 continue;
333                         }
334                         if (sscanf (p, "%lf:%lf:%c", &f1, &f2, &c) != 3) {
335                                 g_message ("impossible data: %s", p);
336                                 continue;
337                         }
338                         x0 = f1 * scalef + bci->margin;
339                         if (mode == '-') {
340                                 y0 = bci->margin + bci->height - 8 * scalef;
341                         } else {
342                                 y0 = bci->margin;
343                         }
344                         lgl_barcode_add_char (gbc, x0, y0, (f2 * FONT_SCALE * scalef), c);
345                 }
346         }
347
348         /* Fill in other info */
349         gbc->height = bci->height + 2.0 * bci->margin;
350         gbc->width = bci->width + 2.0 * bci->margin;
351
352         return gbc;
353 }
354
355
356 /*--------------------------------------------------------------------------*/
357 /* Validate specific length of string (for subtypes).                       */
358 /*--------------------------------------------------------------------------*/
359 static gboolean
360 is_length_valid (const gchar *digits,
361                  gint         n1,
362                  gint         n2)
363 {
364         gchar *p;
365         gint   i;
366
367         if (!digits) {
368                 return FALSE;
369         }
370
371         for (p = (gchar *)digits, i=0; *p != 0; p++) {
372                 if (g_ascii_isdigit (*p)) {
373                         i++;
374                 }
375         }
376
377         return (i >= n1) && (i <= n2);
378 }
379
380
381 /*--------------------------------------------------------------------------*/
382 /* Validate specific length of string (for subtypes).                       */
383 /*--------------------------------------------------------------------------*/
384 static gboolean
385 is_length1_valid (const gchar *digits,
386                   gint         n1,
387                   gint         n2)
388 {
389         gchar *p;
390         gint   i;
391
392         if (!digits) {
393                 return FALSE;
394         }
395
396         for (p = (gchar *)digits, i=0; !g_ascii_isspace (*p) && *p != 0; p++) {
397                 if (g_ascii_isdigit (*p)) {
398                         i++;
399                 }
400         }
401
402         return (i >= n1) && (i <= n2);
403 }
404
405
406 /*--------------------------------------------------------------------------*/
407 /* Validate specific length of second string (for subtypes).                */
408 /*--------------------------------------------------------------------------*/
409 static gboolean
410 is_length2_valid (const gchar *digits,
411                   gint         n1,
412                   gint         n2)
413 {
414         gchar *p;
415         gint   i;
416
417         if (!digits) {
418                 return FALSE;
419         }
420
421         for (p = (gchar *)digits; !g_ascii_isspace (*p) && (*p != 0); p++) {
422                 /* Skip over 1st string */
423         }
424
425         for (i=0; *p != 0; p++) {
426                 if (g_ascii_isdigit (*p)) {
427                         i++;
428                 }
429         }
430
431         return (i >= n1) && (i <= n2);
432 }
433
434 #endif /* HAVE_LIBBARCODE */
435
436
437
438 /*
439  * Local Variables:       -- emacs
440  * mode: C                -- emacs
441  * c-basic-offset: 8      -- emacs
442  * tab-width: 8           -- emacs
443  * indent-tabs-mode: nil  -- emacs
444  * End:                   -- emacs
445  */