]> git.sur5r.net Git - glabels/blob - libglbarcode/lgl-barcode-code39.c
Refactored core barcode subsystem.
[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 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 "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                 return NULL;
221         }
222
223         /* Now vectorize code string */
224         bc = code39_vectorize (code, w, h, text_flag, checksum_flag, canon_data, display_data);
225
226         g_free (canon_data);
227         g_free (display_data);
228         g_free (code);
229
230         return bc;
231 }
232
233
234 /*--------------------------------------------------------------------------*/
235 /* PRIVATE.  Generate string of symbols, representing barcode.              */
236 /*--------------------------------------------------------------------------*/
237 static gchar *
238 code39_encode (const gchar *data,
239                gboolean     checksum_flag)
240 {
241         gchar         *p, c;
242         gint           i, sum;
243         GString       *code;
244
245
246         /* Left frame symbol */
247         code = g_string_new( frame_symbol );
248         code = g_string_append( code, "i" );
249
250         sum = 0;
251         for ( p=(gchar *)data; *p != 0; p++ )
252         {
253                 c = toupper( *p );
254                 for ( i = 0; symbols[i].c != 0; i++ )
255                 {
256                         if ( c == symbols[i].c )
257                         {
258                                 sum += i;
259                                 code = g_string_append (code, symbols[i].sym);
260                                 break;
261                         }
262                 }
263                 code = g_string_append (code, "i");
264         }
265
266         if ( checksum_flag )
267         {
268                 code = g_string_append (code, symbols[sum % 43].sym);
269                 code = g_string_append (code, "i");
270         }
271
272         /* Right frame bar */
273         code = g_string_append (code, frame_symbol);
274
275         return g_string_free (code, FALSE);
276 }
277
278
279 /*--------------------------------------------------------------------------*/
280 /* Generate list of rectangles that form the barcode for the given digits.  */
281 /*--------------------------------------------------------------------------*/
282 static lglBarcode *
283 code39_vectorize (const gchar   *code,
284                   gdouble        w,
285                   gdouble        h,
286                   gboolean       text_flag,
287                   gboolean       checksum_flag,
288                   const gchar   *data,
289                   const gchar   *string)
290 {
291         gint         n_chars;
292         gdouble      min_l;
293         gdouble      scale;
294         gdouble      width, height;
295         gdouble      x_quiet;
296         lglBarcode  *bc;
297         gchar       *p;
298         gdouble      x1;
299         gchar       *string_plus_stars;
300
301         /* determine width and establish horizontal scale */
302         n_chars = strlen (data);
303         if (!checksum_flag)
304         {
305                 min_l = (n_chars + 2)*(3*N + 6)*MIN_X + (n_chars + 1)*MIN_I;
306         }
307         else
308         {
309                 min_l = (n_chars + 3)*(3*N + 6)*MIN_X + (n_chars + 2)*MIN_I;
310         }
311         
312         if ( w == 0 )
313         {
314                 scale = 1.0;
315         }
316         else
317         {
318                 scale = w / (min_l + 2*MIN_QUIET);
319
320                 if ( scale < 1.0 )
321                 {
322                         scale = 1.0;
323                 }
324         }
325         width = min_l * scale;
326
327         /* determine height of barcode */
328         height = text_flag ? h - TEXT_AREA_HEIGHT : h;
329         height = MAX (height, MAX(0.15*width, MIN_HEIGHT));
330
331         /* determine horizontal quiet zone */
332         x_quiet = MAX ((10 * scale * MIN_X), MIN_QUIET);
333
334
335         bc = lgl_barcode_new ();
336
337         /* Now traverse the code string and create a list of rectangles */
338         x1 = x_quiet;
339         for ( p = (gchar *)code; *p != 0; p++ )
340         {
341
342                 switch ( *p )
343                 {
344
345                 case 'i':
346                         /* Inter-character gap */
347                         x1 += scale * MIN_I;
348                         break;
349
350                 case 'N':
351                         /* Narrow bar */
352                         lgl_barcode_add_box (bc, x1, 0.0, (scale * MIN_X - INK_BLEED), height);
353                         x1 += scale * MIN_X;
354                         break;
355
356                 case 'W':
357                         /* Wide bar */
358                         lgl_barcode_add_box (bc, x1, 0.0, (scale * N * MIN_X - INK_BLEED), height);
359                         x1 += scale * N * MIN_X;
360                         break;
361
362                 case 'n':
363                         /* Narrow space */
364                         x1 += scale * MIN_X;
365                         break;
366
367                 case 'w':
368                         /* Wide space */
369                         x1 += scale * N * MIN_X;
370                         break;
371
372                 default:
373                         g_message( "Invalid Code39 symbol, should not happen" );
374                         break;
375                 }
376         }
377
378         if ( text_flag )
379         {
380                 string_plus_stars = g_strdup_printf ("*%s*", string);
381                 lgl_barcode_add_string (bc,
382                                         x_quiet + width/2, height + (TEXT_AREA_HEIGHT-TEXT_SIZE)/2,
383                                         TEXT_SIZE, string_plus_stars, strlen (string_plus_stars));
384                 g_free (string_plus_stars);
385         }
386
387         bc->width  = width + 2*x_quiet;
388         bc->height = text_flag ? height + TEXT_AREA_HEIGHT : height;
389
390         return bc;
391 }
392
393
394
395
396 /*
397  * Local Variables:       -- emacs
398  * mode: C                -- emacs
399  * c-basic-offset: 8      -- emacs
400  * tab-width: 8           -- emacs
401  * indent-tabs-mode: nil  -- emacs
402  * End:                   -- emacs
403  */