Main

Barcoding Archives

May 2, 2006

Barcodes barcodes barcodes ... which ones to pick?

No this is not going to be an article on how to add a barcode to your output ... I'll save that for later. We are finalizing our licensing for our new unicode fonts, that we make available to you. As part of the deal we also have the opportunity to get three barcode fonts from the vendor, we'll have a MICR font too.


So heres the question for you all ... which three would you as customers like? There are so many available but we'd like to get three that you'll actually find useful. If you have suggestions please post them in a comment.


Thanks, Tim

June 6, 2006

Advanced Barcode Support

With XML Publisher 5.6.2 you now have the ability to execute pre processing on your data prior to applying a barcode font to the data in the output document. For example you may need to calculate checksum values or start and end bits for the data before formatting them.


In lieu of Oracle not having the barcode fonts available I have worked with the popular IDAutomation fonts, they provide the necessary java code support for their fonts.
Information on their fornts can be found here: http://www.idautomation.com/fonts/

Information on their java source code here: http://www.idautomation.com/fonts/tools/sourcecode/FontIDAutomation.txt


The solution requires that you register a barcode encoding class with XML Publisher that can then be instantiated at runtime to carry out the formatting in the template. This is covered in <<link to class description and sample and setup>>
In the template there are two new commands that need to be used to enable the formatting feature.


<?register-barcode-vendor:java_class_name;barcode_vendor_id?>


This command registers the barcode encoding class. It requires a java class name, this will carry out the encoding and a barcode vendor ID as defined by the class. This command needs to appear before the commands to encode the data in the template. For example:
<?register-barcode-vendor:弛racle.apps.xdo.template.rtf.util.barcoder.BarcodeUtil?;'XMLPBarVendor'?>


So the class is defined as 双racle.apps.xdo.template.rtf.util.barcoder.BarcodeUtil? and the vendor id as 'XMLPBarVendor'


For the data to be encoded the following command is required:


<?format-barcode:data;barcode_type;barcode_vendor_id?>


This accepts:
?    data ? the element in the XML to be encoded
?    barcode_type ? this is the method in the encoding class used to format the data e.g. Code128a
?    barcode_vendor_id ? this is the ID as defined in the register-barcode-vendor field.


At runtime the barcode_type method will be called to format the data value and the barcode font will then be applied to the data in the final output. So in the template create the field and highlight it with you chosen bardcode (make sure the font is available at runtime) and you're done .. for the template anyway. Now you'll need to write the class to carry out the encoding ... dont panic we have an example you can copy.


Advanced Barcode Font Formatting Implementation


For the advanced formatting to work in the template a java class is required that will have the appropriate methods to format the data at runtime. Many font vendors offer the code with their fonts to carry out the formatting, these need to be incorporated as methods into a class that is available to the XML Publisher formatting libraries at runtime. There are some specific interfaces that need to be provided in the class for the library to call the correct method for encoding.
This class has to have the following 3 methods implemented:


/**
 * 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);


The class should located in the classpath for the middle tier JVM in which XML Publisher is running.


Note: For EBS users the class needs to be in the classpath for the middle tier and any concurrent tiers that are present.


If in the <?register-barcode-vendor:...?> command, the barcode_vendor_id is not provided. XML Publisher will call the getVendorID() and use the result of the method as ID for the vendor.


An example class that supports the code128 a, b and c encodings follows. You'll need to compile the class and to do that you'll need the XMLP java libraries in the classpath to compile. Once you have that you can simply copy the following code and add your encoding methods.


--------------------------------------------------------------------------------


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;
  }
}



--------------------------------------------------------------------------------


Once the class is created and placed in the correct class path your template creators can start using it to format the data for barcodes. You will need to communicate to them the following:
1.    The class name and path. In this example: oracle.apps.xdo.template.rtf.util.barcoder.BarcodeUtil
2.    The barcode vendor id you created. In this example 噌MLPBarVendor?
3.    The available encoding methods, in this example, code128a, code128b and code128c
They can then use this information to sucessfully encode their data for barcode output.

May 8, 2007

Barcoding 101

I covered what I thought were the tougher 2D barcodes a while back , we documented the simpler codes (and the 2D's for that matter ... same principle) but I think because we decided to break the doc into two sections, one for the template designer and another for the developer I think we confused things a little and caused a disconnect for most folks. So to make ammends until we revamp the docs. Today, I'll cover the simple stuff ...


Simple Barcodes


If you are using something like a code 39 barcode it may not require any pre-processing on the data to be 'coded'. This is the simplest form of barcoding. Firstly you need to build and test on your desktop. Here are the steps:




  1. Install the barcode font in your WINDOWS_HOME/fonts directory
  2. In MSWord highlight the field/data you want to have the barcode applied to
  3. To test the output you need to let the publisher engine know where the font is on the desktop. Under the cleint install directory you'll find a config directory and under there an 'xdo example.cfg' file. Open it up and you'll see a sample entry for a font:

    <font family="GnuMICR" style="normal" weight="normal">
     <truetype path="C:\WINNT\fonts\GnuMICR.ttf" />
    </font>

  4. Just change the entry to point to your font and for the family attribute pput in the name you see in Word when selecting the font.
  5. Rename the file to xdo.cfg
  6. Test your template, you should see the barcode font being used to replace the data in the output 

Now you need to deploy to the server. The template is simple enough, but we need the server publisher engine to know the location of the font. For both EBS and standalone there is an interface to load the font to the server, the steps are now pretty straightforward. In EBS just use the Font Manager, I covered how to load fonts in this article. For standalone:




  1. On the operating system copy the font to the JDK/fonts directory
  2. Login as an administrator and go to Admin tab > Font Settings
  3. Now fill the details out about your font and select the font file from the drop list.
    Font:
  4. The font is now available to all templates in the system.
 Thats it for simple barcodes ... I'll cover how to pre-process data before applying the bar code next.

May 9, 2007

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!


 



 


 


 


September 25, 2007

Bursting Barcodes

those of you pulling hair out over getting your barcodes into your bursting outputs under EBS - stop pulling, you're ot going crazy. You're going to have to go old school to get them showing up - the bursting engine will not check the Font Manager for fonts - its an bug or enhancement depending on how you look at it - we'll get it sorted either. In the mean time you can still get those barcodes or other fonts into hte output.


Two methods are available:


1. RTF Properties - I have covered these previously, you create a custom property in the RTF.

Name: xdo-font.<<font family name>>.normal.normal

Type: Text

Value: truetype.font <<directory+name>>

Its quick, the oly problem being the template might not be very portable unless the font directory is mounted across instances.


2. Config File - thats covered somewhere too. A file, xdo.cfg is put into the JRE_TOP/lib directory. Needs the following to map the font location to a name in the template. 

<config version="1.0.0"  xmlns="http://xmlns.oracle.com/oxp/config/">
   <!-- Font setting -->
    <fonts>
      <font family="3 of 9 Barcode" style="normal" weight="normal">
       <truetype path="C:\WINNT\fonts\3of9.ttf" />
      </font>
    </fonts>
</config>


Need to bounce those servers to take effect. Good Luck!

March 10, 2008

Client Side Barcode Testing

We had a customer issue a few weeks back ... 'really Tim, I never would have guessed' :0) Yeah, we get some issues sometimes. This was a JD Edwards customer that had developed an encoding class for a barcode. Lost? Check out the Advanced Barcode Support article. Basically, some barcodes need the data encoded, calculating stop bits and such like, prior to applying the barcode font. To support this, we allow you to create a java class that implements a Publisher interface to encode the data values. This can then be called from the RTF template layer using commands - check the user guide people!


The encoding class needs to be in the classpath on the server be it JDE, EBS or standalone - this is straightforward enough but how about being able to test on the Template Builder for Word interface?


It can be done, no support directly in the tool but with a little jiggery pokery we can get the class tested. The list of jar files that the builder will load at runtime is limited but the main one we can highjack is the xdocore.jar. You'll find it under the jlib directory under the Template Builder install directory.


BarcodeTest1:


You can open the jar file using something like WinZip, with it you can see the structure of the contents,


BarcodeTest2:


The class you developed will no doubt have a package declaration in it e.g. oracle.apps.xx.xmlp.barcode - this needs to map to the path in the jar file. To get it into the jar in the right place just get the class into the directory path on your local drive.


BarcodeTest3:


Then, in the case of Winzip, open the jar, then drag and drop the class and ensure the 'Use Full Path Info' option is set to get the class into the correct path that will map to your package specification.


BarcodeTest4:


Now, you can test to your hearts content in the comfort and relative speed of MSWord.


 

September 9, 2008

Barcoding Again!

More on barcoding today but I think this will be it ... read on!

I have been exchanging emails with Nisha from the warm and sunny town of St.Petersburg That should have been enough of a clue for you to realize I did not mean the cooler and only slightly less sunny Russian city of the same name. Im talking about the Floridian 'St Pete'. Geography lesson over ... we have been talking barcodes, more specifically advanced barcodes, in fact the Code128 variety.

Those of you that have used these barcodes will know that the data needs to be encoded before applying the barcode font. you will also know that you need to write and mount some java code for us to call so the data can be encoded. I think Nisha will not mind me saying that she was a java novice between her, Kevin in Oracle support and myself, Nisha now has Code128 barcodes coming out on their outputs in E Business Suite.

So what? I hear you cry, 'you've written loads of articles on barcoding.' Well once we had gotten things sorted out, I cheekily asked Nisha to come up with a small doc on the process that was followed to get the barcodes out on to the paper. It seems the solution is somewhat broken up and spread out. Unbelievably, Nisha stepped up and wrote a document for us. Im very grateful.

Get the real Barcoding 101 for EBS here!
Yes, its specific to Nisha's requirement and yours might vary a little but its a good guide on what you need to do to get everything up and in the right place to get advanced barcoding working. Its written by someone that actually needed to get it into production, rather than us writing what we think you need. Maybe we should send developers to customers before they write a jot of code and then send them again afterward so that they know what its like to use their software. Anyhoo, thanks Nisha, you rock!

September 2, 2009

Whats up with my MICR?

Long'ish on and off exchange with 'Super Support Man' aka Kevin M over IM this morning trying to sort out a frustrating check printing issue a customer was having. Their bank had provided a specification sheet showing the required layout for the check including the the 'where' and 'how' the MICR string should look.

A commercial font was being used and it was not rendering correctly, basically the string was too long for the space allocated by the bank. It looked like a spacing or scaling issue on BIP's part. I had been scratching my head over it for a while - too long in fact. I can fully understand the pain and misery of not being able to cut checks - they still have to be one of the most mission critical documents for an organization today.

While chatting (typing) to 'Super Kev', I was idly trying some of the other MICR fonts I have on my machine in MSWord against the customer's font. Naming no names here are the results.

micr.jpg

Not huge differences but enough that over a 25 character MICR string they will be significantly different enough for certain banks to reject test checks because of the length of the string.

Its not a completely fair image, two of the fonts are 'free', one of those is the MICR font included with Publisher. There are a few commercial fonts in there too but just using the vanilla font flavor.

If you want to use Publisher to generate checks and you purchase a commercial font, please check out the user guides. Im not a 'user guide' type of guy - strange considering how much effort I put into the BIP docs with Leslie. Writing documentation yourself gives you a whole new appreciation for the pain and misery folks go through writing it and then no one reads it. But I digress ... again!

Typically, the commercial vendors don't just provide a single font. They provide a range of them and a test document for you to print. From that printed document, you can then match the correct font to your bank's specification requirement.

If you use Publisher's font you should be good for spacing but there is an enhancement request out there to help you to tweak the character spacing if you need it.

Hopefully, in this case, things will now be resolved quickly and the customer can at last start cutting checks. Good luck to you with check printing and don't forget, RTFM :0)

About Barcoding

This page contains an archive of all entries posted to Oracle BI Publisher Blog in the Barcoding category. They are listed from oldest to newest.

Charting is the next category.

Many more can be found on the main index page or by looking through the archives.

Top Tags

Powered by
Movable Type and Oracle