]> git.sur5r.net Git - glabels/blob - libglbarcode/lgl-barcode-code39.c
Imported Upstream version 3.0.0
[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
53 /*===========================================*/
54 /* Private globals                           */
55 /*===========================================*/
56
57 /* Code 39 alphabet. Position indicates value. */
58 static gchar *alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";
59
60 /* Code 39 symbols. Position must match position in alphabet. */
61 static gchar* symbols[43] = {
62         /*        BsBsBsBsB */
63         /* 0 */  "NnNwWnWnN",
64         /* 1 */  "WnNwNnNnW",
65         /* 2 */  "NnWwNnNnW",
66         /* 3 */  "WnWwNnNnN",
67         /* 4 */  "NnNwWnNnW",
68         /* 5 */  "WnNwWnNnN",
69         /* 6 */  "NnWwWnNnN",
70         /* 7 */  "NnNwNnWnW",
71         /* 8 */  "WnNwNnWnN",
72         /* 9 */  "NnWwNnWnN",
73         /* A */  "WnNnNwNnW",
74         /* B */  "NnWnNwNnW",
75         /* C */  "WnWnNwNnN",
76         /* D */  "NnNnWwNnW",
77         /* E */  "WnNnWwNnN",
78         /* F */  "NnWnWwNnN",
79         /* G */  "NnNnNwWnW",
80         /* H */  "WnNnNwWnN",
81         /* I */  "NnWnNwWnN",
82         /* J */  "NnNnWwWnN",
83         /* K */  "WnNnNnNwW",
84         /* L */  "NnWnNnNwW",
85         /* M */  "WnWnNnNwN",
86         /* N */  "NnNnWnNwW",
87         /* O */  "WnNnWnNwN",
88         /* P */  "NnWnWnNwN",
89         /* Q */  "NnNnNnWwW",
90         /* R */  "WnNnNnWwN",
91         /* S */  "NnWnNnWwN",
92         /* T */  "NnNnWnWwN",
93         /* U */  "WwNnNnNnW",
94         /* V */  "NwWnNnNnW",
95         /* W */  "WwWnNnNnN",
96         /* X */  "NwNnWnNnW",
97         /* Y */  "WwNnWnNnN",
98         /* Z */  "NwWnWnNnN",
99         /* - */  "NwNnNnWnW",
100         /* . */  "WwNnNnWnN",
101         /*   */  "NwWnNnWnN",
102         /* $ */  "NwNwNwNnN",
103         /* / */  "NwNwNnNwN",
104         /* + */  "NwNnNwNwN",
105         /* % */  "NnNwNwNwN",
106 };
107
108 static gchar *frame_symbol = "NwNnWnWnN";
109
110 static gchar *ascii_map[128] =
111 {
112         /* NUL */ "%U",   /* SOH */ "$A",   /* STX */ "$B",   /* ETX */ "$C",
113         /* EOT */ "$D",   /* ENQ */ "$E",   /* ACK */ "$F",   /* BEL */ "$G",
114         /* BS  */ "$H",   /* HT  */ "$I",   /* LF  */ "$J",   /* VT  */ "$K",
115         /* FF  */ "$L",   /* CR  */ "$M",   /* SO  */ "$N",   /* SI  */ "$O",
116         /* DLE */ "$P",   /* DC1 */ "$Q",   /* DC2 */ "$R",   /* DC3 */ "$S",
117         /* DC4 */ "$T",   /* NAK */ "$U",   /* SYN */ "$V",   /* ETB */ "$W",
118         /* CAN */ "$X",   /* EM  */ "$Y",   /* SUB */ "$Z",   /* ESC */ "%A",
119         /* FS  */ "%B",   /* GS  */ "%C",   /* RS  */ "%D",   /* US  */ "%E",
120         /* " " */ " ",    /* !   */ "/A",   /* "   */ "/B",   /* #   */ "/C",
121         /* $   */ "/D",   /* %   */ "/E",   /* &   */ "/F",   /* '   */ "/G",
122         /* (   */ "/H",   /* )   */ "/I",   /* *   */ "/J",   /* +   */ "/K",
123         /* ,   */ "/L",   /* -   */ "-",    /* .   */ ".",    /* /   */ "/O",
124         /* 0   */ "0",    /* 1   */ "1",    /* 2   */ "2",    /* 3   */ "3",
125         /* 4   */ "4",    /* 5   */ "5",    /* 6   */ "6",    /* 7   */ "7",
126         /* 8   */ "8",    /* 9   */ "9",    /* :   */ "/Z",   /* ;   */ "%F",
127         /* <   */ "%G",   /* =   */ "%H",   /* >   */ "%I",   /* ?   */ "%J",
128         /* @   */ "%V",   /* A   */ "A",    /* B   */ "B",    /* C   */ "C",
129         /* D   */ "D",    /* E   */ "E",    /* F   */ "F",    /* G   */ "G",
130         /* H   */ "H",    /* I   */ "I",    /* J   */ "J",    /* K   */ "K",
131         /* L   */ "L",    /* M   */ "M",    /* N   */ "N",    /* O   */ "O",
132         /* P   */ "P",    /* Q   */ "Q",    /* R   */ "R",    /* S   */ "S",
133         /* T   */ "T",    /* U   */ "U",    /* V   */ "V",    /* W   */ "W",
134         /* X   */ "X",    /* Y   */ "Y",    /* Z   */ "Z",    /* [   */ "%K",
135         /* \   */ "%L",   /* ]   */ "%M",   /* ^   */ "%N",   /* _   */ "%O",
136         /* `   */ "%W",   /* a   */ "+A",   /* b   */ "+B",   /* c   */ "+C",
137         /* d   */ "+D",   /* e   */ "+E",   /* f   */ "+F",   /* g   */ "+G",
138         /* h   */ "+H",   /* i   */ "+I",   /* j   */ "+J",   /* k   */ "+K",
139         /* l   */ "+L",   /* m   */ "+M",   /* n   */ "+N",   /* o   */ "+O",
140         /* p   */ "+P",   /* q   */ "+Q",   /* r   */ "+R",   /* s   */ "+S",
141         /* t   */ "+T",   /* u   */ "+U",   /* v   */ "+V",   /* w   */ "+W",
142         /* x   */ "+X",   /* y   */ "+Y",   /* z   */ "+Z",   /* {   */ "%P",
143         /* |   */ "%Q",   /* }   */ "%R",   /* ~   */ "%S",   /* DEL */ "%T" 
144 };
145
146
147 /*===========================================*/
148 /* Local function prototypes                 */
149 /*===========================================*/
150
151 static gboolean    code39_is_data_valid     (const gchar *data);
152 static gboolean    code39_ext_is_data_valid (const gchar *data);
153
154 static gchar      *code39_encode            (const gchar *data,
155                                              gboolean     checksum_flag);
156
157 static lglBarcode *code39_vectorize         (const gchar *code,
158                                              gdouble      w,
159                                              gdouble      h,
160                                              gboolean     text_flag,
161                                              gboolean     checksum_flag,
162                                              const gchar *data,
163                                              const gchar *string);
164
165
166 /****************************************************************************/
167 /* Generate new Code 39 barcode structure from data.                        */
168 /****************************************************************************/
169 lglBarcode *
170 lgl_barcode_code39_new (lglBarcodeType  type,
171                         gboolean        text_flag,
172                         gboolean        checksum_flag,
173                         gdouble         w,
174                         gdouble         h,
175                         const gchar    *data)
176 {
177         gchar         *canon_data;
178         gchar         *display_data;
179         gchar         *code, *p;
180         lglBarcode    *bc;
181
182         if ( (type != LGL_BARCODE_TYPE_CODE39) &&
183              (type != LGL_BARCODE_TYPE_CODE39_EXT) )
184         {
185                 g_message ("Invalid barcode type for CODE39 backend.");
186                 return NULL;
187         }
188
189
190         /* Validate data. */
191         if (type == LGL_BARCODE_TYPE_CODE39)
192         {
193                 if ( !code39_is_data_valid (data) )
194                 {
195                         return NULL;
196                 }
197                 canon_data = g_ascii_strup (data, -1);
198                 display_data = g_strdup (canon_data);
199         }
200         else
201         {
202                 GString *canon_data_str;
203
204                 if ( !code39_ext_is_data_valid (data) )
205                 {
206                         return NULL;
207                 }
208
209                 canon_data_str = g_string_new ("");
210                 for ( p = (gchar *)data; *p != '\0'; p++ )
211                 {
212                         canon_data_str = g_string_append (canon_data_str, ascii_map[(int)*p]);
213                 }
214                 canon_data   = g_string_free (canon_data_str, FALSE);
215
216                 display_data = g_strdup (data);
217         }
218
219         /* First get code string */
220         code = code39_encode (canon_data, checksum_flag);
221         if (code == NULL)
222         {
223                 g_free (canon_data);
224                 g_free (display_data);
225                 return NULL;
226         }
227
228         /* Now vectorize code string */
229         bc = code39_vectorize (code, w, h, text_flag, checksum_flag, canon_data, display_data);
230
231         g_free (canon_data);
232         g_free (display_data);
233         g_free (code);
234
235         return bc;
236 }
237
238
239 /*--------------------------------------------------------------------------*/
240 /* PRIVATE.  Validate data for Code 39.                                     */
241 /*--------------------------------------------------------------------------*/
242 static gboolean
243 code39_is_data_valid (const gchar *data)
244 {
245         gchar *p;
246         gchar  c;
247
248         if (!data || (*data == '\0'))
249         {
250                 return FALSE;
251         }
252
253         for ( p = (gchar *)data; *p != 0; p++ )
254         {
255                 c = g_ascii_toupper (*p);
256
257                 if ( strchr(alphabet, c) == NULL )
258                 {
259                         return FALSE;
260                 }
261         }
262
263         return TRUE;
264 }
265
266
267 /*--------------------------------------------------------------------------*/
268 /* PRIVATE.  Validate data for Extended Code 39.                            */
269 /*--------------------------------------------------------------------------*/
270 static gboolean
271 code39_ext_is_data_valid (const gchar *data)
272 {
273         gchar *p;
274
275         if (!data || (*data == '\0'))
276         {
277                 return FALSE;
278         }
279
280         for ( p = (gchar *)data; *p != 0; p++ )
281         {
282                 if ( (*p < 0) || (*p > 0x7f)  )
283                 {
284                         return FALSE;
285                 }
286         }
287
288         return TRUE;
289 }
290
291
292 /*--------------------------------------------------------------------------*/
293 /* PRIVATE.  Generate string of symbols, representing barcode.              */
294 /*--------------------------------------------------------------------------*/
295 static gchar *
296 code39_encode (const gchar *data,
297                gboolean     checksum_flag)
298 {
299         gchar         *p, c;
300         gint           c_value, sum;
301         GString       *code;
302
303
304         /* Left frame symbol */
305         code = g_string_new( frame_symbol );
306         code = g_string_append( code, "i" );
307
308         sum = 0;
309         for ( p=(gchar *)data; *p != 0; p++ )
310         {
311                 c = g_ascii_toupper( *p );
312                 c_value = strchr(alphabet, c) - alphabet;
313                 code = g_string_append (code, symbols[c_value]);
314                 code = g_string_append (code, "i");
315
316                 sum += c_value;
317         }
318
319         if ( checksum_flag )
320         {
321                 code = g_string_append (code, symbols[sum % 43]);
322                 code = g_string_append (code, "i");
323         }
324
325         /* Right frame bar */
326         code = g_string_append (code, frame_symbol);
327
328         return g_string_free (code, FALSE);
329 }
330
331
332 /*--------------------------------------------------------------------------*/
333 /* PRIVATE.  Vectorize encoded barcode.                                     */
334 /*--------------------------------------------------------------------------*/
335 static lglBarcode *
336 code39_vectorize (const gchar   *code,
337                   gdouble        w,
338                   gdouble        h,
339                   gboolean       text_flag,
340                   gboolean       checksum_flag,
341                   const gchar   *data,
342                   const gchar   *string)
343 {
344         gint         n_chars;
345         gdouble      min_l;
346         gdouble      scale;
347         gdouble      width, height;
348         gdouble      x_quiet;
349         lglBarcode  *bc;
350         gchar       *p;
351         gdouble      x1;
352         gchar       *string_plus_stars;
353
354         /* determine width and establish horizontal scale */
355         n_chars = strlen (data);
356         if (!checksum_flag)
357         {
358                 min_l = (n_chars + 2)*(3*N + 6)*MIN_X + (n_chars + 1)*MIN_I;
359         }
360         else
361         {
362                 min_l = (n_chars + 3)*(3*N + 6)*MIN_X + (n_chars + 2)*MIN_I;
363         }
364         
365         if ( w == 0 )
366         {
367                 scale = 1.0;
368         }
369         else
370         {
371                 scale = w / (min_l + 2*MIN_QUIET);
372
373                 if ( scale < 1.0 )
374                 {
375                         scale = 1.0;
376                 }
377         }
378         width = min_l * scale;
379
380         /* determine height of barcode */
381         height = text_flag ? h - TEXT_AREA_HEIGHT : h;
382         height = MAX (height, MAX(0.15*width, MIN_HEIGHT));
383
384         /* determine horizontal quiet zone */
385         x_quiet = MAX ((10 * scale * MIN_X), MIN_QUIET);
386
387
388         bc = lgl_barcode_new ();
389
390         /* Now traverse the code string and create a list of rectangles */
391         x1 = x_quiet;
392         for ( p = (gchar *)code; *p != 0; p++ )
393         {
394
395                 switch ( *p )
396                 {
397
398                 case 'i':
399                         /* Inter-character gap */
400                         x1 += scale * MIN_I;
401                         break;
402
403                 case 'N':
404                         /* Narrow bar */
405                         lgl_barcode_add_box (bc, x1, 0.0, (scale * MIN_X - INK_BLEED), height);
406                         x1 += scale * MIN_X;
407                         break;
408
409                 case 'W':
410                         /* Wide bar */
411                         lgl_barcode_add_box (bc, x1, 0.0, (scale * N * MIN_X - INK_BLEED), height);
412                         x1 += scale * N * MIN_X;
413                         break;
414
415                 case 'n':
416                         /* Narrow space */
417                         x1 += scale * MIN_X;
418                         break;
419
420                 case 'w':
421                         /* Wide space */
422                         x1 += scale * N * MIN_X;
423                         break;
424
425                 default:
426                         g_message( "Invalid Code39 symbol, should not happen" );
427                         break;
428                 }
429         }
430
431         if ( text_flag )
432         {
433                 string_plus_stars = g_strdup_printf ("*%s*", string);
434                 lgl_barcode_add_string (bc,
435                                         x_quiet + width/2, height + (TEXT_AREA_HEIGHT-TEXT_SIZE)/2,
436                                         TEXT_SIZE, string_plus_stars, strlen (string_plus_stars));
437                 g_free (string_plus_stars);
438         }
439
440         bc->width  = width + 2*x_quiet;
441         bc->height = text_flag ? height + TEXT_AREA_HEIGHT : height;
442
443         return bc;
444 }
445
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  */