One of the projects I’ve been working on recently requires the ability to receive faxes and place them into a customer’s account. At first we looked at using optical character recognition to scan out the customer’s ID from the cover sheet, but this route was looking expensive and time consuming, and we weren’t sure we would be able to reliably read the customer IDs, since faxes are sometimes hard to read even with the human eye. We are providing a pre-filled out cover sheet, so the customer ID would be printed, not hand written, but I think it would still be difficult to read.

Just around this time I saw Jim Rising‘s post on cf-talk saying that if you need to do this kind of thing, you really should be looking at using a 2D barcode instead of trying to OCR a number. So I did. He mentioned Java4Less, a company that sells reasonably priced (~$100) java libraries for reading and writing barcodes, including data matrix barcodes. A datamatrix barcode is a 2D barcode that can encode more data in less space than a traditional barcode. Its also much more durable, and can be read even when faxed several times. They look like this:

datamatrix barcode example


I’ve finally got this all working, and have packaged it up as a CFC. Here’s how its used:
<cfset BarCoder = CreateObject("component","2DBarCode_j4l").init()>

Reading Barcodes

<cfset results = BarCoder.readFromImage(myImageObject)>
or
<cfset results = BarCoder.readFromFile("myFile.png")>

These methods return an array of barcodes that were found in the image. Each element in the array contains the following structure:

Key Description
x The x coordinate of where the barcode was found on the image.
y The y coordinate of where the barcode was found on the image.
value The value that was read from the barcode.

If no barcodes were found, an empty array is returned.

Creating Barcodes

Basic example:
<cfset BCImage = BarCoder.createBarCode(text='my text to place into barcode')>

BCImage now contains a ColdFusion image object that you can write to file or send it to the browser or whatever you need to do. There are several other options you can pass to createBarCode(), they described in the readme file included in the download.

To make this all work you’ll need to get three jar files from Java4Less and place them into your {coldfusion-install-dir}/lib directory, or place them somewhere else and add the path to them in your java.class.path setting in jvm.config. The three files are rdmvision.jar, rbarcode.jar, and rvision.jar. These come from a combination of two Java4Less products: RDataMatrix and J4L Datamatrix Vision.

Java4Less.com does have evaluation versions of these libraries available for download, so you can play with this before spending any money. My cfc only deals with 2D/Data Matrix barcodes. Java4Less offers libraries for working with regular barcodes, but I have not used them.

I have to say that being on ColdFusion 8 made this project much easier. I think it may have still been possible to do in CF7, but it would have required a lot more psudo-java code to be written in CF.

You can download the cfc from RiaForge:

http://2dbarcode.riaforge.org/

25 Comments

  1. mark Kruger says:

    Ryan,

    Awesome stuff…. nice job on this.

    -mark

  2. Jimmy Winter says:

    Very cool! I was working with some basic barcode stuff this spring so I can see how this could really come in handy.

    Jimmy

  3. Jason Troy says:

    Thanks for publishing and sharing this! I can see some interesting possibilities here. Please let us know if anyone extends this and uses it with CF 7!

  4. Troy Allen says:

    Great job…Thanks for SHARING it with the world!

    Regards,
    Troy

  5. Göran says:

    Would it be easy to make the cfc to work with CF 7 or BlueDragon?

  6. Darken says:

    In case anyone was wondering what the barcode said.

    X-2E7EAB12-28BB-4095-97-86-9299CA865AD7

  7. Marco Antonio says:

    Really awesome. I’ll want to use j4less Barcode1DReader Java class with CF 7. Your example for 2D help me so much. Do you have another example for CF 7?

  8. Ryan Stille says:

    I do not have any examples for CF7 or Blue Dragon. It should be possible, you’ll just have to code around the CF8 image stuff. Some of the java methods require passing in a BufferedImage object, and there may be some other things to watch for. Its not as simple as just swapping in ImageCFC for the reads and writes.

  9. charlie griefer says:

    hey ryan… this rocks! i’ve had a client request a datamatrix implementation, for the same reasons you’re using it (fax cover pages). never worked with it before and i’m sure this will help me to get it done. thanks for making it available.

    quick question if you don’t mind… we’d like the barcode to be as inconspicuous as possible, while at the same time being aware of keeping it legible over multiple instances of being faxed. have you done any tests on your own to determine what a decent size is to meet those requirements?

    thanks again!
    charlie

  10. Ryan Stille says:

    Charlie, to keep the barcode reading reliable, we were forced to use a barcode thats about 1.5 inches square. I would not characterize it as inconspicuous, but I think it looks ok at that size. 🙂

    We had the need to read barcodes after they had been faxed at least twice, if you are only planning on faxing them once you may be able to get by with a smaller barcode.

  11. Brad says:

    Hi
    I have no idea about the tech side of it but I am looking for a solution to do something like this.
    we setting up the paperless office and I want all the staff to fax the documents to a number with a coversheet and then the fax automatically goes to where it should go in the database(we use Zoho CRM) for that client.

    how can I do this ,how much would it cost,..

    Thanks
    Brad

  12. Dmitriy Goltseker says:

    I’m just curious, what is the recognition rate?

  13. Ryan Stille says:

    I believe its around 90%. Usually the ones that failed were ones that had a slight paper jam as the fax was going through. You aren’t going to be able to read that no matter what the size. Or ones that had been faxes many times, so that the squares were really no longer square.

    Don’t forget that some of your faxes will come in upside down. I was originally only scanning the upper right (where we had placed our barcode), but that lead to not reading faxes that were sent upside down. Scanning the entire page is more time consuming by several seconds, so I changed it to first look in the upper right corner, then the lower left corner. You don’t need to worry about rotating the barcode or anything, the decoder will work no matter what orientation the barcode is in.

  14. jurli says:

    hi

    what is the correct url on http://www.java4less.com to download the right trial package?

    http://www.java4less.com/barcodes/barcodes.php?info=download

    what is the name of the two .jar files?

    i have always the same error: Class not found: com.java4less.vision.datamatrix.DatamatrixReader

    thanks

  15. Ryan Stille says:

    jurli, I’m not positive but I think you need the first package listed on the page you linked to, rbard10.zip – in there you will find the rbarcode.jar file thats used for generating the barcodes.

    Then from this page: http://www.java4less.com/vision/vision.php?info=download you’ll need the “Datamatrix Vision for Java” package (RDMVisionJava.zip) for reading the 2d barcodes. In the zip file you’ll find two jar files you need, rvision.jar and rdmvision.jar.

    Update: Actually you need to have *three* jar files installed: rdmvision.jar, rbarcode.jar, and rvision.jar.

  16. Natalie says:

    I downloaded the RBarcode (1D, Post 4 state barcodes, PDF417 and RDataMatrix) trial version. Now I’m ready to buy it, but I wanted to make sure I’m choosing the right one to purchase. I am guessing it’s the RDataMatrix Binary (and J4L Datamatrix Vision (Java) to read). Do you need the versions with source code?

  17. Natalie says:

    How many characters does this encode? Mine does not seem to read the barcode if there are more than 230 characters in the string. Is this the maximum?

  18. Simon Harper says:

    Great stuff, Ryan, thanks. Saved us a fortune!

  19. Mike D says:

    Thanks Ryan, this package is very helpful and nicely written.   I seem to have encountered a probelm though…   When testing with the attached code it does not always produce consistent results.   For example #results# is, only sometimes, missing the "my" from the bc string.  Likewise, #results2# is missing, only sometimes, the "Th" from "The".  Is this something I'm doing wrong, or maybe an issue with the demo versions of the jar files?

    Thanks.

    <cfset objBarCoder = CreateObject("component","#APPLICATION.componentPath#/2DBarCode_j4l").init()>
    <cfset BCImage = objBarCoder.createBarCode(text='my text to place into barcode',margin=50)>
    <cfimage action = "writeToBrowser" source = "#BCImage#" >
    <br />
    <cfset results = objBarCoder.readFromImage(#BCImage#)>
    <cfdump var=#results# /><br />
    <cfset BCImage2 = objBarCoder.createBarCode(text='The quick brown fox jumps over the lazy dog',margin=50)>
    <cfimage action = "writeToBrowser" source = "#BCImage2#" >
    <br />
    <cfset results2 = objBarCoder.readFromImage(#BCImage2#)>
    <cfdump var=#results2# /><br />
    <cfimage action = "writeToBrowser" source = "#BCImage2#" >
    <br />
    <cfset results2 = objBarCoder.readFromImage(#BCImage2#)>
    <cfdump var=#results2# /><br />

  20. Christian says:

    Ryan,

    I'm trying to run this in Railo (v 3.1.1.000) and getting the following error:

    "can't assign value to a Object of this type [com.java4less.rdatamatrix.RDataMatrix] with key DOTPIXELS"

    for this source code line in your CFC:
    <cfset Writer.dotPixels = Arguments.dotPixels>

    The code is just the demo code from above. Any idea what I'm doing wrong?

  21. David says:

    Hi Ryan, firstly thanks for the tutorial… great work!

    Right now I am working on modifying your cf code to write PDF417 barcodes, simple right, just change the java class and the correct param names/values… but no, I have hit a brick-wall and I would really appreciate your help if possible.

    For writing matrix barcodes in your example, you specify the following input parameters, but I can't see how this is working, ie. the names you use are different to those as specified in the J4L documentation (help: http://www.java4less.com/barcodes/barcodes.php?info=guide#dm):

    <cfset var Writer = CreateObject('java',"com.java4less.rdatamatrix.RDataMatrix").init()>
            
            <cfset Writer.code = Arguments.text>
            <cfset Writer.dotPixels = Arguments.dotPixels>
            <cfset Writer.encoding = Writer[Arguments.encoding]>
            <cfset Writer.preferredFormat = Writer[Arguments.preferredFormat]>
            <cfset Writer.margin = Arguments.margin>
            <cfset Writer.setSize(Arguments.width,Arguments.height)>

    ie. you specify 'dotPixels' and 'code' but the J4L documentation specifies the Parameters and properties of the Java Class as
    'DOT_PIXELS' and 'BARCODE'. There are others but I am just showing these examples.
    Perhaps the J4L documentation wrong?

    Also confusing: The J4L documentation (help url as above) specifies the 3 main RBarcode classes:

    com.java4less.rbarcode.BarCode: for 1D symbologies
    com.java4less.rbarcode.BarCode2D: for PDF417 and Macro PDF417 symbologies
    com.java4less.rdatamatrix.DataMatrix: for Datamatrix symbology

    But when you create the java object for a matrix barcode, you use:
    com.java4less.rdatamatrix.RDataMatrix
    ie. *R*DataMatrix, with an 'R'.

    Could it be possible that your matrix barcode example uses different RBarcode java classes? Perhaps an earlier version to those as current on their site now?

    Also…. if you could show a working example, writing a PDF417 barcode, I would be more than happy to make a nice donation via paypal.

    Thanks in advance,
    David

  22. David says:

    PDF417 update…

    OK, I got this working for PDF417 barcodes. From what I can tell, the input params as specified in this J4L doc are incorrect (or I am missing the point there!): http://www.java4less.com/barcodes/barcodes.php?info=guide

    The correct PDF417 input params are listed in the Java doc html page BarCode2D.html, in example 1. This html doc is located in the rbard10.zip file: location: rbard10\javadoc\com\java4less\rbarcode\BarCode2D.html.

    To clarify, to create PDF417 barcodes you need only this jar file:
    http://www.java4less.com/rbard10.zip copy only this file rbarcode.jar to the coldfusion8 lib folder as Ryan describes.

    ——–
    CODE: there are 2 files:

    1) The CFC: save as PDF417BarCode_j4l.cfc

    <cfcomponent displayname="Wrapper for Java4Less 2D BarCode PDF417">
        <cffunction name="init">
            <!— used below, see comment there. —>
            <cfset var sys = ''>
            <!— create a temporary instance of the writer, just to make sure it is available.
            We can't create a persistance instance of this because it is not thread safe. —>
            <cfset var Writer = CreateObject('java',"com.java4less.rdatamatrix.RDataMatrix").init()>
            
            <cfreturn this>
        </cffunction>

        
        
        <cffunction name="createBarCode" access="public" returntype="Any" output="yes" hint="Creates a barcode and returns a ColdFusion image object containing the barcode.">
            <cfargument name="width" default="90">
            <cfargument name="height" default="250">
            <cfargument name="resolution" default="1">
            <cfargument name="PDFECLevel" default="4">
            
            <!— PDF_TEXT, PDF_BYTE, PDF_NUMERIC —>
            <cfargument name="PDFMode" default="PDF_TEXT">
            
            <cfargument name="barType" default="PDF417">
            <cfargument name="code" hint="Text to encode in barcode" required="yes">
            
            <!— dont know max? —>
            <cfargument name="PDFMaxRows" default="30">
            
            <!— 1 – 30 —>
            <cfargument name="PDFColumns" default="10">
            
            <!—  3 – 90 —>
            <cfargument name="PDFRows" default="10">
            
            <cfargument name="rotate" default="90">
            
            
            
            <!— create an image to place this barcode into —>
            <cfset var ReturnImage = ImageNew('',90,250,'grayscale')>
            
            <!— the writer is not thread safe, so we must create an instance of it for each request —>
            <cfset var Writer = CreateObject('java',"com.java4less.rbarcode.BarCode2D").init()>
            
            <cfset Writer.setSize(Arguments.width,Arguments.height)>
            <cfset Writer.resolution = Arguments.resolution>
            <cfset Writer.PDFECLevel = Arguments.PDFECLevel>
            <cfset Writer.PDFMode = Writer[Arguments.PDFMode]>
            <cfset Writer.barType = Writer[Arguments.barType]>
            <cfset Writer.code = Arguments.code>
            <cfset Writer.PDFMaxRows = Arguments.PDFMaxRows>
            <cfset Writer.PDFColumns = Arguments.PDFColumns>
            <cfset Writer.PDFRows = Arguments.PDFRows>
            <cfset Writer.rotate = Arguments.rotate>
            
            
            
            
            <!— now 'paint' the barcode into our ColdFusion image object —>
            <cfset Writer.paint(ImageGetBufferedImage(ReturnImage).createGraphics())>
            
            <cfreturn ReturnImage>
            
        </cffunction>

    </cfcomponent>

    2) The cfm file: save as test.cfm (in the same folder as the CFC above).

    <cfset BarCoder = CreateObject("component","PDF417BarCode_j4l").init()>

    <cfset ImageObjContainingBarcode = BarCoder.createBarCode(code='My text to encode')>

    <p>
    <cfimage action="writetobrowser" source="#ImageObjContainingBarcode#">
    </p>
    ——————-

    Nb. i have rotated the barcode, but you can change the rotate angle as you wish, see above in the CFC <cfargument name="rotate" default="90">

    also note, in setting the input params there are 2 methods used above:
    1. <cfset Writer.barType = Writer[Arguments.barType]>
    2. <cfset Writer.code = Arguments.code>

    1 used when selecting an option from an array of possible types.
    2 used when setting a simple value like the code (text to encode).

    good luck… and thanks again Ryan.

  23. cflove says:

    A similar, but free, solution can be found here:  http://www.cflove.org/examples/barcode/datamatrix.cfm

    The source code can be found here: http://www.cflove.org/examples/barcode/bc.rar

  24. Andy says:

    Hi Cfloe

    code did not work with french.

    Illegal character: `  

      
    The error occurred in D:\iis\cflove.org\examples\barcode\datamatrix.cfc: line 57

    55 :     </cfswitch>
    56 :    
    57 :     <cfset Reader.generateBarcode(canvas, JavaCast('string',value))>
    58 :     <cfset canvas.finish()>
    59 :     <cfset out.close()>

  25. Dave says:

    I get crazy results back. It seems I get results that I did the last time I ran the script? Also, now I am getting 08:07:02.002 – java.lang.ArrayIndexOutOfBoundsException – in : line -1