Using Apache FOP from .NET level
- by Lukasz Kurylo
In one of my previous posts I was talking about FO.NET which I was using to generate a pdf documents from XSL-FO. FO.NET is one of the .NET ports of Apache FOP. Unfortunatelly it is no longer maintained. I known it when I decidec to use it, because there is a lack of available (free) choices for .NET to render a pdf form XSL-FO. I hoped in this implementation I will find all I need to create a pdf file with my really simple requirements. FO.NET is a port from some old version of Apache FOP and I found really quickly that there is a lack of some features that I needed, like dotted borders, double borders or support for margins. So I started to looking for some alternatives. I didn’t try the NFOP, another port of Apache FOP, because I found something I think much more better, the IKVM.NET project. IKVM.NET it is not a pdf renderer. So what it is? From the project site: IKVM.NET is an implementation of Java for Mono and the Microsoft .NET Framework. It includes the following components: a Java Virtual Machine implemented in .NET a .NET implementation of the Java class libraries tools that enable Java and .NET interoperability In the simplest form IKVM.NET allows to use a Java code library in the C# code and vice versa. I tried to use an Apache FOP, the best I think open source pdf –> XSL-FO renderer written in Java from my project written in C# using an IKVM.NET and it work like a charm. In the rest of the post I want to show, how to prepare a .NET *.dll class library from Apache FOP *.jar’s with IKVM.NET and generate a simple Hello world pdf document. To start playing with IKVM.NET and Apache FOP we need to download their packages: IKVM.NET Apache FOP and then unpack them. From the FOP directory copy all the *.jar’s files from lib and build catalogs to some location, e.g. d:\fop. Second step is to build the *.dll library from these files. On the console execute the following comand: ikvmc –target:library –out:d:\fop\fop.dll –recurse:d:\fop The ikvmc is located in the bin subdirectory where you unpacked the IKVM.NET. You must execute this command from this catalog, add this path to the global variable PATH or specify the full path to the bin subdirectory. In no error occurred during this process, the fop.dll library should be created. Right now we can create a simple project to test if we can create a pdf file. So let’s create a simple console project application and add reference to the fop.dll and the IKVM dll’s: IKVM.OpenJDK.Core and IKVM.OpenJDK.XML.API. Full code to generate a pdf file from XSL-FO template: static void Main(string[] args) { //initialize the Apache FOP FopFactory fopFactory = FopFactory.newInstance(); //in this stream we will get the generated pdf file OutputStream o = new DotNetOutputMemoryStream(); try { Fop fop = fopFactory.newFop("application/pdf", o); TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); //read the template from disc Source src = new StreamSource(new File("HelloWorld.fo")); Result res = new SAXResult(fop.getDefaultHandler()); transformer.transform(src, res); } finally { o.close(); } using (System.IO.FileStream fs = System.IO.File.Create("HelloWorld.pdf")) { //write from the .NET MemoryStream stream to disc the generated pdf file var data = ((DotNetOutputMemoryStream)o).Stream.GetBuffer(); fs.Write(data, 0, data.Length); } Process.Start("HelloWorld.pdf"); System.Console.ReadLine(); } Apache FOP be default using a Java’s Xalan to work with XML files. I didn’t find a way to replace this piece of code with equivalent from .NET standard library. If any error or warning will occure during generating the pdf file, on the console will ge shown, that’s why I inserted the last line in the sample above. The DotNetOutputMemoryStream this is my wrapper for the Java OutputStream. I have created it to have the possibility to exchange data between the .NET <-> Java objects. It’s implementation: class DotNetOutputMemoryStream : OutputStream { private System.IO.MemoryStream ms = new System.IO.MemoryStream(); public System.IO.MemoryStream Stream { get { return ms; } } public override void write(int i) { ms.WriteByte((byte)i); } public override void write(byte[] b, int off, int len) { ms.Write(b, off, len); } public override void write(byte[] b) { ms.Write(b, 0, b.Length); } public override void close() { ms.Close(); } public override void flush() { ms.Flush(); } } The last thing we need, this is the HelloWorld.fo template. <?xml version="1.0" encoding="utf-8"?> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <fo:layout-master-set> <fo:simple-page-master master-name="simple" page-height="29.7cm" page-width="21cm" margin-top="1.8cm" margin-bottom="0.8cm" margin-left="1.6cm" margin-right="1.2cm"> <fo:region-body margin-top="3cm"/> <fo:region-before extent="3cm"/> <fo:region-after extent="1.5cm"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="simple"> <fo:flow flow-name="xsl-region-body"> <fo:block font-size="18pt" color="black" text-align="center"> Hello, World! </fo:block> </fo:flow> </fo:page-sequence> </fo:root> I’m not going to explain how how this template is created, because this will be covered in the near future posts. Generated pdf file should look that: