12 December 2009

Ruby on Rails PDF generator : Prawn gem & Prawnto plugin

Download the pdf_demo application from GitHub

It has been amazing working for a project using Ruby on rails framework. And with the awesome support from its community, the experience has been always smooth. In this post I will be introducing you with a wonderful gem/plugin for Rails to generate PDF's. Generating PDF's has never been this easy. When I was asked to generate PF reports in my current project, the hunt for the best plugin began and after testing a few plugins I came across a gem called 'prawn'.

"Prawn takes the pain out of generating beautiful printable documents, while still remaining fast, tiny and nimble."

And Prawnto is a rails plugin which leverages the new prawn library to produce compiled pdf views.

In this post , I will provide simple step by step instruction to install and configure prawn/prawnto and hence will be walking through a simple PDF generating demo application.

Installing Prawn and Prawnto:

To begin with, we have to install prawn gem. From your command prompt(windows OS)

gem install prawn

Once we have successfully installed prawn gem, we need to install Prawnto plugin. Before installing the plugin, lets create the demo application which will be using MySQL database..

From the command prompt

C:\>rails pdf_demo -d mysql

After the project folder structure is created, move into the project folder:

C:\>cd pdf_demo

And now we will be installing the Prawnto plugin


C:\pdf_demo>ruby script/plugin install http://github.com/thorny-sun/prawnto.git/

After we have succesfully installed this plugin, we need to add the following line inside the environment.rb file of our project.

config.gem 'prawn'
within "Rails::Initializer.run do |config|"

Next, lets go ahead and create the Controller named Book


C:\pdf_demo>ruby script/generate controller Book
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/book
      exists  test/functional/
      create  test/unit/helpers/
      create  app/controllers/book_controller.rb
      create  test/functional/book_controller_test.rb
      create  app/helpers/book_helper.rb
      create  test/unit/helpers/book_helper_test.rb

Next lets create the model named book


C:\pdf_demo>ruby script/generate model book
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/book.rb
      create  test/unit/book_test.rb
      create  test/fixtures/books.yml
      create  db/migrate
      create  db/migrate/20091211170948_create_books.rb


Our project contains one database table(books) with two columns namely name and author.So add the following code inside your migration file (20091211170948_create_books.rb)


class CreateBooks < ActiveRecord::Migration
  def self.up
    create_table :books do |t|
      t.string   :name
      t.string   :author
      t.timestamps
    end
  end

  def self.down
    drop_table :books
  end
end

Before we go further with any more code, if you open up your database.yml file, it says


development:
  adapter: mysql
  encoding: utf8
  reconnect: false
  database: pdf_demo_development
  pool: 5
  username: root
  password:
  host: localhost

So we need to create "pdf_demo_development" named database. We can do that from the command prompt itself as shown below


C:\>mysql -u root

Here I have assumed that the username is "root" and password is nil.
If you have got password then use the following command

C:\>mysql -u root -p (password)

Once you get inside the mysql, we can go ahead and create the required database using following command

mysql> CREATE DATABASE pdf_demo_development;

Once this is successfull, we move ahead and migrate the database for our project.


C:\pdf_demo>rake db:migrate
(in C:/pdf_demo)
==  CreateBooks: migrating ====================================================
-- create_table(:books)
   -> 0.0630s
==  CreateBooks: migrated (0.0630s) ===========================================

Next we will be adding some dummy data's into our newly created database table namely 'books'

Use the following command to add data to the table. First select the database from mysql using


mysql> use pdf_demo_development;

Next lets add the dummy data


mysql> INSERT INTO books (name,author) VALUES ("The Rails Way", "Obie Fernandez");
Query OK, 1 row affected (0.06 sec)

mysql> INSERT INTO books (name,author) VALUES ("Agile Web Development with Rails", "David");
Query OK, 1 row affected (0.01 sec)

So finally we write the codes for generating the PDF and showing the datas entered into the database.

Add the following codes into your app/controllers/book_controller.rb

class BookController < ApplicationController
 def index
 
 end
 
 def show
  @book = Book.find(:all)
  respond_to do |format|
   format.pdf { render :layout => false }
  end   
 end
end

Here we have created a method called 'show' and created a variable called '@book' which contains all the record from the 'books' table.

Next lets create a view(.rhtml file) for the index method within the books folder and add the following line of code inside this file, which is just a link to generate the PDF file.
app/views/book/index.rhtml

<%= link_to "(PDF Report)", :controller=>"book", :action=>"show", :id=>"2", :format=>'pdf' %>

And one final step before we run our server to view the result is to create a file with extension .pdf.prawn
This is the file where we write the code to display the contents in the PDF file. So create a 'show.pdf.prawn' file and add the following code
app/views/book/show.pdf.prawn

pdf.move_down(30) 
books =  @book.map do |item|
 [
  item.name,
  item.author
 ]
end

pdf.table books, :border_style => :grid,
     :row_colors => ["FFFFFF", "DDDDDD"],
     :headers => ["book", "author"],
     :align => { 0 => :left, 1 => :right, 2 => :right, 3 => :right }

Now lets go ahead and run the server

C:\pdf_demo>ruby script/server

And in your favorite browser go to => http://localhost:3000/book/index
and you will be taken to a page with your (PDF report) link and on clicking on this link you can get your PDF report.


You can visit prawn and prawnto websites to view their documentations and generate PDF's with various effects.

13 comments:

  1. I wish I knew about prawn and prawnto when I was writing creating PDFs for fedena. I chose PDF::Writer instead and hated the fact that I would have to put all view logic for the pdf in the controller. Having seen you work on prawnto, I'm impressed by how easy it to generate PDFs. Also, I love the fact that it separates the view from the controller logic. Thanks for the tutorial. I might need it the next time I need a PDF plugin.

    ReplyDelete
  2. can a make double line table outter border for pdf.table

    ReplyDelete
  3. i m usig prawnto ,but i want some more properties of pdf.table
    where i have to show difference from outter border to in border
    and outter font size to inner font size for the table

    ReplyDelete
  4. thanks for u response RALU

    ReplyDelete
  5. how can I show the difference b/w header text and body text in
    font or font style or size when i m using pdf.table,can i give header_font_size for header text in table properties it is not working ,kindly plz help me

    pdf.table data,
    :position => :center,
    :font_size => 8,
    :header_text_color => "0000CC",
    :border_style => :grid,
    :headers => [ "Room No", "Room "],
    :align => { 0 => :left, 1 =>:center,},
    :align_headers => { 0 => :left,1 =>:left)

    ReplyDelete
  6. @vara

    Hi
    You can surely show the difference between the table header and the table body using header background color or header text color.

    The entire header cells can be given background color using :header_color=>"#000000" (Entire header row will be having black background)

    The header text color can be set using :header_text_color=>"#ffffff"

    ReplyDelete
  7. thanks for u response TechSlam
    but i want to show the entire header cells with some specified
    font_size and body text with another font_size

    can u plz suggest me

    ReplyDelete
  8. @vara
    u giv the header and the content as different tables , with diferent font size
    align it well...
    i think there is no other straight way, if u got any pls post it :)

    ReplyDelete
  9. @Vara

    Yea, what Ralu has suggested would be the best idea to get this worked out. I could not find any option to get the header font size and body font size changed in prawn to either.

    ReplyDelete
  10. if i use two different tables means for data which having more than one page the header vil not come in next page and the width of the header table also vil not set correctly to the width of other table data.for my case this vil not.

    very very thankful for ur responses

    ReplyDelete
  11. i have seen in google groups these properties in
    pdf.table .to show variations in columns font size
    i.e
    :column_font_sizes => { 0 => 8, 2 => 12 }

    but it is not working ,if it work means some what my work vil go cool.
    here is link:

    http://groups.google.com/group/prawn-ruby/browse_thread/thread/72bf44b3f7eab333

    if u have any suggestions plz give me

    ReplyDelete
  12. Excellent tutorial, thank you!

    ReplyDelete
  13. Hi is there a way to display different colors for text in tables.
    EG. I have a table with some fields and due date field.
    i want to display the text in red if due_date > Date.today and in green color if due_date < Date.today.

    Thanks
    Aj

    ReplyDelete

Share your thoughts, Lets have a discussion :)