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

  • Live Event Date: May 6, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT While you likely have very good reasons for remaining on WinXP after end of support -- an estimated 20-30% of worldwide devices still are -- the bottom line is your security risk is now significant. In the absence of security patches, attackers will certainly turn their attention to this new opportunity. Join Lumension Vice President Paul Zimski in this one-hour webcast to discuss risk and, more importantly, 5 pragmatic risk mitigation techniques …

  • Ever-increasing workloads and the challenge of containing costs leave companies conflicted by the need for increased processing capacity while limiting physical expansion. Migration to HP's new generation of increased-density rack-and-blade servers can address growing demands for compute capacity while reducing costly sprawl. Sponsored by: HP and Intel® Xeon® processors Intel, the Intel logo, and Xeon Inside are trademarks of Intel Corporation in the U.S. and/or other countries. HP is the sponsor …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds