Create a PDF with Prawn
September 7, 2014 Leave a comment
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 question, Stack 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.
Recent Comments