Skip to content

Repository files navigation

PlutoPrint Ruby

Ruby bindings for the PlutoBook rendering engine. Convert HTML, XML, SVG, and images into high-quality PDFs and PNG images — without a browser.

PlutoPrint Ruby is a native C extension. No headless Chrome, no Node.js, no subprocess overhead.

Note: PlutoPrint Ruby (plutoprint-ruby) is maintained by Ajaya Agrawalla / ClearStack Inc and is not affiliated with or endorsed by the PlutoBook project.

Installation

Prerequisites: The PlutoBook C library must be installed on your system.

# macOS (Homebrew)
brew install plutobook
# Ubuntu/Debian
sudo apt-get install libplutobook-dev

Then add the gem:

gem"plutoprint-ruby"

Or install directly:

gem install plutoprint-ruby

If PlutoBook is installed in a non-standard location:

gem install plutoprint-ruby -- --with-plutobook-dir=/path/to/plutobook

Quick Start

Plain Ruby

require"plutoprint"# HTML string to PDF filePlutoprint.html_to_pdf("<h1>Hello World</h1>","output.pdf")# HTML string to PNG filePlutoprint.html_to_png("<h1>Hello World</h1>","output.png")# URL to PDFPlutoprint.url_to_pdf("https://example.com","output.pdf")# Output to a stream (StringIO, File, etc.)io=StringIO.newPlutoprint.html_to_pdf("<h1>Hello</h1>",io)pdf_bytes=io.string

With options

Plutoprint.html_to_pdf("<h1>Report</h1>","report.pdf",size: :letter,margins: :wide,media: :print,user_style: "body { font-family: serif; }",title: "My Report",author: "Jane Doe")

Configuration

Use Plutoprint.configure to set project-wide defaults. This works in any Ruby application — Rails, Sinatra, plain scripts.

Plutoprint.configuredo |config|
config.use_pdf_middleware=true# Enable .pdf URL interception (default: true)config.use_png_middleware=false# Enable .png URL interception (default: false)config.root_url=nil# Override base URL for asset resolutionconfig.ignore_path=nil# String, Regexp, or Proc to skip pathsconfig.ignore_request=nil# Proc receiving Rack::Request to skip requestsconfig.options={size: :letter,margins: {top: "0.75in",right: "0.75in",bottom: "0.75in",left: "0.75in"},media: :screen,user_style: "html { zoom: 0.8; }"}end

Options reference

OptionTypeDefaultDescription
sizeSymbol:a4Page size: :a3, :a4, :a5, :b4, :b5, :letter, :legal, :ledger
marginsSymbol or Hash:normalPreset (:none, :narrow, :normal, :moderate, :wide) or hash with sides
mediaSymbol:printCSS media type: :print or :screen
user_styleStringnilCSS injected before rendering
user_scriptStringnilJavaScript passed to PlutoBook

Margin hash accepts unit strings ("0.75in", "19mm", "54pt", "2cm", "100px") or numeric point values:

{top: "0.75in",right: "19mm",bottom: "54pt",left: "2cm"}

Three-tier configuration

Options merge in order — each tier overrides the previous:

  1. Gem defaults:a4, :normal margins, :print media
  2. Initializer — your Plutoprint.configure block
  3. Per-request — controller-level overrides via Plutoprint.set_options

Deep merge is used for nested hashes like margins, so overriding one side preserves the others.

Rack Middleware

For Sinatra, Hanami, Roda, or any Rack app, add the middleware explicitly:

require"plutoprint"require"plutoprint/rack/middleware"usePlutoprint::Rack::Middleware

Any request ending in .pdf is intercepted: the extension is stripped, the HTML response is rendered normally, then converted to PDF and returned with Content-Type: application/pdf.

The same works for .png when use_png_middleware is enabled.

Ignoring paths

Plutoprint.configuredo |config|
# Skip paths starting with /adminconfig.ignore_path="/admin"# Skip paths matching a patternconfig.ignore_path=/\/api\//# Skip based on the full requestconfig.ignore_request=->(request){request.params["skip_pdf"]}end

Rails Integration

Add the gem to your Gemfile:

gem"plutoprint-ruby"

Create an initializer at config/initializers/plutoprint.rb:

Plutoprint.configuredo |config|
config.use_pdf_middleware=trueconfig.options={size: :letter,margins: {top: "0.75in",right: "0.75in",bottom: "0.75in",left: "0.75in"},media: :screen,user_style: "html { zoom: 0.8; }"}end

That's it. The Railtie auto-registers the middleware. No config.middleware.use needed.

Detecting PDF mode in controllers

The middleware sets request.env["plutoprint.middleware"] to true when a .pdf request is being processed:

classReportsController < ApplicationControllerdefshow@pdf=request.env["plutoprint.middleware"]renderlayout: "print"if@pdfendend

Per-controller options

Override options for specific actions without affecting the global config:

classReportsController < ApplicationControllerbefore_action:set_landscape_pdf,only: [:wide_report]privatedefset_landscape_pdfPlutoprint.set_options(request,size: :a4,margins: {top: "0.5in"})endend

This is thread-safe — options are stored in the request env (per-request, per-thread).

Migrating from Grover

PlutoPrint can replace Grover as your PDF engine. Key differences:

AspectGroverPlutoPrint
EngineHeadless Chrome (Puppeteer)PlutoBook (native C)
JS executionFull browser JSLimited
CSS supportFull Chrome CSSCSS 2.1 + some CSS 3
MemoryHeavy (Chrome process)Lightweight
StartupSlow (browser launch)Instant
DependenciesNode.js + PuppeteerPlutoBook C library

Migration steps

  1. Replace the gem in your Gemfile:
# Remove:gem"grover"# Add:gem"plutoprint-ruby"
  1. Replace the initializer:
# config/initializers/grover.rb → config/initializers/plutoprint.rb# Before (Grover):Grover.configuredo |config|
config.use_pdf_middleware=trueconfig.options={margin: {top: "0.75in",right: "0.75in",bottom: "0.75in",left: "0.75in"},scale: 0.8,emulate_media: "screen"}end# After (PlutoPrint):Plutoprint.configuredo |config|
config.use_pdf_middleware=trueconfig.options={margins: {top: "0.75in",right: "0.75in",bottom: "0.75in",left: "0.75in"},media: :screen,user_style: "html { zoom: 0.8; }"}end
  1. Remove require 'grover' from config/application.rb (PlutoPrint's Railtie handles everything).

  2. Remove any manual config.middleware.use Grover::Middleware lines.

  3. Update controller PDF detection:

# Before:@pdf=request.env["Rack-Middleware-Grover"]# After:@pdf=request.env["plutoprint.middleware"]
  1. Guard any window.print() JavaScript in print layouts — PlutoPrint doesn't execute JavaScript like Chrome does:
<% unless @pdf %><script>window.print();</script><%end%>

What to watch for

  • CSS rendering: PlutoPrint supports CSS 2.1 with some CSS 3. Complex flexbox/grid layouts may render differently. Simple table-based print layouts work well.
  • JavaScript: PlutoPrint has limited JS support compared to Chrome. Guard browser-specific JS behind unless @pdf.
  • Scale: Grover's scale: 0.8 maps to PlutoPrint's user_style: "html { zoom: 0.8; }".
  • Media type: Grover's emulate_media: "screen" maps to PlutoPrint's media: :screen.

CLI

PlutoPrint includes a command-line tool:

# HTML file to PDF
plutoprint convert input.html output.pdf
# With options
plutoprint convert input.html output.pdf --size letter --margins wide
# Custom margins
plutoprint convert input.html output.pdf --margin-top 0.75in --margin-right 0.5in
# To PNG
plutoprint convert input.html output.png --width 800 --height 600
# With custom CSS
plutoprint convert input.html output.pdf --user-style "body { color: navy; }"# From URL
plutoprint convert https://example.com output.pdf
# Version info
plutoprint version
plutoprint info

API Reference

Convenience methods

Plutoprint.html_to_pdf(html,output,size:,margins:,media:,user_style:,user_script:, **metadata)Plutoprint.html_to_png(html,output,size:,margins:,media:,width:,height:,user_style:,user_script:)Plutoprint.url_to_pdf(url,output,size:,margins:,media:,user_style:,user_script:, **metadata)Plutoprint.url_to_png(url,output,size:,margins:,media:,width:,height:,user_style:,user_script:)

output can be a file path (String) or any IO-like object (StringIO, File).

Book (advanced)

book=Plutoprint::Book.new(size: :letter,margins: :normal,media: :print)book.load_html(html,user_style,user_script,base_url)book.load_url(url,user_style,user_script)book.page_countbook.set_metadata(:title,"My Document")book.set_metadata(:author,"Jane Doe")book.write_to_pdf("output.pdf")book.write_to_pdf_stream(io)book.write_to_png("output.png",width,height)book.write_to_png_stream(io,width,height)

Page sizes

:none, :a3, :a4, :a5, :b4, :b5, :letter, :legal, :ledger

Margin presets

PresetTopRightBottomLeft
:none0000
:narrow0.5in0.5in0.5in0.5in
:normal1in1in1in1in
:moderate1in0.75in1in0.75in
:wide1in2in1in2in

PDF metadata

book.set_metadata(:title,"Document Title")book.set_metadata(:author,"Author Name")book.set_metadata(:subject,"Subject")book.set_metadata(:keywords,"ruby, pdf")book.set_metadata(:creator,"PlutoPrint Ruby")book.set_metadata(:creation_date,Time.now.iso8601)book.set_metadata(:modification_date,Time.now.iso8601)

Development

# Install dependencies
bundle install
# Compile the C extension
bundle exec rake compile
# Run tests
bundle exec rspec
# Run linter
bundle exec standardrb
# Run everything (compile + test + lint)
bundle exec rake

Related Projects

  • plutoprint (Python) — Python bindings for PlutoBook, the inspiration for this Ruby gem's API design
  • Grover — HTML-to-PDF via headless Chrome; inspired this gem's configuration pattern, Rack middleware, and Rails integration approach

License

MIT License. See LICENSE for details.

About

Ruby bindings for PlutoBook — convert HTML, XML, SVG to PDF/PNG without a browser. Includes Rack middleware and Rails integration.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages