]> git.sur5r.net Git - glabels/blob - src/bc-gnubarcode.c
Provides barcode even if invalid for correct sizing
[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 glBarcode *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 glBarcode *
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         glBarcode           *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 glBarcode 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 glBarcode *
215 render_pass1 (struct Barcode_Item *bci,
216               gint                 flags)
217 {
218         gint                 validbits = BARCODE_NO_ASCII;
219         glBarcode           *gbc;
220         glBarcodeShapeLine  *line;
221         glBarcodeShapeAlpha *bchar;
222         gdouble              scalef = 1.0;
223         gdouble              x;
224         gint                 i, j, barlen;
225         gdouble              f1, f2;
226         gint                 mode = '-'; /* text below bars */
227         gdouble              x0, y0, yr;
228         gchar               *p, c;
229
230         if (bci->width > (2*bci->margin)) {
231                 bci->width -= 2*bci->margin;
232         }
233         if (bci->height > (2*bci->margin)) {
234                 bci->height -= 2*bci->margin;
235         }
236
237         /* If any flag is clear in "flags", inherit it from "bci->flags" */
238         if (!(flags & BARCODE_NO_ASCII)) {
239                 flags |= bci->flags & BARCODE_NO_ASCII;
240         }
241         flags = bci->flags = (flags & validbits) | (bci->flags & ~validbits);
242
243         /* First calculate barlen */
244         barlen = bci->partial[0] - '0';
245         for (p = bci->partial + 1; *p != 0; p++) {
246                 if (isdigit (*p)) {
247                         barlen += *p - '0';
248                 } else {
249                         if ((*p != '+') && (*p != '-')) {
250                                 barlen += *p - 'a' + 1;
251                         }
252                 }
253         }
254
255         /* The scale factor depends on bar length */
256         if (!bci->scalef) {
257                 if (!bci->width) bci->width = barlen; /* default */
258                 scalef = bci->scalef = (double)bci->width / (double)barlen;
259                 if (scalef < 0.5) scalef = 0.5;
260         }
261
262         /* The width defaults to "just enough" */
263         bci->width = barlen * scalef + 1;
264
265         /* But it can be too small, in this case enlarge and center the area */
266         if (bci->width < barlen * scalef) {
267                 int wid = barlen * scalef + 1;
268                 bci->xoff -= (wid - bci->width)/2 ;
269                 bci->width = wid;
270                 /* Can't extend too far on the left */
271                 if (bci->xoff < 0) {
272                         bci->width += -bci->xoff;
273                         bci->xoff = 0;
274                 }
275         }
276
277         /* The height defaults to 80 points (rescaled) */
278         if (!bci->height)
279                 bci->height = 80 * scalef;
280
281         /* If too small (5 + text), reduce the scale factor and center */
282         i = 5 + 10 * ((bci->flags & BARCODE_NO_ASCII)==0);
283         if (bci->height < i * scalef ) {
284                 bci->height = i * scalef;
285         }
286
287         gbc = g_new0 (glBarcode, 1);
288
289         /* Now traverse the code string and create a list of lines */
290         x = bci->margin + (bci->partial[0] - '0') * scalef;
291         for (p = bci->partial + 1, i = 1; *p != 0; p++, i++) {
292                 /* special cases: '+' and '-' */
293                 if (*p == '+' || *p == '-') {
294                         mode = *p;      /* don't count it */
295                         i++;
296                         continue;
297                 }
298                 /* j is the width of this bar/space */
299                 if (isdigit (*p))
300                         j = *p - '0';
301                 else
302                         j = *p - 'a' + 1;
303                 if (i % 2) {    /* bar */
304                         x0 = x + (j * scalef) / 2;
305                         y0 = bci->margin;
306                         yr = bci->height;
307                         if (!(bci->flags & BARCODE_NO_ASCII)) { /* leave space for text */
308                                 if (mode == '-') {
309                                         /* text below bars: 10 or 5 points */
310                                         yr -= (isdigit (*p) ? 10 : 5) * scalef;
311                                 } else {        /* '+' */
312                                         /* above bars: 10 or 0 from bottom,
313                                            and 10 from top */
314                                         y0 += 10 * scalef;
315                                         yr -= (isdigit (*p) ? 20 : 10) * scalef;
316                                 }
317                         }
318                         line = gl_barcode_shape_line_new ();
319                         line->x = x0;
320                         line->y = y0;
321                         line->length = yr;
322                         line->width = (j * scalef) - SHRINK_AMOUNT;
323                         gl_barcode_add_shape (gbc, (glBarcodeShape *)line);
324                 }
325                 x += j * scalef;
326
327         }
328
329         /* Now the text */
330         mode = '-';             /* reinstantiate default */
331         if (!(bci->flags & BARCODE_NO_ASCII)) {
332                 for (p = bci->textinfo; p; p = strchr (p, ' ')) {
333                         while (*p == ' ')
334                                 p++;
335                         if (!*p)
336                                 break;
337                         if (*p == '+' || *p == '-') {
338                                 mode = *p;
339                                 continue;
340                         }
341                         if (sscanf (p, "%lf:%lf:%c", &f1, &f2, &c) != 3) {
342                                 g_message ("impossible data: %s", p);
343                                 continue;
344                         }
345                         bchar = gl_barcode_shape_alpha_new ();
346                         bchar->x = f1 * scalef + bci->margin;
347                         if (mode == '-') {
348                                 bchar->y =
349                                     bci->margin + bci->height - 8 * scalef;
350                         } else {
351                                 bchar->y = bci->margin;
352                         }
353                         bchar->fsize = f2 * FONT_SCALE * scalef;
354                         bchar->c = c;
355                         gl_barcode_add_shape (gbc, (glBarcodeShape *)bchar);
356                 }
357         }
358
359         /* Fill in other info */
360         gbc->height = bci->height + 2.0 * bci->margin;
361         gbc->width = bci->width + 2.0 * bci->margin;
362
363         return gbc;
364 }
365
366
367 /*--------------------------------------------------------------------------*/
368 /* Validate specific length of string (for subtypes).                       */
369 /*--------------------------------------------------------------------------*/
370 static gboolean
371 is_length_valid (const gchar *digits,
372                  gint         n1,
373                  gint         n2)
374 {
375         gchar *p;
376         gint   i;
377
378         if (!digits) {
379                 return FALSE;
380         }
381
382         for (p = (gchar *)digits, i=0; *p != 0; p++) {
383                 if (g_ascii_isdigit (*p)) {
384                         i++;
385                 }
386         }
387
388         return (i >= n1) && (i <= n2);
389 }
390
391
392 /*--------------------------------------------------------------------------*/
393 /* Validate specific length of string (for subtypes).                       */
394 /*--------------------------------------------------------------------------*/
395 static gboolean
396 is_length1_valid (const gchar *digits,
397                   gint         n1,
398                   gint         n2)
399 {
400         gchar *p;
401         gint   i;
402
403         if (!digits) {
404                 return FALSE;
405         }
406
407         for (p = (gchar *)digits, i=0; !g_ascii_isspace (*p) && *p != 0; p++) {
408                 if (g_ascii_isdigit (*p)) {
409                         i++;
410                 }
411         }
412
413         return (i >= n1) && (i <= n2);
414 }
415
416
417 /*--------------------------------------------------------------------------*/
418 /* Validate specific length of second string (for subtypes).                */
419 /*--------------------------------------------------------------------------*/
420 static gboolean
421 is_length2_valid (const gchar *digits,
422                   gint         n1,
423                   gint         n2)
424 {
425         gchar *p;
426         gint   i;
427
428         if (!digits) {
429                 return FALSE;
430         }
431
432         for (p = (gchar *)digits; !g_ascii_isspace (*p) && (*p != 0); p++) {
433                 /* Skip over 1st string */
434         }
435
436         for (i=0; *p != 0; p++) {
437                 if (g_ascii_isdigit (*p)) {
438                         i++;
439                 }
440         }
441
442         return (i >= n1) && (i <= n2);
443 }
444
445 #endif /* HAVE_LIBBARCODE */
446
447
448
449 /*
450  * Local Variables:       -- emacs
451  * mode: C                -- emacs
452  * c-basic-offset: 8      -- emacs
453  * tab-width: 8           -- emacs
454  * indent-tabs-mode: nil  -- emacs
455  * End:                   -- emacs
456  */