How to Create a PDF Using Prawn in Ruby on Rails

15 March 2017 by Aniket Joshi No comments

While working on a client project, I needed to create a PDF for generating invoices on the fly. I wanted something that could be fast and easy to use. As I started searching my way for options, I discovered that there are lot of gems and plugins available in the Rails community for generating PDF.

I found two options which are widely used for generating PDF.

1. Prawn – http://prawn.majesticseacreature.com/

2. PDFkit – https://github.com/jdpace/PDFKit

So I chose Prawn for the project and believe me adding this to my rails application wasn’t difficult.

1. First thing that we need to do here is, to add the prawn gem inside our gemfile and run the bundle command.

gem ‘prawn’
2. Next we need to create a PDF Mime :: Type inside config/initializers/mime_types.rb – we need to notify rails about the PDF mime type.

Mime::Type.register “application/pdf”, :pdf

(Now when there is a pdf request our application can respond to it. )

3. Then we have to do a couple of changes inside our controller action for which we need to return a pdf version.

This is how my Controller looks like.

class InvoicesController < ApplicationController

  before_filter :authenticate_customer!, :only => [:index, :show]

  def index
    @invoices = Invoice.all_invoices(current_customer)
  end

  def show
    @invoice = Invoice.find(params[:id])
    respond_to do |format|
      format.html
      format.pdf do
        pdf = InvoicePdf.new(@invoice, view_context)
        send_data pdf.render, filename:
        “invoice_#{@invoice.created_at.strftime(“%d/%m/%Y”)}.pdf”,
        type: “application/pdf”
      end
    end
  end
end

Here you can see inside my show controller that I have created a respond block with html and pdf format. And inside the format.pdf block there is a send_data method we are calling.

send_data pdf.render, filename:
“invoice_#{@invoice.created_at.strftime(“%d/%m/%Y”)}.pdf”,
type: “application/pdf”

And what it does is, it sends the data for creating the pdf document. We can have a couple of options passed to this send_data method. Here, I have passed a filename option to name the pdf document, and the type option.

One more option you could use here is to pass the disposition: “inline” after type and what this does is instead of downloading the pdf it would open it in the browser.

Now to work more on the PDF document, we will create a new class.
app/pdfs/invoice_pdf

One good way would be to create a new directory called pdfs inside the apps folder and then creating a new file called invoice_pdf (this is what I have named) and then create a class inside that called InvoicePdf and inherit that with a prawn document.

class InvoicePdf < Prawn::Document

end

And then inside that we have an initialize method where we called a super method and display the string that we need to show on our PDF.

class InvoicePdf < Prawn::Document

def initialize(invoice, view)
    super()
    text “This is an order invoice”
  end
end

4. In the Invoices controller, inside the show action of the format.pdf block, we have instantiated the InvoicePdf.

pdf = InvoicePdf.new(@invoice, view_context)
Here, @invoice is the invoice instance that can be accessed inside our pdf document. And view_context is used to access all the view helpers in our PDF document.

5. Restart your application.

And we are ready to go. To add styling to the PDF document, we make changes in the InvoicePdf class.

6. Now inside our InvoicePdf class we need to set @invoice and view_context.

def initialize(invoice, view)
  super()
  @invoice = invoice
  @view = view
  text “Invoice #{@invoice.id}”
end
7. Now we can various methods inside the InvoicePdf class depending on the requirement. For example, to display a logo on the pdf we can have a logo method as defined below.

def logo
  logo_path = “#{Rails.root}/app/assets/images/logo.png”
  image logo_path, :width => 197, :height => 91
end
and then invoke the logo method inside the initialize method and voila the logo shows up on the pdf.

For more styling options you can refer to – http://prawn.majesticseacreature.com/manual.pdf.

In the below code snippet, we create a a standard invoice. We can modify it as per our needs.

class InvoicePdf < Prawn::Document

  def initialize(invoice, view)
    super()
    @invoice = invoice
    @view = view
    logo
    thanks_message
    subscription_date
    subscription_details
    subscription_amount
    regards_message
  end

  def logo
    logopath = “#{Rails.root}/app/assets/images/logo.png”
    image logopath, :width => 197, :height => 91
    move_down 10
    draw_text “Receipt”, :at => [220, 575], size: 22
  end

 def thanks_message
    move_down 80
    text “Hello #{@invoice.customer.profile.first_name.capitalize},”
    move_down 15
    text “Thank you for your order.Print this receipt as
    confirmation of your order.”,
    :indent_paragraphs => 40, :size => 13
  end

  def subscription_date
    move_down 40
    text “Subscription start date:
    #{@invoice.start_date.strftime(“%d/%m/%Y”)} “, :size => 13
    move_down 20
    text “Subscription end date :
    #{@invoice.end_date.strftime(“%d/%m/%Y”)}”, :size => 13
  end

  def subscription_details
    move_down 40
    table subscription_item_rows, :width => 500 do
      row(0).font_style = :bold
      columns(1..3).align = :right
      self.header = true
      self.column_widths = {0 => 200, 1 => 100, 2 => 100, 3 => 100}
    end
  end

  def subscription_amount
    subscription_amount = @invoice.calculate_subscription_amount
    vat = @invoice.calculated_vat
    delivery_charges = @invoice.calculated_delivery_charges
    sales_tax = @invoice.calculated_sales_tax
    table ([[“Vat (12.5% of Amount)”, “”, “”, “#{precision(vat)}”] ,
    [“Sales Tax (10.3% of half the Amount)”, “”, “”,
    “#{precision(sales_tax)}”] ,
    [“Delivery charges”, “”, “”, “#{precision(delivery_charges)} “],
    [“”, “”, “Total Amount”, “#{precision(@invoice.total_amount) } “]]),
    :width => 500 do
      columns(2).align = :left
      columns(3).align = :right
      self.header = true
      self.column_widths = {0 => 200, 1 => 100, 2 => 100, 3 => 100}
      columns(2).font_style = :bold
    end
  end

  def subscription_item_rows
    [[“Description”, “Quantity”, “Rate”, “Amount”]] +
    @invoice.subscriptions.map do |subscribe|
    [ “#{subscribe.description} “, subscribe.quantity,
    “#{precision(subscribe.rate)} “,
    “#{precision(subscribe.quantity * subscribe.rate)}” ]
    end
  end

  def precision(num)
    @view.number_with_precision(num, :precision => 2)
  end

  def regards_message
    move_down 50
    text “Thank You,” ,:indent_paragraphs => 400
    move_down 6
    text “XYZ”,
    :indent_paragraphs => 370, :size => 14, style: :bold
  end

end
Now if we open the browser and navigate to http://localhost:3000/invoices/1.pdf, the invoice would be downloaded.

or we can have a link like this in the views –
<%= link_to “Download invoice”, invoice_path(invoice.id, :format => “pdf”) %>;

Remember I had pointed out earlier that if we don’t need to download the PDF and just display it in the browser then we can add disposition: “inline” in the send_data method as given below.

send_data pdf.render, filename:
“invoice_#{@invoice.created_at.strftime(“%d/%m/%Y”)}.pdf”,
type: “application/pdf”, disposition: “inline” 

Hope this helps all those who are looking for generating PDFs using Prawn. There is lot more to Prawn and I have tried to provide as much information as I could. If there’s something else that you find useful on the topic, please share your thoughts in the comments section.

Aniket Joshi

Delivery Director, Thinker & Tinkerer at Idyllic.

Follow me on Twitter

Leave a Reply

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

Subscribe To Our Blog

Get access to proven marketing ideas, latest trends and best practices.

Next up home

Contact

Lets build cool stuff

Share your contact information & we will get in touch!

I want (Tell us more about your dream project)