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

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.
      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
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.

6 thoughts on “How to create PDF’s and Images from your website in Rails

  1. 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. 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, 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.randygirard.com/
    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’
    }

  4. Somebody has fix the issue?

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

    Thanks

Leave a Reply

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