Skip to content

Instantly share code, notes, and snippets.

@GusGA
Created July 1, 2014 15:01
Show Gist options
  • Save GusGA/11af9bd9a83d5bb26762 to your computer and use it in GitHub Desktop.
Save GusGA/11af9bd9a83d5bb26762 to your computer and use it in GitHub Desktop.
Article writed by Someone about Prawn gem and rails

##CREATING PDF USING PRAWN IN ROR

Recently i was working on a client project and i realized that i need to create a PDF for generating a invoice on the fly. I wanted something

As i started searching my way for options, i found out that there are lot of gems and plugins available in the rails community for generating PDF.

There were two options for me which were widely used for generating PDF.

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

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

So I chose Prawn for this project and believe me adding this to my rails application wasn’t that 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'

  1. Second thing that we need to do is that we need to create a PDF Mime::Type inside config/initializers/mime_types.rb that is because 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. )
  1. Then we have to do 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 u can see inside my show controller 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, that it sends the data for the pdf document. we can have couple of options passed to this send data method. Like here i have passed a filename option to name the pdf document. and the type option.

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

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

Good way would be creating a new directory called pdfs inside our apps folder and then creating a new file called inovice_pdf (this is what i have created) and then create a class inside that called InvoicePdf and inherit that with a prawn document.

class InvoicePdf < Prawn::Document

end
  #then inside that we can have a initialize method

class InvoicePdf < Prawn::Document

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

here we gave a call to a super method and display the string that we need to show on our PDF.

  1. Now remember the format.pdf block inside the show action in Invoices controller. inside that we have a line
  pdf = InvoicePdf.new(@invoice, view_context)

Here we have instantiated the InvoicePdf. here @invoice is the invoice instance so it can be accessible inside our pdf document and to access to all the view helpers in our PDF document we would pass view_context as the argument.

  1. Restart your application.

And we are ready to go. now we have to make the changes inside the InvoicePdf class for the styling that we need to apply to our PDF document.

  1. Now inside our InvoicePdf class we need to set the @invoice and view_context.
  def initialize(invoice, view)
    super()
    @invoice = invoice
    @view = view
    text "Invoice #{@invoice.id}"
  end
  1. Now you can create different method inside your InvoicePdf class as per what you want to show on your pdf.

For example to show your logo on the pdf we can have a logo method.

```ruby
def logo
  logopath =  "#{Rails.root}/app/assets/images/logo.png"
  image logopath, :width => 197, :height => 91
end
```

and then give a call to that logo method inside your initialize method.and wow logo shows up on the pdf.

and this is how we can have separate method for separate section of our pdf.

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

I have created a standard invoice and have pasted the code below.You can modify it as you want your PDF to look like.

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 go to the address bar and type the url for the show action.

http://localhost:3000/invoices/1.pdf (press enter)

or you can have a link like this in your view

<%= link_to "Download invoice", invoice_path(invoice.id, :format => 'pdf') %>;

the invoice would be downloaded. Remember at top i told you if you dont want to download and just show up in your browser just add this option to the send_data disposition: "inline"

  send_data pdf.render, filename: 
"invoice_#{@invoice.created_at.strftime("%d/%m/%Y")}.pdf",
type: "application/pdf", disposition: "inline"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment