Perl programmer for hire: download my resume (PDF).
John Bokma MexIT
freelance Perl programmer

Barcodes with Apache FOP

Thursday, February 11, 2010 | 0 comments

The past two days I've been working on a small project which required the generating of a PDF file with a barcode and some other text data, and a watermark image. Today I decided to do a clean install on Ubuntu 9.10 AMD64 running in VirtualBox of all programs I use to convert an XML file, via an intermediate XML file, to a PDF file, . And a good thing I did, since my customer had trouble with making Apache FOP, one of the programs I use in the project, work with the Barcode4J extension. The FOP shell script, located in /usr/bin/, which launches the Java FOP program is different in more recent versions of Ubuntu from the script in Ubuntu 8.10, which I am (still) running. Instead of setting CLASSPATH one now has to set JAVA_CLASSPATH to the full path of a jar file to make Apache FOP see the Barcode4J extension. This is explained in more detail in the "Testing Barcode4J via Apache FOP" section below.

Installing the Sun Java Runtime Environment

Since Apache Formatting Objects Processor (FOP) is a Java application I first installed the Sun Java Runtime Environment on a clean install of Ubuntu 9.10 as follows:

sudo apt-get install sun-java6-jre

Maybe you prefer a different Java Runtime Environment, but I am OK with the version Sun provides. Note that if you want to write your own Java programs you might want to install the Java Development Kit instead, i.e.:

sudo apt-get install sun-java6-jdk

After some time, depending on your network speed, a "window" showing the "Operating System Distributor License for Java version 1.1 (DLJ)" should appear. Press tab to navigate to the Ok button and press enter if you want to agree with the license. In the next "window", press the left arrow key to select the Yes button and press Enter, and the installation of the Java runtime environment should continue.

Now the command java -version should report a message identical or similar to:

java version "1.6.0_15"
Java(TM) SE Runtime Environment (build 1.6.0_15-b03)
Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02, mixed mode)

Installing Apache FOP

The Apache Formatting Objects Processor can be installed via the command line as follows:

sudo apt-get install fop

After a successful installation entering just fop on the command line should give a long list of options, and near the very end:

SEVERE: Exception
org.apache.fop.apps.FOPException: No input file specified

followed by a stack trace.

I also got the following warning at the beginning of the output:

[warning] /usr/bin/fop: Unable to locate servlet-api in /usr/share/java

which can be ignored.

Downloading and installing Barcode4J

A few days ago, when looking online for a way to generate barcodes via Apache FOP I came upon Barcode4J, a flexible generator for barcodes, supporting several formats including: Code 39, Code 128, EAN-128, UPC-A, UPC-E, EAN-13, etc., written in Java. On top of that, barcodes can be rotated as well. Barcode4J can be used as an extension to Apache FOP and is supported via fo:instream-object.

In order to download Barcode4J use a web browser to navigate to the Browse Barcode4J Files page on SourceForge.net, and click the large green button to start the downloading of barcode4j-2.0-bin-zip.

After the download has finished, move the zip file to a directory you want it installed in. This can be a newly created (hidden) directory in your home directory, for example. Or if you want to make the extension available to other users on the same computer you might want to use (a sub directory in) /usr/local/share. Extract the file:

unzip barcode4j-2.0-bin.zip

This will create a new sub directory barcode4j-2.0 in the same directory.

Testing Barcode4J via Apache FOP

In order to make Barcode4J available to Apache FOP as an extension one has to add the correct jar to the class path of Apache FOP. The jar files are located inside the build sub directory of barcode4j-2.0. In Ubuntu 9.10, and most likely on Ubuntu 9.04 as well, you can do this by setting the JAVA_CLASSPATH variable. However, on Ubuntu 8.10 - and most likely older versions as well - you have to use CLASSPATH. Something I (and my customer) bumped into today, since I develop on Ubuntu 8.10 while he tests on Ubuntu 9.04. Anyway, use the following command and change the variable name if you're on an older Ubuntu version:

export JAVA_CLASSPATH='/path/to/barcode4j-fop-ext-complete.jar'

replace /path/to with the absolute path to the location of the jar file, which as explained above is located in the build sub directory of the barcode4j-2.0 directory.

Do not accidentally use the full path of barcode4j-fop-ext-0.20.5-complete.jar since this file is for use with a much older version of Apache FOP and will result in the following error if you run a recent fop:

Exception in thread "main" java.lang.IncompatibleClassChangeError:
Implementing class

Next, change your working directory to the xsl-fo directory which is located in the examples sub directory of barcode4j-2.0 and enter the following command:

fop fop-extension-demo.fo test.pdf

This will create a file named test.pdf in the xls-fo directory. Open this file and verify it has several generated barcodes in it; you can open this PDF file from the command line as follows:

xdg-open test.pdf

On my system I've made g an alias for xdg-open, see A Windows start alternative for Ubuntu

If you don't see any barcodes, you most likely also got a lot of "SEVERE: Intrinsic dimensions of instream-foreign-object could not be determined" messages reported by the fop command. This is most likely caused by the Barcode4J extension not being available in the class path. You either used the wrong variable name (or made a typo in it), or you didn't provide the correct path to the jar file. You can check this using:

ls $JAVA_CLASSPATH

which should report the absolute path, including the name of the jar file. If, however, you get:

ls: cannot access /some/path: No such file or directory

you can be quite sure that JAVA_CLASSPATH contains the wrong value.

If you do get the barcodes in the PDF file you might want to add the export command to your .bashrc.

Note that if you use the Document Viewer program to view your pdf files you don't have to close the program and reopen the pdf file if you make changes to your input XML file or stylesheet. The Document Viewer notices that the pdf file has changed, and reloads it. This is very handy when tweaking XML files.

Installing Saxon-B XSLT

For the aforementioned project I had decided to use Saxon-B as an XSLT processor; it would be used to transform an XML file into another XML file with XSL-FO elements added to it, which could be further processed by Apache FOP. Saxon-B was installed using:

sudo apt-get install libsaxonb-java

After the installation has completed, entering saxonb-xslt on the command line should report version information, for example "Saxon 9.0.0.4J from Saxonica", followed by a link to usage information, followed by a long list of options.

Putting it all together

Below follow two files which can be used to create a simple PDF file with a barcode and, bonus, a "watermark" image; an image that is used as a background on top of which the text and the barcode are rendered. The image itself is not available for download, so you have to add an alternative image named watermark.png yourself to the directory you saved the XML and XSL file to, or remove the four background related attributes from the fo:region-body element.

Top left part of the PDF file generated from given XML files.
Top left part of the PDF file generated from given XML files.

hello-barcode.xml

Save the XML code that follows below to a file named hello-barcode.xml. This file can be converted to an XML file containing formatting objects using Saxon-B and the stylesheet that follows after this code.

<?xml version="1.0"?>
<example>
  <heading>Hello, barcode world!</heading>
  <barcode>123456789012</barcode>
</example>

hello-barcode.xsl

Save the XML stylesheet code that follows below to a file named hello-barcode.xsl. This file directs Saxon-B on how to convert the previous XML code into an XML file containing formatting objects, which Apache FOP can render into PDF or any of the outer output formats supported, like PostScript, PNG, TIFF, SVG, etc.

The stylesheet consists roughly of four parts:

  1. a part defining the region body, including specifying a background image (watermark),
  2. a (very small) part selecting the value of the heading element and putting it in a block element with a font size of 18 point and a margin of 0.5 cm around it,
  3. a part wrapping the result of applying another template to the barcode element in a block, which has the top margin set to 2.4cm and the left margin to 3cm (some tweaking going on here),
  4. and finally, a separate template, which obtains the text contents of the barcode element, assigns it to the message variable, and assigns this value to the message attribute of the barcode element.
<?xml version="1.0"?>
<xsl:stylesheet
	version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <xsl:output method="xml"/>
  <xsl:template match="example">
    <fo:root>
      <fo:layout-master-set>
	<fo:simple-page-master
		master-name="page">
	  <fo:region-body
		  region-name="body"
		  margin-top="0.5in"
		  margin-bottom="1in"
		  margin-left="0.5in"
		  margin-right="0.5in"
		  background-image="watermark.png"
		  background-repeat="no-repeat"
		  background-position-horizontal="left"
		  background-position-vertical="top"/>
	  />
	</fo:simple-page-master>
      </fo:layout-master-set>
      <fo:page-sequence master-reference="page">
	<fo:flow flow-name="body">
	  <fo:block font-size="18pt" margin="0.5cm">
	    <xsl:value-of select="heading"/>
	  </fo:block>
	  <fo:block margin-top="2.4cm" margin-left="3cm">
	    <xsl:apply-templates select="barcode"/>
	  </fo:block>
	</fo:flow>
      </fo:page-sequence>
    </fo:root>
  </xsl:template>

  <xsl:template match="barcode">
    <xsl:variable name="message" select="."/>
    <fo:instream-foreign-object>
      <barcode:barcode
	      xmlns:barcode="http://barcode4j.krysalis.org/ns"
	      message="{$message}">
	<barcode:ean-13>
	  <barcode:height>15mm</barcode:height>
	</barcode:ean-13>
      </barcode:barcode>
    </fo:instream-foreign-object>
  </xsl:template>
</xsl:stylesheet>

 

Generating the PDF file

The PDF of which the top left part is shown above was generated using the following two commands:

saxonb-xslt -o:hello-barcode.fo hello-barcode.xml hello-barcode.xsl
fop hello-barcode.fo hello-barcode.pdf

Note that in the above XSL example the margins of the fo:block element are (ab)used to line out the barcode on top of the "watermark", a photo of a wasp moth, tentative id Cosmosoma ethodaea, I took in August last year.

Recommended Books on XSLT, XPath and XSL-FO

After reading several reviews I purchased the following three books on XSLT, XPath and XSL-FO some time ago. I especially like the book on XSLT by Doug Tidwell a lot, maybe also because I've used it the most so far.

Related

Please post a comment | read 0 comments | RSS feed
Formatting a USB flash drive as ext3 >
< Mexican sonogram