Tuesday, May 06, 2014

Using camel FOP to convert text documents to PDF

Camel is awesome. No doubts about it. While working on camel I encountered a requirement where I had to convert text files to PDF. After looking into camel components I found out about "FOP" ( You just need to add dependency and create the required route. Seems, simple at the beginning, looking at the few snippets on the website. But, to convert the text file to PDF you need to generate the XSL-FO that contains both the formatting instructions (xslt) and the real data (xml). While this is good when you know the documents that you need to generate, it is not that obvious how to generate/convert any random document text file to PDF. But fear not, there is a way.
Create a route that reads text documents from a directory, send it through a processor that creates the XSL-FO around the document, set the file name, author etc attributes, forward to the FOP component, then to the file system. The code is shown below and is well documented where necessary.
package com.bitourea.camel.routes;
import com.bitourea.camel.util.AppUtil;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.fop.FopConstants;

public class TextToPdf extends RouteBuilder{
public void configure() throws Exception {
String readDir = "c:/Temp/camel/textToPdf";

//read from directory, filter for text files
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
final String body = exchange.getIn().getBody(String.class);
final String fileNameWithoutExtension = AppUtil.getFileNameWithoutExtension(exchange);
final String convertToXSLFOBody = AppUtil.getFilledXSLFO(body);
exchange.getIn().setHeader(Exchange.FILE_NAME, fileNameWithoutExtension + ".pdf");
exchange.getIn().setHeader(FopConstants.CAMEL_FOP_RENDER + "author", "Shreyas Purohit");
.to("file://" + readDir);

The AppUtil used in the above code is shown below.

package com.bitourea.camel.util;
import org.apache.camel.Exchange;

public class AppUtil {
public static final String EXT_DELIM = ".";
private static final String fopMainTemplate = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"\n" +
"<fo:root xmlns:fo=\"\">\n" +
"\n" +
"<fo:layout-master-set>\n" +
" <fo:simple-page-master master-name=\"A4\">\n" +
" <fo:region-body margin=\"25pt\"/>\n" +
" </fo:simple-page-master>\n" +
"</fo:layout-master-set>\n" +
"\n" +
"<fo:page-sequence master-reference=\"A4\">\n" +
" <fo:flow flow-name=\"xsl-region-body\">\n" +
" </fo:flow>\n" +
"</fo:page-sequence>\n" +
"\n" +
private static final String fopBlockTemplate = " <fo:block font-family=\"Courier\" font-weight=\"normal\" " +
"font-style=\"normal\" score-spaces=\"true\" white-space=\"pre\" linefeed-treatment=\"preserve\" " +
"white-space-collapse=\"false\" white-space-treatment=\"preserve\" font-size=\"10pt\">#CONTENT</fo:block>\n";

public static String getFileNameWithoutExtension(Exchange exchange){
String fileName = (String) exchange.getIn().getHeader(Exchange.FILE_NAME);
return fileName.substring(0, fileName.indexOf(EXT_DELIM));

public static String getFilledXSLFO(String content){
return fopMainTemplate.replaceAll("#BLOCK_CONTENT", getXSLFOBlock(content));

private static String getXSLFOBlock(String line){
return fopBlockTemplate.replaceAll("#CONTENT", line);

If you want, you can externalize the template into xslt, and create required XML for the XSLT to generate the right XSL-FO. I find that overkill when all I want is to append the strings and forward to next route for all incoming documents. Also, note that I am using the monospace font Courier in the template since it maintains the formatting the best and I dont have to supply additional fonts(One of the 13 font family which are directly available with PDF readers- Helvetica, sans-serif, SansSerif, Times, Times Roman, Times-Roman, serif, any, Courier, monospace, Monospaced, Symbol and ZapfDingbats). The FOP component also support the XML configuration file that can be used to define/load the fonts from the file system if necessary that can be used instead. Change 'margin' in 'region-body' if you wish to increase or decrease the PDF document margin. For the text to be embedded as is in the PDF the following attributes are set, white-space=pre, linefeed-treatment=preserve, white-space-collapse=false, white-space-treatment=preserve.

At the moment of this writing the FOP component supported loading the configuration file from classpath, but, did not support custom UriResolvers to be plugged in or provided a classpath resolver. In effect, we could not load the fonts defined in the XML FOP configuration from classpath and they must be present on the filesystem.

This is one of the easiest way to convert text files to PDF documents.

Saturday, March 29, 2014

Releasing chrome extension for UptimeRobot monitor

I have developed an opensource chrome browser extension to enable easy integration with Uptime Robot API's and monitor server/monitor statuses using chrome browser. This extension helps one to see server stats and get notification while the chrome browser is on.


  • Server up and down desktop notifications.
  • Immediate visible server up and down notifications in the extension browser action icon.
  • Group monitors to see them separated in the extension display.
  • Options and data stored using chrome storage sync API allowing it to be sync'ed between chrome's if signed in.
  • Uses jquery.plugin.uptimeRobotMonitor to provide a beautiful visualization of server status and statistics related to 1 day,7 day and all time server uptime percentages.
  • Uses only Monitor API keys (not Account API keys) and hence trustable and secure.
Check for more new features and contribute to source at the project site. Download by clicking on the image below.

 Chrome Webstore

Thursday, March 20, 2014

Releasing my first android app "Unlock Alert"

A project I started to learn android ended up in a full fledged app that can be used by many. I am releasing this app today on Google Play Store. 
Visit website to find out about the app and download it. Please remember to rate/comment it. The app has been in beta testing for some time now and is pretty stable. But since "things" happens, do let me know if it doesn't work or crashes on your device at google groups. I would like to get together and debug/solve the issue.
If you rather just download it, then here it is:

Download From Play