EAN13 Barcode Class

This is a lightweight class that prints EAN13 barcodes. It displays captions for the manufacturer and product descriptions as well as the numerical representation of the code.

All initialisation is performed in the following constructors:

CBarcode(CString strCode, CString strText, UINT nNum);
CBarcode(CString strCode, CString strText, CWnd* pWnd);

Parameters

The parameters are self-explanatory:

  • strCode takes a 12-digit numerical string
  • strText takes a newline-separated string that contains both the company and the product captions
  • nNum specifies the number of labels to be printed
  • pWnd takes a pointer to a window into which to draw the image

Under the Bonnet

  • If you are using the printing constructor, the class initialises the default printer on the system, determining the maximum width and height of the printable area based on your defaults in printer settings.
  • The drawing constructor fits the image of the barcode into the client rectangle of the supplied window object.
  • The barcode lines, font size, and margins are all calculated from the above.
  • The code parses the 12-character code into the appropriate EAN13 format.
  • Finally, depending on the constructor, the code either starts the printing process, looping nNum times, or simply draws the image into the client area of the window.

Credits

I outright stole and modified the ConvertToDigitPatterns() function I found on the Net (wtitten in C# by rainman_63). The rest of the code is 'home-grown.'

Usage

  1. Do the inclusion:
    #include "CBarcode.h"
  2. Construct the CBarcode object, e.g.:
    CBarcode bc(L"0123456789123", L"My Company\nThe product", 5);
    or:
    CBarcode bc(L"0123456789123", L"My Company\nThe product",
                GetDlgItem(IDC_SOMEWINDOW));

References

This site gives quite an insight into EAN13 and other barcode structures and standards: http://www.activebarcode.com/codes/ean13.html

EAN13 Barcode Class

The Class Source

#pragma once

class CBarcode
{
   public:
   CBarcode(CString strCode, CString strText, UINT nNum)
   {
      strCode = strCode.Right(12);

      ASSERT(strText.GetLength() > 0);
      ASSERT(nNum > 0);

      if(strCode.GetLength() !=  12)
      {
         AfxMessageBox(L"Invalid code format.",  MB_ICONWARNING);
         return;
      }

      CDC dc;
      CPrintDialog printDlg(FALSE);
      printDlg.GetDefaults();

      if(!printDlg.m_pd.hDevMode)
      {
         AfxMessageBox(L"No printer found.");
         return;
      }

      printDlg.CreatePrinterDC();

      //Attach a printer DC
      dc.Attach(printDlg.GetPrinterDC());

      int nWidth  =  dc.GetDeviceCaps(HORZRES);
      int nHeight =  dc.GetDeviceCaps(VERTRES);

      //Begin a new print job
      dc.StartDoc(AfxGetAppName());

      for(UINT i = 0; i <  nNum; i++)
      {
         dc.StartPage();

         Print(&dc, strCode, strText, nWidth, nHeight);

         dc.EndPage();
      }

       dc.EndDoc();
       dc.Detach();
   }

   CBarcode(CString strCode, CString strText, CWnd* pWnd)
   {
      strCode = strCode.Right(12);

      ASSERT(strText.GetLength() > 0);
      ASSERT(IsWindow(pWnd->m_hWnd));

      if(strCode.GetLength() !=  12)
      {
         AfxMessageBox(L"Invalid code format.",  MB_ICONWARNING);
         return;
      }

      CDC* dc = pWnd->GetDC();
      CRect rc;
      pWnd->GetClientRect(&rc);
      dc->FillSolidRect(rc, RGB(255, 255, 255));
      Print(dc, strCode, strText, rc.Width(), rc.Height());

      pWnd->ReleaseDC(dc);
}

private:
   void Print(CDC* dc, CString strCode, CString strText,
              int nWidth, int nHeight)
   {
      const CString strOddLeft[10] =
        {L"0001101", L"0011001", L"0010011", L"0111101",
          L"0100011", L"0110001", L"0101111", L"0111011",
          L"0110111", L"0001011"};
      const CString strEvenLeft[10] =
         {L"0100111", L"0110011", L"0011011", L"0100001",
          L"0011101", L"0111001", L"0000101", L"0010001",
          L"0001001", L"0010111"};
      const CString strRight[10] =
         {L"1110010", L"1100110", L"1101100", L"1000010",
          L"1011100", L"1001110", L"1010000", L"1000100",
          L"1001000", L"1110100"};
      const CString  strQuiteZone = L"000000000";
      const CString strLeadTail = L"101";
      const CString strSeparator = L"01010";

      int nFontSize = nHeight / 8;
      int nMargin   = nHeight / 15;

      nWidth  -= 2*nMargin;
      nHeight -= 2*nMargin;

      dc->SetMapMode(MM_ANISOTROPIC);
      dc->SetWindowExt(nWidth, nHeight);
      dc->SetWindowOrg(0, 0);
      dc->SetViewportExt(nWidth, nHeight);
      dc->SetViewportOrg(0, 0);

      //    EAN13 Barcode should be a total of 113 modules wide.
      int nLineWidth = nWidth / 113;
      int xStart = nMargin + (nWidth - (nLineWidth * 113)) / 2;
      int xPosition = xStart;

      CFont font;
      font.CreateFont(nFontSize, 0, 0, 0, FW_NORMAL, FALSE,
                      FALSE, 0, EASTEUROPE_CHARSET,
                      OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                      DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS,
                      L"Arial");
      CFont* pOldFont = dc->SelectObject(&font);
      int nOldBk      = dc->SetBkMode(TRANSPARENT);

      // Calculate the Check  Digit.///////////////////////
      int nSum = 0;
      int nDigit = 0;

      for(int i = 12; i >= 1; i--)
      {
         nDigit = _wtoi(strCode.Mid(i - 1, 1));

         if(i % 2 == 0)
            nSum += nDigit * 3;
         else
            nSum += nDigit * 1;
      }

      CString strCheckSum;
      strCheckSum.Format(L"%i", (10 - (nSum % 10)) %  10);
      strCode += strCheckSum;
      ////////////////////////////////////////////////////

      // Convert the left hand numbers.
      CString strLeftPattern;

      switch(_wtoi(strCode.Left(1)))
      {
         case 0:
            strLeftPattern =
               ConvertToDigitPatterns(strCode.Mid(1, 6), strOddLeft);
            break;
         case 1:
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(0, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(1, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(2, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(3, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(4, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(5, 1), strEvenLeft);
            break;
         case 2:
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(0, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(1, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(2, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(3, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(4, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(5, 1), strEvenLeft);
            break;
         case 3:
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(0, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(1, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(2, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(3, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(4, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(5, 1), strOddLeft);
            break;
         case 4:
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(0, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(1, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(2, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(3, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(4, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(5, 1), strEvenLeft);
            break;
         case 5:
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(0, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(1, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(2, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(3, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(4, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(5, 1), strEvenLeft);
            break;
         case 6:
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(0, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(1, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(2, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(3, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(4, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(5, 1), strOddLeft);
            break;
         case 7:
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(0, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(1, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(2, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(3, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(4, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(5, 1), strEvenLeft);
            break;
         case 8:
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(0, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(1, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(2, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(3, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(4, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(5, 1), strOddLeft);
            break;
         case 9:
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(0, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(1, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(2, 1), strEvenLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(3, 1), strOddLeft);
            strLeftPattern +=
               ConvertToDigitPatterns(strCode.Mid(4, 1), strEvenLeft);
            strLeftPattern +=
                ConvertToDigitPatterns(strCode.Mid(5, 1), strOddLeft);
            break;
      }

      // Build the UPC Code.
      CString strEAN13 = strQuiteZone + strLeadTail +
                         strLeftPattern + strSeparator +
                         ConvertToDigitPatterns(strCode.Mid(7),
                                                strRight) +
                         strLeadTail + strQuiteZone;

      // Draw the barcode lines.
      for(int i = 0; i < strEAN13.GetLength(); i++)
      {
         if(strEAN13.Mid(i, 1) == L"1")
         {
            // Save room for the UPC number below the bar code.
            if((i > 12 && i < 55 ) || (i > 57 && i < 101))
               // Draw space  for the number
               dc->FillSolidRect(xPosition, nMargin +
                  (2 * nFontSize), nLineWidth, nHeight -
                  (3 * nFontSize), RGB(0, 0, 0));
            else
                  // Draw a full line.
                  dc->FillSolidRect(xPosition, nMargin +
                     (2 * nFontSize), nLineWidth, nHeight -
                     (2 * nFontSize), RGB(0, 0, 0));
         }

         xPosition += nLineWidth;
      }

      int yPosition = nMargin +  nHeight - nFontSize;

      // Draw 1st digit of the country code.
      dc->DrawText(strCode.Left(1),
         CRect(xStart - dc->GetTextExtent(strCode.Left(1)).cx +
         (7 * nLineWidth), yPosition, xStart + (7 * nLineWidth),
         nHeight + nMargin), DT_CENTER);

      // Draw MFG Number.
      dc->DrawText(strCode.Mid(1, 6), CRect(xStart +
                      (12 * nLineWidth), yPosition, xStart +
                      (55 * nLineWidth), nHeight + nMargin),
                      DT_CENTER);

      // Draw Product ID.
      dc->DrawText(strCode.Mid(7), CRect(xStart + (57 * nLineWidth),
                   yPosition, xStart + (101 * nLineWidth),
                   nHeight + nMargin),  DT_CENTER);

        dc->DrawText(strText, CRect(xStart, nMargin, xStart +
                     (113 * nLineWidth), nMargin + (2 * nFontSize)),
                     DT_CENTER | DT_VCENTER);

      //Restore the DC
      dc->SelectObject(pOldFont);
      nOldBk = dc->SetBkMode(nOldBk);
   }

   CString ConvertToDigitPatterns(CString strInputNumber,
                                  const CString strPatterns[10])
   {
      CString strTemp;
      int nIndex = 0;

      for(int i = 0; i < strInputNumber.GetLength(); i++)
      {
         nIndex = _wtoi(strInputNumber.Mid(i, 1));
         strTemp += strPatterns[nIndex];
      }

      return strTemp;
   }
};


Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Hurricane Sandy was one of the most destructive natural disasters that the United States has ever experienced. Read this success story to learn how Datto protected its partners and their customers with proactive business continuity planning, heroic employee efforts, and the right mix of technology and support. With storm surges over 12 feet, winds that exceeded 90 mph, and a diameter spanning more than 900 miles, Sandy resulted in power outages to approximately 7.5 million people, and caused an estimated $50 …

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds