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!