Wednesday, October 15, 2008

Expanding barcode scans into EANs

*UPDATED*
I just discovered a bug in the check digit calculation, fixed below.  Essentially, the check digit is only subtracted from 10 if the sum_of_totals % 10 is greater than 0.  I'm sorry for any inconvenience!

I recently had a project where I had to use a barcode scanner to make scans of numbers that were possibly smaller than the 13 digit EAN standard. I had to then check these against the same number in a list, but the numbers in the list had been expanded to 13 digits using the algorithm below:

If the scanned number has 13 or more digits, return it.
If the scanned number has 12 digits, prepend a zero and return the result.

If the scanned number had 6 digits, treat it as a Zero Compressed UPC-E, expand it 11 digits using the following table and continue:

Last digit UPC-E equivalent is UPC-A equivalent is
0 XXNNN0 0XX000-00NNN + check
1 XXNNN1 0XX100-00NNN + check
2 XXNNN2 0XX200-00NNN + check
3 XXXNN3 0XXX00-000NN + check
4 XXXXN4 0XXXX0-0000N + check
5 XXXXX5 0XXXXX-00005 + check
6 XXXXX6 0XXXXX-00006 + check
7 XXXXX7 0XXXXX-00007 + check
8 XXXXX8 0XXXXX-00008 + check
9 XXXXX9 0XXXXX-00009 + check

If the number (as scanned or expanded) has less than 12 digits, left pad it to 12 digits using zeros, add a check digit, and return the result.

The check digit is computed as follows:
  1. Sum the digits in all odd positions, and multiply the result by 3.
  2. Sum the digits in all even positions.
  3. Sum the totals calculated in steps 2 and 3.
  4. *UPDATED* Check digit is sum_of_totals % 10 > 0 ? 10 - (sum_of_totals % 10) : 0
Here is my C# program that I used to work this out:
using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1 {
    class Program {
        protected static int[][] upcPartialChecksumDigit = new int[][]{ 
            new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 0
            new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }, // 1
            new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 2
            new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }, // 3
            new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 4
            new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }, // 5
            new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 6
            new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }, // 7
            new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 8
            new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }, // 9
            new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 10
            new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }  // 11
        };

        /*
        Note 6 digit UPC-E codes are expanded to 12 digit UPC-A codesas follows:
        0  XXNNN0  00XX000-00NNN + check
        1  XXNNN1  00XX100-00NNN + check
        2  XXNNN2  00XX200-00NNN + check
        3  XXXNN3  00XXX00-000NN + check
        4  XXXXN4  00XXXX0-0000N + check
        5  XXXXX5  00XXXXX-00005 + check
        6  XXXXX6  00XXXXX-00006 + check
        7  XXXXX7  00XXXXX-00007 + check
        8  XXXXX8  00XXXXX-00008 + check
        9  XXXXX9  00XXXXX-00009 + check
        */
        public static string getUPC(string id) {
            if (id == null) return null;

            StringBuilder s = new StringBuilder("0", 11);
            if (id.Length == 6) {
                switch (id[5] - '0') {
                    case 0:
                        id = s.Append(id[0]).Append(id[1]).Append("00000").Append(id[2]).Append(id[3]).Append(id[4]).ToString();
                        break;
                    case 1:
                        id = s.Append(id[0]).Append(id[1]).Append("10000").Append(id[2]).Append(id[3]).Append(id[4]).ToString();
                        break;
                    case 2:
                        id = s.Append(id[0]).Append(id[1]).Append("20000").Append(id[2]).Append(id[3]).Append(id[4]).ToString();
                        break;
                    case 3:
                        id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append("00000").Append(id[3]).Append(id[4]).ToString();
                        break;
                    case 4:
                        id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append("00000").Append(id[4]).ToString();
                        break;
                    case 5:
                        id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append(id[4]).Append("00005").ToString();
                        break;
                    case 6:
                        id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append(id[4]).Append("00006").ToString();
                        break;
                    case 7:
                        id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append(id[4]).Append("00007").ToString();
                        break;
                    case 8:
                        id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append(id[4]).Append("00008").ToString();
                        break;
                    case 9:
                        id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append(id[4]).Append("00009").ToString();
                        break;
                    default:
                        return null;
                }
            }
            if (id.Length < 12) {
                id = id.PadLeft(12, '0');
                int checksum = 0;
                for (int i = 0; i < 12; i++) { //should get unrolled by compiler hopefully
                    int c = id[i] - '0';
                    if (c < 0 || c > 9) return null;
                    int partial = upcPartialChecksumDigit[i][c];
                    checksum += partial;
                }
                // *UPDATED*
                char checksumDigit = '0';
                checksum %= 10;
                if(checksum > 0) checksumDigit += (char)(10 - checksum);
                id += checksumDigit;
            }
            else if (id.Length == 12) {
                id = '0' + id;
            }
            return id;
        }
        


        static void Main(string[] args) {
            string s = ConvertUPC("654321");
            Console.WriteLine();
        }

        public static String ConvertUPC(string arg) {
            return arg + " -> " + getUPC(arg);
        }
    }
}

No comments:

Content © didge

About Me

didge is my professional nickname, it's short for digital dave
Powered By Blogger