And if I told you that 20 lines of code was probably plenty for fine looking PDFs? So you’ve just written a killer blog entry that legions of fan are keen to copy to their PDAs to read on the way home? Just click Gravl’s “PDF” icon and you’re off:

PDF is a visual medium, so why design everything in non-visual XML… why not design your PDF export in html (which you’ve got great tools for), then pass it through a simple library to tranform from html straight to PDF?
I was totally inspired by Josh’s article on using Flying Saucer (which is a html rendering component for Java which can read a bunch of html then render to a Swing component or to PDF).
Integrating into Gravl was a matter of creating a PdfController then passing it a relative URL to render. Because rendering can take a little time, I wrap it in cache to make it faster for the next guy:
import org.xhtmlrenderer.pdf.ITextRenderer;
class PdfController {
CacheService cacheService
def index = {
redirect(action: show)
}
private byte[] buildPdf(url) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(url);
renderer.layout();
renderer.createPDF(baos);
byte[] b = baos.toByteArray();
return b
}
def show = {
def baseUri = request.scheme + "://" + request.serverName + ":" + request.serverPort +
grailsAttributes.getApplicationUri(request)
log.debug "BaseUri is $baseUri"
def url = baseUri + params.url + "?print=true"
log.debug "Fetching url $url"
// grab pdf bytes from cache if possible
byte[] b = cacheService.getFromCache("pdfCache", 60, url)
if (!b) {
b = buildPdf(url)
cacheService.putToCache("pdfCache", 60, url, b)
}
response.setContentType("application/pdf")
response.setHeader("Content-disposition", "attachment; filename=blog.pdf")
response.setContentLength(b.length)
response.getOutputStream().write(b)
}
}
Then you just need to add yourself a link to the PDF creation...
<g:if test="${print!=true}">
<g:link controller="pdf" action="show" params="[url: entry.toPermalink('')]">PDF</g:link>
</g:if>
Now Flying Saucer is a strict xhtml renderer, so if all those <p> tags don’t terminate nicely… you need to know a few tricks. If you’re coming to my “whole nine yards” session at 2GX and I’ll let you in on a few other tricks…
Go forth and PDF with pleasure!