How to create PDF’s and Images from your website in Rails 5

Posted by Randy on July 29, 2010

I am going to show you how to generate both a pdf and image from a single action in a controller using the awesome, wkhtmltopdf library. This also uses PDFKit and WebSnap gems available on GitHub.

This example assumes the following:

  • wkhtmltopdf and wkhtmltoimage are already installed and accessible on in the PATH.
  • You have an html page setup to display the record.
  • You have created a pdf CSS file to help display the pdf, if you so choose.
  # config/initializers/mime_types.rb
  Mime::Type.register "application/pdf", :pdf
  Mime::Type.register "image/png", :png
 
  # app/controllers/items_controller.rb
  def show
    @item = Item.find(params[:id])
 
    respond_to do |format|
      format.html { }
      format.pdf {
        html = render :action => "show.html.erb"
        kit  = PDFKit.new( html, :zoom => 0.75 )
        kit.stylesheets << File.join( RAILS_ROOT, "public", "stylesheets", "pdf.css" )
 
        send_data kit.to_pdf, :filename => "item.pdf", :type => 'application/pdf', :disposition => 'inline'
      }
      format.png {
        html = render :action => "show.html.erb", :layout => "application.html.erb"
 
        # I am nil'ing these options out because my version of wkhtmltoimage does
        # not support the scale options and I do not want to crop the image at all.
        snap = WebSnap.new(html, :format => 'png', :'scale-h' => nil, :'scale-w' => nil,
          :'crop-h' => nil, :'crop-w' => nil, :quality => 100, :'crop-x' => nil, :'crop-y' => nil)
 
        send_data snap.to_bytes, :filename => "item.png", :type => "image/png", :disposition => 'inline'
      }
    end

Now you should be able to access three distinct views, each producing a different result

  http://example.com/items/1 # => Generates an html page.
  http://example.com/items/1.pdf # => Generates a pdf of the html page.
  http://example.com/items/1.png # => Generates a png of the html page.

You could easily also add more image types by just created another block for each format, and
changing the :format to whatever one you would like.

Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. Pedro Santos Wed, 15 Jun 2011 10:00:14 UTC

    Hello!
    I’ve tried your approach but i get the error ‘Render and/or redirect were called multiple times in this action.’ on the line of send_data.
    I’m using Rails 3, do you know what can be wrong?
    Thanks!

  2. Scott Shea Fri, 23 Sep 2011 16:41:36 UTC

    Randy, thank you for this excellent help. I am running into a problem with the Websnap library and so far have not received a response from the author (although it has not been 24 hours; I am on a deadline).

    When I do WebSnap::Snapper.new (args) I get “private method `chomp’ called for nil:NilClass”. Have you encountered this and if so, how did you get around it?

    Thanks for any help you can provide.

    Scott

  3. Randy Sun, 25 Sep 2011 15:05:57 UTC

    Scott, I am not sure about that without seeing the code. Also, my example is going WebSnap.new, not Websnap::Snapper.new. Could that be part of the issue?

  4. Scott Shea Mon, 26 Sep 2011 16:56:25 UTC

    Randy, thank you for the response. I went to the Websnap::Snapper.new thing as part of my troubleshooting and forgot to revert. I have added the code below. Also, I forgot to mention that I am coding this on Windows (long story) and I suspect that the issue may be because I cannot find wkhtmltoimage-proxy in the path… even though the path is set.

    format.png { # pulled from http://www.freezzo.com/2010/07/29/how-to-create-pdfs-and-images-from-your-website-in-rails/
    html = render :action => “show.html.erb”, :layout => “application.html.erb”

    Rails.logger.debug(“html: ” + html.inspect)
    snap = WebSnap.new(html, :format => ‘png’, :’scale-h’ => nil, :’scale-w’ => nil,:’crop-h’ => nil, :’crop-w’ => nil, :quality => 100, :’crop-x’ => nil, :’crop-y’ => nil)

    send_data snap.to_bytes, :filename => “dashboard.png”, :type => “image/png”, :disposition => ‘inline’
    }

  5. Juan Jose Wed, 25 Jul 2012 14:25:39 UTC

    Somebody has fix the issue?

    ‘Render and/or redirect were called multiple times in this action.’ on the line of send_data

    Thanks

Comments