logo

Gravl: Insanely simple PDFs for Grails

logo

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:

The result of a PDF export

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!

7 Responses to “Gravl: Insanely simple PDFs for Grails”

  1. Anonymous says:

    Amazing.

  2. doylecentral says:

    Nicely done. It sure does seem simpler than loading up the entire iText library. Sorry.. I’ll miss your 2GEX … I did get to see Graeme’s talks at the SpringEX this past week. Quite good. I also demoed some Grails apps I’ve kicked around to some innocent by standards.

  3. Jim LoVerde says:

    Very nice.

    How much work would it be to bundle up the dependent frameworks and package this as a Grails plugin? In an ideal world, adding this functionality would be as simple as:

    grail install-plugin pdf-generator

  4. Paulo Freitas says:

    Cool plugin! Simple and clean!

    One question: what about using acegi? I generate the PDF OK but the content of PDF is the login page and not the page I’ve requested :S

    How can I workaround this issue?

    Regards

  5. firnnauriel says:

    @Paulo Freitas
    any update on using iText on acegi? i’m having the same issue now.

  6. deadman says:

    what is this : url: entry.toPermalink(”) passing to controller

  7. alex says:

    what is this : url: entry.toPermalink(”) passing to controller

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

logo
logo
Powered by WordPress | Designed by Elegant Themes