]> git.sur5r.net Git - glabels/blob - libglbarcode/lgl-barcode-code39.c
Fixed potential memory leak.
[glabels] / libglbarcode / lgl-barcode-code39.c
1 /*
2  *  lgl-barcode-code39.c
3  *  Copyright (C) 2001-2010  Jim Evins <evins@snaught.com>.
4  *
5  *  This file is part of libglbarcode.
6  *
7  *  libglbarcode is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser 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  *  libglbarcode 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 Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with libglbarcode.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include "lgl-barcode-code39.h"
24
25 #include <glib.h>
26 #include <ctype.h>
27 #include <string.h>
28
29
30 /*========================================================*/
31 /* Private macros and constants.                          */
32 /*========================================================*/
33
34 #define PTS_PER_INCH 72.0
35
36 #define MIN_X        ( 0.01 *  PTS_PER_INCH )
37 #define N            2.5
38 #define MIN_I        MIN_X
39 #define MIN_HEIGHT   ( 0.25 *  PTS_PER_INCH )
40 #define MIN_QUIET    ( 0.10 *  PTS_PER_INCH )
41
42 #define INK_BLEED    ( 0.00325 * PTS_PER_INCH )
43
44 #define TEXT_AREA_HEIGHT 14.0
45 #define TEXT_SIZE        10.0
46
47
48 /*========================================================*/
49 /* Private types.                                         */
50 /*========================================================*/
51
52 typedef struct {
53         gchar  c;
54         gchar *sym;
55 } Code39Symbol;
56
57
58 /*===========================================*/
59 /* Private globals                           */
60 /*===========================================*/
61
62 static Code39Symbol symbols[] = {
63         /*      BsBsBsBsB */
64         { '0', "NnNwWnWnN" },
65         { '1', "WnNwNnNnW" },
66         { '2', "NnWwNnNnW" },
67         { '3', "WnWwNnNnN" },
68         { '4', "NnNwWnNnW" },
69         { '5', "WnNwWnNnN" },
70         { '6', "NnWwWnNnN" },
71         { '7', "NnNwNnWnW" },
72         { '8', "WnNwNnWnN" },
73         { '9', "NnWwNnWnN" },
74         { 'A', "WnNnNwNnW" },
75         { 'B', "NnWnNwNnW" },
76         { 'C', "WnWnNwNnN" },
77         { 'D', "NnNnWwNnW" },
78         { 'E', "WnNnWwNnN" },
79         { 'F', "NnWnWwNnN" },
80         { 'G', "NnNnNwWnW" },
81         { 'H', "WnNnNwWnN" },
82         { 'I', "NnWnNwWnN" },
83         { 'J', "NnNnWwWnN" },
84         { 'K', "WnNnNnNwW" },
85         { 'L', "NnWnNnNwW" },
86         { 'M', "WnWnNnNwN" },
87         { 'N', "NnNnWnNwW" },
88         { 'O', "WnNnWnNwN" },
89         { 'P', "NnWnWnNwN" },
90         { 'Q', "NnNnNnWwW" },
91         { 'R', "WnNnNnWwN" },
92         { 'S', "NnWnNnWwN" },
93         { 'T', "NnNnWnWwN" },
94         { 'U', "WwNnNnNnW" },
95         { 'V', "NwWnNnNnW" },
96         { 'W', "WwWnNnNnN" },
97         { 'X', "NwNnWnNnW" },
98         { 'Y', "WwNnWnNnN" },
99         { 'Z', "NwWnWnNnN" },
100         { '-', "NwNnNnWnW" },
101         { '.', "WwNnNnWnN" },
102         { ' ', "NwWnNnWnN" },
103         { '$', "NwNwNwNnN" },
104         { '/', "NwNwNnNwN" },
105         { '+', "NwNnNwNwN" },
106         { '%', "NnNwNwNwN" },
107         { 0, NULL }
108 };
109
110 static gchar *frame_symbol = "NwNnWnWnN";
111
112 static gchar *ascii_map[128] =
113 {
114         /* NUL */ "%U",   /* SOH */ "$A",   /* STX */ "$B",   /* ETX */ "$C",
115         /* EOT */ "$D",   /* ENQ */ "$E",   /* ACK */ "$F",   /* BEL */ "$G",
116         /* BS  */ "$H",   /* HT  */ "$I",   /* LF  */ "$J",   /* VT  */ "$K",
117         /* FF  */ "$L",   /* CR  */ "$M",   /* SO  */ "$N",   /* SI  */ "$O",
118         /* DLE */ "$P",   /* DC1 */ "$Q",   /* DC2 */ "$R",   /* DC3 */ "$S",
119         /* DC4 */ "$T",   /* NAK */ "$U",   /* SYN */ "$V",   /* ETB */ "$W",
120         /* CAN */ "$X",   /* EM  */ "$Y",   /* SUB */ "$Z",   /* ESC */ "%A",
121         /* FS  */ "%B",   /* GS  */ "%C",   /* RS  */ "%D",   /* US  */ "%E",
122         /* " " */ " ",    /* !   */ "/A",   /* "   */ "/B",   /* #   */ "/C",
123         /* $   */ "/D",   /* %   */ "/E",   /* &   */ "/F",   /* '   */ "/G",
124         /* (   */ "/H",   /* )   */ "/I",   /* *   */ "/J",   /* +   */ "/K",
125         /* ,   */ "/L",   /* -   */ "-",    /* .   */ ".",    /* /   */ "/O",
126         /* 0   */ "0",    /* 1   */ "1",    /* 2   */ "2",    /* 3   */ "3",
127         /* 4   */ "4",    /* 5   */ "5",    /* 6   */ "6",    /* 7   */ "7",
128         /* 8   */ "8",    /* 9   */ "9",    /* :   */ "/Z",   /* ;   */ "%F",
129         /* <   */ "%G",   /* =   */ "%H",   /* >   */ "%I",   /* ?   */ "%J",
130         /* @   */ "%V",   /* A   */ "A",    /* B   */ "B",    /* C   */ "C",
131         /* D   */ "D",    /* E   */ "E",    /* F   */ "F",    /* G   */ "G",
132         /* H   */ "H",    /* I   */ "I",    /* J   */ "J",    /* K   */ "K",
133         /* L   */ "L",    /* M   */ "M",    /* N   */ "N",    /* O   */ "O",
134         /* P   */ "P",    /* Q   */ "Q",    /* R   */ "R",    /* S   */ "S",
135         /* T   */ "T",    /* U   */ "U",    /* V   */ "V",    /* W   */ "W",
136         /* X   */ "X",    /* Y   */ "Y",    /* Z   */ "Z",    /* [   */ "%K",
137         /* \   */ "%L",   /* ]   */ "%M",   /* ^   */ "%N",   /* _   */ "%O",
138         /* `   */ "%W",   /* a   */ "+A",   /* b   */ "+B",   /* c   */ "+C",
139         /* d   */ "+D",   /* e   */ "+E",   /* f   */ "+F",   /* g   */ "+G",
140         /* h   */ "+H",   /* i   */ "+I",   /* j   */ "+J",   /* k   */ "+K",
141         /* l   */ "+L",   /* m   */ "+M",   /* n   */ "+N",   /* o   */ "+O",
142         /* p   */ "+P",   /* q   */ "+Q",   /* r   */ "+R",   /* s   */ "+S",
143         /* t   */ "+T",   /* u   */ "+U",   /* v   */ "+V",   /* w   */ "+W",
144         /* x   */ "+X",   /* y   */ "+Y",   /* z   */ "+Z",   /* {   */ "%P",
145         /* |   */ "%Q",   /* }   */ "%R",   /* ~   */ "%S",   /* DEL */ "%T" 
146 };
147
148
149 /*===========================================*/
150 /* Local function prototypes                 */
151 /*===========================================*/
152
153 static gchar      *code39_encode    (const gchar *data,
154                                      gboolean     checksum_flag);
155
156 static lglBarcode *code39_vectorize (const gchar *code,
157                                      gdouble      w,
158                                      gdouble      h,
159                                      gboolean     text_flag,
160                                      gboolean     checksum_flag,
161                                      const gchar *data,
162                                      const gchar *string);
163
164
165 /****************************************************************************/
166 /* Generate list of lines that form the barcode for the given digits.       */
167 /****************************************************************************/
168 lglBarcode *
169 lgl_barcode_code39_new (lglBarcodeType  type,
170                         gboolean        text_flag,
171                         gboolean        checksum_flag,
172                         gdouble         w,
173                         gdouble         h,
174                         const gchar    *data)
175 {
176         gchar         *canon_data;
177         gchar         *display_data;
178         gchar         *code, *p;
179         lglBarcode    *bc;
180
181         if ( (type != LGL_BARCODE_TYPE_CODE39) &&
182              (type != LGL_BARCODE_TYPE_CODE39_EXT) )
183         {
184                 g_message ("Invalid barcode type for CODE39 backend.");
185                 return NULL;
186         }
187
188
189         /* Canonicalize data. */
190         if ( data[0] == '\0' )
191         {
192                 return NULL;
193         }
194         if (type == LGL_BARCODE_TYPE_CODE39_EXT)
195         {
196                 GString *canon_data_str;
197                 GString *display_data_str;
198
199                 canon_data_str = g_string_new ("");
200                 display_data_str = g_string_new ("");
201                 for ( p = (gchar *)data; *p != '\0'; p++ )
202                 {
203                         canon_data_str = g_string_append (canon_data_str, ascii_map[(*p) & 0x7F]);
204                         display_data_str = g_string_append_c (display_data_str, (*p) & 0x7F);
205                 }
206
207                 canon_data   = g_string_free (canon_data_str, FALSE);
208                 display_data = g_string_free (display_data_str, FALSE);
209         }
210         else
211         {
212                 canon_data = g_ascii_strup (data, -1);
213                 g_strcanon (canon_data, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%", ' ');
214                 display_data = g_strdup (canon_data);
215         }
216
217         /* First get code string */
218         code = code39_encode (canon_data, checksum_flag);
219         if (code == NULL) {
220                 g_free (canon_data);
221                 g_free (display_data);
222                 return NULL;
223         }
224
225         /* Now vectorize code string */
226         bc = code39_vectorize (code, w, h, text_flag, checksum_flag, canon_data, display_data);
227
228         g_free (canon_data);
229         g_free (display_data);
230         g_free (code);
231
232         return bc;
233 }
234
235
236 /*--------------------------------------------------------------------------*/
237 /* PRIVATE.  Generate string of symbols, representing barcode.              */
238 /*--------------------------------------------------------------------------*/
239 static gchar *
240 code39_encode (const gchar *data,
241                gboolean     checksum_flag)
242 {
243         gchar         *p, c;
244         gint           i, sum;
245         GString       *code;
246
247
248         /* Left frame symbol */
249         code = g_string_new( frame_symbol );
250         code = g_string_append( code, "i" );
251
252         sum = 0;
253         for ( p=(gchar *)data; *p != 0; p++ )
254         {
255                 c = toupper( *p );
256                 for ( i = 0; symbols[i].c != 0; i++ )
257                 {
258                         if ( c == symbols[i].c )
259                         {
260                                 sum += i;
261                                 code = g_string_append (code, symbols[i].sym);
262                                 break;
263                         }
264                 }
265                 code = g_string_append (code, "i");
266         }
267
268         if ( checksum_flag )
269         {
270                 code = g_string_append (code, symbols[sum % 43].sym);
271                 code = g_string_append (code, "i");
272         }
273
274         /* Right frame bar */
275         code = g_string_append (code, frame_symbol);
276
277         return g_string_free (code, FALSE);
278 }
279
280
281 /*--------------------------------------------------------------------------*/
282 /* Generate list of rectangles that form the barcode for the given digits.  */
283 /*--------------------------------------------------------------------------*/
284 static lglBarcode *
285 code39_vectorize (const gchar   *code,
286                   gdouble        w,
287                   gdouble        h,
288                   gboolean       text_flag,
289                   gboolean       checksum_flag,
290                   const gchar   *data,
291                   const gchar   *string)
292 {
293         gint         n_chars;
294         gdouble      min_l;
295         gdouble      scale;
296         gdouble      width, height;
297         gdouble      x_quiet;
298         lglBarcode  *bc;
299         gchar       *p;
300         gdouble      x1;
301         gchar       *string_plus_stars;
302
303         /* determine width and establish horizontal scale */
304         n_chars = strlen (data);
305         if (!checksum_flag)
306         {
307                 min_l = (n_chars + 2)*(3*N + 6)*MIN_X + (n_chars + 1)*MIN_I;
308         }
309         else
310         {
311                 min_l = (n_chars + 3)*(3*N + 6)*MIN_X + (n_chars + 2)*MIN_I;
312         }
313         
314         if ( w == 0 )
315         {
316                 scale = 1.0;
317         }
318         else
319         {
320                 scale = w / (min_l + 2*MIN_QUIET);
321
322                 if ( scale < 1.0 )
323                 {
324                         scale = 1.0;
325                 }
326         }
327         width = min_l * scale;
328
329         /* determine height of barcode */
330         height = text_flag ? h - TEXT_AREA_HEIGHT : h;
331         height = MAX (height, MAX(0.15*width, MIN_HEIGHT));
332
333         /* determine horizontal quiet zone */
334         x_quiet = MAX ((10 * scale * MIN_X), MIN_QUIET);
335
336
337         bc = lgl_barcode_new ();
338
339         /* Now traverse the code string and create a list of rectangles */
340         x1 = x_quiet;
341         for ( p = (gchar *)code; *p != 0; p++ )
342         {
343
344                 switch ( *p )
345                 {
346
347                 case 'i':
348                         /* Inter-character gap */
349                         x1 += scale * MIN_I;
350                         break;
351
352                 case 'N':
353                         /* Narrow bar */
354                         lgl_barcode_add_box (bc, x1, 0.0, (scale * MIN_X - INK_BLEED), height);
355                         x1 += scale * MIN_X;
356                         break;
357
358                 case 'W':
359                         /* Wide bar */
360                         lgl_barcode_add_box (bc, x1, 0.0, (scale * N * MIN_X - INK_BLEED), height);
361                         x1 += scale * N * MIN_X;
362                         break;
363
364                 case 'n':
365                         /* Narrow space */
366                         x1 += scale * MIN_X;
367                         break;
368
369                 case 'w':
370                         /* Wide space */
371                         x1 += scale * N * MIN_X;
372                         break;
373
374                 default:
375                         g_message( "Invalid Code39 symbol, should not happen" );
376                         break;
377                 }
378         }
379
380         if ( text_flag )
381         {
382                 string_plus_stars = g_strdup_printf ("*%s*", string);
383                 lgl_barcode_add_string (bc,
384                                         x_quiet + width/2, height + (TEXT_AREA_HEIGHT-TEXT_SIZE)/2,
385                                         TEXT_SIZE, string_plus_stars, strlen (string_plus_stars));
386                 g_free (string_plus_stars);
387         }
388
389         bc->width  = width + 2*x_quiet;
390         bc->height = text_flag ? height + TEXT_AREA_HEIGHT : height;
391
392         return bc;
393 }
394
395
396
397
398 /*
399  * Local Variables:       -- emacs
400  * mode: C                -- emacs
401  * c-basic-offset: 8      -- emacs
402  * tab-width: 8           -- emacs
403  * indent-tabs-mode: nil  -- emacs
404  * End:                   -- emacs
405  */