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