how to create a pdf editor for grails

398 Views Asked by At

In my Grails project I'm using the PDF plugin to generate PDFs from gsp page.

It works well, but I would like to add more functionalities to users, so I would like to allow user to edit the PDF basic template (defined in a gsp page), in particular I would like to allow the editing of the text inside the template and store it somewhere.

Anybody knows how could it be done?

3

There are 3 best solutions below

1
On

If you only want to change the text, you could store the edited text in a database with the user id and loading it into the gsp page instead of the standard text.

If you also want to change the style of the page you could try to store the whole gsp page in the database and let the user editing it with an HTML Editor.

that's how i would start with, maybe someone has an better idea

2
On

The underlying components of the pdf plugin don't strictly require a .gsp file. It simply uses .gsps rendered as strings and feeds those into the flyingsaucer lib. So you could use a WYSIWYG type editor to allow users to create html snippets, save those strings somehow and then feed those though to the flyingsaucer libs yourself. just look into the included service methods of the plugin for an example. That might sound scary, but it really isn't very complicated.

You might want to wrap the user generated content with some of your own HTML markup for sanity and styling purposes of course, but the idea you are going for is entirely doable.

3
On

You could have a GSP that behaves of two different ways. First of all the GSP will be rendered in a Editable state. In this state the user could do some edits in some parts of the GSP. After that, the GSP will be rendered in a Preview state, where user could check the modifications that he has done in the previous step (nothing can be edited in this state). Finally, the GSP will be rendered as a PDF (using Grails Rendering Plugin).

Note that user will not edit the GSP itself. You need to allow him to edit through HTML elements as Text Areas, for instance. In this case we're using an WYSWYG editor. This editor allows the user to put text as Bold, Italic, etc.

Therefore, the most important step of this solution is to distinguish the two states in the same GSP. To do that you can use a boolean variable (called editing, for instance). This variable, if true, will render the GSP with the elements that will allow him to perform changes in the document. For other side, if the editing variable is false, the GSP will be rendered just with texts, not allowing any kind of editing.

The user could Check or Uncheck checkboxes (to show or hide some part of the document) and write or change texts in the Text Areas elements.

Below I'll show how this solution works.

GSP

The GSP is a template GSP and is called _quote.gsp

The piece of code below shows the use of the editing variable. Note that if editing = true, then a textarea is rendered and user can edit the text. There is a standard text that can be changed.

The post variable keeps what user has done after the editing phase. We use JQuery serialize to get all paramaters and pass it to a Grails Controller.

<p>
    <g:if test="${editing}">
        <pgs:textArea html="true" autosize="true" name="fraseInicial" rows="2" cols="80">
            ${post?.fraseInicial?post.fraseInicial:"Conforme sua solicitação, a empresa tem a satisfação de informar-lhe os métodos e preços."}
        </pgs:textArea>
    </g:if>
    <g:else>
        ${post.fraseInicial}
    </g:else>
</p>

pgs:textArea is a specific taglib of this system and is used to render a WYSWYG editor, you can replace it for a simple TextArea HTML element.

An example with checkbox:

<g:if test="${editing || post.temPrazoAnalise}">
    <h1>
        Teste teste
    </h1>
    <g:if test="${editing}"><g:checkBox name="temPrazoAnalise" value="${!post?true:post?.temPrazoAnalise == null?false:true}"/></g:if>
    <g:if test="${editing || post.temPrazoAnalise}">
        <p>Teste teste teste </p>
    </g:if>
</g:if>

Controller

The previewQuote() is called from an AJAX call that serializes (via JQuery) all parameters of GSP.

The back() action allows the user to back to the editing state from the preview state. This is the reason why we set session["paramsReport"] = params inside previewQuote(). Doing this way it's possible to use session["paramsReport"] inside back() and restore the values changed by the user.

    def editQuote() {
        def quote = Quote.get(params.id)
        render(template: "/quote/report/quote", model: [editing:true, quote:quote])
    }


    def previewQuote() {
      Quote quote = Quote.get(params.id)          
      session["paramsReport"] = params        
      render(template: "/quote/report/quote", model: [quote:quote, post:params])
    }


    def back() {
      def quote = Quote.get(params.id)

      if (session["paramsReport"]) {
          render(template: "/quote/report/quote", model: [editing:true, post:session["paramsReport"], quote:quote])
      }
    }

    def generateQuote() {
      Quote quote = Quote.get(params.id)
      def f = new  File(grailsApplication.mainContext.servletContext.getRealPath("/app/temp/${quote.code}.pdf"))

       if (f.exists())
           f.delete()

       f.withOutputStream { os ->
        pdfRenderingService.render([template: '/quote/report/quote', model: [quote:this, post:session["paramsReport"], pdf:true]], os)
       }
   }

This solution was developed by wanderson-santos (https://stackoverflow.com/users/128857/wanderson-santos) and me.

I hope you understand the overall idea of the solution. I understand that could be a bit complicated at a first view. Anyway, is a solution that allows flexibility for this kind of requirement (i.e. allow the user to customize a report before the PDF is generated).