This package is a Ruby client for Gotenberg, a developer-friendly API to interact with powerful tools like Chromium and LibreOffice for converting numerous document formats (HTML, Markdown, Word, Excel, etc.) into PDF files, and more!
This packages requires Gotenberg, a Docker-powered stateless API for PDF files:
gem "gotenberg-ruby"Run microservice with docker-compose:
version: "3"
services:
gotenberg:
image: gotenberg/gotenberg:8
restart: always
ports:
- 3000:3000
After having created the HTTP request (see below), you have two options:
- Get the response from the API and handle it according to your need.
- Save the resulting file to a given directory.
In the following examples, we assume the Gotenberg API is available at http://localhost:3000.
The Chromium module interacts with the Chromium browser to convert HTML documents to PDF.
See https://gotenberg.dev/docs/routes#url-into-pdf-route.
Converting a target URL to PDF is as simple as:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.url 'https://my.url'
end# generate pdf output binary data or raise method exception
pdf = document.to_binary
# safe check if pdf generate is success
success = document.success?
# fetch exception data
error_message = document.exception.message
# save PDF file
File.open('filename.pdf', 'wb') do |file|
file << document.to_binary
endPass additional headers for faraday client instance (Authorization, User-Agent, etc.):
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL'], headers: { 'Authorization': 'Bearer token123' }) do |doc|
doc.url 'https://my.url'
endAvailable exceptions:
# raise while PDF transform failed
Gotenberg::TransformError
# raise while change PDF metadata failed
Gotenberg::ModifyMetadataError
# raise while loading asset source failed
Gotenberg::RemoteSourceErrorYou may inject <link> and <script> HTML elements thanks to the extra_link_tags and extra_script_tags arguments:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.url 'https://my.url', ['https://my.css'], ['https://my.js']
endPlease note that Gotenberg will add the <link> and <script> elements based on the order of the arguments.
For rails apps gem provide few helpful helpers for easier access to assets inside your rails app:
# read from assets pipeline or webpacker
gotenberg_image_tag 'logo.svg'
# read from absolute file path (use with carefully for security reasons)
gotenberg_image_tag 'app/assets/images/logo.svg', absolute_path: true
# also you can encode you source as base64 data resource (useful for header and footer)
gotenberg_image_tag 'app/assets/images/logo.svg', absolute_path: true, inline: true
# same methods available for js
gotenberg_javascript_tag 'application.js', inline: true
# ... and css.
gotenberg_stylesheet_tag 'application.css', inline: true# skip nested resources auto extracting
gotenberg_stylesheet_tag 'application.css', inline: true, skip_analyze: trueSee See https://gotenberg.dev/docs/routes#url-into-pdf-route.
Prepare HTML content with build-in Rails methods:
# declare HTML renderer
renderer = ApplicationController.renderer.new(https: true, http_host: 'localhost:3000')
# render HTML string for passing into service
index_html = renderer.render 'pdf/document', layout: 'pdf', locals: {}You may convert an HTML document string with:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
endYou may also send additional files, like images, fonts, stylesheets, and so on. The only requirement is that their paths in the HTML DOM are on the root level.
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.assets '/path/to/my.css', '/path/to/my.js', ['<binary string>', 'my.png']
endIf you want to use this feature, you need to install additional package to host system:
sudo apt install exiftool
and now you can change PDF metatags using exiftools:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.meta title: 'Custom PDF title'
endNote: for Rails based apps, you can setup <title>Custom PDF title</title> header in index.html and it will be automatically added to output PDF.
Gotenberg.configure do |config|
# activate HTML debug mode
config.html_debug = false
# default temporary directory for output
config.backtrace_dir = Rails.root.join('tmp', 'gotenberg')
endSee https://gotenberg.dev/docs/routes#markdown-files-into-pdf-route.
You may convert markdown files with:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.markdown wrapper_html, '/path/to/file.md'
endOr with raw input:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.markdown wrapper_html, ['<binary data>', 'file.md']
endThe first argument is a Stream with HTML content, for instance:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My PDF</title>
</head>
<body>
{{ toHTML "file.md" }}
</body>
</html>Here, there is a Go template function toHTML. Gotenberg will use it to convert a markdown file's content to HTML.
Like the HTML conversion, you may also send additional files, like images, fonts, stylesheets, and so on. The only requirement is that their paths in the HTML DOM are on the root level.
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.markdown wrapper_html, '/path/to/file.md'
doc.assets '/path/to/my.css', '/path/to/my.js', '/path/to/my2.md', ['<binary data>', 'file_2.md']
endYou may override the default paper size with:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.paper_size width, height
endExamples of paper size (width x height, in inches):
Letter- 8.5 x 11 (default)Legal- 8.5 x 14Tabloid- 11 x 17Ledger- 17 x 11A0- 33.1 x 46.8A1- 23.4 x 33.1A2- 16.54 x 23.4A3- 11.7 x 16.54A4- 8.27 x 11.7A5- 5.83 x 8.27A6- 4.13 x 5.83
You may override the default margins (i.e., 0.39, in inches):
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.margins top: 1, bottom: 1, left: 0.39, right: 0.39
endYou may force page size as defined by CSS:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.prefer_css_page_size
enddocument = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.print_background
endYou may override the default portrait orientation with:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.landscape
endYou may override the default scale of the page rendering (i.e., 1.0) with:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.scale 2
endYou may set the page ranges to print, e.g., 1-5, 8, 11-13. Empty means all pages.
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.native_page_ranges '1-2'
endYou may add a header and/or a footer to each page of the PDF:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.header header_html
doc.html index_html
doc.footer footer_html
doc.margins top: 1, bottom: 1
endEach of them has to be a complete HTML document:
<html>
<head>
<style>
body {
font-size: 8rem;
margin: 4rem auto;
}
</style>
</head>
<body>
<p><span class="pageNumber"></span> of <span class="totalPages"></span></p>
</body>
</html>The following classes allow you to inject printing values:
date- formatted print date.title- document title.url- document location.pageNumber- current page number.totalPages- total pages in the document.
- Margins top and bottom are large enough (i.e.,
margins(top: 1, bottom: 1, left: 0.39, right: 0.39)) - The font size is big enough.
- No JavaScript.
- The CSS properties are independent of the ones from the HTML document.
- The footer CSS properties override the ones from the header;
- Only fonts installed in the Docker image are loaded - see the Fonts chapter.
- Images only work using a base64 encoded source - i.e.,
data:image/png;base64, iVBORw0K.... background-colorand colorCSSproperties require an additional-webkit-print-color-adjust: exactCSS property in order to work.- Assets are not loaded (i.e., CSS files, scripts, fonts, etc.).
When the page relies on JavaScript for rendering, and you don't have access to the page's code, you may want to wait a
certain amount of time (i.e., 1s, 2ms, etc.) to make sure Chromium has fully rendered the page you're trying to generate.
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.wait_delay '3s'
endYou may also wait until a given JavaScript expression returns true:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.wait_for_expression "window.status === 'ready'"
endYou may override the default User-Agent header used by Gotenberg:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.html index_html
doc.user_agent("Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0")
endYou may add HTTP headers that Chromium will send when loading the HTML document:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.url 'https://my.url'
doc.extra_http_headers({'My-Header-1' => 'My value', 'My-Header-2' => 'My value'})
endThis method just duplicate way of passing headers into faraday client. Have same purpose:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.url 'https://my.url'
doc.client_extra_http_headers({ 'Authorization': 'Bearer token123' })
endYou may force Gotenberg to return a 409 Conflict response if there are exceptions in the Chromium console:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.url 'https://my.url'
doc.fail_on_console_exceptions
endSome websites have dedicated CSS rules for print. Using screen allows you to force the "standard" CSS rules.
You may also force the print media type:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.url 'https://my.url'
doc.emulate_media_type 'screen'
endSee https://gotenberg.dev/docs/routes#pdfa-chromium.
You may set the PDF format of the resulting PDF with:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.url 'https://my.url'
doc.pdf_format 'PDF/A-1a'
endThe LibreOffice module interacts with LibreOffice to convert documents to PDF, thanks to unoconv.
See https://gotenberg.dev/docs/routes#office-documents-into-pdfs-route.
Converting a document to PDF is as simple as:
document = Gotenberg::Libreoffice.call(ENV['GOTENBERG_URL']) do |doc|
doc.convert '/path/to/my.docx'
endIf you send many documents, Gotenberg will return a ZIP archive with the PDFs:
document = Gotenberg::Libreoffice.call(ENV['GOTENBERG_URL']) do |doc|
doc.convert '/path/to/my.docx', '/path/to/my.xlsx', ['<binary data>', 'some.odt']
end
# will return binary data with zip archive content
File.open('archive.zip', 'wb') do |file|
file << document.to_binary
endYou may also merge them into one unique PDF:
document = Gotenberg::Libreoffice.call(ENV['GOTENBERG_URL']) do |doc|
doc.merge
doc.convert '/path/to/my.docx', '/path/to/my.xlsx', ['<binary data>', 'some.odt']
endPlease note that the merging order is determined by the order of the arguments.
You may override the default portrait orientation with:
document = Gotenberg::Libreoffice.call(ENV['GOTENBERG_URL']) do |doc|
doc.landscape
doc.convert '/path/to/my.docx'
endYou may set the page ranges to print, e.g., 1-4. Empty means all pages.
document = Gotenberg::Libreoffice.call(ENV['GOTENBERG_URL']) do |doc|
doc.native_page_ranges '1-2'
doc.convert '/path/to/my.docx'
endSee https://gotenberg.dev/docs/routes#pdfa-libreoffice.
You may set the PDF format of the resulting PDF(s) with:
document = Gotenberg::Libreoffice.call(ENV['GOTENBERG_URL']) do |doc|
doc.pdf_format 'PDF/A-1a'
doc.convert '/path/to/my.docx'
endYou may also explicitly tell Gotenberg to use unoconv to convert the resulting PDF(s) to a PDF format:
document = Gotenberg::Libreoffice.call(ENV['GOTENBERG_URL']) do |doc|
doc.native_pdf_format 'PDF/A-1a'
doc.convert '/path/to/my.docx'
end400 Bad Request response.
The PDF Engines module gathers all engines that can manipulate PDF files.
See https://gotenberg.dev/docs/routes#merge-pdfs-route.
Merging PDFs is as simple as:
document = Gotenberg::PdfEngines.call(ENV['GOTENBERG_URL']) do |doc|
doc.merge '/path/to/my.pdf', '/path/to/my2.pdf', ['<binary data>', 'some.pdf']
endPlease note that the merging order is determined by the order of the arguments.
You may also set the PDF format of the resulting PDF(s) with:
document = Gotenberg::PdfEngines.call(ENV['GOTENBERG_URL']) do |doc|
doc.pdf_format 'PDF/A-1a'
doc.merge '/path/to/my.pdf', '/path/to/my2.pdf', '/path/to/my3.pdf'
endSee https://gotenberg.dev/docs/routes#convert-into-pdfa--pdfua-route.
You may convert a PDF to a specific PDF format with:
document = Gotenberg::PdfEngines.call(ENV['GOTENBERG_URL']) do |doc|
doc.convert '/path/to/my.pdf', format: 'PDF/A-1a'
endIf you send many PDFs, Gotenberg will return a ZIP archive with the PDFs:
document = Gotenberg::PdfEngines.call(ENV['GOTENBERG_URL']) do |doc|
doc.convert '/path/to/my.pdf', '/path/to/my2.pdf', '/path/to/my3.pdf', ['<binary data>', 'some.pdf'], format: 'PDF/A-1a'
end
# will return binary data with zip archive content
File.open('archive.zip', 'wb') do |file|
file << document.to_binary
endThe Webhook module is a Gotenberg middleware that sends the API responses to callbacks.
document.to_binary method if you're using the webhook feature.
For instance:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.url 'https://my.url'
doc.webhook 'https://my.webhook.url', 'https://my.webhook.error.url'
endYou may also override the default HTTP method (POST) that Gotenberg will use to call the webhooks:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.url 'https://my.url'
doc.webhook_method('PATCH')
doc.webhook_error_method('PUT')
doc.webhook 'https://my.webhook.url', 'https://my.webhook.error.url'
endYou may also tell Gotenberg to add extra HTTP headers that it will send alongside the request to the webhooks:
document = Gotenberg::Chromium.call(ENV['GOTENBERG_URL']) do |doc|
doc.url 'https://my.url'
doc.webhook_extra_http_headers({'My-Header-1' => 'My value', 'My-Header-2' => 'My value'})
doc.webhook 'https://my.webhook.url', 'https://my.webhook.error.url'
endGem also proxify (expert mode) access to mini_exiftools througth Gotenberg::Exiftools class. You can change PDF metadata manually:
binary = Gotenberg::Exiftools.modify(pdf_binary, { title: 'Document 1' })
# save PDF file
File.open('filename.pdf', 'wb') do |file|
file << binary
end