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