A Ruby gem to transform HTML into PDFs, PNGs or JPEGs using Google Puppeteer/Chromium
A Ruby gem to transform HTML into PDFs, PNGs or JPEGs using Google Puppeteer/Chromium
A Ruby gem to transform HTML into PDFs, PNGs or JPEGs using Google Puppeteer and Chromium.
Add this line to your application's Gemfile:
gem 'grover'npm install puppeteer# Grover.new accepts a URL or inline HTML and optional parameters for Puppeteer
grover = Grover.new('https://google.com', format: 'A4')
# Get an inline PDF
pdf = grover.to_pdf
# Get a screenshot
png = grover.to_png
jpeg = grover.to_jpeg
# Get the HTML content (including DOCTYPE)
html = grover.to_html
# Options can be provided through meta tags
Grover.new(' ... }
})
pdf = Grover.new(html, **grover_options).to_pdfIf calling Grover directly (not through middleware) you will need to either specify a display_url or modify your
HTML by converting any relative paths to absolute paths before passing to Grover.
This can be achieved using the HTML pre-processor helper (pay attention to the slash at the end of the url):
absolute_html = Grover::HTMLPreprocessor.process relative_html, 'http://my.server/', 'http'This is important because Chromium will try and resolve any relative paths via the display url host. If not provided,
the display URL defaults to http://example.com.
display_urlThere are many scenarios where specifying a different host of relative paths would be preferred. For example, your server might be behind a NAT gateway and the display URL in front of it. The display URL might be shown in the header/footer, and as such shouldn't expose details of your private network.
If you run into trouble, take a look at the debugging section below which would allow you to inspect the page content and devtools.
Grover can be configured to adjust the layout of the resulting PDF/image.
For available PDF options, see https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.pdfoptions.md
Also available are the emulate_media, cache, viewport, timeout, requestTimeout, convertTimeout
and launch_args options.
…For available PNG/JPEG options, see https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.page.screenshot.md#remarks
Note that by default the full_page option is set to false and you will get a 800x600 image. You can either specify
the image size using the clip options, or capture the entire page with full_page set to true.
For viewport options, see https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.page.setviewport.md#remarks
For launch_args options, see http://peter.sh/experiments/chromium-command-line-switches/
Launch parameter args can also be provided using a meta tag:
For timezone IDs see ICUs metaZones.txt.
Passing nil disables timezone emulation.
The vision_deficiency option can be passed one of achromatopsia, deuteranopia, protanopia, tritanopia,
blurredVision or none.
The focus option takes a CSS selector and will focus on the first matching element after rendering is complete
(including waiting for the specified wait_for_selector).
The hover option takes a CSS selector and will hover on the first matching element after rendering is complete
(including waiting for the specified wait_for_selector).
For wait_until option, default for URLs is networkidle2 and for HTML content networkidle0.
For available options see https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.page.goto.md#remarks
The wait_for_selector option can also be used to wait until an element appears on the page. Additional waiting parameters can be set with the wait_for_selector_options options hash. For available options, see: https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.page.waitforselector.md#remarks.
The wait_for_function option can be used to wait until a specific function returns a truthy value. Additional parameters can be set with the wait_for_function_options options hash. For available options, see: https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.page.waitforfunction.md#remarks
The wait_for_timeout option can also be used to wait the specified number of milliseconds have elapsed.
The raise_on_request_failure option, when enabled, will raise a Grover::JavaScript::RequestFailedError
if the initial content request or any subsequent asset request returns a bad response or times out.
The raise_on_js_error option, when enabled, will raise a Grover::JavaScript::PageRenderError if any uncaught
Javascript errors occur when trying to render the page.
The javascript_enabled option can be used to disable JavaScript. See https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.page.setjavascriptenabled.md
The Chrome/Chromium executable path can be overridden with the executable_path option.
Supplementary JavaScript can be executed on the page (after render and before conversion to PDF/image)
by passing it to the execute_script option.
Grover.new(, execute_script: 'document.getElementsByTagName("footer")[0].innerText = "Hey"').to_pdfYou can also evaluate JavaScript on the page before any of its scripts is run, by passing it a string to the evaluate_on_new_document option. See https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.page.evaluateonnewdocument.md
Grover.new(, evaluate_on_new_document: 'window.someConfig = "some value"').to_pdfFor requesting a page with basic authentication, username and password options can be provided. Note that this
only really makes sense if you're calling Grover directly (and not via middleware).
Grover.new('', browser: 'firefox').to_pdfBy default, Grover launches a local Chromium instance. You can connect to a remote/external
Chromium with the browser_ws_endpoint options.
For example, to connect to a chrome instance started with docker using docker run -p 3000:3000 ghcr.io/browserless/chrome:latest:
grover = Grover.new("https://mysite.com/path/to/thing", browser_ws_endpoint: "ws://localhost:3000/chrome")
File.open("grover.png", "wb") { |f| f
Heading
', style_tag_options: style_tag_options).to_pdfTo add script tags, pass an array of script tag options as such See documentation for more details page.addScriptTag (old documentation is available here).
script_tag_options = [
{ url: 'http://example.com/script.js' },
{ path: 'script.js' },
{ content: 'document.querySelector("h1").style.display = "none"' }
]
Grover.new('
Heading
', script_tag_options: script_tag_options).to_pdfIf you want to have the header or footer display the page URL, Grover requires that this is passed through via the
display_url option. This is because the page URL is not available in the raw HTML!
For Rack middleware conversions, the original request URL (without the .pdf extension) will be passed through and
assigned to display_url for you. You can of course override this by using a meta tag in the downstream HTML response.
For raw HTML conversions, if the display_url is not provided http://example.com will be used as the default.
Should be valid HTML markup with following classes used to inject printing values into them:
date formatted print datetitle document titleurl document locationpageNumber current page numbertotalPages total pages in the documentIn respective controller's action use:
respond_to do |format|
format.html do
response.headers['content-disposition'] = %(attachment; filename="lorem_ipsum.pdf")
render layout: 'pdf'
end
endThe node_env_vars configuration option enables you to set custom environment variables for the spawned node process. For example you might need to disable jemalloc in some environments (https://github.com/Studiosity/grover/issues/80).
# config/initializers/grover.rb
Grover.configure do |config|
config.node_env_vars = { "LD_PRELOAD" => "" }
endIf you are using the Yarn PnP strategy, you can override the run JS runtime for grover:
Grover.configure do |config|
config.js_runtime_bin = ['yarn', 'node']
endGrover comes with a middleware that allows users to get a PDF, PNG or JPEG view of any page on your site by appending .pdf, .png or .jpeg/.jpg to the URL.
Non-Rails Rack apps
# in config.ru
require 'grover'
use Grover::MiddlewareRails apps
# in application.rb
require 'grover'
config.middleware.use Grover::MiddlewareN.B. by default PNG and JPEG are not modified in the middleware to prevent breaking standard behaviours. To enable them, there are configuration options for each image type as well as an option to disable the PDF middleware (on by default).
If either of the image handling middleware options are enabled, the ignore_path and/or ignore_request should also be configured, otherwise assets are likely to be handled which would likely result in 404 responses.
# config/initializers/grover.rb
Grover.configure do |config|
config.use_png_middleware = true
config.use_jpeg_middleware = true
config.use_pdf_middleware = false
endThe root_url option can be specified either when configuring the middleware or as a global option. This is needed
when running the Grover middleware behind a URL rewriting proxy or within a containerised system.
As a middleware option:
# in application.rb
require 'grover'
config.middleware.use Grover::Middleware, root_url: 'https://my.external.domain'or as a global option:
# config/initializers/grover.rb
Grover.configure do |config|
config.root_url = 'https://my.external.domain'
endThe ignore_path configuration option can be used to tell Grover's middleware whether it should handle/modify
the response. There are three ways to set up the ignore_path:
String which matches the start of the request path.Regexp which could match any part of the request path.Proc which accepts the request path as a parameter.# config/initializers/grover.rb
Grover.configure do |config|
# assigning a String
config.ignore_path = '/assets/'
# matches `www.example.com/assets/foo.png` and not `www.example.com/bar/assets/foo.png`
# assigning a Regexp
config.ignore_path = /my\/path/
# matches `www.example.com/foo/my/path/bar.png`
# assigning a Proc
config.ignore_path = ->(path) do
/\A\/foo\/.+\/[0-9]+\.png\z/.match path
end
# matches `www.example.com/foo/bar/123.png`
endThe ignore_request configuration option can be used to tell Grover's middleware whether it should handle/modify
the response. It should be set with a Proc which accepts the request (Rack::Request) as a parameter.
# config/initializers/grover.rb
Grover.configure do |config|
# assigning a Proc
config.ignore_request = ->(req) do
req.host == 'www.example.com'
end
# matches `www.example.com/foo/bar/123.png`
config.ignore_request = ->(req) do
req.has_header?('X-BLOCK')
end
# matches `HTTP Header X-BLOCK`
endNo open issues yet, or sync has not completed.