]> git.sur5r.net Git - glabels/blob - src/bc-postnet.c
Imported Upstream version 2.2.8
[glabels] / src / bc-postnet.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_postnet.c:  GLabels POSTNET barcode module
7  *
8  *  Copyright (C) 2001-2003  Jim Evins <evins@snaught.com>.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
23  */
24
25 /*
26  * This module implements the POSTNET barcode specified in the USPS
27  * publication 25, Mar 2001.
28  */
29
30 #include <config.h>
31
32 #include "bc-postnet.h"
33
34 #include <ctype.h>
35 #include <glib/gstring.h>
36 #include <glib/gstrfuncs.h>
37 #include <glib/gmessages.h>
38
39 #include "debug.h"
40
41 /*========================================================*/
42 /* Private macros and constants.                          */
43 /*========================================================*/
44 #define POSTNET_BAR_WIDTH      1.25
45 #define POSTNET_FULLBAR_HEIGHT 9.00
46 #define POSTNET_HALFBAR_HEIGHT 3.50
47 #define POSTNET_BAR_PITCH      3.25
48 #define POSTNET_HORIZ_MARGIN   9.00
49 #define POSTNET_VERT_MARGIN    3.00
50
51 /*===========================================*/
52 /* Private globals                           */
53 /*===========================================*/
54 static gchar *symbols[] = {
55         /* 0 */ "11000",
56         /* 1 */ "00011",
57         /* 2 */ "00101",
58         /* 3 */ "00110",
59         /* 4 */ "01001",
60         /* 5 */ "01010",
61         /* 6 */ "01100",
62         /* 7 */ "10001",
63         /* 8 */ "10010",
64         /* 9 */ "10100",
65 };
66
67 static gchar *frame_symbol = "1";
68
69 /*===========================================*/
70 /* Local function prototypes                 */
71 /*===========================================*/
72 static gchar    *postnet_code    (const gchar *digits);
73
74 static gboolean  is_length_valid (const gchar *digits,
75                                   gint         n);
76
77 \f
78 /****************************************************************************/
79 /* Generate list of lines that form the barcode for the given digits.       */
80 /****************************************************************************/
81 glBarcode *
82 gl_barcode_postnet_new (const gchar    *id,
83                         gboolean        text_flag,
84                         gboolean        checksum_flag,
85                         gdouble         w,
86                         gdouble         h,
87                         const gchar    *digits)
88 {
89         gchar         *code, *p;
90         glBarcode     *gbc;
91         glBarcodeLine *line;
92         gdouble        x;
93
94         /* Validate code length for all subtypes. */
95         if ( (g_strcasecmp (id, "POSTNET") == 0) ) {
96                 if (!is_length_valid (digits, 5) &&
97                     !is_length_valid (digits, 9) &&
98                     !is_length_valid (digits, 11)) {
99                         return NULL;
100                 }
101         }
102         if ( (g_strcasecmp (id, "POSTNET-5") == 0) ) {
103                 if (!is_length_valid (digits, 5)) {
104                         return NULL;
105                 }
106         }
107         if ( (g_strcasecmp (id, "POSTNET-9") == 0) ) {
108                 if (!is_length_valid (digits, 9)) {
109                         return NULL;
110                 }
111         }
112         if ( (g_strcasecmp (id, "POSTNET-11") == 0) ) {
113                 if (!is_length_valid (digits, 11)) {
114                         return NULL;
115                 }
116         }
117         if ( (g_strcasecmp (id, "CEPNET") == 0) ) {
118                 if (!is_length_valid (digits, 8)) {
119                         return NULL;
120                 }
121         }
122
123         /* First get code string */
124         code = postnet_code (digits);
125         if (code == NULL) {
126                 return NULL;
127         }
128
129         gbc = g_new0 (glBarcode, 1);
130
131         /* Now traverse the code string and create a list of lines */
132         x = POSTNET_HORIZ_MARGIN;
133         for (p = code; *p != 0; p++) {
134                 line = g_new0 (glBarcodeLine, 1);
135                 line->x = x;
136                 line->y = POSTNET_VERT_MARGIN;
137                 if (*p == '0') {
138                         line->y +=
139                             POSTNET_FULLBAR_HEIGHT - POSTNET_HALFBAR_HEIGHT;
140                         line->length = POSTNET_HALFBAR_HEIGHT;
141                 } else {
142                         line->length = POSTNET_FULLBAR_HEIGHT;
143                 }
144                 line->width = POSTNET_BAR_WIDTH;
145
146                 gbc->lines = g_list_append (gbc->lines, line);
147
148                 x += POSTNET_BAR_PITCH;
149         }
150
151         g_free (code);
152
153         gbc->width = x + POSTNET_HORIZ_MARGIN;
154         gbc->height = POSTNET_FULLBAR_HEIGHT + 2 * POSTNET_VERT_MARGIN;
155
156         gbc->chars = NULL;
157
158         return gbc;
159 }
160
161 /*--------------------------------------------------------------------------*/
162 /* PRIVATE.  Generate string of symbols, representing barcode.              */
163 /*--------------------------------------------------------------------------*/
164 static gchar *
165 postnet_code (const gchar *digits)
166 {
167         gchar   *p;
168         gint     len;
169         gint     d, sum;
170         GString *code;
171         gchar   *ret;
172
173         /* Left frame bar */
174         code = g_string_new (frame_symbol);
175
176         sum = 0;
177         for (p = (gchar *)digits, len = 0; (*p != 0) && (len < 11); p++) {
178                 if (g_ascii_isdigit (*p)) {
179                         /* Only translate valid characters (0-9) */
180                         d = (*p) - '0';
181                         sum += d;
182                         code = g_string_append (code, symbols[d]);
183                         len++;
184                 }
185         }
186
187         /* Create correction character */
188         d = (10 - (sum % 10)) % 10;
189         code = g_string_append (code, symbols[d]);
190
191         /* Right frame bar */
192         code = g_string_append (code, frame_symbol);
193
194         ret = g_strdup (code->str);
195         g_string_free (code, TRUE);
196
197         return ret;
198 }
199
200 /*--------------------------------------------------------------------------*/
201 /* Validate specific length of string (for subtypes).                       */
202 /*--------------------------------------------------------------------------*/
203 static gboolean
204 is_length_valid (const gchar *digits,
205                  gint         n)
206 {
207         gchar *p;
208         gint   i;
209
210         if (!digits) {
211                 return FALSE;
212         }
213
214         for (p = (gchar *)digits, i=0; *p != 0; p++) {
215                 if (g_ascii_isdigit (*p)) {
216                         i++;
217                 }
218         }
219
220         return (i == n);
221 }
222