Create a PDF with Prawn

Prawn is a pdf generator built in ruby, that is pretty full featured.  However, I found it pretty hard to find good current documentation on using it in Rails.  Much of it was years old, and referred to older versions of prawn and older versions of Rails.  Ryan Bates’ RailsCast #153 was very helpful though, as usual.

Fortunately, there is a gem we can use:

gem 'prawn'

And of course run bundle install

Next we need to make sure Rails can accept pdfs. Your app may already have this, but if not, add this to your config/initializers/mime_types.rb file:

Mime::Type.register "application/pdf", :pdf

Now in our controller, inside the function for the view where you want to show the pdf, we need to add code for format.pdf. If you use the scaffold generator, you can see similar code to this generated automatically in the create, update, and destroy methods.

respond_to do |format|
    format.html
    format.pdf do
      pdf = Prawn::Document.new
      pdf.text "this is a pdf"
      send_data pdf.render_file
    end
  end

So we add the respond_to block, and we need the format.html line to ensure the normal html view still gets loaded. The format.pdf block will be run when we load the .pdf extension of the url address in the browser. For example, if your show view is localhost:3000/users/1, then if you now load localhost:3000/users/1.pdf, it should load the sample pdf we created. The pdf = Prawn::Document.new instantiates a new pdf using Prawn, and the pdf.render_file line actually creates the file. Now you’ll probably notice if you run this that the pdf will download. That’s because the send_data default is to download. If we pass the option disposition: inline then it will show in the browser rather than download. As shown below:

send_data pdf.render, filename: "mypdf.pdf",
                      type: "application/pdf",
  	              disposition: "inline"

We can also pass in several options when we create the pdf

img = "#{Rails.root}/app/assets/images/my_image.jpg"
pdf = Prawn::Document.new(background: img, :page_size => "LETTER", :page_layout => :landscape)

Here I create a background image for the pdf, and pass in the page size and make the layout landscape. A good resource for all the options is the Prawn resource manual. Now in my case I wanted to save the file into the database, rather than just displaying it in the browser.  So in my User model I created a column called certificate, and this is how I save the pdf to that column in the database. First I setup the paperclip gem to handle attached files. To do this, add gem ‘paperclip’ in your gem file, run bundle install, then run a migration from the command line adding the column for the attached file to the database, in my case

rails generate paperclip user certificate

and add this in the model where you want the attachment:

has_attached_file :certificate
validates_attachment_content_type :certificate, :content_type => "application/pdf"

Now that paperclip is setup, back to Prawn.  I want set a custom path to where to save the pdf. I created a path in my app to store pdfs: /app/pdfs and run this line to save the file there:

pdf.render_file File.join(Rails.root, "app/pdfs", "x.pdf")

Now that its saved in my app, I can save it to the database,

current_user.certificate = File.open("#{Rails.root}/app/pdfs/x.pdf")
current_user.save!

Now I can access the file in the view if I want

<iframe class="center" src="<%= current_user.certificate %>" width="300" height="150"> width="750" height="580"style="border: none;"> 

In summary, here’s the full code I use in the controller. Note that since I’m saving the file rather than displaying it directly in the browser, I don’t need the format.pdf block anymore.

img = "#{Rails.root}/app/assets/images/certificate.jpg"
pdf = Prawn::Document.new(background: img, :page_size => "LETTER", :page_layout => :landscape)
pdf.font "Helvetica", style: :bold
pdf.font_size 34
pdf.draw_text current_user.name, at: [300, 380]
pdf.draw_text Date.today.strftime("%A %B %e %Y "), at: [135, 55]
pdf.render_file File.join(Rails.root, "app/pdfs", "x.pdf")
current_user.certificate = File.open("#{Rails.root}/app/pdfs/x.pdf")
current_user.save!

I have another post about using strftime to format Dates in Ruby, and one about fonts that you can use with Prawn. Other helpful resources I found were:

StackOverflow questionStack Overflow question 2, SO question 3, and this Sitepoint post,

There is also a cool prawn-rails gem that is convenient, although note that in the Gemfile you need to put prawn_rails with an underscore, not a dash. Here’s a demo site with examples of this gem.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: