]> git.sur5r.net Git - glabels/blob - libglbarcode/lgl-barcode-postnet.c
Refactored core barcode subsystem.
[glabels] / libglbarcode / lgl-barcode-postnet.c
1 /*
2  *  lgl-barcode-postnet.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 /*
22  * This module implements the POSTNET barcode specified in the USPS
23  * publication 25, Mar 2001.
24  */
25
26 #include <config.h>
27
28 #include "lgl-barcode-postnet.h"
29
30 #include <glib.h>
31 #include <ctype.h>
32
33
34 /*========================================================*/
35 /* Private macros and constants.                          */
36 /*========================================================*/
37
38 #define PTS_PER_INCH 72.0
39
40 #define POSTNET_BAR_WIDTH      ( 0.02    * PTS_PER_INCH )
41 #define POSTNET_FULLBAR_HEIGHT ( 0.125   * PTS_PER_INCH )
42 #define POSTNET_HALFBAR_HEIGHT ( 0.05    * PTS_PER_INCH )
43 #define POSTNET_BAR_PITCH      ( 0.04545 * PTS_PER_INCH )
44 #define POSTNET_HORIZ_MARGIN   ( 0.125   * PTS_PER_INCH )
45 #define POSTNET_VERT_MARGIN    ( 0.04    * PTS_PER_INCH )
46
47
48 /*===========================================*/
49 /* Private globals                           */
50 /*===========================================*/
51 static gchar *symbols[] = {
52         /* 0 */ "11000",
53         /* 1 */ "00011",
54         /* 2 */ "00101",
55         /* 3 */ "00110",
56         /* 4 */ "01001",
57         /* 5 */ "01010",
58         /* 6 */ "01100",
59         /* 7 */ "10001",
60         /* 8 */ "10010",
61         /* 9 */ "10100",
62 };
63
64 static gchar *frame_symbol = "1";
65
66
67 /*===========================================*/
68 /* Local function prototypes                 */
69 /*===========================================*/
70 static gchar       *postnet_encode    (const gchar *digits);
71
72 static gboolean     is_length_valid   (const gchar *digits,
73                                        gint         n);
74
75 static lglBarcode  *postnet_vectorize (const gchar *code);
76
77
78
79 /****************************************************************************/
80 /* Generate list of lines that form the barcode for the given digits.       */
81 /****************************************************************************/
82 lglBarcode *
83 lgl_barcode_postnet_new (lglBarcodeType  type,
84                          gboolean        text_flag,
85                          gboolean        checksum_flag,
86                          gdouble         w,
87                          gdouble         h,
88                          const gchar    *data)
89 {
90         gchar              *code;
91         lglBarcode         *bc;
92
93         /* Validate code length for all subtypes. */
94         switch (type)
95         {
96
97         case LGL_BARCODE_TYPE_POSTNET:
98                 if (!is_length_valid (data, 5) &&
99                     !is_length_valid (data, 9) &&
100                     !is_length_valid (data, 11))
101                 {
102                         return NULL;
103                 }
104                 break;
105
106         case LGL_BARCODE_TYPE_POSTNET_5:
107                 if (!is_length_valid (data, 5))
108                 {
109                         return NULL;
110                 }
111                 break;
112
113         case LGL_BARCODE_TYPE_POSTNET_9:
114                 if (!is_length_valid (data, 9))
115                 {
116                         return NULL;
117                 }
118                 break;
119
120         case LGL_BARCODE_TYPE_POSTNET_11:
121                 if (!is_length_valid (data, 11))
122                 {
123                         return NULL;
124                 }
125                 break;
126
127         case LGL_BARCODE_TYPE_CEPNET:
128                 if (!is_length_valid (data, 8))
129                 {
130                         return NULL;
131                 }
132                 break;
133
134         default:
135                 g_message ("Invalid barcode type for POSTNET backend.");
136                 return NULL;
137
138         }
139
140         /* First get code string */
141         code = postnet_encode (data);
142         if (code == NULL)
143         {
144                 return NULL;
145         }
146
147         /* Now vectorize encoded data */
148         bc = postnet_vectorize (code);
149
150         g_free (code);
151
152         return bc;
153 }
154
155
156 /*--------------------------------------------------------------------------*/
157 /* PRIVATE.  Generate string of symbols, representing barcode.              */
158 /*--------------------------------------------------------------------------*/
159 static gchar *
160 postnet_encode (const gchar *data)
161 {
162         gchar   *p;
163         gint     len;
164         gint     d, sum;
165         GString *code;
166
167         /* Left frame bar */
168         code = g_string_new (frame_symbol);
169
170         sum = 0;
171         for (p = (gchar *)data, len = 0; (*p != 0) && (len < 11); p++)
172         {
173                 if (g_ascii_isdigit (*p))
174                 {
175                         /* Only translate valid characters (0-9) */
176                         d = (*p) - '0';
177                         sum += d;
178                         code = g_string_append (code, symbols[d]);
179                         len++;
180                 }
181         }
182
183         /* Create correction character */
184         d = (10 - (sum % 10)) % 10;
185         code = g_string_append (code, symbols[d]);
186
187         /* Right frame bar */
188         code = g_string_append (code, frame_symbol);
189
190         return g_string_free (code, FALSE);
191 }
192
193
194 /*--------------------------------------------------------------------------*/
195 /* PRIVATE.  Validate specific length of string (for subtypes).             */
196 /*--------------------------------------------------------------------------*/
197 static gboolean
198 is_length_valid (const gchar *data,
199                  gint         n)
200 {
201         gchar *p;
202         gint   i;
203
204         if (!data)
205         {
206                 return FALSE;
207         }
208
209         for (p = (gchar *)data, i=0; *p != 0; p++)
210         {
211                 if (g_ascii_isdigit (*p))
212                 {
213                         i++;
214                 }
215         }
216
217         return (i == n);
218 }
219
220
221 /*--------------------------------------------------------------------------*/
222 /* PRIVATE.  Vectorize encoded barcode.                                     */
223 /*--------------------------------------------------------------------------*/
224 static lglBarcode *
225 postnet_vectorize (const gchar *code)
226 {
227         lglBarcode         *bc;
228         gchar              *p;
229         gdouble             x, y, length, width;
230
231         bc = lgl_barcode_new ();
232
233         /* Now traverse the code string and create a list of lines */
234         x = POSTNET_HORIZ_MARGIN;
235         for (p = (gchar *)code; *p != 0; p++)
236         {
237                 y = POSTNET_VERT_MARGIN;
238                 switch (*p)
239                 {
240                 case '0':
241                         y += POSTNET_FULLBAR_HEIGHT - POSTNET_HALFBAR_HEIGHT;
242                         length = POSTNET_HALFBAR_HEIGHT;
243                         break;
244                 case '1':
245                         length = POSTNET_FULLBAR_HEIGHT;
246                         break;
247                 default:
248                         break;
249                 }
250                 width = POSTNET_BAR_WIDTH;
251
252                 lgl_barcode_add_box (bc, x, y, width, length);
253
254                 x += POSTNET_BAR_PITCH;
255         }
256
257         bc->width = x + POSTNET_HORIZ_MARGIN;
258         bc->height = POSTNET_FULLBAR_HEIGHT + 2 * POSTNET_VERT_MARGIN;
259
260         return bc;
261 }
262
263
264
265 /*
266  * Local Variables:       -- emacs
267  * mode: C                -- emacs
268  * c-basic-offset: 8      -- emacs
269  * tab-width: 8           -- emacs
270  * indent-tabs-mode: nil  -- emacs
271  * End:                   -- emacs
272  */