Flying-Saucer is a nice library to generate PDF documents from within Java applications. Just generate a bunch of XHTML, throw it into the renderer and let it produce the desired document utilizing iText.
When it comes to barcodes however, Flying-Saucer cannot access the built in barcode functionality of iText (at least I didn't find any documentation for it).
However, being OpenSource and well designed, one only needs to create one subclass to achieve the task: Flying-Saucer relies on a factory named ReplacedElementFactory, which can replace elements by custom objects. This is also used to embed images, as the class ITextReplacedElementFactory shows. Now we can simply create a subclass, which replaces images with an appropriate barcode:
<img src="0123456789" type="code128" style="height: 1cm" />
One simply needs to override the createReplacedElement method like this (the whole code can be found here: BarcodeReplacedElementFactory.java (GitHub)):
@Override
public ReplacedElement createReplacedElement(
LayoutContext c,
BlockBox box,
UserAgentCallback uac,
int cssWidth,
int cssHeight)
{
Element e = box.getElement();
if (e == null) {
return null;
}
String nodeName = e.getNodeName();
if (nodeName.equals("img")) {
if ("code128".equals(e.getAttribute("type"))) {
try {
Barcode128 code = new Barcode128();
code.setCode(e.getAttribute("src"));
FSImage fsImage = new ITextFSImage(
Image.getInstance(
code.createAwtImage(
Color.BLACK,
Color.WHITE
),
Color.WHITE
));
if (cssWidth != -1 || cssHeight != -1) {
fsImage.scale(cssWidth, cssHeight);
}
return new ITextImageElement(fsImage);
} catch (Throwable e1) {
return null;
}
}
}
return super.createReplacedElement(
c, box, uac, cssWidth, cssHeight);
}
Granted, "type" is no valid XHTML-Element for <img /> but as you can see in the code above, you could easily replace it with data-type or any other attribute. Flying-Saucer doesn't seem to care about this anyway.
Note: The code above can only handle Code128-Barcodes, but can easily be extended to handle EAN and the like (iText supports a whole bunch of barcodes by default).
In order to make our factory work, we need to pass it to the renderer - which is pretty darn easy:
ITextRenderer renderer = new ITextRenderer();
renderer.getSharedContext().setReplacedElementFactory(
new BarcodeReplacedElementFactory(
renderer.getOutputDevice()
));
renderer.setDocumentFromString(inputAsString);
renderer.layout();
renderer.createPDF(outputAsStream);