Navigation

ADS specializes in using Ruby on Rails to build advanced, scalable, database-backed web sites for organizations of all sizes. Find out more at our website.

Atlantic Dominion Solutions

For a recent project we needed (and wanted) a simple solution to generate PDF files. Ideally, the solution would use HTML for the general layout and design of the generated PDF, working just like a normal view in Rails.

After testing a number of potential PDF solutions we came across a neat little library called HTMLDOC. What it does is take basic HTML and converts it to PDF, among many other output formats.

For the situation and solution we wanted it does a great job, especially for the price. To make it even easier to use, there is a also a Ruby HTMLDOC Gem to use along with it. Score!

To generate the PDF files we used plain old HTML, something we were familiar with. Exactly like creating a normal rhtml view.

Below follows our experience installing and using HTMLDOC to get PDF file generation out of our Rails application. This has been tested and used on Linux and MacOS X 10.4.9.

Note: You will need the proper tools installed to compile HTMLDOC. On MacOS X, that usually consists of installing the developer tools.

1. Installing HTMLDOC

The first thing we need to do is get HTMLDOC downloaded, compiled, and installed. Copy and past the following in your console:

curl -O http://ftp.easysw.com/pub/htmldoc/snapshots/htmldoc-1.9.x-r1521.tar.gz

tar zxvf htmldoc-1.9.x-r1521.tar.gz

cd htmldoc-1.9.x-r1521

./configure --prefix=/usr/local

make

sudo make install

2. Install the HTMLDOC Gem

Now that we have the HTMLDOC application ready to go, we want to install the HTMLDOC Ruby Gem to interface HTMLDOC with our Rails application:

sudo gem install htmldoc

3. Configuring Your Application

Next we need to configure our application. Open up your config/environment.rb file and add the following (to the end):

Mime::Type.register 'application/pdf', :pdf
require 'htmldoc'

Note: There is a way to make Rails handle the ‘.pdf’ extension format, but when we tried it, it kept asking us to download a file no matter what format we requested on every page. After many attempts at trying to rectify the issue, we eventually decided on the following solution:

4. PDF Renderer

We also added a method to our app/controllers/application.rb file to help DRY up the PDF generation, sort of like the render methods already included in Rails:

def render_to_pdf(options = nil)
  data = render_to_string(options)
  pdf = PDF::HTMLDoc.new
  pdf.set_option :bodycolor, :white
  pdf.set_option :toc, false
  pdf.set_option :portrait, true
  pdf.set_option :links, false
  pdf.set_option :webpage, true
  pdf.set_option :left, '2cm'
  pdf.set_option :right, '2cm'
  pdf << data
  pdf.generate
end

Just pass it the same options you would pass render. Check the HTMLDOC Gem rdoc page for more options and configurations.

Example

Here is an example controller method:

  def index
    @items = Item.find(:all)
 
    respond_to do |format|
      format.html # index.html
      format.xml { head :ok }
      format.pdf { send_data render_to_pdf({ :action => 'index.rpdf', :layout => 'pdf_report' }) }
    end
  end

Pretty typical Rails, no? We tell it to explicitly use that action/view and to use a different layout file.

Now an example of a view:

<h3>Showing: <%= pluralize(@items.size, 'item') %></h3>
<table><tbody>
<tr>
<th>Field1</th>
<th>Field1</th>
<th>Field1</th>
</tr>
<% @items.each do |item| %>
<tr>
<td><%= item.field1 %></td>
<td><%= item.field1 %></td>
<td><%= item.field1 %></td>
</tr>
<% end %>
</tbody></table>

Maybe it’s just me, but that sure beats using the examples using the PDF Writer plugin. At least from what I have seen.

Finally, to generate a link to the PDF, assuming you are using restful routes:

 
<%= link_to 'PDF', formatted_items_path(:pdf) %>

HTMLDOC may not be perfect, but I found it’s ease of use to generate nicely formatted PDF files far outweighed it’s limitations. I hope you found this useful.

Update: 12-26-2007

I made a little helper method for images also, put this in your application_helper.rb:

def pdf_image_tag(image, options = {})
  options[:src] = File.expand_path(RAILS_ROOT) + '/public/images/' + image 
  tag(:img, options)
end
Share this post

Related Posts

You can leave a response, or trackback from your own site.

Print This Post Print This Post

36 Responses to “Easy PDF Generation with Ruby, Rails, and HTMLDOC”

On November 20th, 2007 at 3:47 am robbert shell said:

Lovely approach, used the same in my projects. Only really problem Have had is with embedded, generated images which are sometime lost as if htmldoc expects file paths and not urls. Have on todo to look at fixing this with some pre caching.

Tried a number of other approaches before this and definitely the best and really easy to implement

Robert.

On November 20th, 2007 at 3:58 am Chris Kaukis said:

Robert,

I had the same problem with images, but have not looked into it yet as it was not of primary concern.
Chris

On November 20th, 2007 at 2:11 pm Jamie Hill said:

Looks a nice alternative to PDF Writer. Does is take into account CSS?

On November 20th, 2007 at 2:21 pm Chris Kaukis said:

Jamie,

I am not entirely sure. I believe the website states the nightly builds have at least some support for CSS.

Chris

On November 20th, 2007 at 4:33 pm Dan Kubb said:

Would you mind posting an example of what the example view renders to when rendered as PDF?

I’ve got some code using PDF::Writer, and managing the templates is a pain. If I could design the templates using normal HTML views, and then render them as PDF with some reasonable control over the style, I’d be a happy man.

On November 20th, 2007 at 6:01 pm Chris Kaukis said:

Dan,

I will try and post an example PDF tomorrow morning. However, I can tell you it looks pretty close to what a normal HTML page would look like.

@Robert Shell: I tested images and you give it the full path. For example /Users/chris/Projects/railsapp/public/images/logo.gif

Chris

On November 20th, 2007 at 6:04 pm Chris W said:

If you have to have css support and don’t mind spending some money, check this solution out.

HTML / CSS to PDF using Ruby on Rails
http://sublog.subimage.com/articles/2007/05/29/html-css-to-pdf-using-ruby-on-rails

On November 21st, 2007 at 12:05 am » The Links » roarin’ reporter said:

[...] Easy PDF Generation with Ruby, Rails, and HTMLDOC [...]

On November 22nd, 2007 at 4:56 pm Ruby, Rails, Rails Plugins, Mac OS X « exceptionz said:

[...] PDF generation with Ruby, Rails and HTMLDoc [...]

On November 30th, 2007 at 1:22 pm magnum blog » Blog Archive » links for 2007-11-30 said:

[...] Ruby on Rails Website Development Blog from Atlantic Dominion Solutions For a recent project we needed (and wanted) a simple solution to generate PDF files. Ideally, the solution would use HTML for the general layout and design of the generated PDF, working just like a normal view in Rails. (tags: pdf rails ruby htmldoc rubyonrails development webdev) [...]

On January 4th, 2008 at 11:28 pm A Drop In The Stream › links for 2008-01-05 said:

[...] Ruby on Rails Website Development Blog from Atlantic Dominion Solutions (tags: development pdf rails ruby) [...]

On January 27th, 2008 at 3:20 pm Dave said:

WTF is formatted_items_path? I can’t find documentation on this anywhere.

On January 27th, 2008 at 3:38 pm Chris Kaukis said:

@Dave:

It comes from restful routes. Example:

map.resources :posts

gives you:

posts_path as well as formatted_posts_path(:format)

Try rake routes within in your project directory to see all your routes.

Chris

On January 27th, 2008 at 3:46 pm Dave said:

also, how do you name the pdf

On January 27th, 2008 at 3:47 pm Dave said:

Chris, thanks for the clarification on the routing for me. Much obliged.

On January 27th, 2008 at 4:10 pm Chris Kaukis said:

@Dave:

send_data has an option for filename.

send_data render_to_pdf({ :action => ‘index.rpdf’, :layout => ‘pdf_report’ }), :filename => “foobar.pdf”

Chris

On January 30th, 2008 at 3:55 am Nidhika said:

Whem I am puting
” Mime::Type.register ‘application/pdf’, :pdf
require ‘htmldoc’”

In config/enviroment.rb file in the last webrick server not restart. How am I solve this issue.
Is anybody help me

Thanks
Nidhika

On January 30th, 2008 at 8:22 am Robert Dempsey said:

@Nidhika: ensure that you are using the ‘ mark and not the full quotation mark if you copy/pasted from the post.

On February 27th, 2008 at 4:43 am Matt said:

Hi all, am I the only one who can’t ever get images to be included in the document using PDF::HTMLDoc?

If I run HTMLDOC from the command line with supposedly the same arguments (basically just –webpage) then it works a treat, even if I point it at the URL for my dynamically generated rails page. However if I call it using the Rails GEM then it renders everything except the images.

It’s driving me bonkers and pretty urgent. I’ve tried all sorts of things including the pdf_image_tag helper above with numerous modifications (eg. adding file:/// at the start).

Cheers,
Matt

On February 27th, 2008 at 5:46 pm Chris Kaukis said:

Matt,

I found that I had to use absolute path to the image. Thus, for example:

/Users/chris/src/my_rails_app/public/images/some_image.png

I hope that helps.

On February 27th, 2008 at 6:56 pm Matt said:

Hi Chris,

Thanks for that. The trouble is (and I should have mentioned) that it’s on Windoze. If I set it to ‘c:\rails\project\public\images\some_image.jpg’, load the page directly and save it to disk and then load the page in explorer it looks fine (that is, it loads the image/s from disk). But the Ruby htmldoc plugin still doesn’t get them.

I’ve also tried prepending file:/// and a few other things.

//matt

On April 4th, 2008 at 5:18 am Kelly said:

Hi Chris. Say quick question.. I have everything wired up but I’m actually calling the render_to_pdf from another controller in my app. It work fine but give me this error.
undefined method `size’ for false:FalseClass
I walk thru it with the debugger and it seems that data is nil so it can’t do a size. I assume that is because the model for my control is not an option to post to the render_as_pdf but not sure.

On April 4th, 2008 at 5:24 am Kelly said:

As an addition when I debug it I get here in the streaming and I see data is null..

********************
def send_data(data, options = {}) #:doc:
logger.info “Sending data #{options[:filename]}” unless logger.nil?
send_file_headers! options.merge(:length => data.size)
@performed_render = false
********************
is there a way to pass the record I have when I call this?
right now we do send_data render_to_pdf but is there a way to say @object so I can pass the object created by the finder? send_data @object.render_to_pdf does not work either as I get a missing method render_to_string that way

On May 14th, 2008 at 11:20 am Christoph said:

same error here, false class when using an image… any solution?

On May 14th, 2008 at 11:25 am Chris Kaukis said:

It sounds like you are missing something. I would have to see your code to help more I think. Sorry.

On May 14th, 2008 at 1:23 pm Christoph said:

I tracked it down to some weird htmldoc output, which contains whitespaces sometimes.

Here is a patch that solves my problem:
http://textmode.at/2008/5/14/ruby-htmldoc-gem-falseclass-error

Not sure if it’s the same problem as the user above has.
I contacted the author htmldoc people.

greetings

On May 16th, 2008 at 6:24 pm kajinski said:

Christoph, YOU ROCK.

Thanks for the patch!

I was getting the exact same error when using the pdf_image_tag helper. Applied the patch and viola! Image renders perfect.

On May 22nd, 2008 at 2:20 am Chris said:

sorry to ask what may be a simple answer, but how do you apply that patch?

thx…

On May 30th, 2008 at 3:45 am srishti said:

How can we use html doc in rails 1.2.3

On May 30th, 2008 at 4:16 pm Scott Motte » Blog Archive » Generate PDFs said:

[...] HTMLDoc This looks really GREAT. I’m used to html so the new level of coding to learn is much less. awesomeness. There is also this video tutorial. [...]

On June 9th, 2008 at 4:09 pm Building a Ruby on Rails application in a week « 41 technologies said:

[...] (tutorial) I had to modify it though, when certain PDFs was generated, the gem reported false as return [...]

On June 14th, 2008 at 1:32 am Mark’s Link Blog » links for 2008-06-14 said:

[...] Easy PDF Generation with Ruby, Rails, and HTMLDOC | Atlantic Dominion Solutions (tags: pdf rails htmldoc rubyonrails ruby) [...]

On July 28th, 2008 at 9:51 am Webagentur said:

Hey, that has me very helped. Thanks!

On August 15th, 2008 at 3:38 am dafinn said:

First of all, Great work! Due to the Patch of Christoph printing Images with a complete path is working now!
BUT… I am storing Articles in my Database. I use FCKEditor for the User-interface - the text is stored HTML formatted and the path for Images is stored in the HTML Code in form of “/public/uploads/Images” and so on. I have explored that HTMLDOC needs the full path(as some People above). For Example http://0.0.0.0:3000/public/uploads/Images
Has anyone an idea how to fix this? I have seen the pdf_image_tag but i am not sure how it works…

On August 22nd, 2008 at 2:31 pm Eric Wagoner said:

dafinn:

I’ve got an app that does the same. I wrote a very simple helper that expands the image paths.

def write_full_image_path(text)
newtext = text.gsub(’img src=”‘, ‘img src=”‘ + File.expand_path(RAILS_ROOT) + ‘/public/’)
newtext
end

Then, in my .rpdf files, I call for write_full_image_path(model.formatted_text)

On September 14th, 2008 at 6:50 am Francesco said:

Just a note for Ubuntu users: HTMLDOC must be installed by hand (configure, make, make install as stated here by author): if you install it with apt-get then HTMLDOC is not working in rails :)
With Debian is fine to install htmldoc with apt-get install.

The patch for images is necessary in both Debian and Ubuntu.

Thank you *very much* to all the people who shared their knowledge here :)

Leave a Reply