/* * ean.c -- encoding for ean, upc and isbn * * Copyright (c) 1999 Alessandro Rubini * Copyright (c) 1999 Prosa Srl. * Copyright (c) 2001 Boszormenyi Zoltan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include "barcode.h" /* * IMPORTANT NOTE: if you are reading this file to learn how to add a * new encoding type, this is the wrong place as there are too many * special cases. Please refer to code39.c instead. If you want to * learn how UPC, EAN, ISBN work, on the other hand, I did my best to * commend things and hope you enjoy it. */ /* * These following static arrays are used to describe the barcode. * * The various forms of UPC and EAN are documented as using three * different alphabets to encode the ten digits. However, each digit * has exactly one encoding; only, it is sometimes mirrored. Moreover, * if you represent the width of each symbol (bar/space) instead of * the sequence of 1's and 0's, you find that even-parity and odd-parity * encoding are exactly the same. So, here are the digits: */ static char *digits[] = { "3211","2221","2122","1411","1132", "1231","1114","1312","1213","3112"}; /* * What EAN encoding does is adding a leading digit (the 13th digit). * Such an extra digit is encoded by mirroring three of the six digits that * appear in the left half of the UPC code. Here how mirroring works: */ static char *ean_mirrortab[] = { "------","--1-11","--11-1","--111-","-1--11", "-11--1","-111--","-1-1-1","-1-11-","-11-1-" }; /* * UPC-E (the 6-digit one), instead, encodes the check character as * a mirroring of the symbols. This is similar, but the encoding for "0" is * different (EAN uses no mirroring for "0" to be compatible with UPC). * The same rule is used for UPC-5 (the supplemental digits for ISBN) */ static char *upc_mirrortab[] = { "---111","--1-11","--11-1","--111-","-1--11", "-11--1","-111--","-1-1-1","-1-11-","-11-1-" }; /* * UPC-E mirroring for encoding "1" */ static char *upc_mirrortab1[] = { "111---","11-1--","11--1-","11---1","1-11--", "1--11-","1---11","1-1-1-","1-1--1","1--1-1" }; /* UPC-2 has just two digits to mirror */ static char *upc_mirrortab2[] = { "11","1-","-1","--" }; /* * initial, middle, final guard bars (first symbol is a a space). * EAN-13 overwrites the first "0" with "9" to make space for the extra digit. */ static char *guard[] = {"0a1a","1a1a1","a1a"}; /* initial, final guard bars for UPC-E*/ static char *guardE[] = {"0a1a","1a1a1a"}; /* initial and inter-char guard bars for supplementals (first is space) */ static char *guardS[] = {"9112","11"}; /* * These functions are shortcuts I use in the encoding engine */ static int ean_make_checksum(char *text, int mode) { int esum = 0, osum = 0, i; int even=1; /* last char is even */ if (strchr(text, ' ')) i = strchr(text, ' ') - text; /* end of first part */ else i = strlen(text); /* end of all */ while (i-- > 0) { if (even) esum += text[i]-'0'; else osum += text[i]-'0'; even = !even; } if (!mode) { /* standard upc/ean checksum */ i = (3*esum + osum) % 10; return (10-i) % 10; /* complement to 10 */ } else { /* add-5 checksum */ i = (3*esum + 9*osum); return i%10; } } /* * Check that the text can be encoded. Returns 0 or -1. * Accept: * 13 or 12 digits: EAN-13 w/ or w/o checksum * or * 8 or 7 digits: EAN-8 w/ or w/o checksum. * For both EAN-13 and EAN-8, accept an addon of 2 or 5 digits, * separated by ' ' */ int Barcode_ean_verify(unsigned char *text) { int i, len0, len, addon; unsigned char tmp[24], *spc; len = strlen(text); spc = strchr(text, ' '); if (spc) { len0 = spc - text; addon = len - len0 - 1; if (addon != 2 && addon != 5) return -1; for (i=len0+1; i= '5' && text[10] <= '9') { memcpy(&result[1], text+1, 5); result[6] = text[10]; } else { return NULL; } result[7] = chksum + '0'; return result; } /* * UPC-A is the same as EAN, but accept * 12 or 11 digits (UPC-A w/ or w/o checksum) * or accept UPC-E as: * 6 digits (w/o number system and checksum): number system '0' assumed, * 7 digits (either w/o number system or checksum), * 8 digits (w/ number system and checksum) * plus the 2 or 5-digit add-on */ int Barcode_upc_verify(unsigned char *text) { int i, len0, len, addon; unsigned char tmp[24], *spc; len = strlen(text); spc = strchr(text, ' '); if (spc) { len0 = spc - text; addon = len - len0 - 1; if (addon != 2 && addon != 5) return -1; for (i=len0+1; iascii) { bc->error = EINVAL; return -1; } /* Find out whether the barcode has addon and * the length of the barcode w/o the addon. */ len = strlen(bc->ascii); spc = strchr(bc->ascii, ' '); if (spc) { len0 = spc - bc->ascii; addon = strlen(spc + 1); if (addon != 2 && addon != 5) { bc->error = EINVAL; /* impossible, actually */ return -1; } } else { len0 = len; addon = 0; } if (!bc->encoding) { /* ISBN already wrote what it is; if unknown, find it out */ /* * Do not decide only by barcode length, it may be ambiguous. * Anyway, either the user specified the barcode type or * we already found a fitting one. */ switch(bc->flags & BARCODE_ENCODING_MASK) { case BARCODE_EAN: switch (len0) { case 7: case 8: bc->encoding = strdup("EAN-8"); encoding = EAN8; break; case 12: case 13: bc->encoding = strdup("EAN-13"); encoding = EAN13; break; default: bc->error = -EINVAL; return -1; } break; case BARCODE_UPC: switch (len0) { case 6: case 7: case 8: bc->encoding = strdup("UPC-E"); encoding = UPCE; break; case 11: case 12: bc->encoding = strdup("UPC-A"); encoding = UPCA; break; default: bc->error = -EINVAL; return -1; } break; default: /* else, it's wrong (impossible, as the text is checked) */ bc->error = -EINVAL; return -1; } } /* better safe than sorry */ if (bc->partial) free(bc->partial); bc->partial = NULL; if (bc->textinfo) free(bc->textinfo); bc->textinfo = NULL; if (encoding == UPCA) { /* add the leading 0 (not printed) */ text[0] = '0'; strcpy(text+1, bc->ascii); } else if (encoding == UPCE) { strcpy(text, upc_a_to_e(upc_e_to_a(bc->ascii))); } else { strcpy(text, bc->ascii); } /* * build the checksum and the bars: any encoding is slightly different */ if (encoding == UPCA || encoding == EAN13 || encoding == ISBN) { if (!(encoding == UPCA && len0 == 12) && !(encoding == EAN13 && len0 == 13)) { checksum = ean_make_checksum(text, 0); text[12] = '0' + checksum; /* add it to the text */ text[13] = '\0'; } strcpy(partial, guard[0]); if (encoding == EAN13 || encoding == ISBN) { /* The first digit */ sprintf(tptr,"0:12:%c ",text[0]); tptr += strlen(tptr); partial[0] = '9'; /* extra space for the digit */ } else if (encoding == UPCA) partial[0] = '9'; /* UPC has one digit before the symbol, too */ xpos = width_of_partial(partial); mirror = ean_mirrortab[text[0]-'0']; /* left part */ for (i=1;i<7;i++) { ptr1 = partial + strlen(partial); /* target */ ptr2 = digits[text[i]-'0']; /* source */ strcpy(ptr1, ptr2); if (mirror[i-1] == '1') { /* mirror this */ ptr1[0] = ptr2[3]; ptr1[1] = ptr2[2]; ptr1[2] = ptr2[1]; ptr1[3] = ptr2[0]; } /* * Write the ascii digit. UPC has a special case * for the first digit, which is out of the bars */ if (encoding == UPCA && i==1) { sprintf(tptr, "0:10:%c ", text[i]); tptr += strlen(tptr); ptr1[1] += 'a'-'1'; /* bars are long */ ptr1[3] += 'a'-'1'; } else { sprintf(tptr, "%i:12:%c ", xpos, text[i]); tptr += strlen(tptr); } /* count the width of the symbol */ xpos += 7; /* width_of_partial(ptr2) */ } strcat(partial, guard[1]); /* middle */ xpos += width_of_partial(guard[1]); /* right part */ for (i=7;i<13;i++) { ptr1 = partial + strlen(partial); /* target */ ptr2 = digits[text[i]-'0']; /* source */ strcpy(ptr1, ptr2); /* * Ascii digit. Once again, UPC has a special * case for the last digit */ if (encoding == UPCA && i==12) { sprintf(tptr, "%i:10:%c ", xpos+13, text[i]); tptr += strlen(tptr); ptr1[0] += 'a'-'1'; /* bars are long */ ptr1[2] += 'a'-'1'; } else { sprintf(tptr, "%i:12:%c ", xpos, text[i]); tptr += strlen(tptr); } xpos += 7; /* width_of_partial(ptr2) */ } tptr[-1] = '\0'; /* overwrite last space */ strcat(partial, guard[2]); /* end */ xpos += width_of_partial(guard[2]); } else if (encoding == UPCE) { checksum = text[7] - '0'; strcpy(partial, guardE[0]); partial[0] = '9'; /* UPC-A has one digit before the symbol, too */ xpos = width_of_partial(partial); /* UPC-E has the number system written before the bars. */ sprintf(tptr, "0:10:%c ", text[0]); tptr += strlen(tptr); if (text[0] == '0') mirror = upc_mirrortab[checksum]; else mirror = upc_mirrortab1[checksum]; for (i=0;i<6;i++) { ptr1 = partial + strlen(partial); /* target */ ptr2 = digits[text[i+1]-'0']; /* source */ strcpy(ptr1, ptr2); if (mirror[i] != '1') { /* negated wrt EAN13 */ /* mirror this */ ptr1[0] = ptr2[3]; ptr1[1] = ptr2[2]; ptr1[2] = ptr2[1]; ptr1[3] = ptr2[0]; } sprintf(tptr, "%i:12:%c ", xpos, text[i+1]); tptr += strlen(tptr); xpos += 7; /* width_of_partial(ptr2) */ } sprintf(tptr, "%i:10:%c ", xpos+10, text[7]); tptr += strlen(tptr); ptr1[0] += 'a'-'1'; /* bars are long */ ptr1[2] += 'a'-'1'; tptr[-1] = '\0'; /* overwrite last space */ strcat(partial, guardE[1]); /* end */ } else { /* EAN-8 almost identical to EAN-13 but no mirroring */ if (len0 != 8) { checksum = ean_make_checksum(text, 0); text[7] = '0' + checksum; /* add it to the text */ text[8] = '\0'; } strcpy(partial, guard[0]); xpos = width_of_partial(partial); /* left part */ for (i=0;i<4;i++) { strcpy(partial + strlen(partial), digits[text[i]-'0']); sprintf(tptr, "%i:12:%c ", xpos, text[i]); tptr += strlen(tptr); xpos += 7; /* width_of_partial(digits[text[i]-'0' */ } strcat(partial, guard[1]); /* middle */ xpos += width_of_partial(guard[1]); /* right part */ for (i=4;i<8;i++) { strcpy(partial + strlen(partial), digits[text[i]-'0']); sprintf(tptr, "%i:12:%c ", xpos, text[i]); tptr += strlen(tptr); xpos += 7; /* width_of_partial(digits[text[i]-'0' */ } tptr[-1] = '\0'; /* overwrite last space */ strcat(partial, guard[2]); /* end */ } /* * And that's it. Now, in case some add-on is specified it * must be encoded too. Look for it. */ if ( (ptr1 = spc) ) { ptr1++; strcpy(text, ptr1); if (strlen(ptr1)==5) { checksum = ean_make_checksum(text, 1 /* special way */); mirror = upc_mirrortab[checksum]+1; /* only last 5 digits */ } else { checksum = atoi(text)%4; mirror = upc_mirrortab2[checksum]; } strcat(textinfo, " +"); strcat(partial, "+"); tptr = textinfo + strlen(textinfo); for (i=0; ipartial = strdup(partial); if (!bc->partial) { bc->error = errno; return -1; } bc->textinfo = strdup(textinfo); if (!bc->textinfo) { bc->error = errno; free(bc->partial); bc->partial = NULL; return -1; } if (!bc->width) bc->width = width_of_partial(partial); return 0; /* success */ } int Barcode_upc_encode(struct Barcode_Item *bc) { return Barcode_ean_encode(bc); /* UPC is folded into EAN */ } int Barcode_isbn_encode(struct Barcode_Item *bc) { /* For ISBN we must normalize the string and prefix "978" */ unsigned char *text = malloc(24); /* 13 + ' ' + 5 plus some slack */ unsigned char *otext; int i, j, retval; if (!text) { bc->error = ENOMEM; return -1; } strcpy(text, "978"); j=3; otext = bc->ascii; for (i=0; otext[i]; i++) { if (isdigit(otext[i])) text[j++] = otext[i]; if (j == 12) /* checksum added later */ break; } text[j]='\0'; if (strchr(otext, ' ')) strcat(text, strchr(otext, ' ')); bc->ascii = text; bc->encoding = strdup("ISBN"); retval = Barcode_ean_encode(bc); bc->ascii = otext; /* restore ascii for the ps comments */ free(text); return retval; }