Barcoding 102

Yesterday we dealt with relatively simple barcode requirements, just apply the barcode to the incoming data ... done. Today we take it to the next level, we need to pre-process the data before we apply the barcode font. Typically you need to calculate checksum characters and perform various manipulations to the data so that once its rendered as a barcode your (or your customers/supplier) hardware can read it.


We break this process into two, we need an algorithm component to encode the data, this will be a small java class and the second is the template component, we need to let the renedering engine know that we need to take some XML data and apply process X to it.


Encoding Algorithm Component


Just about all the font vendors I have come across, that supply barcodes that require enconding provide the required encoding algorithms in a java format that once you purchase the font you have the right to use in your programs. We are going to deal with the Code 128 barcode, this has encoding formats a, b or c depending on the font and the hardware requirement. We need to write a java class that will do the encoding, for publisher to be able to use it, it needs to conform to a format with specific methods thus:

/**
* Return a unique ID for this bacode encoder
* @return the id as a string
*/
  public String getVendorID();
/**
* Return true if this encoder support a specific type of barcode
* @param type the type of the barcode
* @return true if supported
*/
  public boolean isSupported(String type)
/**
* Encode a barcode string by given a specific type
* @param data the original data for the barcode
* @param type the type of the barcode
* @return the formatted data
*/
  public String encode(String data, String type);

Not too strenuous even for a java novice and Im going to provide a cut and paste example in just a minute. Once you have the class it needs to sit in the classpath of the java virtual machine you're using. What does this mean practically, well the class needs to be available to the publisher engine, its not enough to drop it into a directory under $APPL_TOP for apps or a lib directory for standalone. When the java machine starts up that hosts the publisher engine, it is given a set of paths/libraries of classes, called a 'CLASSPATH', it can then 'find' these as an when they are needed. Now you do not need to update the classpath for either flavor. For EBS put the compiled class under the java_top area in my example I use a new directoy under oracle/apps/xdo/. For the standalone release just drop the class (wrapped into a jar or zip) into the lib directory under the xmlpserver/xmlpserver/WEB-INF/lib and then bounce the server.


Heres my sample class to encode based on code128, a, b and c:

package oracle.apps.xdo.template.rtf.util.barcoder;

import java.util.Hashtable;
import java.lang.reflect.Method;
import oracle.apps.xdo.template.rtf.util.XDOBarcodeEncoder;
import oracle.apps.xdo.common.log.Logger;
// This class name will be used in the register vendor field in the template.

public class BarcodeUtil  implements XDOBarcodeEncoder

// The class implements the XDOBarcodeEncoder interface

{

  // This is the barcode vendor id that is used in the register vendor field and
  // format-barcode fields
  public static final String BARCODE_VENDOR_ID = "XMLPBarVendor";
  // The hastable is used to store references to the encoding methods

  public static final Hashtable ENCODERS = new Hashtable(10);

  // The BarcodeUtil class needs to be instantiated
  public static final BarcodeUtil mUtility = new BarcodeUtil();

// This is the main code that is executed in the class, it is loading the methods
// for the encoding into the hashtable. In this case we are loading the three code128
// encoding methods we have created.

  static {

    try {

      Class[] clazz = new Class[] { "".getClass() }     

      ENCODERS.put("code128a",mUtility.getClass().getMethod("code128a", clazz));
      ENCODERS.put("code128b",mUtility.getClass().getMethod("code128b", clazz));
      ENCODERS.put("code128c",mUtility.getClass().getMethod("code128c", clazz));

     } catch (Exception e) {

 // This is using the XML Publisher logging class to push errors to the XMLP log file.

      Logger.log(e,5);
    }

  }

 

// The getVendorID method is called from the template layer at runtime to ensure the correct
// encoding method are used

    public final String getVendorID()

    {
        return BARCODE_VENDOR_ID;
    }

// The isSupported method is called to ensure that the encoding method
// called from the template is actually present in this class. If not
// then XMLP will report this in the log.
    public final boolean isSupported(String s)

    {
        if(s != null)
            return ENCODERS.containsKey(s.trim().toLowerCase());
        else
            return false;
    }
 

// The encode method is called to then call the appropriate encoding method,
// in this example the code128a/b/c methods.
   public final String encode(String s, String s1)

    {
        if(s != null && s1 != null)

        {
            try

            {
                Method method = (Method)ENCODERS.get(s1.trim().toLowerCase());
                if(method != null)
                    return (String)method.invoke(this, new Object[] {
                        s
                    });
                else
                    return s;
            }

            catch(Exception exception)

            {
                  Logger.log(exception,5);
            }

            return s;

        } else

        {
            return s;
        }

    }

  /** This is the complete method for Code128a */

  public static final String code128a( String DataToEncode )

  {

    char C128_Start = (char)203;
    char C128_Stop = (char)206;
    String Printable_string = "";
    char CurrentChar;
    int CurrentValue=0;
    int weightedTotal=0;
    int CheckDigitValue=0;
    char C128_CheckDigit='w';
    DataToEncode = DataToEncode.trim();
    weightedTotal = ((int)C128_Start) - 100;

    for( int i = 1; i <= DataToEncode.length(); i++ )
      {

            //get the value of each character
            CurrentChar = DataToEncode.charAt(i-1);
            if( ((int)CurrentChar) < 135 )
              CurrentValue = ((int)CurrentChar) - 32;
            if( ((int)CurrentChar) > 134 )

              CurrentValue = ((int)CurrentChar) - 100;
            CurrentValue = CurrentValue * i;
            weightedTotal = weightedTotal + CurrentValue;

      }

    //divide the WeightedTotal by 103 and get the remainder,
    //this is the CheckDigitValue
    CheckDigitValue = weightedTotal % 103;
    if( (CheckDigitValue < 95) && (CheckDigitValue > 0) )
      C128_CheckDigit = (char)(CheckDigitValue + 32);
    if( CheckDigitValue > 94 )
      C128_CheckDigit = (char)(CheckDigitValue + 100);
    if( CheckDigitValue == 0 ){
      C128_CheckDigit = (char)194;

    }

     Printable_string = C128_Start + DataToEncode + C128_CheckDigit + C128_Stop + " ";
    return Printable_string;

  }

  /** This is the complete method for Code128b ***/
  public static final String code128b( String DataToEncode )

  {

    char C128_Start = (char)204;
    char C128_Stop = (char)206;
    String Printable_string = "";
    char CurrentChar;
    int CurrentValue=0;
    int weightedTotal=0;
    int CheckDigitValue=0;
    char C128_CheckDigit='w';

    DataToEncode = DataToEncode.trim();
    weightedTotal = ((int)C128_Start) - 100;
    for( int i = 1; i <= DataToEncode.length(); i++ )

      {

            //get the value of each character

            CurrentChar = DataToEncode.charAt(i-1);

            if( ((int)CurrentChar) < 135 )

              CurrentValue = ((int)CurrentChar) - 32;

            if( ((int)CurrentChar) > 134 )
              CurrentValue = ((int)CurrentChar) - 100;
              CurrentValue = CurrentValue * i;
              weightedTotal = weightedTotal + CurrentValue;

      }

    //divide the WeightedTotal by 103 and get the remainder,

    //this is the CheckDigitValue
    CheckDigitValue = weightedTotal % 103;
    if( (CheckDigitValue < 95) && (CheckDigitValue > 0) )
      C128_CheckDigit = (char)(CheckDigitValue + 32);
    if( CheckDigitValue > 94 )
      C128_CheckDigit = (char)(CheckDigitValue + 100);
    if( CheckDigitValue == 0 ){
      C128_CheckDigit = (char)194;
    }
 
    Printable_string = C128_Start + DataToEncode + C128_CheckDigit + C128_Stop + " ";
    return Printable_string;

  }

  /** This is the complete method for Code128c **/

 
  public static final String code128c( String s )
  {
    char C128_Start = (char)205;
    char C128_Stop = (char)206;
    String Printable_string = "";
    String DataToPrint = "";
    String OnlyCorrectData = "";
    int i=1;
    int CurrentChar=0;
    int CurrentValue=0;
    int weightedTotal=0;
    int CheckDigitValue=0;
    char C128_CheckDigit='w';
    DataToPrint = "";
    s = s.trim();
    for(i = 1; i <= s.length(); i++ )
      {
            //Add only numbers to OnlyCorrectData string
            CurrentChar = (int)s.charAt(i-1);
            if((CurrentChar < 58) && (CurrentChar > 47))
              {
                OnlyCorrectData = OnlyCorrectData + (char)s.charAt(i-1);
              }
      }
    s = OnlyCorrectData;
    //Check for an even number of digits, add 0 if not even
    if( (s.length() % 2) == 1 )
      {
            s = "0" + s;

      }
    //<<<< Calculate Modulo 103 Check Digit and generate DataToPrint >>>>
    //Set WeightedTotal to the Code 128 value of the start character
    weightedTotal = ((int)C128_Start) - 100;
    int WeightValue = 1;
    for( i = 1; i <= s.length(); i += 2 )
      {
            //Get the value of each number pair (ex: 5 and 6 = 5*10+6 =56)
            //And assign the ASCII values to DataToPrint
            CurrentChar = ((((int)s.charAt(i-1))-48)*10) + (((int)s.charAt(i))-48);
            if((CurrentChar < 95) && (CurrentChar  > 0))
              DataToPrint = DataToPrint + (char)(CurrentChar + 32);
            if( CurrentChar > 94 )
              DataToPrint = DataToPrint + (char)(CurrentChar + 100);
            if( CurrentChar == 0)
              DataToPrint = DataToPrint + (char)194;
            //multiply by the weighting character
            //add the values together to get the weighted total
            weightedTotal = weightedTotal + (CurrentChar * WeightValue);
            WeightValue = WeightValue + 1;
      }

    //divide the WeightedTotal by 103 and get the remainder,
    //this is the CheckDigitValue
    CheckDigitValue = weightedTotal % 103;
    if((CheckDigitValue < 95) && (CheckDigitValue > 0))
      C128_CheckDigit = (char)(CheckDigitValue + 32);
    if( CheckDigitValue > 94 )
      C128_CheckDigit = (char)(CheckDigitValue + 100);
    if( CheckDigitValue == 0 ){
      C128_CheckDigit = (char)194;
    }
    Printable_string = C128_Start + DataToPrint + C128_CheckDigit + C128_Stop + " ";
    Logger.log(Printable_string,5);
    return Printable_string;

  }

}


You can get a copy of the java class here. Looks complex but most of the code you can get from your font vendor for the encoding. The important pieces for publisher are:


1.   public static final String BARCODE_VENDOR_ID = "XMLPBarVendor";


You'll use this name in the RTF template


2.    public final String encode(String s, String s1)


The encode method, this redirects to the appropriate encoding method as defined next


3.   ENCODERS.put("code128a",mUtility.getClass().getMethod("code128a", clazz));
      ENCODERS.put("code128b",mUtility.getClass().getMethod("code128b", clazz));
      ENCODERS.put("code128c",mUtility.getClass().getMethod("code128c", clazz)); 


This defines your encoder methods, in this case code128a, b and c. If you have a new encoding method just add it here e.g.


      ENCODERS.put("myEncoder",mUtility.getClass().getMethod("myEncoder", clazz));


You can take this class and just add the encoding method you have and add it to the list as above, compile and you're off.


We now have the back end piece as it were, we now need the code to add to the template.


Template Component


All we need in the template is a means to let the rendering engine know that we need the data to be encoded before the barcode is applied. Let's assume we have a field called 'INVOICE_CODE' that we want to apply the Code128a encoding to prior to applying the barcode. We have defined our encoding class called 'BarcodeUtil' and its under the following path in EBS, JAVA_TOP/oracle/apps/xdo/template/rtf/util/barcoder or its zipped or jarred using the same path.


In our template we are going to use two formfields, the first will declare the barcode encoder. It takes the following format:

<?register-barcode-vendor:男ava_class_name?;鍛arcode_vendor_id??>

 where 'java_class_name' is the 'path' to our class and the 'barcode_vendor_id' is the name given in the java class. For our case we would have:

<?register-barcode-vendor:弛racle.apps.xdo.template.rtf.util.barcoder.BarcodeUtil?;湛MLPBarVendor??>

Now the engine knows where to look for the class. Next we need the data to be encoded. It takes the format:


<?format-barcode:data;鍛arcode_type?;鍛arcode_vendor_id??>


where 'data' is the element name to be encoded
barcode_type is the encoding method and
barcode_vendor_id is the vendor name we defined in our class again


For our scenario we have:

<?format-barcode:INVOICE_CODE;辰ode128a?;湛MLPBarVendor??>

 remember all of these strings are case sensitive, its the first thing to check if things go wrong.


Thats pretty much it, so far as trouble shooting:
1. Check that the class is in the classpath
2. Check that the package name in the class matches the directory or path you have put the class in to
3. check the case sensitivity



Update

Thanks to Jorgen for being sharp eyed on the class, please check this update on the case sensitivity.


Actually when you look at the class, the call to the method encode() makes the string, that contains the encoding method to call, lowercase by default. So, it's probably more important that when user create new encoding methods in the class, they are added to the ENCODERS hash table in lowercase letters.



You can add some logging into your class for debugging purposes. Im going to cover that in the next article, it has more appeal than just for barcode encoding. If you take a closer look at the code you'll see the Logger method and can probably work out whats going on anyway.


Good Luck!


 



 


 


 


Comments:

Hello Tim! I think a small error snuck into your example. Shouldn't: <?register-barcode-vendor:弛racle.apps.xdo.template.rtf.util.barcoder.XDOBarcodeEncoder?; 湛MLPBarVendor??> actually be: <?register-barcode-vendor:弛racle.apps.xdo.template.rtf.util.barcoder.BarcodeUtil?; 湛MLPBarVendor??> ? The class you created was named BarcodeUtil. Best regards, J�rgen

Posted by J�rgen Andersson on May 10, 2007 at 03:51 AM MDT #

Thanks J�rgen

I have updated ... appreciate your review ... it was a loooong article :o)

Tim

Posted by Tim Dexter on May 10, 2007 at 03:56 AM MDT #

No problem. I think I found another smaller type-o. You write that "remember all of these strings are case sensitive, its the first thing to check if things go wrong." Actually when you look at the class, the call to the method encode() makes the string, that contains the encoding method to call, lowercase by default. So, it's probably more important that when user create new encoding methods in the class, they are added to the ENCODERS hash table in lowercase letters. Otherwise they will get incorrect behaviour. Best regards, J�rgen

Posted by J�rgen Andersson on May 10, 2007 at 09:33 PM MDT #

J�rgen, thanks again ... I have updated the text with the correction. Tim

Posted by Tim Dexter on May 11, 2007 at 05:55 AM MDT #

Hello Tim; How we can test the barcode template on desktop without loading the template into the middletier? Thank you. Shaun S.

Posted by Shaun S on September 17, 2008 at 05:24 AM MDT #

Hello, I am creating the labels that should match the requirements of GM1724. The data syntax structure in this case looks like this: [)>RS06GSQItem_QuantityGSPSupplier_NumberRSEOT (ompliance Indicator, Group Separator, Data Format etc together with data from E1) How should I use the in this case? Best regards, Lyyli

Posted by Lyyli on September 08, 2009 at 12:12 AM MDT #

Correction: How should I use the "format-barcode" in this case?

Posted by Lyyli on September 08, 2009 at 12:14 AM MDT #

Hi Tim, I have done the same think what you have explained in the article. But when I tried to preview it is showing an error message that "Main method is not available". Below are the step what i have done. 1. I compile the the class and add the same in the XDCORE jar file with the path "oracle.apps.xdo.template.rtf.util.barcoder.BarcodeUtil" Please help me out what is the solution?

Posted by Rohit Gupta on July 21, 2010 at 08:22 PM MDT #

I always read the article on weblogs in advance of I leave a comment or short. If I have nada to say about the piece topic, I might not leave any remarks. And I prefer to read someone else’s remarks on that post as well. Because I can find valuable ideas through the good comments. I would have posted “Really good blog! Cool posts!” then I could be spamming your blog. I as well develop a web-site, the very first thing I'd do every single day once i signed up on my blog page is examine reviews to my articles and manually approve or reject these comments. Keywordluv as well as brings you more commenters. It promotes your position in search engines being you’re not merely on many “dofollow blog lists” but additionally on a variety of those “keywordluv lists”. Thanks, Catherine

Posted by Socorro Gosz on July 30, 2010 at 05:03 PM MDT #

It's an impressive blog post. I've one matching web log myself now I may very well keep coming back to see more. thank you for this sort of a interesting time. Babs

Posted by Laverne Funt on August 03, 2010 at 06:55 AM MDT #

For the first time in quite a few years I can finally say that this remedy honestly works. My buddies told me that I would be described as a twit for buying this kind of an item, but now I recognize exactly who the real hard-heads are. Don?t be worried about spending money over the internet. For everybody who is set on needing a larger erection, this is actually the most trusted, most effective and most comfortable approach.

Posted by Stimelex on October 10, 2010 at 04:52 PM MDT #

Hi,

I am trying to implement the same , I have downloaded demo font from IADautomation, i have uploaded the font in XML publisher administration
.whenever i runs the program it completes successfully, but it does not convert into barcode, i have checked the case and path of the class file.
Please let me know if anything else i can check or what else can be done.

Thanks,
Param

Posted by guest on April 19, 2012 at 10:19 AM MDT #

Hi Param
I assume you have take the IDAutomation java class and incorporated it into your own class?
Best way to test is to first not apply the font in the RTF template ie leave it as a readable font. That way you can test if your data is being encoded correctly.
Make sure BIP is in debug mode so you can see any error messages.
Tim

Posted by Tim on April 20, 2012 at 06:17 AM MDT #

I am able to resolve the issue and report is printing fine, Thanks for your help. I have one more thing to ask, Is it mandatory to have check digit, as whenever barcode is created, as it is displaying one additional character at end for each barcode

Thanks,
Param

Posted by param on May 07, 2012 at 04:40 PM MDT #

The check digit is up to the font vendor and their encoding class. You might want to check with them.
Tim

Posted by Tim on May 07, 2012 at 05:02 PM MDT #

Hello All,

The oracle application we are running is for a Retail customer, Kohls.com, packing slip. It requires that the printed packing slip has 128 barcode at the top with the dimensions of 2 inches wide by .5 inch height.

The data (large data like 35 digits) is coming from an Oracle database and we are using XML and a word document template.

We were using true type font but were unable to resize the true type font down to the dimensions needed.

please help us, thanks in advanced.

Regards,
Chandrakanth.K

Posted by Chandrakanth.K on February 25, 2013 at 07:41 AM MST #

I need some help...

I downloaded this CODE 128 font from IDAUtomation:

http://www.idautomation.com/barcode-fonts/code-128/

I successfully installed the font.

I then downloaded this encoder java class from IDAutomation:

http://downloads.idautomation.com/IDAutomation_LinearJavaEncoder.zip

But I am unsure what vendor_id to use when formatting the RTF file as I do not see the vendor id to use in the java file.

Posted by Manny on October 10, 2013 at 12:25 PM MDT #

Hello All,

I have treid the Barcode encoding which works great for encoding for Code 128 except it will not encode the space character. Has anyone come accorss this issue and how di you resolve it.

Thanks

Joe

Posted by guest on November 08, 2013 at 12:00 PM MST #

Hi,

I am trying to implement the same, I have purchased the Code128 font from IADautomation, i have uploaded the font in XML publisher administration.whenever i run the program it completes successfully, but it does not convert the space character into barcode, i have checked the case and path of the class file.
Please let me know if there is anything else I can check or what else can be done.

Thanks,
Joe

Posted by guest on November 08, 2013 at 12:02 PM MST #

Hi Joe
I assume you used the IDAutomation encoding class rather than ours? If so, its a question for them to address. If you havent used their class, you should :0)
Regards

Tim

Posted by guest on November 08, 2013 at 12:07 PM MST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Follow bipublisher on Twitter Find Us on Facebook BI Publisher Youtube ChannelDiscussion Forum

Join our BI Publisher community to get the most and keep updated with the latest news, How-to, Solutions! Share your feedback and let us hear your voice @bipublisher on Twitter, on our official Facebook page, and Youtube!

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today