iText on the JVM

Java Virtual Machine Languages

Since a few years Java got some company on the JVM. Languages like Jython, JRuby and Groovy have emerged and claimed their place in the spotlight on the JVM. The exciting advantage of this is that it enables other paradigms to use your Java jars. It’s almost no hassle to use a jar in e.g. Groovy. This allows a lot of interesting possibilities. One of the biggest disadvantages is that some ports of non-JVM languages turn out to be a bit slower than the original: I’ve found Jython to be a lot slower than Python. Nevertheless, I’m still in love with Python and a Java implementation is still awesome.

If you want to learn more about the Java Virtual Machine and its languages, feel free to continue reading on the respective wikipedia pages here and here.

Eclipse and JVM Languages

Integrating these languages into Eclipse, my IDE of choice, is a smoother process than expected. I chose to integrate the following languages:

Most languages had a plugin in the Eclipse Marketplace (Jython, Groovy, Clojure) and the other had very informative websites on how to install the plugins (JRuby, Scala). I also chose not to download modified Eclipse versions. Most installs went very smooth. A few clicks, a restart and I was off to coding in a new language.

There was only one conflict. It seems that JRuby and Jython can not coexist in the same Eclipse. I had to install JRuby in a seperate Eclipse instance for it to work. In the end I was able to install 4 plugins in the same Eclipse instance, which is amazing.

There’s one odd thing about those 4 plugins though: the Scala plugin somehow seems to reference a JRuby file or two and causes an error every time I start Eclipse. Eclipse doesn’t crash or anything, but it’s annoying. That same Scala plugin somehow turned the background color of the editor to black for everything Jython.

There’s also a highly noticeable slowdown of Eclipse. I haven’t pinpointed the exact reason, but it happened after installing either Scala or Clojure.

So, in conclusion about multiple language support in Eclipse: try to keep the amount of language plugins as low as possible per instance to keep performance up and to avoid any conflicts between the plugins.

Interoperability

The main purpose of this article is to find the answer to the question: “How easy is it to use a jar in a non-Java project?” As it turns out: it’s very easy. For most languages it was only a matter of including the jar to the build path.

iText 5.1.3

iText is most likely the best pdf library in the Java ecosystem and it’s being used by a lot of companies to create pdf’s on the fly. I used iText to make it clear that, unless you have some odd reason not to use Java jars in your project, iText is not only the best choice to create pdf’s in Java, but for the JVM as a whole.

Java

In this post I’ll repeat the same Hello World example taken from the book “iText in Action” in each language to show how easy it is to use iText. This is the original source in Java:

/*
 * This class is part of the book "iText in Action - 2nd Edition"
 * written by Bruno Lowagie (ISBN: 9781935182610)
* For more info, go to: http://itextpdf.com/examples/
 * This example only works with the AGPL version of iText.
 */

package part1.chapter01;

import java.io.FileOutputStream;
import java.io.IOException;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfWriter;

/**
 * First iText example: Hello World.
 */
public class HelloWorld {

 /** Path to the resulting PDF file. */
 public static final String RESULT
 = "results/part1/chapter01/hello.pdf";

 /**
 * Creates a PDF file: hello.pdf
 * @param args no arguments needed
 */
 public static void main(String[] args)
 throws DocumentException, IOException {
 new HelloWorld().createPdf(RESULT);
 }

 /**
 * Creates a PDF document.
 * @param filename the path to the new PDF document
 * @throws DocumentException
 * @throws IOException
 */
 public void createPdf(String filename)
 throws DocumentException, IOException {
 // step 1
 Document document = new Document();
 // step 2
 PdfWriter.getInstance(document, new FileOutputStream(filename));
 // step 3
 document.open();
 // step 4
 document.add(new Paragraph("Hello World!"));
 // step 5
 document.close();
 }
}

Now, onto porting it to other languages!

Jython

Jython is the Java implementation of Python (really clever name guys) and it was the first plugin I installed. That was an easy process thanks to the Eclipse Marketplace. Importing iText is also very easy:

Project Properties > PyDev PYTHONPATH > External Libraries > Add zip/jar/egg  and navigate to your jar.

Hello World example:

'''
Created on 28-dec.-2011
Creating a simple Hello World pdf in Jython using iText 5.1.3
@author: iText
'''
from com.itextpdf.text import Document
from com.itextpdf.text import Paragraph
from com.itextpdf.text.pdf import PdfWriter
from java.io import FileOutputStream

# step 1
document = Document()
print "Document Created"

# step 2
PdfWriter.getInstance(document, FileOutputStream("document.pdf"))
print "PdfWriter Created"

# step 3
document.open()
print "Document Opened"

# step 4
document.add(Paragraph("Hello Jython!"))
print "Content Added"

# step 5
document.close()
print "Document Closed"

That went relatively easy, didn’t it?

Next up,

Groovy

I followed some Groovy talks during Devoxx 2011 and I was impressed by the language, but I haven’t been able to test it out, up until now. As with Jython, it was pretty easy to install and to import iText into a project. It’s actually the same process as you would import a jar into a Java project. Assuming that you’re able to do that without instructions, so here’s the Hello World code for Groovy:

/*
 * Created on 28-dec.-2011
 * Creating a simple Hello World pdf in Groovy using iText 5.1.3
 * @author: iText
 */
import com.itextpdf.text.Document
import com.itextpdf.text.Paragraph
import com.itextpdf.text.pdf.PdfWriter

// step 1
def document = new Document()
println("Document Created")

// step 2
PdfWriter.getInstance(document, new FileOutputStream("document.pdf"))
println("PdfWriter Created")

// step 3
document.open()
println("Document Opened")

// step 4
document.add(new Paragraph("Hello Groovy!"))
println("Content Added")

// step 5
document.close()
println("Document Closed")

As you can see, the code is straightforward and simple and it looks pretty much like Jython. I prefer Groovy over Jython because it’s much faster and actually feels like a JVM language and not like a port of another language. Still a lot of love for Python though.

Scala

I hadn’t heard of Scala until Devoxx 2011. The first talk I followed that year was a crash course into Scala. I wasn’t very impressed by the talk, but my colleague seemed to have some fun programming Scala, so I gave it a try. Installing the plugin wasn’t as easy as going to the Marketplace, instead I had to search the Scala website and find which link to use in the “Install new Software” function.

Importing iText is the same as in a Java project. This is the Scala Hello World example:

package com.itextpdf.scala

import com.itextpdf.text.Document
import com.itextpdf.text.pdf.PdfWriter
import java.io.FileOutputStream
import com.itextpdf.text.Paragraph

object ITextTest {
 def main(args: Array[String]): Unit = {
 val document = new Document()
 PdfWriter.getInstance(document, new FileOutputStream("document.pdf"))
 document.open()
 document.add(new Paragraph("Hello Scala!"))
 document.close()
 }
}

It works like a charm, but I’m still not convinced about Scala. It will probably never be one of my favorite languages.

(Clojure)

Seriously, what is up with those parenthesis? Clojure is most definitely the strangest of the new JVM languages, but according to an old proverb “unknown is unloved”.

Installing Clojure happens through the marketplace and importing the jar is as easy as it is in Java. So, without further ado, let’s take a look at the code:

(ns helloworld)

(defn makePdf []
 (def document (new com.itextpdf.text.Document))
 (println "Document Created")
 (com.itextpdf.text.pdf.PdfWriter/getInstance document, (new java.io.FileOutputStream "document.pdf"))
 (println "PdfWriter Created")
 (.open document)
 (println "Document Opened")
 (.add document (new com.itextpdf.text.Paragraph "Hello Clojure!"))
 (println "Content Added")
 (.close document)
 (println "Document Closed")
)

I told you it was strange. It took me several tries to get a working example and this is it. Pretty sure it’s not the best looking Clojure code around, but it somehow works.

JRuby

Having some minor experience with Ruby, I thought I’d have the most fun using JRuby. So, I set out to use a plugin for JRuby in Eclipse and found one. I followed a few simple Hello World example and they worked, but as soon as I tried to talk to the Java classes and jars, the magic disappeared. Looking a bit further I downloaded Aptana Studio 3. But, by default, Aptana Studio takes the ruby.exe declared on your path. I tried to change this to use jruby instead of ruby, but to no avail.

And that’s when I built this make-shift IDE:

MacGyver would be proud.

And it works!

Before we go over the Hello World example, it should be noted that the lack of IDE integration is probably my own fault. If anyone knows how to integrate JRuby, let me know. I’ll be more than happy to update this post with your corrections/suggestions.

Integrating Java into JRuby isn’t that hard. All it needs is the following line:

require 'java'

That line of code allows you to use all the standard Java classes. But we need more. We need to use iText in JRuby. The following will allow us to use iText classes in JRuby.

require 'itextpdf-5.1.3.jar'

Do note that I put the jar in the same folder as the Ruby file.

With all of that out of the way, let’s move on to the Hello World example:

 require 'java'
require 'itextpdf-5.1.3.jar'

document = com.itextpdf.text.Document.new
pdfWriter = com.itextpdf.text.pdf.PdfWriter.getInstance(document, java.io.FileOutputStream.new("document.pdf"))
document.open
document.add(com.itextpdf.text.Paragraph.new("Hello JRuby"))
document.close

And that code snippet produces the last of our Hello World PDFs.

And a more elaborate example

Hello World examples are nice and everything, but never very practical. For this section I took one language and I ported one random example from the iText in Action book. I picked Groovy as I love that language. The example is the NUp example. NUp scales the pages of a pdf to fit mulitple pages onto one page.

Ported to Groovy that gives us the following:

package com.itextpdf.groovy

import com.itextpdf.text.Document
import com.itextpdf.text.Rectangle
import com.itextpdf.text.pdf.PdfReader
import com.itextpdf.text.pdf.PdfWriter

class NUp {
 static main(args) {
 def nup = new NUp()
 nup.manipulatePdf("test.pdf", "result.pdf", 4)
 }

 def manipulatePdf(src, dest, pow) {
 def reader = new PdfReader(src)
 def pageSize = reader.getPageSize(1)

 def newSize = (pow % 2) == 0 ?
 new Rectangle(pageSize.getWidth(), pageSize.getHeight()) :
 new Rectangle(pageSize.getHeight(), pageSize.getWidth())

 def unitSize = new Rectangle(pageSize.getWidth(), pageSize.getHeight())
 for ( i in 0..pow) {
 unitSize = new Rectangle((float)unitSize.getHeight() / 2, unitSize.getWidth())
 }

 def n = 2**pow
 def r = 2**(pow/2)
 def c = n / r

 def document = new Document(newSize, 0, 0, 0, 0)
 def pdfWriter = PdfWriter.getInstance(document, new FileOutputStream(dest))

 document.open()

 def contentBytes = pdfWriter.getDirectContent()
 def total = reader.getNumberOfPages()
 def currentSize
 def factor
 def offsetX
 def offsetY
 def page
 def i = 0

 while ( i < total) {
 if ( i % n == 0 ) {
 document.newPage()
 }
 currentSize = reader.getPageSize(++i)

 factor = Math.min(
 unitSize.getWidth() / currentSize.getWidth(),
 unitSize.getHeight() / currentSize.getHeight())

 offsetX = unitSize.getWidth() * ((i % (int) n ) % (int) c) + (unitSize.getWidth() - (currentSize.getWidth() * factor)) / 2f
 offsetY = newSize.getHeight() - (unitSize.getHeight() * (((i % (int) n) / (int) c) + 1)) + (unitSize.getHeight() - (currentSize.getHeight() * factor)) / 2f

 page = pdfWriter.getImportedPage(reader, i)
 contentBytes.addTemplate(page, (float) factor, 0f, 0f, (float) factor, (float) offsetX, (float) offsetY)
 }
 document.close()
 }
}

There is one error left in this last code snippet: the pages are shifted along the Y-axis when they are added to the new pdf and I cannot find where the error is. If you find it, let me know. But the example works and should prove that iText is a viable choice for any JVM language around.

Conclusion

iText is one of the best choices for any JVM language, if not the best. Not many hoops need to be jumped to get a working example although I wouldn’t advise you to use Clojure to write PDFs.

Also, Groovy rocks.

Leave a Reply