]> git.sur5r.net Git - glabels/blob - barcode-0.98/ean.c
Imported Upstream version 2.2.8
[glabels] / barcode-0.98 / ean.c
1 /*
2  * ean.c -- encoding for ean, upc and isbn
3  *
4  * Copyright (c) 1999 Alessandro Rubini <rubini@gnu.org>
5  * Copyright (c) 1999 Prosa Srl. <prosa@prosa.it>
6  * Copyright (c) 2001 Boszormenyi Zoltan <zboszor@mail.externet.hu>
7  *
8  *   This program is free software; you can redistribute it and/or modify
9  *   it under the terms of the GNU General Public License as published by
10  *   the Free Software Foundation; either version 2 of the License, or
11  *   (at your option) any later version.
12  *
13  *   This program is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with this program; if not, write to the Free Software
20  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <errno.h>
28
29 #include "barcode.h"
30
31 /*
32  * IMPORTANT NOTE: if you are reading this file to learn how to add a
33  * new encoding type, this is the wrong place as there are too many
34  * special cases. Please refer to code39.c instead. If you want to
35  * learn how UPC, EAN, ISBN work, on the other hand, I did my best to
36  * commend things and hope you enjoy it.
37  */
38
39 /*
40  * These following static arrays are used to describe the barcode.
41  *
42  * The various forms of UPC and EAN are documented as using three
43  * different alphabets to encode the ten digits. However, each digit
44  * has exactly one encoding; only, it is sometimes mirrored. Moreover,
45  * if you represent the width of each symbol (bar/space) instead of
46  * the sequence of 1's and 0's, you find that even-parity and odd-parity
47  * encoding are exactly the same. So, here are the digits: */
48 static char *digits[] = {
49      "3211","2221","2122","1411","1132",
50      "1231","1114","1312","1213","3112"};
51
52 /*
53  * What EAN encoding does is adding a leading digit (the 13th digit).
54  * Such an extra digit is encoded by mirroring three of the six digits that
55  * appear in the left half of the UPC code. Here how mirroring works:
56  */
57 static char *ean_mirrortab[] = {
58      "------","--1-11","--11-1","--111-","-1--11",
59      "-11--1","-111--","-1-1-1","-1-11-","-11-1-"
60 };
61
62 /*
63  * UPC-E (the 6-digit one), instead, encodes the check character as
64  * a mirroring of the symbols. This is similar, but the encoding for "0" is
65  * different (EAN uses no mirroring for "0" to be compatible with UPC).
66  * The same rule is used for UPC-5 (the supplemental digits for ISBN)
67  */
68 static char *upc_mirrortab[] = {
69      "---111","--1-11","--11-1","--111-","-1--11",
70      "-11--1","-111--","-1-1-1","-1-11-","-11-1-"
71 };
72
73 /*
74  * UPC-E mirroring for encoding "1"
75  */
76 static char *upc_mirrortab1[] = {
77      "111---","11-1--","11--1-","11---1","1-11--",
78      "1--11-","1---11","1-1-1-","1-1--1","1--1-1"
79 };
80
81 /* UPC-2 has just two digits to mirror */
82 static char *upc_mirrortab2[] = {
83     "11","1-","-1","--"
84 };
85
86 /*
87  * initial, middle, final guard bars (first symbol is a a space).
88  * EAN-13 overwrites the first "0" with "9" to make space for the extra digit.
89  */
90 static char *guard[] = {"0a1a","1a1a1","a1a"};
91
92 /* initial, final guard bars for UPC-E*/
93 static char *guardE[] = {"0a1a","1a1a1a"};
94
95 /* initial and inter-char guard bars for supplementals (first is space) */
96 static char *guardS[] = {"9112","11"};
97
98 /*
99  * These functions are shortcuts I use in the encoding engine
100  */
101 static int ean_make_checksum(char *text, int mode)
102 {
103     int esum = 0, osum = 0, i;
104     int even=1; /* last char is even */
105
106     if (strchr(text, ' '))
107         i = strchr(text, ' ') - text; /* end of first part */
108     else 
109         i = strlen(text); /* end of all */
110
111     while (i-- > 0) {
112         if (even) esum += text[i]-'0';
113         else      osum += text[i]-'0';
114         even = !even;
115     }
116     if (!mode) { /* standard upc/ean checksum */
117         i = (3*esum + osum) % 10;
118         return (10-i) % 10; /* complement to 10 */
119     } else { /* add-5 checksum */
120         i = (3*esum + 9*osum);
121         return i%10;
122     }
123 }
124
125 /*
126  * Check that the text can be encoded. Returns 0 or -1.
127  * Accept:
128  *   13 or 12 digits: EAN-13 w/ or w/o checksum
129  * or
130  *   8 or 7 digits: EAN-8 w/ or w/o checksum.
131  * For both EAN-13 and EAN-8, accept an addon of 2 or 5 digits,
132  * separated by ' '
133  */
134 int Barcode_ean_verify(unsigned char *text)
135 {
136     int i, len0, len, addon;
137     unsigned char tmp[24], *spc;
138
139     len = strlen(text);
140     spc = strchr(text, ' ');
141     if (spc) {
142         len0 = spc - text;
143         addon = len - len0 - 1;
144         if (addon != 2 && addon != 5)
145                 return -1;
146         for (i=len0+1; i<len; i++)
147             if (!isdigit(text[i]))
148                 return -1;
149     } else
150         len0 = len;
151
152     for (i=0; i<len0; i++)
153         if (!isdigit(text[i]))
154             return -1;
155
156     switch (len0) {
157     case 8:
158         strncpy(tmp, text, 7);
159         tmp[7] = '\0';
160         if (text[7] != (ean_make_checksum(tmp, 0) + '0'))
161                 return -1;
162     case 7:
163         break;
164     case 13:
165         strncpy(tmp, text, 12);
166         tmp[12] = '\0';
167         if (text[12] != (ean_make_checksum(tmp, 0) + '0'))
168                 return -1;
169     case 12:
170         break;
171     default:
172         return -1;
173     }
174     return 0;
175 }
176
177 /* Expand the middle part of UPC-E to UPC-A */
178 static char *upc_e_to_a0(unsigned char *text)
179 {
180     static char result[16];
181     strcpy(result, "00000000000"); /* 11 0's */
182
183     switch(text[5]) { /* last char */
184         case '0': case '1': case '2':
185             strncpy(result+1, text,  2); result[3]=text[5]; /* Manuf. */
186             memcpy(result+8, text+2, 3); /* Product */
187             break;
188         case '3':
189             memcpy(result+1, text,   3); /* Manufacturer */
190             memcpy(result+9, text+3, 2); /* Product */
191             break;
192         case '4':
193             memcpy(result+1,  text,   4); /* Manufacturer */
194             memcpy(result+10, text+4, 1); /* Product */
195             break;
196         default:
197             memcpy(result+1,  text,   5); /* Manufacturer */
198             memcpy(result+10, text+5, 1); /* Product */
199             break;
200     }
201     return result;
202 }
203
204 /* Try to expand an UPC-E barcode to its UPC-A equivalent.
205  * Accept 6, 7 or 8-digit sequence (not counting the addon):
206  *  6:  only the middle part, encoding "0", w/o checksum.
207  *  7:  the middle part, encoding "0" with a correct checksum
208  *    or
209  *      the middle part, encoding "0" or "1" prepended
210  *  8:  fully qualified UPC-E with checksum.
211  *
212  * Returns a 11 digit UPC-A (w/o checksum) for valid EPC-E barcode
213  * or an empty string for an invalid one.
214  *
215  * The checksum for UPC-E is calculated using its UPC-A equivalent.
216  */
217 static char *upc_e_to_a(unsigned char *text)
218 {
219     static unsigned char        result[16], *spc;
220     int                         len, chk;
221
222     spc = strchr(text, ' ');
223     if (spc)
224         len = spc - text;
225     else
226         len = strlen(text);
227
228     switch (len) {
229     case 6:
230         strcpy(result, upc_e_to_a0(text));
231         return result;
232     case 7:
233         /* the first char is '0' or '1':
234          * valid number system for UPC-E and no checksum
235          */
236         if (text[0] == '0' || text[0] == '1') {
237                 strcpy(result, upc_e_to_a0(text+1));
238                 result[0] = text[0];
239                 return result;
240         }
241
242         /* Find out whether the 7th char is correct checksum */
243         strcpy(result, upc_e_to_a0(text));
244         chk = ean_make_checksum(result, 0);
245
246         if (chk == (text[len-1] - '0'))
247                 return result;
248         /* Invalid 7 digit representation for UPC-E. */
249         return NULL;
250     case 8:
251         if (text[0] == '0' || text[0] == '1') {
252                 strcpy(result, upc_e_to_a0(text+1));
253                 result[0] = text[0];
254                 chk = ean_make_checksum(result, 0);
255                 if (chk == (text[len-1] - '0'))
256                         return result;
257         }
258     default:
259         /* Invalid representation for UPC-E. */
260         return NULL;
261     }
262 }
263
264 /*
265  * Accept a 11 or 12 digit UPC-A barcode and
266  * shrink it into an 8-digit UPC-E equivalent if possible.
267  * Return NULL if impossible, the UPC-E barcode if possible.
268  */
269 static unsigned char *upc_a_to_e(unsigned char *text)
270 {
271     static unsigned char        result[16];
272     int                         len, chksum;
273
274     len = strlen(text);
275     switch (len) {
276     case 12:
277         strcpy(result, text);
278         result[11] = '\0';
279         chksum = ean_make_checksum(result, 0);
280         if (text[11] != (chksum - '0'))
281                 return NULL;
282         break;
283     case 11:
284         chksum = ean_make_checksum(text, 0);
285         break;
286     default:
287         return NULL;
288     }
289
290     strcpy(result, "00000000"); /* 8 0's*/
291
292     /* UPC-E can only be used with number system 0 or 1 */
293     if (text[0] != '0' && text[0] != '1')
294         return NULL;
295
296     result[0] = text[0];
297
298     if ((text[3] == '0' || text[3] == '1' || text[3] == '2')
299             && !strncmp(text+4, "0000", 4)) {
300         memcpy(&result[1], text+1, 2);
301         memcpy(&result[3], text+8, 3);
302         result[6] = text[3];
303     } else if (!strncmp(text+4, "00000", 5)) {
304         memcpy(&result[1], text+1, 3);
305         memcpy(&result[4], text+9, 2);
306         result[6] = '3';
307     } else if (!strncmp(text+5, "00000", 5)) {
308         memcpy(&result[1], text+1, 4);
309         result[5] = text[10];
310         result[6] = '4';
311     } else if ((text[5] != '0') && !strncmp(text+6, "0000", 4)
312             && text[10] >= '5' && text[10] <= '9') {
313         memcpy(&result[1], text+1, 5);
314         result[6] = text[10];
315     } else {
316         return NULL;
317     }
318     result[7] = chksum + '0';
319
320     return result;
321 }
322
323 /*
324  * UPC-A is the same as EAN, but accept
325  *    12 or 11 digits (UPC-A w/ or w/o checksum)
326  * or accept UPC-E as:
327  *    6 digits (w/o number system and checksum): number system '0' assumed,
328  *    7 digits (either w/o number system or checksum),
329  *    8 digits (w/ number system and checksum)
330  * plus the 2 or 5-digit add-on
331  */
332 int Barcode_upc_verify(unsigned char *text)
333 {
334     int i, len0, len, addon;
335     unsigned char tmp[24], *spc;
336
337     len = strlen(text);
338     spc = strchr(text, ' ');
339     if (spc) {
340         len0 = spc - text;
341         addon = len - len0 - 1;
342         if (addon != 2 && addon != 5)
343                 return -1;
344         for (i=len0+1; i<len; i++)
345             if (!isdigit(text[i]))
346                 return -1;
347     } else
348         len0 = len;
349
350     for (i=0; i<len0; i++)
351         if (!isdigit(text[i]))
352             return -1;
353
354     switch (len0) {
355     case 6: case 7: case 8:
356         strncpy(tmp, text, len0);
357         tmp[len0] = '\0';
358         if (!upc_e_to_a(tmp))
359                 return -1;
360         break;
361     case 12:
362         strncpy(tmp, text, 11);
363         tmp[11] = '\0';
364         if (text[11] != (ean_make_checksum(tmp, 0) + '0'))
365                 return -1;
366     case 11:
367         break;
368     default:
369         return -1;
370     }
371     return 0;
372 }
373
374 /*
375  * Isbn is the same as EAN, just shorter. Dashes are accepted, the
376  * check character (if specified) is skipped, the extra 5 digits are
377  * accepted after a blank.
378  */
379 int Barcode_isbn_verify(unsigned char *text)
380 {
381     int i, ndigit=0;
382
383     for (i=0; text[i]; i++) {
384         if (text[i] == '-')
385             continue;
386         if (isdigit(text[i])) {
387             ndigit++;
388             if (ndigit == 9) { /* got it all */
389                 i++; break;
390             }
391             continue;
392         }
393         return -1; /* found non-digit */
394     }
395     if (ndigit!=9) return -1; /* too short */
396
397     /* skip an hyphen, if any */
398     if (text[i] == '-')
399         i++;
400     /* accept one more char if any (the checksum) */
401     if (isdigit(text[i]) || toupper(text[i])=='X')
402         i++;
403     if (text[i] == '\0')
404         return 0; /* Ok */
405
406     /* and accept the extra price tag (blank + 5 digits), if any */
407     if (strlen(text+i) != 6)
408         return -1;
409     if (text[i] != ' ')
410         return -1;
411     i++; /* skip the blank */
412     while (text[i]) {
413         if (!isdigit(text[i]))
414             return -1;
415         i++;
416     }
417     return 0; /* Ok: isbn + 5-digit addon */
418 }
419
420 static int width_of_partial(unsigned char *partial)
421 {
422     int i=0;
423     while (*partial) {
424         if (isdigit(*partial))
425             i += *partial - '0';
426         else if (islower(*partial))
427             i += *partial - 'a' + 1;
428         partial++;
429     }
430     return i;
431 }
432
433 /*
434  * The encoding functions fills the "partial" and "textinfo" fields.
435  * This one deals with both upc (-A and -E) and ean (13 and 8).
436  */
437 int Barcode_ean_encode(struct Barcode_Item *bc)
438 {
439     static char text[24];
440     static char partial[256];
441     static char textinfo[256];
442     char *mirror, *ptr1, *ptr2, *tptr = textinfo; /* where text is written */
443     char *spc;
444
445     enum {UPCA, UPCE, EAN13, EAN8, ISBN} encoding = ISBN;
446     int i, xpos, checksum, len, len0, addon;
447
448     if (!bc->ascii) {
449         bc->error = EINVAL;
450         return -1;
451     }
452
453     /* Find out whether the barcode has addon and
454      * the length of the barcode w/o the addon.
455      */
456     len = strlen(bc->ascii);
457     spc = strchr(bc->ascii, ' ');
458     if (spc) {
459         len0 = spc - bc->ascii;
460         addon = strlen(spc + 1);
461         if (addon != 2 && addon != 5) {
462             bc->error = EINVAL; /* impossible, actually */
463             return -1;
464         }
465     } else {
466         len0 = len;
467         addon = 0;
468     }
469
470     if (!bc->encoding) {
471         /* ISBN already wrote what it is; if unknown, find it out */
472
473         /*
474          * Do not decide only by barcode length, it may be ambiguous.
475          * Anyway, either the user specified the barcode type or
476          * we already found a fitting one.
477          */
478         switch(bc->flags & BARCODE_ENCODING_MASK) {
479         case BARCODE_EAN:
480                 switch (len0) {
481                 case 7: case 8:
482                         bc->encoding = strdup("EAN-8");
483                         encoding = EAN8;
484                         break;
485                 case 12: case 13:
486                         bc->encoding = strdup("EAN-13");
487                         encoding = EAN13;
488                         break;
489                 default:
490                         bc->error = -EINVAL;
491                         return -1;
492                 }
493                 break;
494
495         case BARCODE_UPC:
496                 switch (len0) {
497                 case 6: case 7: case 8:
498                         bc->encoding = strdup("UPC-E");
499                         encoding = UPCE;
500                         break;
501                 case 11: case 12:
502                         bc->encoding = strdup("UPC-A");
503                         encoding = UPCA;
504                         break;
505                 default:
506                         bc->error = -EINVAL;
507                         return -1;
508                 }
509                 break;
510         default:
511                 /* else, it's wrong (impossible, as the text is checked) */
512                 bc->error = -EINVAL;
513                 return -1;
514         }
515     }
516
517     /* better safe than sorry */
518     if (bc->partial)    free(bc->partial);  bc->partial =  NULL;
519     if (bc->textinfo)   free(bc->textinfo); bc->textinfo = NULL;
520
521     if (encoding == UPCA) { /* add the leading 0 (not printed) */
522         text[0] = '0';
523         strcpy(text+1, bc->ascii);
524     } else if (encoding == UPCE) {
525         strcpy(text, upc_a_to_e(upc_e_to_a(bc->ascii)));
526     } else {
527         strcpy(text, bc->ascii);
528     }
529
530     /*
531      * build the checksum and the bars: any encoding is slightly different
532      */
533     if (encoding == UPCA || encoding == EAN13 || encoding == ISBN) {
534         if (!(encoding == UPCA && len0 == 12) &&
535                 !(encoding == EAN13 && len0 == 13)) {
536                 checksum = ean_make_checksum(text, 0);
537                 text[12] = '0' + checksum; /* add it to the text */
538                 text[13] = '\0';
539         }
540
541         strcpy(partial, guard[0]);
542         if (encoding == EAN13 || encoding == ISBN) { /* The first digit */
543             sprintf(tptr,"0:12:%c ",text[0]);
544             tptr += strlen(tptr);
545             partial[0] = '9'; /* extra space for the digit */
546         } else if (encoding == UPCA)
547             partial[0] = '9'; /* UPC has one digit before the symbol, too */
548         xpos = width_of_partial(partial);
549         mirror = ean_mirrortab[text[0]-'0'];
550
551         /* left part */
552         for (i=1;i<7;i++) {      
553             ptr1 = partial + strlen(partial); /* target */
554             ptr2 =  digits[text[i]-'0'];      /* source */
555             strcpy(ptr1, ptr2);
556             if (mirror[i-1] == '1') {
557                 /* mirror this */
558                 ptr1[0] = ptr2[3];
559                 ptr1[1] = ptr2[2];
560                 ptr1[2] = ptr2[1];
561                 ptr1[3] = ptr2[0];
562             }
563             /*
564              * Write the ascii digit. UPC has a special case
565              * for the first digit, which is out of the bars
566              */
567             if (encoding == UPCA && i==1) {
568                 sprintf(tptr, "0:10:%c ", text[i]);
569                 tptr += strlen(tptr);
570                 ptr1[1] += 'a'-'1'; /* bars are long */
571                 ptr1[3] += 'a'-'1';
572             } else {
573                 sprintf(tptr, "%i:12:%c ", xpos, text[i]);
574                 tptr += strlen(tptr);
575             }
576             /* count the width of the symbol */
577             xpos += 7; /* width_of_partial(ptr2) */
578         }
579
580         strcat(partial, guard[1]); /* middle */
581         xpos += width_of_partial(guard[1]);
582     
583         /* right part */
584         for (i=7;i<13;i++) {  
585             ptr1 = partial + strlen(partial); /* target */
586             ptr2 =  digits[text[i]-'0'];      /* source */
587             strcpy(ptr1, ptr2);
588             /*
589              * Ascii digit. Once again, UPC has a special
590              * case for the last digit
591              */
592             if (encoding == UPCA && i==12) {
593                 sprintf(tptr, "%i:10:%c ", xpos+13, text[i]);
594                 tptr += strlen(tptr);
595                 ptr1[0] += 'a'-'1'; /* bars are long */
596                 ptr1[2] += 'a'-'1';
597             } else {
598                 sprintf(tptr, "%i:12:%c ", xpos, text[i]);
599                 tptr += strlen(tptr);
600             }
601             xpos += 7; /* width_of_partial(ptr2) */
602         }
603         tptr[-1] = '\0'; /* overwrite last space */
604         strcat(partial, guard[2]); /* end */
605         xpos += width_of_partial(guard[2]);
606
607     } else if (encoding == UPCE) {
608         checksum = text[7] - '0';
609
610         strcpy(partial, guardE[0]);
611         partial[0] = '9'; /* UPC-A has one digit before the symbol, too */
612         xpos = width_of_partial(partial);
613
614         /* UPC-E has the number system written before the bars. */
615         sprintf(tptr, "0:10:%c ", text[0]);
616         tptr += strlen(tptr);
617
618         if (text[0] == '0')
619                 mirror = upc_mirrortab[checksum];
620         else
621                 mirror = upc_mirrortab1[checksum];
622
623         for (i=0;i<6;i++) {      
624             ptr1 = partial + strlen(partial); /* target */
625             ptr2 =  digits[text[i+1]-'0'];      /* source */
626             strcpy(ptr1, ptr2);
627             if (mirror[i] != '1') { /* negated wrt EAN13 */
628                 /* mirror this */
629                 ptr1[0] = ptr2[3];
630                 ptr1[1] = ptr2[2];
631                 ptr1[2] = ptr2[1];
632                 ptr1[3] = ptr2[0];
633             }
634             sprintf(tptr, "%i:12:%c ", xpos, text[i+1]);
635             tptr += strlen(tptr);
636             xpos += 7; /* width_of_partial(ptr2) */
637         }
638
639         sprintf(tptr, "%i:10:%c ", xpos+10, text[7]);
640         tptr += strlen(tptr);
641         ptr1[0] += 'a'-'1'; /* bars are long */
642         ptr1[2] += 'a'-'1';
643
644         tptr[-1] = '\0'; /* overwrite last space */
645         strcat(partial, guardE[1]); /* end */
646
647     } else { /* EAN-8  almost identical to EAN-13 but no mirroring */
648
649         if (len0 != 8) {
650             checksum = ean_make_checksum(text, 0);
651             text[7] = '0' + checksum; /* add it to the text */
652             text[8] = '\0';
653         }
654
655         strcpy(partial, guard[0]);
656         xpos = width_of_partial(partial);
657
658         /* left part */
659         for (i=0;i<4;i++) {      
660             strcpy(partial + strlen(partial), digits[text[i]-'0']);
661             sprintf(tptr, "%i:12:%c ", xpos, text[i]);
662             tptr += strlen(tptr);
663             xpos += 7; /* width_of_partial(digits[text[i]-'0' */
664         }
665         strcat(partial, guard[1]); /* middle */
666         xpos += width_of_partial(guard[1]);
667     
668         /* right part */
669         for (i=4;i<8;i++) {      
670             strcpy(partial + strlen(partial), digits[text[i]-'0']);
671             sprintf(tptr, "%i:12:%c ", xpos, text[i]);
672             tptr += strlen(tptr);
673             xpos += 7; /* width_of_partial(digits[text[i]-'0' */
674         }
675         tptr[-1] = '\0'; /* overwrite last space */
676         strcat(partial, guard[2]); /* end */
677     }
678
679     /*
680      * And that's it. Now, in case some add-on is specified it
681      * must be encoded too. Look for it.
682      */
683     if ( (ptr1 = spc) ) {
684         ptr1++;
685         strcpy(text, ptr1);
686         if (strlen(ptr1)==5) {
687             checksum = ean_make_checksum(text, 1 /* special way */);
688             mirror = upc_mirrortab[checksum]+1; /* only last 5 digits */
689         } else {
690             checksum = atoi(text)%4;
691             mirror = upc_mirrortab2[checksum];
692         }
693         strcat(textinfo, " +"); strcat(partial, "+");
694         tptr = textinfo + strlen(textinfo);
695         for (i=0; i<strlen(text); i++) {
696             if (!i) {
697                 strcat(partial, guardS[0]); /* separation and head */
698                 xpos += width_of_partial(guardS[0]);
699             } else {
700                 strcat(partial, guardS[1]);
701                 xpos += width_of_partial(guardS[1]);
702             }
703             ptr1 = partial + strlen(partial); /* target */
704             ptr2 =  digits[text[i]-'0'];      /* source */
705             strcpy(ptr1, ptr2);
706             if (mirror[i] != '1') { /* negated wrt EAN13 */
707                 /* mirror this */
708                 ptr1[0] = ptr2[3];
709                 ptr1[1] = ptr2[2];
710                 ptr1[2] = ptr2[1];
711                 ptr1[3] = ptr2[0];
712             }
713             /* and the text */
714             sprintf(tptr, " %i:12:%c", xpos, text[i]);
715             tptr += strlen(tptr);
716             xpos += 7; /* width_of_partial(ptr2) */
717         }
718     }
719
720     /* all done, copy results to the data structure */
721     bc->partial = strdup(partial);
722     if (!bc->partial) {
723         bc->error = errno;
724         return -1;
725     }
726     bc->textinfo = strdup(textinfo);
727     if (!bc->textinfo) {
728         bc->error = errno;
729         free(bc->partial);
730         bc->partial = NULL;
731         return -1;
732     }
733     if (!bc->width)
734         bc->width = width_of_partial(partial);
735
736     return 0; /* success */
737 }
738
739 int Barcode_upc_encode(struct Barcode_Item *bc)
740 {
741     return Barcode_ean_encode(bc); /* UPC is folded into EAN */
742 }
743
744 int Barcode_isbn_encode(struct Barcode_Item *bc)
745 {
746     /* For ISBN we must normalize the string and prefix "978" */
747     unsigned char *text = malloc(24); /* 13 + ' ' + 5 plus some slack */
748     unsigned char *otext;
749     int i, j, retval;
750
751     if (!text) {
752         bc->error = ENOMEM;
753         return -1;
754     }
755     strcpy(text, "978"); j=3;
756
757     otext = bc->ascii;
758     for (i=0; otext[i]; i++) {
759         if (isdigit(otext[i]))
760             text[j++] = otext[i];
761         if (j == 12) /* checksum added later */
762             break;
763     }
764     text[j]='\0';
765     if (strchr(otext, ' '))
766         strcat(text, strchr(otext, ' '));
767     bc->ascii = text;
768     bc->encoding = strdup("ISBN");
769     retval = Barcode_ean_encode(bc);
770     bc->ascii = otext; /* restore ascii for the ps comments */
771     free(text);
772     return retval;
773 }
774