Generate pdf from html with PhantomJS
brew install phantomjs
if you are on MacOS using homebrew.
To install shrimp just type
gem install shrimp
1 2 3 4
Et voilá! A rendered pdf of your website.
Shrimp comes with plenty of options that you can pass to the Phantom Object. However, you can also configure shrimp to your needs with a config file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Keep in mind, that the rendering_timeout should be higher than the rendering_time.
The Phantom Class come with three different rendering options:
1 2 3 4 5 6 7 8 9 10 11
Shit’s being weird
If some error occurs you will still get a result - an empty file. This is necessary to let some asynchronous rendering like Shrimp::Middleware know about it. However you still can check the error response.
1 2 3 4 5
To make sure the resulting pdf has the expected content, phantom does not follow redirects or render weird 500 status pages. So everything other than a 200 response results in an empty output file.
If you prefer bang methods each of the rendering options comes with a bang!
1 2 3
The shrimp gem comes with a rack-aware Middleware that allows users to get a pdf view of any page on your site by appending .pdf to the URL.
Non-Rails Rack apps
# in config.ru require 'shrimp' use Shrimp::Middleware
1 2 3
With Shrimp options
With conditions to limit routes that can be generated in pdf
1 2 3 4 5 6 7 8 9 10 11 12 13
To avoid deadlocks, Shrimp::Middleware renders the pdf in a separate process retuning a 503 Retry-After response Header. you can setup the polling interval and the polling offset in seconds.
config.middleware.use Shrimp::Middleware, :polling_interval => 1, :polling_offset => 5
To avoid rendering the page on each request you can setup some the cache ttl in seconds
config.middleware.use Shrimp::Middleware, :cache_ttl => 3600, :out_path => "/my/pdf/store"
If you use
Rack::Session::Cookie in your RackApp the user cookie is passed to PhantomJS. Thus you don’t need to worry about Login Credentials or other
session based content.
However, as we also send pdf reports to our customers we want to render resources without being logged in. Since we use devise for user handling in our Rails App, things get easy with our own devise SignInInterceptor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
With this setup we can add a
to_pdf method to our resource
1 2 3 4 5 6 7 8
The middleware return three different status codes based on the rendering status.
503 Retry-After # as long as the rendering is still in progress 504 # if rendering took longer than request_timeout 200 Content-Type application/pdf # delivering the pdf file if rendering is finished if request was HTTP_X_REQUESTED_WITH (Ajax) 200 Content-Type text/html # delivering html with the link to the pdf file
To include some fancy Ajax stuff with jquery you can do
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
The good thing about PhantomJS is that you only need to take care of webkit’s css implementation. To implement manual page breaks you can do:
1 2 3 4 5 6 7 8 9 10 11 12
You don’t always have to fight the fat prawn when a lightweight shrimp can do.