page_print 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4756973315a1710c09f8fb293da5057e5a16690d128a55e63b1e36dbec037ea6
4
- data.tar.gz: e267eadccf8a7dcc8efb43f90d968fc016dafb43e9ff2610cbd0258c3340199b
3
+ metadata.gz: ca32ba67da6a5c3cfe5c66b3eac1242ffc1068ccce7838820d4550fffbe9d73d
4
+ data.tar.gz: 90ad2b4d3612d123f562ebd43098ed110c2ccb608d058409c496ce76e455e25d
5
5
  SHA512:
6
- metadata.gz: 486e05765bcc2f15584c1c694468c7c1cd4fe000bb5c20e71b1ece8006068f154b287969a8b902232d2e1414b0d0807edaf0ecbb8f143195e220e9a84da770d4
7
- data.tar.gz: d58d39763a9ba7f6da46e0d0449e4be802de2f067773345a51003960775d2a0f785e98ffe145c8d8df96356421b9ed64707b018ce28ce36f2dc31e8bbf36bd60
6
+ metadata.gz: 68dc83310a8f0d25281d22f1d66c771c2d87a855c4802690df8ecf2e3bc74127602d405ccc4746ddf4d1495f94aa079aa3cc4668f023ca68bccec3360096b46a
7
+ data.tar.gz: 3fa636884d4c816a4aea7c3b34262a3f21c1d09e59c44cc8b0fde3c2bb145cdfbc26a5a9468c636d92057cfa8736906c671933e0b97f1b2ba1bafd183f21d3d5
data/README.md CHANGED
@@ -1,284 +1,205 @@
1
1
  # PagePrint
2
2
 
3
+ Fast, in-process HTML-to-PDF rendering for Ruby, powered by [PlutoBook](https://github.com/plutoprint/plutobook).
4
+
3
5
  > [!IMPORTANT]
4
- > PagePrint is not production-ready yet. The gem is still in active testing, especially around native packaging and platform compatibility.
6
+ > PagePrint is still pre-1.0 and is not yet recommended as a drop-in production dependency without application-specific testing. Native packaging and platform compatibility are still being validated. See the [production guide](docs/production.md) before deploying it.
5
7
 
6
- `page_print` renders HTML strings to PDF files from Ruby using the `plutobook` library through a native C extension.
8
+ PagePrint turns an HTML string into PDF bytes—or writes it directly to a file—through a small native C extension. It is designed for server-generated documents such as invoices, reports, labels, and statements where the HTML is known in advance and does not depend on JavaScript.
7
9
 
8
- 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.
9
10
 
10
- ## Installation
11
+ ## Why PagePrint?
11
12
 
12
- Add PagePrint to your Gemfile:
13
+ PDFKit and Wicked PDF render documents by launching the `wkhtmltopdf` executable. PagePrint instead embeds a purpose-built paged-media renderer in the Ruby process.
13
14
 
14
- ```ruby
15
- gem "page_print"
16
- ```
15
+ That gives PagePrint a few useful properties:
17
16
 
18
- Or install from RubyGems directly:
17
+ - **No renderer subprocess:** no executable discovery, shelling out, or process startup per render.
18
+ - **A small Ruby API:** pass HTML in and receive PDF bytes, or write directly to a path.
19
+ - **Print-oriented CSS:** PlutoBook supports paged-media features such as `@page`, page counters, and running headers and footers.
20
+ - **Controlled resource loading:** PagePrint does not fetch unresolved HTTP URLs. Your application decides which stylesheets, images, and fonts may be loaded.
21
+ - **Rails asset integration:** Propshaft and `public/assets` resources work without HTTP requests back into the application.
19
22
 
20
- ```sh
21
- gem install page_print
22
- ```
23
+ ### How does it compare with PDFKit and Wicked PDF?
23
24
 
24
- Native gems are published for `x86_64-linux` and `arm64-darwin`. Other platforms build from source and require PlutoBook development headers and library files.
25
+ PDFKit and Wicked PDF are Ruby integrations for `wkhtmltopdf`; their rendering behavior and operational requirements come from that executable.
25
26
 
26
- ## Rails Usage
27
+ | | PagePrint / PlutoBook | PDFKit or Wicked PDF / `wkhtmltopdf` |
28
+ | --- | --- | --- |
29
+ | Runtime model | Native library in the Ruby process | External `wkhtmltopdf` executable |
30
+ | Rendering engine | Purpose-built for static documents and CSS paged media | Legacy Qt WebKit browser engine |
31
+ | JavaScript | Not supported | Supported by legacy Qt WebKit |
32
+ | Network access | Denied unless your fetcher supplies the resource | Built in |
33
+ | CSS compatibility | Print-focused and partial; no CSS Grid | Limited by the old WebKit engine |
34
+ | Operational footprint | Native gem and bundled libraries on supported platforms | `wkhtmltopdf` binary plus Qt/WebKit dependencies |
35
+ | Upstream status | PagePrint currently packages PlutoBook 0.19.0 | `wkhtmltopdf` was archived in 2023 |
27
36
 
28
- 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`.
37
+ PagePrint is a strong fit when you control the templates, value predictable print layout, and do not need JavaScript. Stay with a `wkhtmltopdf`-based gem if your existing templates depend on its JavaScript or WebKit-specific rendering behavior. When migrating, compare representative output carefully because changing engines can alter pagination, fonts, and layout.
29
38
 
30
- Render a PDF from a controller:
39
+ The `wkhtmltopdf` project was [archived in 2023](https://github.com/wkhtmltopdf/wkhtmltopdf) and its own [status document](https://github.com/wkhtmltopdf/wkhtmltopdf/blob/master/docs/status.md) describes its Qt 4 / WebKit stack as unsupported and outdated. PlutoBook is purpose-built for static paged output, but has a narrower feature set; review its [feature matrix](https://github.com/plutoprint/plutobook/blob/main/FEATURES.md) before migrating a complex template.
40
+
41
+ ## Installation
42
+
43
+ Add the gem to your `Gemfile`:
31
44
 
32
45
  ```ruby
33
- class PrintsController < ApplicationController
34
- def pdf
35
- html = render_to_string(template: "prints/pdf", formats: [:html], layout: "pdf")
36
- pdf = PagePrint.html_to_pdf_string(
37
- html,
38
- page_size: :a4,
39
- margins: :normal,
40
- media: :print,
41
- metadata: { title: "Print PDF", author: "PagePrint" }
42
- )
43
-
44
- send_data pdf, filename: "print.pdf", type: "application/pdf", disposition: "inline"
45
- end
46
- end
46
+ gem "page_print"
47
47
  ```
48
48
 
49
- Use normal Rails asset helpers in the PDF template or layout:
49
+ Then run:
50
50
 
51
- ```erb
52
- <%= stylesheet_link_tag "pdf" %>
53
- <%= image_tag "logo.png" %>
51
+ ```sh
52
+ bundle install
54
53
  ```
55
54
 
56
- 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.
57
-
58
- You can still override `base_url` explicitly:
55
+ PagePrint requires Ruby 3.2 or newer. Native gems with vendored PlutoBook libraries are published for:
59
56
 
60
- ```ruby
61
- pdf = PagePrint.html_to_pdf_string(html, base_url: "https://example.com")
62
- ```
57
+ - `x86_64-linux`
58
+ - `arm64-darwin` (Apple Silicon)
63
59
 
64
- Override the fetcher only when needed:
60
+ Other platforms build from source and require PlutoBook development headers and libraries. See [Installing on other platforms](docs/production.md#installing-on-other-platforms).
65
61
 
66
- ```ruby
67
- # config/initializers/page_print.rb
68
- PagePrint.configure do |config|
69
- config.resource_fetcher = MyResourceFetcher.new
70
- end
71
- ```
62
+ ## Quick start
72
63
 
73
- ## Ruby Usage
64
+ Require PagePrint and render an HTML string:
74
65
 
75
66
  ```ruby
76
67
  require "page_print"
77
- require "tmpdir"
78
68
 
79
69
  html = <<~HTML
70
+ <!doctype html>
80
71
  <html>
72
+ <head>
73
+ <meta charset="utf-8">
74
+ <style>
75
+ @page {
76
+ size: A4;
77
+
78
+ @bottom-center {
79
+ content: counter(page) " / " counter(pages);
80
+ color: #666;
81
+ font-size: 10pt;
82
+ }
83
+ }
84
+
85
+ body { font-family: sans-serif; }
86
+ </style>
87
+ </head>
81
88
  <body>
82
- <h1>Hello</h1>
83
- <p>This PDF was generated by PagePrint.</p>
89
+ <h1>Invoice #1042</h1>
90
+ <p>Thank you for your business.</p>
84
91
  </body>
85
92
  </html>
86
93
  HTML
87
94
 
88
- output_path = File.join(Dir.tmpdir, "page_print-output.pdf")
89
-
90
- PagePrint.html_to_pdf(html, output_path, base_url: "https://example.com")
95
+ pdf = PagePrint.render(html, page_size: :a4, margins: :normal)
91
96
  ```
92
97
 
93
- To get the generated PDF as a binary string instead of writing directly to a file:
98
+ For large documents, write directly to a file instead of retaining the PDF in a Ruby string:
94
99
 
95
100
  ```ruby
96
- pdf = PagePrint.html_to_pdf_string(html, base_url: "https://example.com")
101
+ PagePrint.render_to_file(html, "invoice.pdf", page_size: :a4, margins: :normal)
97
102
  ```
98
103
 
99
- Use custom page dimensions and margins when a preset is not enough:
104
+ `render_to_file` returns `true`; `render` returns an `ASCII-8BIT` Ruby string containing the PDF.
100
105
 
101
- ```ruby
102
- pdf = PagePrint.html_to_pdf_string(
103
- html,
104
- page_size: { width: 100, height: 150, unit: :mm },
105
- margins: { top: 5, right: 6, bottom: 7, left: 8, unit: :mm }
106
- )
107
- ```
106
+ ## Rails
108
107
 
109
- You can configure default options once, for example to provide a resource fetcher used by all renders:
108
+ PagePrint integrates automatically with Rails applications that use Propshaft. Render a template to HTML, pass it to PagePrint, then send the resulting bytes:
110
109
 
111
110
  ```ruby
112
- PagePrint.configure do |config|
113
- config.resource_fetcher = lambda do |url|
114
- next unless url == "asset:pdf.css"
111
+ class InvoicesController < ApplicationController
112
+ def show
113
+ html = render_to_string(template: "invoices/show", formats: [:html], layout: "pdf")
114
+ pdf = PagePrint.render(html, page_size: :a4, margins: :normal, metadata: { title: "Invoice ##{params[:id]}", author: "Example Ltd" })
115
115
 
116
- { content: "body { font-family: sans-serif; }", mime_type: "text/css" }
116
+ send_data pdf, filename: "invoice-#{params[:id]}.pdf", type: "application/pdf", disposition: "inline"
117
117
  end
118
118
  end
119
119
  ```
120
120
 
121
- ## Options
122
-
123
- Supported keyword options:
124
-
125
- - `base_url:` string used to resolve relative URLs in the HTML
126
- - `page_size:` one of `:a3`, `:a4`, `:a5`, `:b4`, `:b5`, `:letter`, `:legal`, `:ledger`, or `{ width:, height:, unit: }`
127
- - `margins:` one of `:none`, `:normal`, `:narrow`, `:moderate`, `:wide`, or `{ top:, right:, bottom:, left:, unit: }`
128
- - `media:` one of `:print`, `:screen`
129
- - `resource_fetcher:` callable that receives a URL and returns `nil` or `{ content:, mime_type:, text_encoding: nil }`
130
- - `metadata:` hash with `:title`, `:author`, `:subject`, `:keywords`, `:creation_date`, or `:modification_date`
131
-
132
- Unresolved URLs are not fetched over the network. Provide assets through `resource_fetcher`, or return `nil` to skip a URL.
133
-
134
- `metadata[:creation_date]` and `metadata[:modification_date]` should be ISO-8601 strings, for example `2026-05-10T12:00:00Z`.
135
-
136
- Custom dimensions and margins require `unit:`. Supported units are `:pt`, `:pc`, `:in`, `:cm`, `:mm`, and `:px`.
137
-
138
- ## Benchmarking
139
-
140
- ```sh
141
- RUNS=30 WARMUPS=3 bundle exec ruby benchmark/pdf_renderers.rb
142
- ```
143
-
144
- Measured on 2026-05-30 with Ruby 3.4.7 on Apple Silicon:
145
-
146
- | Renderer | Avg wall | P95 wall | Avg CPU | Avg peak RSS | Avg PDF |
147
- | --- | ---: | ---: | ---: | ---: | ---: |
148
- | `page_print` | 78.2ms | 89.6ms | 93.4ms | 42.1MB | 33.0KB |
149
- | PDFKit | 782.2ms | 2127.1ms | 824.8ms | 59.4MB | 27.9KB |
150
-
151
- For options and CSV output, see `benchmark/README.md`.
152
-
153
- ## Requirements
154
-
155
- - Ruby 3.2+
156
- - Native gems are published for `x86_64-linux` and `arm64-darwin`.
157
- - Native gems vendor PlutoBook but compile the small Ruby extension during install so it links against your local Ruby.
158
- - Source builds on unsupported platforms require PlutoBook development headers and library files.
159
-
160
- Supported platforms:
161
-
162
- | Platform | Install Type |
163
- | --- | --- |
164
- | `x86_64-linux` | Vendored PlutoBook, local Ruby extension build |
165
- | `arm64-darwin` | Vendored PlutoBook, local Ruby extension build |
166
- | Other platforms | Source build |
167
-
168
- Source build requirements on macOS with Homebrew:
169
-
170
- ```sh
171
- brew install plutobook pkg-config
172
- ```
173
-
174
- If `pkg-config` cannot find `plutobook`, install the gem with explicit include and library paths:
175
-
176
- ```sh
177
- gem install page_print -- --with-plutobook-include=/path/to/include --with-plutobook-lib=/path/to/lib
178
- ```
179
-
180
- 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.
181
-
182
- ## Notes
183
-
184
- - The extension supports writing to a file path with `html_to_pdf` or returning PDF bytes with `html_to_pdf_string`.
185
- - JavaScript execution is intentionally unsupported.
186
- - Native gems disable PlutoBook's optional curl, TurboJPEG, and WebP features.
121
+ Normal asset helpers can be used in the PDF template:
187
122
 
188
- ## Development
189
-
190
- Install development dependencies:
191
-
192
- ```sh
193
- bundle install
194
- ```
195
-
196
- Compile the native extension into `lib/page_print`:
197
-
198
- ```sh
199
- bundle exec rake compile
123
+ ```erb
124
+ <%= stylesheet_link_tag "pdf" %>
125
+ <%= image_tag "logo.png", alt: "Example Ltd" %>
200
126
  ```
201
127
 
202
- Open an interactive Ruby session against the local checkout:
128
+ During a controller action, PagePrint uses `request.base_url` to resolve relative URLs. Its Rails resource fetcher reads `/assets/...` from Propshaft or `public/assets`; it does not make an HTTP request back to Rails.
203
129
 
204
- ```sh
205
- bundle exec irb -Ilib
206
- ```
130
+ ## Assets and network access
207
131
 
208
- Then load the gem from the repo and try it:
132
+ PagePrint intentionally does not download unresolved resources. Supply a `resource_fetcher` when non-Rails HTML needs external styles, images, or fonts:
209
133
 
210
134
  ```ruby
211
- require "page_print"
212
- require "tmpdir"
213
-
214
- output_path = File.join(Dir.tmpdir, "page_print-output.pdf")
215
-
216
- PagePrint.html_to_pdf("<html><body><h1>Hello</h1></body></html>", output_path, page_size: :letter, margins: :narrow, media: :screen)
217
- ```
218
-
219
- You can also do a quick one-shot smoke test from the shell:
220
-
221
- ```sh
222
- 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)'
223
- ```
224
-
225
- Or use the development console, which compiles the native extension first and then starts IRB with `PagePrint` loaded:
226
-
227
- ```sh
228
- bin/console
135
+ assets = {
136
+ "asset:pdf.css" => {
137
+ content: File.binread("app/assets/stylesheets/pdf.css"),
138
+ mime_type: "text/css"
139
+ }
140
+ }
141
+
142
+ pdf = PagePrint.render(
143
+ "<link rel=\"stylesheet\" href=\"asset:pdf.css\"><h1>Report</h1>",
144
+ resource_fetcher: ->(url) { assets[url] }
145
+ )
229
146
  ```
230
147
 
231
- ## Running Tests
232
-
233
- Run the test suite only:
148
+ The fetcher receives each resolved URL and returns `{ content:, mime_type: }` or `nil`. Returning `nil` skips the resource. This makes resource access explicit, but it does not make untrusted HTML safe; see [Security](docs/production.md#security).
234
149
 
235
- ```sh
236
- bundle exec rake test
237
- ```
150
+ ## Configuration
238
151
 
239
- Or run the default rake task, which compiles the extension and then runs tests:
152
+ The common rendering options are:
240
153
 
241
- ```sh
242
- bundle exec rake
154
+ ```ruby
155
+ PagePrint.render(
156
+ html,
157
+ base_url: "https://example.test",
158
+ page_size: :letter,
159
+ margins: :narrow,
160
+ media: :print,
161
+ metadata: { title: "Quarterly report" },
162
+ resource_fetcher: fetcher
163
+ )
243
164
  ```
244
165
 
245
- ## Local Build
166
+ Defaults are A4 paper, normal margins, and print media. See the [configuration reference](docs/configuration.md) for every preset, custom dimensions, metadata fields, global configuration, and resource fetcher behavior.
246
167
 
247
- Build and install locally:
168
+ ## Performance
248
169
 
249
- ```sh
250
- gem build page_print.gemspec
251
- gem install ./page_print-*.gem
252
- ```
170
+ The repository includes a reproducible benchmark against PDFKit using the same static HTML input. On May 30, 2026, the recorded run used Ruby 3.4.7 on an Apple Silicon development machine:
253
171
 
254
- ## Releasing
172
+ | Renderer | Average wall time | P95 wall time | Average peak RSS |
173
+ | --- | ---: | ---: | ---: |
174
+ | PagePrint | 78.2 ms | 89.6 ms | 42.1 MB |
175
+ | PDFKit | 782.2 ms | 2,127.1 ms | 59.4 MB |
255
176
 
256
- Publish a new version with:
177
+ These numbers are illustrative, not a capacity guarantee. Rendering cost varies significantly with fonts, images, document length, and host configuration. Run the benchmark—or your own production templates—on the deployment target before sizing infrastructure.
257
178
 
258
179
  ```sh
259
- bin/bump 0.1.6
180
+ RUNS=30 WARMUPS=3 bundle exec ruby benchmark/pdf_renderers.rb
260
181
  ```
261
182
 
262
- That updates `lib/page_print/version.rb` and `Gemfile.lock`, commits `Release 0.1.6`, creates annotated tag `v0.1.6`, and pushes `main` plus the tag. Requires a clean worktree on `main`.
183
+ See [benchmark/README.md](benchmark/README.md) for methodology and CSV output.
263
184
 
264
- The Package workflow then builds the source and platform gems, pushes them to RubyGems (trusted publishing / OIDC, GitHub environment `release`), and creates a GitHub Release with the gem artifacts and commit notes since the previous `v*` tag.
185
+ ## Documentation
265
186
 
266
- ## Building Native Gems
187
+ - [Configuration and API reference](docs/configuration.md)
188
+ - [Production guide](docs/production.md)
189
+ - [Benchmark methodology](benchmark/README.md)
190
+ - [PlutoBook feature matrix](https://github.com/plutoprint/plutobook/blob/main/FEATURES.md)
267
191
 
268
- Build an `x86_64-linux` platform gem with a vendored PlutoBook library:
269
-
270
- ```sh
271
- bundle exec rake package:linux
272
- ```
192
+ ## Development
273
193
 
274
- Build an Apple Silicon macOS platform gem with a vendored PlutoBook library:
194
+ Install dependencies, compile the extension, and run the test suite:
275
195
 
276
196
  ```sh
277
- bundle exec rake package:darwin_arm64
197
+ bundle install
198
+ bundle exec rake
278
199
  ```
279
200
 
280
- These tasks check out PlutoBook `v0.18.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.
201
+ Use `bin/console` for an IRB session with the extension compiled and PagePrint loaded.
281
202
 
282
- 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.
203
+ ## License
283
204
 
284
- 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.
205
+ PagePrint is available under the [MIT License](LICENSE).
@@ -0,0 +1,204 @@
1
+ # Configuration and API reference
2
+
3
+ PagePrint exposes two rendering methods and a small set of keyword options.
4
+
5
+ ## Rendering methods
6
+
7
+ ### `PagePrint.render(html, **options)`
8
+
9
+ Renders a non-empty HTML string and returns the PDF as an `ASCII-8BIT` Ruby string.
10
+
11
+ ```ruby
12
+ pdf = PagePrint.render("<h1>Report</h1>")
13
+ ```
14
+
15
+ ### `PagePrint.render_to_file(html, path, **options)`
16
+
17
+ Renders a non-empty HTML string directly to `path` and returns `true`. The destination directory must already exist and be writable.
18
+
19
+ ```ruby
20
+ PagePrint.render_to_file("<h1>Report</h1>", "/tmp/report.pdf")
21
+ ```
22
+
23
+ Writing directly to a file avoids keeping the finished PDF in a Ruby string and is preferable for large documents or background jobs.
24
+
25
+ ## Options
26
+
27
+ Both rendering methods accept the same options.
28
+
29
+ | Option | Default | Accepted values |
30
+ | --- | --- | --- |
31
+ | `base_url:` | `nil` outside Rails | A string or `nil` |
32
+ | `page_size:` | `:a4` | A page preset or custom dimensions |
33
+ | `margins:` | `:normal` | A margin preset or custom dimensions |
34
+ | `media:` | `:print` | `:print` or `:screen` |
35
+ | `resource_fetcher:` | Configured global fetcher or `nil` | Any callable, `nil`, or `false` |
36
+ | `metadata:` | `nil` | A metadata hash or `nil` |
37
+
38
+ Unknown options raise `ArgumentError`; values of the wrong type raise `TypeError`.
39
+
40
+ ### Base URL
41
+
42
+ `base_url` resolves relative URLs found in the HTML:
43
+
44
+ ```ruby
45
+ html = '<link rel="stylesheet" href="/assets/report.css"><h1>Report</h1>'
46
+ PagePrint.render(html, base_url: "https://example.test", resource_fetcher: fetcher)
47
+ ```
48
+
49
+ The fetcher receives `https://example.test/assets/report.css`. A base URL resolves the URL; it does not enable network access.
50
+
51
+ Set an application-wide default when rendering outside Rails:
52
+
53
+ ```ruby
54
+ PagePrint.configure do |config|
55
+ config.base_url = "https://example.test"
56
+ end
57
+ ```
58
+
59
+ An explicit `base_url:` takes precedence over the configured value.
60
+
61
+ ### Page size
62
+
63
+ Available presets:
64
+
65
+ ```text
66
+ :a3 :a4 :a5 :b4 :b5 :letter :legal :ledger
67
+ ```
68
+
69
+ Custom dimensions require a width, height, and unit:
70
+
71
+ ```ruby
72
+ PagePrint.render(
73
+ html,
74
+ page_size: { width: 100, height: 150, unit: :mm }
75
+ )
76
+ ```
77
+
78
+ Width and height must be greater than zero.
79
+
80
+ ### Margins
81
+
82
+ Available presets:
83
+
84
+ ```text
85
+ :none :normal :narrow :moderate :wide
86
+ ```
87
+
88
+ Custom margins require all four sides and a unit:
89
+
90
+ ```ruby
91
+ PagePrint.render(
92
+ html,
93
+ margins: { top: 5, right: 6, bottom: 7, left: 8, unit: :mm }
94
+ )
95
+ ```
96
+
97
+ Margin values must be zero or greater.
98
+
99
+ Page sizes and margins support these units:
100
+
101
+ ```text
102
+ :pt :pc :in :cm :mm :px
103
+ ```
104
+
105
+ ### Media type
106
+
107
+ Use `:print` to apply print styles and `:screen` to apply screen styles:
108
+
109
+ ```ruby
110
+ PagePrint.render(html, media: :screen)
111
+ ```
112
+
113
+ ### PDF metadata
114
+
115
+ Metadata keys and values must be symbols and strings, respectively. A value may also be `nil`.
116
+
117
+ ```ruby
118
+ PagePrint.render(
119
+ html,
120
+ metadata: {
121
+ title: "Quarterly report",
122
+ author: "Example Ltd",
123
+ subject: "Q2 results",
124
+ keywords: "finance,quarterly",
125
+ creation_date: "2026-05-10T12:00:00Z",
126
+ modification_date: nil
127
+ }
128
+ )
129
+ ```
130
+
131
+ Supported keys are:
132
+
133
+ - `:title`
134
+ - `:author`
135
+ - `:subject`
136
+ - `:keywords`
137
+ - `:creation_date`
138
+ - `:modification_date`
139
+
140
+ Use ISO 8601 strings for creation and modification dates.
141
+
142
+ ## Resource fetchers
143
+
144
+ A resource fetcher is a callable that receives a resolved URL. It must return either `nil` or a hash containing binary `content` and its `mime_type`. `text_encoding` is optional.
145
+
146
+ ```ruby
147
+ fetcher = lambda do |url|
148
+ case url
149
+ when "asset:report.css"
150
+ {
151
+ content: File.binread("app/assets/stylesheets/report.css"),
152
+ mime_type: "text/css",
153
+ text_encoding: "utf-8"
154
+ }
155
+ when "asset:logo.png"
156
+ {
157
+ content: File.binread("app/assets/images/logo.png"),
158
+ mime_type: "image/png"
159
+ }
160
+ end
161
+ end
162
+
163
+ PagePrint.render(html, resource_fetcher: fetcher)
164
+ ```
165
+
166
+ Returning `nil` skips the URL. PagePrint does not fall back to an HTTP request. Exceptions raised by the fetcher are propagated to the caller.
167
+
168
+ Configure a default fetcher for every render:
169
+
170
+ ```ruby
171
+ PagePrint.configure do |config|
172
+ config.resource_fetcher = MyResourceFetcher.new
173
+ end
174
+ ```
175
+
176
+ Pass `resource_fetcher: false` or `nil` to disable a configured fetcher for one render.
177
+
178
+ Keep fetchers narrowly scoped. Prefer an allowlist or a fixed asset map over fetching arbitrary URLs supplied by HTML.
179
+
180
+ ## Rails behavior
181
+
182
+ When Rails is loaded, PagePrint installs two defaults:
183
+
184
+ 1. During controller actions, the current `request.base_url` becomes the default base URL for that request.
185
+ 2. `PagePrint::RailsResourceFetcher` resolves URLs under `/assets/` from `public/assets` or Propshaft.
186
+
187
+ An explicit `base_url:` or `resource_fetcher:` still takes precedence. To replace the Rails fetcher globally:
188
+
189
+ ```ruby
190
+ # config/initializers/page_print.rb
191
+ PagePrint.configure do |config|
192
+ config.resource_fetcher = MyResourceFetcher.new
193
+ end
194
+ ```
195
+
196
+ The built-in Rails fetcher only handles `/assets/...` URLs. Other paths are skipped.
197
+
198
+ ## CSS and rendering support
199
+
200
+ PagePrint delegates HTML and CSS support to PlutoBook. It supports print-oriented features including `@page`, page counters, and running headers and footers, but it is not a full web browser. JavaScript and CSS Grid are not supported.
201
+
202
+ Consult the upstream [PlutoBook feature matrix](https://github.com/plutoprint/plutobook/blob/main/FEATURES.md) when designing or migrating templates. Test representative documents after every renderer or font change because pagination can change even when the HTML does not.
203
+
204
+ [Back to the README](../README.md)