htmlToPdf 2.0.0 → 3.0.0

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: eb26af6ab6e2d50a8836366601bfd8fc14c7c4d374a3483c34e2b511d2645634
4
- data.tar.gz: 901949e8b9b71ddf2eca72d7c702718710524af5b45744135267e187102fb712
3
+ metadata.gz: 1c827ae590775d110de569263bc2d4fe04fe424abe2eadbf2d34ccf5f4bbefff
4
+ data.tar.gz: 90ea2e0f7dd5d5b9fefcc441677997480dc5f27d0d02e2852c2be47e7ba9a3e4
5
5
  SHA512:
6
- metadata.gz: 1a8010ab65b9d32b0361b57442c0b095324eaf2222ccb98b2844723830880e60def0c5aaa32516e6d072dff911cd9a6434b33e997938c4f132820f4a47da4339
7
- data.tar.gz: 5fd86086620cd34a5d1b20c1fa4e9f309d1931d04147f8ac43cb23e47020ce68dbcd0a154e06cd90a1331f309a742a342a575d5b9f9e263b3c97b3e1576609a3
6
+ metadata.gz: 4f998e37dcef376f0459b4be1afaafcb20abdb0705f56bf6ca0d6d3e951f0b65aee8f2a0f5dca82d5ddce140ca2fa32561f01ad1a779563b2a86c778e28a8f86
7
+ data.tar.gz: 4e1b9d09c1cbedd080d10b68e9954cda8ff37c4e0dd2e0b28836eb17ca6b3d76e7baa833f775930e5d50788cb733b193e87066f0b70fb5077c96d4e984c9d8d6
@@ -0,0 +1,39 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ # required_ruby_version in htmlToPdf.gemspec is >= 3.1 — test across
16
+ # that whole supported range, not just whatever's newest.
17
+ ruby-version: ["3.1", "3.2", "3.3", "3.4"]
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: ${{ matrix.ruby-version }}
24
+ bundler-cache: true
25
+
26
+ # ubuntu-latest ships google-chrome-stable already; the spec suite
27
+ # exercises real headless Chrome through ferrum, plus pdftotext to
28
+ # assert on rendered PDF content.
29
+ - name: Install poppler-utils (pdftotext)
30
+ run: sudo apt-get update && sudo apt-get install -y poppler-utils
31
+
32
+ - name: Syntax-check every Ruby file
33
+ run: find lib spec -name '*.rb' | xargs -n1 ruby -c
34
+
35
+ - name: Run test suite
36
+ run: bundle exec rake spec
37
+
38
+ - name: Build the gem (packaging sanity check)
39
+ run: gem build htmlToPdf.gemspec
@@ -0,0 +1,39 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ release:
13
+ runs-on: ubuntu-latest
14
+ permissions:
15
+ id-token: write # required for RubyGems Trusted Publishing (OIDC) — no stored API key
16
+ contents: read
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: "3.3"
23
+ bundler-cache: true
24
+
25
+ - name: Install poppler-utils (pdftotext)
26
+ run: sudo apt-get update && sudo apt-get install -y poppler-utils
27
+
28
+ # Same gate CI already ran on the commit this tag points to, run again
29
+ # here as a last check before anything actually gets published.
30
+ - name: Run test suite
31
+ run: bundle exec rake spec
32
+
33
+ # Builds htmlToPdf.gemspec and pushes to rubygems.org via OIDC trusted
34
+ # publishing — see README's "Releasing" section for the one-time setup
35
+ # this depends on (linking this repo + this exact workflow file to the
36
+ # htmlToPdf gem on rubygems.org). No RUBYGEMS_API_KEY secret needed or
37
+ # used anywhere in this workflow.
38
+ - name: Publish to RubyGems
39
+ uses: rubygems/release-gem@v1
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /pkg/
2
+ /*.gem
3
+ /.bundle/
4
+ /spec/internal/log/*.log
5
+ /spec/internal/tmp/
6
+ /tmp/
7
+ .DS_Store
8
+ .idea/
9
+ *.swp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. Format loosely follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ ## [3.0.0] - 2026-10-05
7
+
8
+ - **Breaking: replaced the wkhtmltopdf binary dependency with a pooled headless Chrome instance**,
9
+ driven over the DevTools protocol via the [ferrum](https://github.com/rubycdp/ferrum) gem.
10
+ wkhtmltopdf is an unmaintained, years-out-of-date WebKit build with no flexbox/Grid support;
11
+ headless Chrome is the same engine your users already browse the app with. One `Ferrum::Browser`
12
+ process is kept alive per Ruby process instead of spawning a binary per request — see
13
+ [Architecture](docs/architecture.md#browserpool). `config.wkhtmltopdf_path` is gone, replaced by
14
+ `config.browser_options` (passed straight to `Ferrum::Browser.new`).
15
+ - **Breaking: `pdf_options[:wkhtmltopdf_options]` is gone, replaced by `pdf_options[:print_options]`**
16
+ — Chrome's print-to-PDF parameters (`landscape`, `format`, `margin_*`, `scale`,
17
+ `display_header_footer`/`header_template`/`footer_template`, `page_ranges`,
18
+ `prefer_css_page_size`, plus `javascript_delay` as the gem's own convenience) instead of literal
19
+ wkhtmltopdf CLI flags. The old `orientation`/`page_size` flag names are no longer recognized —
20
+ use `landscape`/`format`. See the README's options table.
21
+ - **Breaking: wire-up changed from `before_action :html_to_pdf` to `include HtmlToPdf` +
22
+ `around_action :html_to_pdf`**, and the gem no longer auto-includes itself into every
23
+ `ActionController::Base` subclass in the host app. This fixes two real bugs found while testing
24
+ the previous design against an actual Rails app rather than a stub: every PDF request raised
25
+ `AbstractController::DoubleRenderError` (Rails' `response=` setter is documented to mark the
26
+ response as already-rendered the instant it's assigned; the old shadow-controller design copied
27
+ that flag onto the real controller), and any `before_action` declared after `html_to_pdf` never
28
+ ran before the view was captured. See
29
+ [The DoubleRenderError trap](https://ramlaxmanyadav.github.io/htmlToPdf/rails-double-render-pdf.html)
30
+ for the full story and [Rails integration](docs/rails-integration.md) for the new wiring.
31
+ - **Security: `header_template`/`footer_template` are now filtered out of request-supplied
32
+ `print_options` by default** — unlike every other print option, they're raw HTML Chrome renders
33
+ as-is, which made them an SSRF vector (`<img src="http://internal-host/...">`) on any action that
34
+ forwards `pdf_options` from request params. Only honored from `config.default_options` unless
35
+ `config.allow_request_templates = true` is set explicitly. See [Security](docs/security.md).
36
+ - **Security: `javascript_delay` is now capped** at `config.max_javascript_delay_ms` (default 10
37
+ seconds) — previously an uncapped value let one caller stall the shared browser pool for every
38
+ other in-flight PDF render in the same process.
39
+ - Fixed: a per-request browser context leaked on every single render (the context Chrome creates
40
+ for isolation was never disposed unless `create_page` was called with its own block).
41
+ - Fixed: a gem-wide default `:format`/margin in `config.default_options` could silently clobber an
42
+ explicit per-request `paper_width`/`paper_height`/`:padding` instead of being overridden by it.
43
+ - Added: an automated test suite (RSpec + [combustion](https://github.com/pat/combustion) for a
44
+ real-Rails-dispatch integration spec) and CI across Ruby 3.1–3.4 on every push/PR.
45
+
46
+ ## [2.0.0] and earlier
47
+
48
+ Rendered via the wkhtmltopdf binary; see git history prior to 3.0.0 for details.
data/Gemfile.lock CHANGED
@@ -1,24 +1,332 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- htmlToPdf (2.0.0)
4
+ htmlToPdf (3.0.0)
5
+ activesupport (>= 7.0, < 9)
6
+ ferrum (>= 0.13, < 1.0)
5
7
 
6
8
  GEM
7
9
  remote: https://rubygems.org/
8
10
  specs:
11
+ action_text-trix (2.1.19)
12
+ railties
13
+ actioncable (8.1.4)
14
+ actionpack (= 8.1.4)
15
+ activesupport (= 8.1.4)
16
+ nio4r (~> 2.0)
17
+ websocket-driver (>= 0.6.1)
18
+ zeitwerk (~> 2.6)
19
+ actionmailbox (8.1.4)
20
+ actionpack (= 8.1.4)
21
+ activejob (= 8.1.4)
22
+ activerecord (= 8.1.4)
23
+ activestorage (= 8.1.4)
24
+ activesupport (= 8.1.4)
25
+ mail (>= 2.8.0)
26
+ actionmailer (8.1.4)
27
+ actionpack (= 8.1.4)
28
+ actionview (= 8.1.4)
29
+ activejob (= 8.1.4)
30
+ activesupport (= 8.1.4)
31
+ mail (>= 2.8.0)
32
+ rails-dom-testing (~> 2.2)
33
+ actionpack (8.1.4)
34
+ actionview (= 8.1.4)
35
+ activesupport (= 8.1.4)
36
+ nokogiri (>= 1.8.5)
37
+ rack (>= 2.2.4)
38
+ rack-session (>= 1.0.1)
39
+ rack-test (>= 0.6.3)
40
+ rails-dom-testing (~> 2.2)
41
+ rails-html-sanitizer (~> 1.6)
42
+ useragent (~> 0.16)
43
+ actiontext (8.1.4)
44
+ action_text-trix (~> 2.1.15)
45
+ actionpack (= 8.1.4)
46
+ activerecord (= 8.1.4)
47
+ activestorage (= 8.1.4)
48
+ activesupport (= 8.1.4)
49
+ globalid (>= 0.6.0)
50
+ nokogiri (>= 1.8.5)
51
+ actionview (8.1.4)
52
+ activesupport (= 8.1.4)
53
+ builder (~> 3.1)
54
+ erubi (~> 1.11)
55
+ rails-dom-testing (~> 2.2)
56
+ rails-html-sanitizer (~> 1.6)
57
+ activejob (8.1.4)
58
+ activesupport (= 8.1.4)
59
+ globalid (>= 0.3.6)
60
+ activemodel (8.1.4)
61
+ activesupport (= 8.1.4)
62
+ activerecord (8.1.4)
63
+ activemodel (= 8.1.4)
64
+ activesupport (= 8.1.4)
65
+ timeout (>= 0.4.0)
66
+ activestorage (8.1.4)
67
+ actionpack (= 8.1.4)
68
+ activejob (= 8.1.4)
69
+ activerecord (= 8.1.4)
70
+ activesupport (= 8.1.4)
71
+ marcel (~> 1.0)
72
+ activesupport (8.1.4)
73
+ base64
74
+ bigdecimal
75
+ concurrent-ruby (~> 1.0, >= 1.3.1)
76
+ connection_pool (>= 2.2.5)
77
+ drb
78
+ i18n (>= 1.6, < 2)
79
+ json
80
+ logger (>= 1.4.2)
81
+ minitest (>= 5.1)
82
+ securerandom (>= 0.3)
83
+ tzinfo (~> 2.0, >= 2.0.5)
84
+ uri (>= 0.13.1)
85
+ addressable (2.9.0)
86
+ public_suffix (>= 2.0.2, < 8.0)
87
+ base64 (0.3.0)
88
+ bigdecimal (4.1.3)
89
+ builder (3.3.0)
90
+ combustion (1.5.0)
91
+ activesupport (>= 3.0.0)
92
+ railties (>= 3.0.0)
93
+ thor (>= 0.14.6)
94
+ concurrent-ruby (1.3.8)
95
+ connection_pool (3.0.2)
96
+ crass (1.0.7)
97
+ date (3.5.1)
98
+ diff-lcs (1.6.2)
99
+ drb (2.2.3)
100
+ erb (6.0.7)
101
+ erubi (1.13.1)
102
+ ferrum (0.18.0)
103
+ addressable (~> 2.5)
104
+ base64 (~> 0.2)
105
+ concurrent-ruby (~> 1.1)
106
+ websocket-driver (~> 0.7)
107
+ globalid (1.4.0)
108
+ activesupport (>= 6.1)
109
+ i18n (1.15.2)
110
+ concurrent-ruby (~> 1.0)
111
+ io-console (0.9.4)
112
+ irb (1.18.0)
113
+ pp (>= 0.6.0)
114
+ prism (>= 1.3.0)
115
+ rdoc (>= 4.0.0)
116
+ reline (>= 0.4.2)
117
+ json (3.0.2)
118
+ logger (1.7.0)
119
+ loofah (2.25.2)
120
+ crass (~> 1.0.2)
121
+ nokogiri (>= 1.12.0)
122
+ mail (2.9.1)
123
+ logger
124
+ mini_mime (>= 0.1.1)
125
+ net-imap
126
+ net-pop
127
+ net-smtp
128
+ marcel (1.2.1)
129
+ mini_mime (1.1.5)
130
+ minitest (6.0.6)
131
+ drb (~> 2.0)
132
+ prism (~> 1.5)
133
+ net-imap (0.6.7)
134
+ date
135
+ net-protocol
136
+ net-pop (0.1.2)
137
+ net-protocol
138
+ net-protocol (0.4.0)
139
+ timeout
140
+ net-smtp (0.5.2)
141
+ net-protocol
142
+ nio4r (2.7.5)
143
+ nokogiri (1.19.4-arm64-darwin)
144
+ racc (~> 1.4)
145
+ pp (0.6.4)
146
+ prettyprint
147
+ prettyprint (0.2.0)
148
+ prism (1.9.0)
149
+ public_suffix (7.0.5)
150
+ puma (6.6.1)
151
+ nio4r (~> 2.0)
152
+ racc (1.8.1)
153
+ rack (3.2.7)
154
+ rack-session (2.1.2)
155
+ base64 (>= 0.1.0)
156
+ rack (>= 3.0.0)
157
+ rack-test (2.2.0)
158
+ rack (>= 1.3)
159
+ rackup (2.3.1)
160
+ rack (>= 3)
161
+ rails (8.1.4)
162
+ actioncable (= 8.1.4)
163
+ actionmailbox (= 8.1.4)
164
+ actionmailer (= 8.1.4)
165
+ actionpack (= 8.1.4)
166
+ actiontext (= 8.1.4)
167
+ actionview (= 8.1.4)
168
+ activejob (= 8.1.4)
169
+ activemodel (= 8.1.4)
170
+ activerecord (= 8.1.4)
171
+ activestorage (= 8.1.4)
172
+ activesupport (= 8.1.4)
173
+ bundler (>= 1.15.0)
174
+ railties (= 8.1.4)
175
+ rails-dom-testing (2.3.0)
176
+ activesupport (>= 5.0.0)
177
+ minitest
178
+ nokogiri (>= 1.6)
179
+ rails-html-sanitizer (1.7.1)
180
+ loofah (~> 2.25, >= 2.25.2)
181
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
182
+ railties (8.1.4)
183
+ actionpack (= 8.1.4)
184
+ activesupport (= 8.1.4)
185
+ irb (~> 1.13)
186
+ rackup (>= 1.0.0)
187
+ rake (>= 12.2)
188
+ thor (~> 1.0, >= 1.2.2)
189
+ tsort (>= 0.2)
190
+ zeitwerk (~> 2.6)
9
191
  rake (13.4.2)
192
+ rbs (4.2.0)
193
+ logger
194
+ prism (>= 1.6.0)
195
+ tsort
196
+ rdoc (8.1.0)
197
+ erb
198
+ prism (>= 1.6.0)
199
+ rbs (>= 4.0.0)
200
+ tsort
201
+ reline (0.7.0)
202
+ io-console (~> 0.5)
203
+ rspec (3.13.2)
204
+ rspec-core (~> 3.13.0)
205
+ rspec-expectations (~> 3.13.0)
206
+ rspec-mocks (~> 3.13.0)
207
+ rspec-core (3.13.6)
208
+ rspec-support (~> 3.13.0)
209
+ rspec-expectations (3.13.5)
210
+ diff-lcs (>= 1.2.0, < 2.0)
211
+ rspec-support (~> 3.13.0)
212
+ rspec-mocks (3.13.8)
213
+ diff-lcs (>= 1.2.0, < 2.0)
214
+ rspec-support (~> 3.13.0)
215
+ rspec-rails (7.1.1)
216
+ actionpack (>= 7.0)
217
+ activesupport (>= 7.0)
218
+ railties (>= 7.0)
219
+ rspec-core (~> 3.13)
220
+ rspec-expectations (~> 3.13)
221
+ rspec-mocks (~> 3.13)
222
+ rspec-support (~> 3.13)
223
+ rspec-support (3.13.7)
224
+ securerandom (0.4.1)
225
+ thor (1.5.0)
226
+ timeout (0.6.1)
227
+ tsort (0.2.0)
228
+ tzinfo (2.0.6)
229
+ concurrent-ruby (~> 1.0)
230
+ uri (1.1.1)
231
+ useragent (0.16.11)
232
+ websocket-driver (0.8.2)
233
+ base64
234
+ websocket-extensions (>= 0.1.0)
235
+ websocket-extensions (0.1.5)
236
+ zeitwerk (2.8.3)
10
237
 
11
238
  PLATFORMS
12
- ruby
239
+ arm64-darwin-25
13
240
 
14
241
  DEPENDENCIES
15
- bundler (>= 2.4)
242
+ bundler (>= 2.4, < 5)
243
+ combustion (~> 1.5)
16
244
  htmlToPdf!
17
- rake (>= 13.0)
245
+ puma (>= 5.0, < 7)
246
+ rails (>= 7.0, < 9)
247
+ rake (>= 13.0, < 14)
248
+ rspec (~> 3.13)
249
+ rspec-rails (~> 7.0)
18
250
 
19
251
  CHECKSUMS
20
- htmlToPdf (2.0.0)
252
+ action_text-trix (2.1.19) sha256=7012f59421009cf284aa651294896414d653a61a2417c9b8714c8476d2f74009
253
+ actioncable (8.1.4) sha256=cfcd72f8c861e01ccdc96d772ad8c5ad6ed0ea7c571aef81a794b6027eaa7f11
254
+ actionmailbox (8.1.4) sha256=9c4525e577ebb671ce558f3ea78b8a634f64e56a0e6e8d105b28b873793a06eb
255
+ actionmailer (8.1.4) sha256=f82d95d6762ae46ea38f221d3363639266af4f8e56d50e2bd367558674baf093
256
+ actionpack (8.1.4) sha256=245493cc51be195a7645e6fc184880fa7e5b32ff1a08e053ee31f58c76e30ea4
257
+ actiontext (8.1.4) sha256=33ee99bea7da6af911fc7d98b17013a8e1ec62aa01b20b20e9a5366f40dbf644
258
+ actionview (8.1.4) sha256=eff1efd07c4d969abad288322b423bcb67fdbab5e324c91ca37352b80f226076
259
+ activejob (8.1.4) sha256=4ed5b2ed8d4cc5c8f5f09becfef8346f202a4884eee0c8c73cae5ac378824099
260
+ activemodel (8.1.4) sha256=9a0906c127bd118aa5f06157db12b8f750da83dccee72db2f658547a0ae7a0e8
261
+ activerecord (8.1.4) sha256=3b4957ea7f246c9231bd7faa909312eac03a9101aaf87977b6c52733ff00b04f
262
+ activestorage (8.1.4) sha256=f4956f3ec9ee63e5e0b332b57c877cb627f82b56f4cc902279310d2aa285c7a8
263
+ activesupport (8.1.4) sha256=891dd3fe43c4fc8391934b237b1189bb1f661c932a84a840043db9d46a4e80df
264
+ addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
265
+ base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
266
+ bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
267
+ builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
268
+ combustion (1.5.0) sha256=5e68cc8c2090ee06196a03e748276d1d63d4f63ed189888a23e1daa4fb79359a
269
+ concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
270
+ connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
271
+ crass (1.0.7) sha256=94868719948664c89ddcaf0a37c65048413dfcb1c869470a5f7a7ceb5390b295
272
+ date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
273
+ diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
274
+ drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
275
+ erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92
276
+ erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
277
+ ferrum (0.18.0) sha256=4cb8be16e352fc1d75f087e9214b34ec1b93ba932410c730a4724909ca89d7c6
278
+ globalid (1.4.0) sha256=037f12fbf1d9d7a014d501c2d5c77356fd4ddd96d7a7991d6700bba96706f427
279
+ htmlToPdf (3.0.0)
280
+ i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
281
+ io-console (0.9.4) sha256=a403304f32928ee85e56d457b4612ca43f2703b561a3258844992bd24085fbb1
282
+ irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
283
+ json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df
284
+ logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
285
+ loofah (2.25.2) sha256=2007f746959ac65552456e04b433e83deb22759ab38c838b4445c70e43425918
286
+ mail (2.9.1) sha256=06574eca475253d6c18145dd70af80d0eb970182d55053497c5f4d797ea160e8
287
+ marcel (1.2.1) sha256=1678e9360e32f9eafa917c80029e2f6d10b2715c66a4b87b6d0da9b9cd1f859f
288
+ mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
289
+ minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
290
+ net-imap (0.6.7) sha256=b5c9573be975d856de252ee851871724da66aa2d449b2482d5690bd12bc23660
291
+ net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3
292
+ net-protocol (0.4.0) sha256=f9f90c8c6f64794a45160bf422177a336db12809de3beeeaa26b88fcd2e8c853
293
+ net-smtp (0.5.2) sha256=35103cc04d6b2d94632f9928ede07483c24b70b9c0db1f231fea43909b678f3c
294
+ nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
295
+ nokogiri (1.19.4-arm64-darwin) sha256=a46db9853286e6597b36ebc6953817d15acf3a299583eb3f89fdc6f91dd63527
296
+ pp (0.6.4) sha256=dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570
297
+ prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
298
+ prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
299
+ public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
300
+ puma (6.6.1) sha256=b9b56e4a4ea75d1bfa6d9e1972ee2c9f43d0883f011826d914e8e37b3694ea1e
301
+ racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
302
+ rack (3.2.7) sha256=93e13e1c24f93556671d85d2d79fa228c3485815c50d7e2f265b5330c6528fb7
303
+ rack-session (2.1.2) sha256=595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8
304
+ rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463
305
+ rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868
306
+ rails (8.1.4) sha256=5f3ecdf9a2a3b79b2e31b8fee3b64a00f7284d76e5631f87d0e58e665911a33b
307
+ rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d
308
+ rails-html-sanitizer (1.7.1) sha256=e797a7c9b01e567307e317c576b49ab4168017e63eea4dba9ce3cb587e2f22c2
309
+ railties (8.1.4) sha256=b5e7a8e8984e33114d68af62227971960aae1e6c02bd7c9d826ffad97fff583c
21
310
  rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
311
+ rbs (4.2.0) sha256=51f7b886dcc05bc09e10b901daa6a81829f6adc03101d6ca9ea4aac6103e0674
312
+ rdoc (8.1.0) sha256=befc37880a5cfd36dcb176c748b5fd5ee39dd3536a75e81824e4972ce230f60a
313
+ reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
314
+ rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
315
+ rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
316
+ rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
317
+ rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
318
+ rspec-rails (7.1.1) sha256=e15dccabed211e2fd92f21330c819adcbeb1591c1d66c580d8f2d8288557e331
319
+ rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
320
+ securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
321
+ thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
322
+ timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
323
+ tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
324
+ tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
325
+ uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
326
+ useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844
327
+ websocket-driver (0.8.2) sha256=97c556b019bf3410b4961002ac501621e9322d3f8a7bc02161a09301cc4c4146
328
+ websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241
329
+ zeitwerk (2.8.3) sha256=2c85125a8467ce069e20123d1e709a08955c9d29c118c25b46b7b7fafdbb92e5
22
330
 
23
331
  BUNDLED WITH
24
332
  4.0.6
data/README.md CHANGED
@@ -1,11 +1,35 @@
1
1
  # HtmlToPdf
2
2
 
3
- This gem will generate pdf of the action's html requested as pdf.
3
+ [![Gem Version](https://img.shields.io/gem/v/htmlToPdf)](https://rubygems.org/gems/htmlToPdf)
4
+ [![CI](https://github.com/ramlaxmanyadav/htmlToPdf/actions/workflows/ci.yml/badge.svg)](https://github.com/ramlaxmanyadav/htmlToPdf/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt)
6
+
7
+ Turn any Rails controller action into a downloadable PDF, rendered exactly as the browser would
8
+ render it — real CSS, real layout, real fonts — via a pooled headless Chrome instance instead of
9
+ an abandoned wkhtmltopdf binary.
4
10
 
5
11
  Supports Rails 7.x and Rails 8.x (latest).
6
12
 
13
+ Renders through a pooled headless Chrome instance (via the [ferrum](https://github.com/rubycdp/ferrum) gem), so you get modern CSS (flexbox, grid, custom fonts) and only need a Chrome/Chromium install — no separate renderer binary to install or package.
14
+
15
+ ## Docs
16
+
17
+ - [Architecture](docs/architecture.md) — why headless Chrome instead of wkhtmltopdf, the request
18
+ lifecycle, the `BrowserPool` design, and the `print_options` security model in detail.
19
+ - [HtmlToPdf in Rails](docs/rails-integration.md) — callback ordering, custom layouts, Chrome in
20
+ Docker, and the `asset_host` gotcha that leaves a PDF unstyled if you miss it.
21
+ - [Security](docs/security.md) — exactly what's safe to expose on a public action and what isn't.
22
+ - [Contributing & Releasing](docs/contributing.md) — running the test suite, extending the gem.
23
+ - Longer-form guides on the [docs site](https://ramlaxmanyadav.github.io/htmlToPdf/):
24
+ - [Headless Chrome vs. wkhtmltopdf for Rails PDF generation](https://ramlaxmanyadav.github.io/htmlToPdf/headless-chrome-vs-wkhtmltopdf.html)
25
+ - [The DoubleRenderError trap: rendering a Rails view to PDF](https://ramlaxmanyadav.github.io/htmlToPdf/rails-double-render-pdf.html)
26
+ - [Choosing a Rails HTML-to-PDF gem: HtmlToPdf vs. Grover vs. WickedPdf vs. Prawn](https://ramlaxmanyadav.github.io/htmlToPdf/choosing-a-rails-pdf-gem.html)
27
+
7
28
  ## Installation
8
- #### Install and setup `wkhtmltopdf`. Please refer https://wkhtmltopdf.org for more details.
29
+
30
+ #### Make sure Chrome or Chromium is installed on the machine that will render PDFs.
31
+
32
+ Ferrum auto-detects a local Google Chrome / Chromium install (including on macOS, Linux and in most CI images). If it's installed somewhere non-standard, or you're running as root in Docker, see [Configuration](#configuration) below, or [Chrome in Docker](docs/rails-integration.md#production-deployment-chrome-in-docker) for a container deployment.
9
33
 
10
34
  Add this line to your application's Gemfile:
11
35
 
@@ -21,9 +45,14 @@ Or install it yourself as:
21
45
 
22
46
  ## Usage
23
47
 
24
- write following code inside Application Controller
48
+ Opt in on the controller(s) that should support PDF export — typically `ApplicationController`:
49
+
50
+ class ApplicationController < ActionController::Base
51
+ include HtmlToPdf
52
+ around_action :html_to_pdf
53
+ end
25
54
 
26
- before_action :html_to_pdf
55
+ It must be an `around_action`, not a `before_action`: the rest of the callback chain and the action itself run normally via `yield`, so any other `before_action`s (e.g. one that loads `@record`) still run in their usual order and their instance variables are available to the view. Nothing happens for ordinary requests — `html_to_pdf` only takes over when the request's format is `pdf`. See [HtmlToPdf in Rails](docs/rails-integration.md) for why this has to be an `around_action` and what that fixed.
27
56
 
28
57
  in views:
29
58
 
@@ -39,45 +68,97 @@ you can customize pdf like you can give the name and layout for the pdf.to custo
39
68
 
40
69
  one can call any action as pdf and he get the pdf file of that action's html.
41
70
 
42
- customizing page padding and wkhtmltopdf options:
71
+ customizing page padding and print options:
43
72
 
44
73
  <%= link_to 'download', your_action_path(:format => 'pdf', :pdf_options => {
45
74
  title: 'pdf_name',
46
75
  layout: 'layout_name',
47
76
  padding: '10mm',
48
- wkhtmltopdf_options: {
49
- orientation: 'Landscape',
50
- page_size: 'A4',
77
+ print_options: {
78
+ landscape: true,
79
+ format: 'A4',
51
80
  margin_top: '15mm',
52
- grayscale: true
81
+ scale: 0.9,
82
+ print_background: true,
83
+ page_ranges: '1-3',
84
+ javascript_delay: 500
53
85
  }
54
86
  }) %>
55
87
 
56
- `padding` is a shorthand that sets all four margins at once. `wkhtmltopdf_options` accepts **any** option the `wkhtmltopdf` CLI supports (underscores or dashes both work, e.g. `margin_top`, `page_size`, `orientation`, `dpi`, `zoom`, `encoding`, `javascript_delay`, `disable_smart_shrinking`, ...) — see https://wkhtmltopdf.org for the full list. Boolean-style flags (e.g. `grayscale: true`) are passed as bare flags; anything else is passed as `--flag value`.
88
+ `padding` is a shorthand that sets all four margins at once. `print_options` accepts the options Chrome's print-to-PDF engine supports:
89
+
90
+ | Option | Type | Notes |
91
+ |---|---|---|
92
+ | `landscape` | boolean | |
93
+ | `format` | string | `'A4'`, `'Letter'`, `'Legal'`, `'Tabloid'`, `'Ledger'`, `'A0'`–`'A6'` (case-insensitive) |
94
+ | `paper_width`, `paper_height` | number or string | used instead of `format`; accepts a plain number (inches) or a string with a unit, e.g. `'210mm'` |
95
+ | `margin_top`, `margin_bottom`, `margin_left`, `margin_right` | number or string | same units as above |
96
+ | `scale` | number | 0.1–2 |
97
+ | `print_background` | boolean | defaults to `true` (configurable, see below) |
98
+ | `display_header_footer`, `header_template`, `footer_template` | boolean / string | templates are HTML; Chrome supports `<span class="pageNumber">`, `<span class="totalPages">`, `<span class="date">`, `<span class="title">`, `<span class="url">` inside them. **`header_template`/`footer_template` are only honored from `config.default_options`, not from a request's `print_options`, unless you explicitly set `config.allow_request_templates = true`** — see [Security](docs/security.md#the-one-exception-header_template--footer_template). |
99
+ | `page_ranges` | string | e.g. `'1-3'` |
100
+ | `prefer_css_page_size` | boolean | honor `@page` CSS size over `format`/`paper_width`/`paper_height` |
101
+ | `javascript_delay` | integer (ms) | pause between page load and capture, for content that renders asynchronously; capped at `config.max_javascript_delay_ms` (default 10s) |
57
102
 
58
- ##### Security note: `pdf_options` is read from the request's query params and forwarded verbatim as CLI flags to `wkhtmltopdf`. If `your_action_path` is publicly reachable, anyone can pass arbitrary `wkhtmltopdf` flags to it. Only expose the pdf format on actions where that's acceptable, or filter `pdf_options` yourself before it reaches `html_to_pdf` (e.g. in a `before_action` that runs earlier).
103
+ ##### Security note: `pdf_options` is read from the request's query params. `print_options` is a fixed, narrow set of PDF layout flags (margins, paper size, etc.) rather than arbitrary CLI arguments forwarded to a subprocess, so it can't be used to inject arbitrary binary flags — `header_template`/`footer_template` are the one exception (raw HTML Chrome renders as-is) and are filtered out of request-supplied options by default. Still, only expose the pdf format on actions where letting a caller tweak layout/margins is acceptable, or filter `pdf_options` yourself before it reaches `html_to_pdf` (e.g. in a `before_action` that runs earlier). Full detail in [Security](docs/security.md).
59
104
 
60
105
  ## Configuration
61
106
 
62
107
  Set gem-wide defaults in an initializer (e.g. `config/initializers/html_to_pdf.rb`):
63
108
 
64
109
  HtmlToPdf.configure do |config|
65
- # Path to the wkhtmltopdf binary. Defaults to "wkhtmltopdf" (resolved via $PATH).
66
- # Useful when it's installed via the wkhtmltopdf-binary gem or a custom location.
67
- config.wkhtmltopdf_path = Wkhtmltopdf.path # or an absolute path string
110
+ # Keyword options passed straight to Ferrum::Browser.new. Leave empty to
111
+ # let Ferrum auto-detect a local Chrome/Chromium install. Useful for a
112
+ # custom binary path, longer timeouts, or Chrome flags such as
113
+ # no-sandbox when running as root in Docker:
114
+ config.browser_options = {
115
+ # browser_path: '/usr/bin/chromium',
116
+ # timeout: 20,
117
+ # process_timeout: 30,
118
+ # browser_options: { 'no-sandbox' => nil, 'disable-gpu' => nil }
119
+ }
68
120
 
69
121
  # Layout used when pdf_options[:layout] isn't given. Defaults to "application".
70
122
  config.default_layout = "pdf"
71
123
 
72
- # wkhtmltopdf options applied to every PDF, merged under any per-request
73
- # padding/wkhtmltopdf_options. Defaults to {}.
74
- config.default_options = { dpi: 300, margin_top: "10mm" }
124
+ # Print options (see the table above) applied to every PDF, merged
125
+ # under any per-request padding/print_options. Defaults to
126
+ # { print_background: true }. header_template/footer_template are safe
127
+ # here (this is your own code, not request input) even though they're
128
+ # rejected from a request's print_options by default — see Security.
129
+ config.default_options = {
130
+ print_background: true,
131
+ format: 'A4',
132
+ footer_template: '<div style="font-size:10px; width:100%; text-align:center;"><span class="pageNumber"></span> / <span class="totalPages"></span></div>',
133
+ display_header_footer: true
134
+ }
75
135
 
76
- # Directory temporary .html/.pdf files are written to. Defaults to Rails.root/tmp.
136
+ # Directory the rendered HTML is temporarily written to before Chrome
137
+ # loads it (via a file:// URL, so relative asset paths resolve).
138
+ # Defaults to Rails.root/tmp.
77
139
  config.tmp_dir = Rails.root.join("tmp", "pdfs")
140
+
141
+ # Only set this if pdf_options never flows from unfiltered request
142
+ # params in your app. Defaults to false. See Security.
143
+ # config.allow_request_templates = true
144
+
145
+ # Upper bound (ms) on a request-supplied javascript_delay, so one
146
+ # caller can't stall the shared browser pool for everyone. Defaults
147
+ # to 10_000.
148
+ # config.max_javascript_delay_ms = 10_000
78
149
  end
79
150
 
80
- ##### Make sure you have configured your `asset_host` in application's Rails environment, otherwise assets might not be loaded properly.
151
+ A single headless Chrome process is started lazily and reused across requests in the same Ruby process (each conversion gets its own isolated browser context), since launching Chrome per-request is comparatively expensive. It's restarted automatically if Chrome crashes or stops responding. See [Architecture](docs/architecture.md#browserpool) for the design and its scaling limits.
152
+
153
+ ##### Make sure you have configured your `asset_host` in application's Rails environment, otherwise assets might not be loaded properly — this is a bigger deal than it sounds for this gem specifically; see [the asset_host gotcha](docs/rails-integration.md#the-asset_host-gotcha).
81
154
 
82
155
  `config.action_controller.asset_host = "YOUR_ASSETS_HOST"`
83
156
 
157
+ ## Contributing
158
+
159
+ See [Contributing & Releasing](docs/contributing.md) for running the test suite and extending the
160
+ gem.
161
+
162
+ ## License
163
+
164
+ MIT — see [LICENSE.txt](LICENSE.txt).