Grails: How do I render GSP files in PDF format?

Playing with Grails Rendering Plugin
Hello there!~ In this tutorial, we will be playing with the Grails rendering plugin. This plugin obviously allows you to render pdf versions of your gsp files. I actually found it very easy to use, and I think you will too! But, before we get started, I will tell you the versions I used, just to be consistent.
Other Information
Installing the rendering plugin
Installation is very easy. Open your BuildConfig.groovy and add 'runtime ":rendering:0.4.3"' under your plugins.
plugins {
runtime ":hibernate:$grailsVersion"
runtime ":jquery:1.7.1"
runtime ":resources:1.1.5"
compile  ":rendering:0.4.3"
build ":tomcat:$grailsVersion"
}
After adding that one line, simply run the script below:
grails refresh-dependencies
I actually prefer this method of installation because I can keep an easy track of the versions I'm using. Also, it's good to check if the plugin was installed successfully. To get the list of plugins installed, simply run the script below. It will give you an informative list of plugins you have installed and their version numbers.
grails list-plugins -installed
It will give a result similar to this. Make sure that you see the rendering plugin! If you don't see the rendering plugin in the list, then there must something wrong with the installation.
Domain Setup
Okay, I created a domain called profile. It's just a sample domain which features a date, a string, a boolean and a byte[] for photos. This will maximize our knowledge on how to use the rendering plugin with different data types.
class Profile {
String firstName
String lastName
Date birthdate
boolean isAvailable

byte[] photo

static mapping = {
photo(sqlType: 'longblob')
}

static constraints = {
firstName (blank:false, maxSize:50, matches: '[A-Za-z]+')
lastName (blank:false, maxSize:50,matches: '[A-Za-z]+')
}

String getFullName(){
"${firstName} ${lastName}"
}
}
The Controller
In our controller, define the pdfRenderingService and then create a method(renderFormPDF()) that will render the form for us. Take note that you will need a _pdf.gsp for the template!
def pdfRenderingService
def renderFormPDF(){
def formInstance = Profile.get(params.id)
def args = [template:"pdf", model:[form:formInstance]]
pdfRenderingService.render(args+[controller:this],response)
}
The Views
Next, we create a template and we call it _pdf.gsp. It can actually be any name as long as it is a template, and it should match the template on our renderFormPDF() method in our controller. The underscore sign "_" is necessary for it to qualify as a template. Below are some of the snippets for getting the values out of the passed model. :)

Rendering Images from the Domain Using Rendering Plugin
<rendering:inlineJpeg bytes="${form?.getPhoto()}" height="200px"/>

Rendering Dates from the Domain Using Rendering Plugin
<g:formatDate date="${form?.getBirthdate()}" format="dd MMM yyyy"/>

Rendering Boolean values Using Rendering Plugin
<g:formatBoolean boolean="${form?.isAvailable}" true="Yes! I'm single!" false="No! I'm in a relationship."/>
It's really just straight forward! You can use existing grails tags here which makes it more convenient. Now, one question you may ask is: How do I adjust the width and height of the pdf rendered? The answer lies below. For instance you want it to be the size of small bond paper, you can simply add the snippet below in your css.
@page {
size: 8in 11.5in; /* width height */
margin: 0.25in;
}
How do I render images with fixed/dynamic paths using rendering plugin?
If you have an image which does not live inside a database (meaning it is contained somewhere inside your project folder), you can simply get the file path and return it as bytes in your controller. Let me demonstrate.
In the controller:
def logo = new File(ApplicationHolder.application.parentContext.servletContext.getRealPath("/images/myPicture.jpg"))
def args = [template:"pdf", model:[logo: logo.bytes]]
pdfRenderingService.render(args+[controller:this],response)

In the view:
<rendering:inlineJpeg bytes="${logo}" height="200px"/>

Getting this error upon deployment?
ERROR errors.GrailsExceptionResolver  - ClassNotFoundException occurred when processing request: [GET] /profile/renderFormPDF/1 
org.springframework.mock.web.MockHttpServletRequest. Stacktrace follows:
java.lang.ClassNotFoundException: org.springframework.mock.web.MockHttpServletRequest
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
    at java.lang.Class.getDeclaredMethods(Class.java:1791)
    at org.codehaus.groovy.util.LazyReference.getLocked(LazyReference.java:46)
    at org.codehaus.groovy.util.LazyReference.get(LazyReference.java:33)
    at grails.plugin.rendering.document.RenderEnvironment.init(RenderEnvironment.groovy:33)
    at grails.plugin.rendering.document.RenderEnvironment.with(RenderEnvironment.groovy:69)
    at grails.plugin.rendering.document.RenderEnvironment.with(RenderEnvironment.groovy:61)
    at grails.plugin.rendering.document.XhtmlDocumentService.generateXhtml(XhtmlDocumentService.groovy:68)
    at grails.plugin.rendering.document.XhtmlDocumentService.createDocument(XhtmlDocumentService.groovy:38)
    at grails.plugin.rendering.RenderingService.render(RenderingService.groovy:34)
    at grails.plugin.rendering.RenderingService.render(RenderingService.groovy:33)
    at grails.plugin.rendering.RenderingService.render(RenderingService.groovy:63)
    at com.icodeya.ProfileController.renderFormPDF(ProfileController.groovy:108)
    at net.stax.appserver.webapp.RequestMonitorValve.invoke(RequestMonitorValve.java:35)
    at net.stax.appserver.admin.StaxApplicationQueryValve.invoke(StaxApplicationQueryValve.java:49)
    at net.stax.appserver.webapp.RequestSetupValve.invoke(RequestSetupValve.java:31)
    at java.lang.Thread.run(Thread.java:662)
Don't worry! Simply add this line into your BuildConfig.groovy under the dependencies section.
runtime 'org.springframework:spring-test:3.1.0.RELEASE'
That's about it! Don't forget to like us on facebook! If you wanna see and run the code for yourself, checkout my source code on bitbucket. If you have more questions, please feel free to comment below! :)

Written by

I am a software developer with 2 years of experience. I like cats. I like going to hackathons for free food and temporary lodging. I want to be a polyglot. Currently, I can speak languages like English, Korean, Java (it counts right?), Groovy, Ruby and C#. I aim to learn German as well.

16 (mga) puna:

  1. Thank you so much on thr hint about returning it in BYTES. It works now.

    ReplyDelete
  2. How would I add page numbers to the pdf document?

    ReplyDelete
  3. @Brandon Wagner

    Good question! The render plugin is actually based on "the flying saucer API", so the answer is pretty much there. All you have to do is add a header and/or footer divs like this:

    "< div class='myPageNumber'> MyPageNumber </div>"

    Then add this in your css:
    "@page { @top-center { content: element(myPageNumber) }}"

    For more info take a look at this:
    Generating header/footer with flying saucer (xHTMLRenderer) and iText

    Generating PDFs for Fun and Profit with Flying Saucer and iText


    Let me know how it goes! Good luck! :)

    ReplyDelete
  4. Hi
    I want to add my own font in rendering plugin using css.when I open it on browser its working fine but when I download it,it does not show my font. what should I do for this.

    ReplyDelete
  5. I have a header and a footer in my gsp. And in the mid section I am using the tag g:each. The number of objects being iterated on is dynamic. How do I get the content to flow into multiple pages with the layout intact.

    ReplyDelete
  6. @Vivek Yadav

    I could be wrong..
    But maybe the font isn't installed in your machine?

    ReplyDelete
  7. you are the one who helped me in this context.

    after wasting so many hours, i got to know the pdf generation using rendering plugin in your site.

    phew... :)

    Thanks a lot...

    keep rocking

    ReplyDelete
  8. understood clearly with help of your source code..

    found here

    https://bitbucket.org/icodeya/pdfsample/overview

    ReplyDelete
  9. Thank you so much, I finally managed to pass images to pdf.

    ReplyDelete
  10. you saved my life, with inline images part of code.

    Thanks

    ReplyDelete
  11. Just a noobie question:

    ...
    repositories {
    grailsPlugins()
    grailsHome()
    grailsCentral()
    }
    ...

    is it ok to add plugins even if grailsPlugins() exists inside repositories?

    plugins {
    compile ":rendering:0.4.3"
    }

    Thanks




    ReplyDelete
    Replies
    1. Hi Anonymous!

      Yes, it's ok. You can take a look at my whole BuildConfig.groovy here:
      https://bitbucket.org/icodeya/pdfsample/src/89e7a62cd8ff60886b4e0e906fad72ee77b9389a/grails-app/conf/BuildConfig.groovy?at=master

      In case you didn't know, these lines:

      repositories {
      grailsPlugins()
      grailsHome()
      grailsCentral()
      }

      are simply a list of repositories where various dependencies are stored online. If for instance, "plugin X" is found inside the "grailsPlugins()" repository, then obviously, if you removed it, grails won't be able to download the "plugin X" because it doesn't know where to find it. :D

      If you want to learn more about plugins, please do read more here:

      http://grails.org/doc/1.1.1/guide/12.%20Plug-ins.html

      Hope that helped. :)

      Delete
  12. Hello,

    I'm facing encoding problems - if a use latin characters everything is printed in the pdf file. If there are any specific characters - they are not displayed. I've made a thorough search on the web but still no subtle solution. Any ideas?
    I'm using STS on Windows 7 and LInux, grails version 2.4, I tried with css, environment settings, fonts

    ReplyDelete
  13. runtime 'org.springframework:spring-test:3.1.0.RELEASE'
    should be replaced by
    runtime 'org.springframework:spring-test:3.1.1.RELEASE'

    in BuildConfig.groovy

    ReplyDelete

 

© 2013 icodeya. All rights resevered.Designed by Templateism

Back To Top