Serge's World

Blogging about software development, astronomy, genealogy and more.

Barcodes in C#: UPC-A

The UPC-A barcode format is very similar to the EAN-13 barcode and is mostly used in America, although in recent years, the US is moving towards the EAN-13 format too. The UPC-A format is 12 digits long, with the last digit being the checksum (or parity) digit.

UPC-A

To calculate the parity for UPC-A, we use the same process as with the EAN-13 barcode. We first need add up the 11 digits of the barcode with the weighting applied. The weighting is the same as the EAN-13 weighting, where the weighting of each digit which is in an odd-numbered position is 1 and for even-numbered positions, the weighting is 3. To find the sum, we need to multiply each digit by its weighting before adding it together.

Once we have the weighted sum, we apply a modulo 10 to the weighted sum, which gets the remainder after applying a modulo of 10 to the weighted sum.

The parity is then 10 - (modulo 10 of the weighted sum).

Now that we have an 12 digit number, we can encode it.

We split the UPC-A barcode into 2 blocks of 6 digits each, with a guard bar at the beginning and end of the barcode and one in the middle of the barcode too. The end guard bars are encoded as 101, while the middle guard bar is 01010.

In the EAN-13 barcode format, the parity digit was not encoded directly, with only the first 12 digits being encoded, whereas with UPC-A, all 12 digits are encoded, including the parity. This simplifies the encoding process, since we do not have to worry about odd and even parity encodings.

The left hand side encodings are identical to the left hand odd parity encodings from EAN-13.

0 0001101
1 0011001
2 0010011
3 0111101
4 0100011
5 0110001
6 0101111
7 0111011
8 0110111
9 0001011

These encodings ensure that UPC-A forms a subset of the EAN-13 barcode.

The right hand side encodings (the last 6 digits) are the same as the right hand codings of EAN-13. The encodings are as follows:

0 1110010
1 1100110
2 1101100
3 1000010
4 1011100
5 1001110
6 1010000
7 1000100
8 1001000
9 1110100

The full source code is available at https://github.com/sjmeunier/barcoder.

namespace BarcoderLib
{
    public class BarcodeUPCA
    {
        private string gLeftGuard = "101";
        private string gCentreGuard = "01010";
        private string gRightGuard = "101";
        private string[] gLH = { "0001101", "0011001", "0010011", "0111101", "0100011", "0110001", "0101111", "0111011", "0110111", "0001011" };
        private string[] gRH = { "1110010", "1100110", "1101100", "1000010", "1011100", "1001110", "1010000", "1000100", "1001000", "1110100" };
        private int[] gWeighting = { 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3 };
        private string gLongBars = "11111111110000000000000000000000000000000000011111000000000000000000000000000000000001111111111";

  

        public Bitmap Encode(string message)
        {
            string encodedMessage;
            string fullMessage;

            Bitmap barcodeImage = new Bitmap(250, 100);
            Graphics g = Graphics.FromImage(barcodeImage);


            Validate(message);
            fullMessage = message + CalcParity(message).ToString().Trim();
            encodedMessage = EncodeBarcode(fullMessage);

            PrintBarcode(g, encodedMessage, fullMessage, 250, 100);

            return barcodeImage;
        }
        private void Validate(string message)
        {

            Regex reNum = new Regex(@"^\d+$");
            if (reNum.Match(message).Success == false)
            {
                throw new Exception("Encode string must be numeric");
            }

            if (message.Length != 11)
            {
                throw new Exception("Encode string must be 11 digits long");
            }
        }

        private void PrintBarcode(Graphics g, string encodedMessage, string message, int width, int height)
        {
            SolidBrush whiteBrush = new SolidBrush(Color.White);
            SolidBrush blackBrush = new SolidBrush(Color.Black);
            Font textFont = new Font(FontFamily.GenericMonospace, 10, FontStyle.Regular);
            g.FillRectangle(whiteBrush, 0, 0, width, height);

            int xPos = 20;
            int yTop = 10;
            int barHeight = 50;
            int barGuardHeight = 7;

            for (int i = 0; i < encodedMessage.Length; i++)
            {
                if (encodedMessage[i] == '1')
                {
                    if (gLongBars[i] == '1')
                    {
                        g.FillRectangle(blackBrush, xPos, yTop, 1, barHeight + barGuardHeight);
                    }
                    else
                    {
                        g.FillRectangle(blackBrush, xPos, yTop, 1, barHeight);
                    }
                }
                xPos += 1;
            }
            
            xPos = 20;
            yTop += barHeight - 2;
            g.DrawString(message[0].ToString().Trim(), textFont, blackBrush, xPos - 10, yTop);

            xPos += 8;
            for (int i = 1; i < 6; i++)
            {
                g.DrawString(message[i].ToString().Trim(), textFont, blackBrush, xPos, yTop);
                xPos += 7;
            }
            xPos += 4;

            for (int i = 6; i <11; i++)
            {
                g.DrawString(message[i].ToString().Trim(), textFont, blackBrush, xPos, yTop);
                xPos += 7;
            }
            xPos += 11;
            g.DrawString(message[11].ToString().Trim(), textFont, blackBrush, xPos, yTop);

        }

        private string EncodeBarcode(string message)
        {
            int i;
            string encodedString = gLeftGuard;

            for (i = 0; i < 6; i++)
            {
                encodedString += gLH[Convert.ToInt32(message[i].ToString())];
            }
            encodedString += gCentreGuard;

            for (i = 6; i < 12; i++)
            {
                encodedString += gRH[Convert.ToInt32(message[i].ToString())];
            }
            encodedString += gRightGuard;

            return encodedString;
        }

        private int CalcParity(string message)
        {
            int sum = 0;
            int parity = 0;

            for (int i = 0; i < 11; i++)
            {
                sum += Convert.ToInt32(message[i].ToString()) * gWeighting[i];
            }

            parity = 10 - (sum % 10);
            if (parity == 10)
            {
                parity = 0;
            }
            return parity;
        }

    }
}

Originally posted on my old blog, Smoky Cogs, on 27 Oct 2009

Tag Cloud

Algorithms (3) Android (10) Astronomy (25) Audio (1) Audiobooks (1) Barcodes (9) C# (69) Css (1) Deep sky (6) Esoteric languages (3) Family (3) Fractals (10) Gaming (1) Genealogy (14) General (2) Geodesy (3) Google (1) Graphics (3) Hubble (2) Humour (1) Image processing (23) Java (8) Javascript (5) jQuery (3) Jupiter (3) Maths (22) Moon (5) Music (4) Pets (5) Programming (88) Saturn (1) Science (1) Spitzer (4) Sun (4) Tutorials (68) Unity (3) Web (9) Whisky (13) Windows (1) Xamarin (2)