Skip to content

WizardComputer/page_print

Repository files navigation

PagePrint

page_print renders HTML strings to PDF files from Ruby using the plutobook library through a native C extension.

It exists as a faster, simpler alternative to PDFKit and other wkhtmltopdf-based gems. There is no external renderer process to shell out to, and the public Ruby API is intentionally small.

Installation

Add PagePrint to your Gemfile:

gem "page_print", "~> 0.1.3"

Or install from RubyGems directly:

gem install page_print

Native gems are published for x86_64-linux and arm64-darwin. Other platforms build from source and require PlutoBook development headers and library files.

Rails Usage

PagePrint is optimized for Rails applications using Propshaft. In Rails, PagePrint installs a default Propshaft-backed resource fetcher and uses the current request URL as the default base_url.

Render a PDF from a controller:

class PrintsController < ApplicationController
  def pdf
    html = render_to_string(template: "prints/pdf", formats: [:html], layout: "pdf")
    pdf = PagePrint.html_to_pdf_string(
      html,
      page_size: :a4,
      margins: :normal,
      media: :print,
      metadata: { title: "Print PDF", author: "PagePrint" }
    )

    send_data pdf, filename: "print.pdf", type: "application/pdf", disposition: "inline"
  end
end

Use normal Rails asset helpers in the PDF template or layout:

<%= stylesheet_link_tag "pdf" %>
<%= image_tag "logo.png" %>

During controller actions, PagePrint defaults base_url to request.base_url. The default Rails resource fetcher resolves /assets/... through Propshaft or public/assets, avoiding HTTP requests back to the Rails app.

You can still override base_url explicitly:

pdf = PagePrint.html_to_pdf_string(html, base_url: "https://example.com")

Override the fetcher only when needed:

# config/initializers/page_print.rb
PagePrint.configure do |config|
  config.resource_fetcher = MyResourceFetcher.new
end

Ruby Usage

require "page_print"
require "tmpdir"

html = <<~HTML
  <html>
    <body>
      <h1>Hello</h1>
      <p>This PDF was generated by PagePrint.</p>
    </body>
  </html>
HTML

output_path = File.join(Dir.tmpdir, "page_print-output.pdf")

PagePrint.html_to_pdf(html, output_path, base_url: "https://example.com")

To get the generated PDF as a binary string instead of writing directly to a file:

pdf = PagePrint.html_to_pdf_string(html, base_url: "https://example.com")

Use custom page dimensions and margins when a preset is not enough:

pdf = PagePrint.html_to_pdf_string(
  html,
  page_size: { width: 100, height: 150, unit: :mm },
  margins: { top: 5, right: 6, bottom: 7, left: 8, unit: :mm }
)

You can configure default options once, for example to provide a resource fetcher used by all renders:

PagePrint.configure do |config|
  config.resource_fetcher = lambda do |url|
    next unless url == "asset:pdf.css"

    { content: "body { font-family: sans-serif; }", mime_type: "text/css" }
  end
end

Options

Supported keyword options:

  • base_url: string used to resolve relative URLs in the HTML
  • page_size: one of :a3, :a4, :a5, :b4, :b5, :letter, :legal, :ledger, or { width:, height:, unit: }
  • margins: one of :none, :normal, :narrow, :moderate, :wide, or { top:, right:, bottom:, left:, unit: }
  • media: one of :print, :screen
  • resource_fetcher: callable that receives a URL and returns nil or { content:, mime_type:, text_encoding: nil }
  • metadata: hash with :title, :author, :subject, :keywords, :creation_date, or :modification_date

metadata[:creation_date] and metadata[:modification_date] should be ISO-8601 strings, for example 2026-05-10T12:00:00Z.

Custom dimensions and margins require unit:. Supported units are :pt, :pc, :in, :cm, :mm, and :px.

Benchmarking

RUNS=30 WARMUPS=3 bundle exec ruby benchmark/pdf_renderers.rb

Measured on 2026-05-30 with Ruby 3.4.7 on Apple Silicon:

Renderer Avg wall P95 wall Avg CPU Avg peak RSS Avg PDF
page_print 78.2ms 89.6ms 93.4ms 42.1MB 33.0KB
PDFKit 782.2ms 2127.1ms 824.8ms 59.4MB 27.9KB

For options and CSV output, see benchmark/README.md.

Requirements

  • Ruby 3.0+
  • Native gems are published for x86_64-linux and arm64-darwin.
  • Native gems vendor PlutoBook but compile the small Ruby extension during install so it links against your local Ruby.
  • Source builds on unsupported platforms require PlutoBook development headers and library files.

Supported platforms:

Platform Install Type
x86_64-linux Vendored PlutoBook, local Ruby extension build
arm64-darwin Vendored PlutoBook, local Ruby extension build
Other platforms Source build

Source build requirements on macOS with Homebrew:

brew install plutobook pkg-config

If pkg-config cannot find plutobook, install the gem with explicit include and library paths:

gem install page_print -- --with-plutobook-include=/path/to/include --with-plutobook-lib=/path/to/lib

Native gems bundle PlutoBook and required non-system shared libraries. Optional PlutoBook features for curl, TurboJPEG, and WebP are disabled in native gems to keep the bundled dependency set smaller.

Notes

  • The extension supports writing to a file path with html_to_pdf or returning PDF bytes with html_to_pdf_string.
  • JavaScript execution is intentionally unsupported.
  • Native gems disable PlutoBook's optional curl, TurboJPEG, and WebP features.

Development

Install development dependencies:

bundle install

Compile the native extension into lib/page_print:

bundle exec rake compile

Open an interactive Ruby session against the local checkout:

bundle exec irb -Ilib

Then load the gem from the repo and try it:

require "page_print"
require "tmpdir"

output_path = File.join(Dir.tmpdir, "page_print-output.pdf")

PagePrint.html_to_pdf("<html><body><h1>Hello</h1></body></html>", output_path, page_size: :letter, margins: :narrow, media: :screen)

You can also do a quick one-shot smoke test from the shell:

bundle exec ruby -Ilib -e 'require "tmpdir"; require "page_print"; output_path = File.join(Dir.tmpdir, "page_print-output.pdf"); p PagePrint.html_to_pdf("<html><body><h1>Hello</h1></body></html>", output_path, page_size: :letter, margins: :narrow, media: :screen)'

Or use the development console, which compiles the native extension first and then starts IRB with PagePrint loaded:

bin/console

Running Tests

Run the test suite only:

bundle exec rake test

Or run the default rake task, which compiles the extension and then runs tests:

bundle exec rake

Local Build

Build and install locally:

gem build page_print.gemspec
gem install ./page_print-*.gem

Building Native Gems

Build an x86_64-linux platform gem with a vendored PlutoBook library:

bundle exec rake package:linux

Build an Apple Silicon macOS platform gem with a vendored PlutoBook library:

bundle exec rake package:darwin_arm64

These tasks check out PlutoBook v0.17.0, build it into lib/page_print/vendor/<platform>, and write a platform gem to pkg/. Platform gems compile the Ruby extension during gem install to avoid tying the gem to the build machine's Ruby version.

Native gems bundle PlutoBook and its non-system shared library dependencies. Optional PlutoBook features for curl, TurboJPEG, and WebP are disabled to keep the bundled dependency set smaller.

The packaging tasks expect PlutoBook's build dependencies to be installed on the build machine, including Meson, Ninja, pkg-config, Cairo, FreeType, HarfBuzz, Fontconfig, Expat, and ICU.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors