wicked_pdf 1.0.6 → 2.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/issue_template.md +15 -0
- data/.github/workflows/ci.yml +56 -0
- data/.rubocop.yml +60 -0
- data/.rubocop_todo.yml +83 -29
- data/CHANGELOG.md +182 -35
- data/README.md +188 -30
- data/Rakefile +13 -10
- data/gemfiles/5.0.gemfile +8 -0
- data/gemfiles/5.1.gemfile +8 -0
- data/gemfiles/5.2.gemfile +9 -0
- data/gemfiles/6.0.gemfile +10 -0
- data/gemfiles/6.1.gemfile +12 -0
- data/gemfiles/7.0.gemfile +12 -0
- data/generators/wicked_pdf/templates/wicked_pdf.rb +9 -0
- data/lib/generators/wicked_pdf_generator.rb +5 -9
- data/lib/wicked_pdf/binary.rb +65 -0
- data/lib/wicked_pdf/middleware.rb +1 -1
- data/lib/wicked_pdf/option_parser.rb +229 -0
- data/lib/wicked_pdf/pdf_helper.rb +99 -85
- data/lib/wicked_pdf/progress.rb +33 -0
- data/lib/wicked_pdf/railtie.rb +6 -33
- data/lib/wicked_pdf/tempfile.rb +38 -7
- data/lib/wicked_pdf/version.rb +1 -1
- data/lib/wicked_pdf/wicked_pdf_helper/assets.rb +213 -91
- data/lib/wicked_pdf/wicked_pdf_helper.rb +29 -26
- data/lib/wicked_pdf.rb +37 -272
- data/test/fixtures/database.yml +4 -0
- data/test/fixtures/manifest.js +3 -0
- data/test/fixtures/wicked.js +1 -0
- data/test/functional/pdf_helper_test.rb +74 -5
- data/test/functional/wicked_pdf_helper_assets_test.rb +27 -9
- data/test/functional/wicked_pdf_helper_test.rb +15 -13
- data/test/test_helper.rb +16 -7
- data/test/unit/wicked_pdf_binary_test.rb +26 -0
- data/test/unit/wicked_pdf_option_parser_test.rb +128 -0
- data/test/unit/wicked_pdf_test.rb +11 -149
- data/test/unit/wkhtmltopdf_location_test.rb +48 -0
- data/wicked_pdf.gemspec +21 -14
- metadata +68 -37
- data/.travis.yml +0 -66
- data/gemfiles/2.3.gemfile +0 -10
- data/gemfiles/3.0.gemfile +0 -7
- data/gemfiles/3.1.gemfile +0 -8
- data/gemfiles/3.2.gemfile +0 -7
- data/gemfiles/4.0.gemfile +0 -6
- data/gemfiles/4.1.gemfile +0 -6
- data/gemfiles/4.2.gemfile +0 -6
- data/gemfiles/rails_edge.gemfile +0 -6
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
# Wicked PDF [![Build Status](https://
|
1
|
+
# Wicked PDF [![Gem Version](https://badge.fury.io/rb/wicked_pdf.svg)](http://badge.fury.io/rb/wicked_pdf) [![Build Status](https://github.com/mileszs/wicked_pdf/actions/workflows/ci.yml/badge.svg)](https://github.com/mileszs/wicked_pdf/actions/workflows/ci.yml) [![Code Climate](https://codeclimate.com/github/mileszs/wicked_pdf/badges/gpa.svg)](https://codeclimate.com/github/mileszs/wicked_pdf) [![Open Source Helpers](https://www.codetriage.com/mileszs/wicked_pdf/badges/users.svg)](https://www.codetriage.com/mileszs/wicked_pdf)
|
2
2
|
|
3
3
|
## A PDF generation plugin for Ruby on Rails
|
4
4
|
|
5
5
|
Wicked PDF uses the shell utility [wkhtmltopdf](http://wkhtmltopdf.org) to serve a PDF file to a user from HTML. In other words, rather than dealing with a PDF generation DSL of some sort, you simply write an HTML view as you would normally, then let Wicked PDF take care of the hard stuff.
|
6
6
|
|
7
|
-
_Wicked PDF has been verified to work on Ruby versions
|
7
|
+
_Wicked PDF has been verified to work on Ruby versions 2.2 through 2.6; Rails 4 through 6.1_
|
8
8
|
|
9
9
|
### Installation
|
10
10
|
|
@@ -26,8 +26,8 @@ to `config/initializers/mime_types.rb` in older versions of Rails.
|
|
26
26
|
|
27
27
|
Because `wicked_pdf` is a wrapper for [wkhtmltopdf](http://wkhtmltopdf.org/), you'll need to install that, too.
|
28
28
|
|
29
|
-
The simplest way to install all of the binaries
|
30
|
-
To install that, add
|
29
|
+
The simplest way to install all of the binaries on most Linux or OSX systems is through the gem [wkhtmltopdf-binary](https://github.com/zakird/wkhtmltopdf_binary_gem). Builds for other systems are available [here](https://wkhtmltopdf.org/downloads.html)
|
30
|
+
To install that gem, add this:
|
31
31
|
|
32
32
|
```ruby
|
33
33
|
gem 'wkhtmltopdf-binary'
|
@@ -35,13 +35,16 @@ gem 'wkhtmltopdf-binary'
|
|
35
35
|
|
36
36
|
To your Gemfile and run `bundle install`.
|
37
37
|
|
38
|
-
This
|
38
|
+
This gem currently installs version 0.12.x of `wkhtmltopdf`. Some of the options listed below are specific 0.9 or below, and others are for 0.12 and up.
|
39
|
+
|
40
|
+
You can see what flags are supported for the current version in [wkhtmltopdf's auto-generated manual](https://wkhtmltopdf.org/usage/wkhtmltopdf.txt)
|
39
41
|
|
40
42
|
If your wkhtmltopdf executable is not on your webserver's path, you can configure it in an initializer:
|
41
43
|
|
42
44
|
```ruby
|
43
45
|
WickedPdf.config = {
|
44
|
-
exe_path: '/usr/local/bin/wkhtmltopdf'
|
46
|
+
exe_path: '/usr/local/bin/wkhtmltopdf',
|
47
|
+
enable_local_file_access: true
|
45
48
|
}
|
46
49
|
```
|
47
50
|
|
@@ -62,7 +65,7 @@ end
|
|
62
65
|
```
|
63
66
|
### Usage Conditions - Important!
|
64
67
|
|
65
|
-
The wkhtmltopdf binary is run outside of your Rails application; therefore, your normal layouts will not work. If you plan to use any CSS,
|
68
|
+
The wkhtmltopdf binary is run outside of your Rails application; therefore, your normal layouts will not work. If you plan to use any CSS, JavaScript, or image files, you must modify your layout so that you provide an absolute reference to these files. The best option for Rails without the asset pipeline is to use the `wicked_pdf_stylesheet_link_tag`, `wicked_pdf_image_tag`, and `wicked_pdf_javascript_include_tag` helpers or to go straight to a CDN (Content Delivery Network) for popular libraries such as jQuery.
|
66
69
|
|
67
70
|
#### wicked_pdf helpers
|
68
71
|
```html
|
@@ -83,23 +86,57 @@ The wkhtmltopdf binary is run outside of your Rails application; therefore, your
|
|
83
86
|
</body>
|
84
87
|
</html>
|
85
88
|
```
|
86
|
-
|
89
|
+
Using wicked_pdf_helpers with asset pipeline raises `Asset names passed to helpers should not include the "/assets/" prefix.` error. To work around this, you can use `wicked_pdf_asset_base64` with the normal Rails helpers, but be aware that this will base64 encode your content and inline it in the page. This is very quick for small assets, but large ones can take a long time.
|
87
90
|
|
88
|
-
In this case, you can use that standard Rails helpers and point to the current CDN for whichever framework you are using. For jQuery, it would look somethng like this, given the current versions at the time of this writing.
|
89
91
|
```html
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
92
|
+
<!doctype html>
|
93
|
+
<html>
|
94
|
+
<head>
|
95
|
+
<meta charset='utf-8' />
|
96
|
+
<%= stylesheet_link_tag wicked_pdf_asset_base64("pdf") %>
|
97
|
+
<%= javascript_include_tag wicked_pdf_asset_base64("number_pages") %>
|
98
|
+
|
99
|
+
</head>
|
100
|
+
<body onload='number_pages'>
|
101
|
+
<div id="header">
|
102
|
+
<%= image_tag wicked_pdf_asset_base64('mysite.jpg') %>
|
103
|
+
</div>
|
104
|
+
<div id="content">
|
105
|
+
<%= yield %>
|
106
|
+
</div>
|
107
|
+
</body>
|
108
|
+
</html>
|
95
109
|
```
|
110
|
+
|
111
|
+
#### Webpacker usage
|
112
|
+
|
113
|
+
wicked_pdf supports webpack assets.
|
114
|
+
|
115
|
+
- Use `wicked_pdf_stylesheet_pack_tag` for stylesheets
|
116
|
+
- Use `wicked_pdf_javascript_pack_tag` for javascripts
|
117
|
+
- Use `wicked_pdf_asset_pack_path` to access an asset directly, for example: `image_tag wicked_pdf_asset_pack_path("media/images/foobar.png")`
|
118
|
+
|
96
119
|
#### Asset pipeline usage
|
97
120
|
|
98
|
-
|
121
|
+
It is best to precompile assets used in PDF views. This will help avoid issues when it comes to deploying, as Rails serves asset files differently between development and production (`config.assets.compile = false`), which can make it look like your PDFs work in development, but fail to load assets in production.
|
99
122
|
|
100
123
|
config.assets.precompile += ['blueprint/screen.css', 'pdf.css', 'jquery.ui.datepicker.js', 'pdf.js', ...etc...]
|
101
124
|
|
125
|
+
#### CDN reference
|
126
|
+
|
127
|
+
In this case, you can use that standard Rails helpers and point to the current CDN for whichever framework you are using. For jQuery, it would look somethng like this, given the current versions at the time of this writing.
|
128
|
+
```html
|
129
|
+
<!doctype html>
|
130
|
+
<html>
|
131
|
+
<head>
|
132
|
+
<%= javascript_include_tag "http://code.jquery.com/jquery-1.10.0.min.js" %>
|
133
|
+
<%= javascript_include_tag "http://code.jquery.com/ui/1.10.3/jquery-ui.min.js" %>
|
134
|
+
```
|
135
|
+
|
102
136
|
### Advanced Usage with all available options
|
137
|
+
|
138
|
+
_NOTE: Certain options are only supported in specific versions of wkhtmltopdf._
|
139
|
+
|
103
140
|
```ruby
|
104
141
|
class ThingsController < ApplicationController
|
105
142
|
def show
|
@@ -108,9 +145,11 @@ class ThingsController < ApplicationController
|
|
108
145
|
format.pdf do
|
109
146
|
render pdf: 'file_name',
|
110
147
|
disposition: 'attachment', # default 'inline'
|
111
|
-
template: 'things/show
|
112
|
-
|
113
|
-
|
148
|
+
template: 'things/show',
|
149
|
+
locals: {foo: @bar},
|
150
|
+
file: "#{Rails.root}/files/foo.erb",
|
151
|
+
inline: '<!doctype html><html><head></head><body>INLINE HTML</body></html>',
|
152
|
+
layout: 'pdf', # for a pdf.pdf.erb file
|
114
153
|
wkhtmltopdf: '/usr/local/bin/wkhtmltopdf', # path to binary
|
115
154
|
show_as_html: params.key?('debug'), # allow debugging based on url param
|
116
155
|
orientation: 'Landscape', # default Portrait
|
@@ -119,6 +158,7 @@ class ThingsController < ApplicationController
|
|
119
158
|
page_width: NUMBER,
|
120
159
|
save_to_file: Rails.root.join('pdfs', "#{filename}.pdf"),
|
121
160
|
save_only: false, # depends on :save_to_file being set first
|
161
|
+
default_protocol: 'http',
|
122
162
|
proxy: 'TEXT',
|
123
163
|
basic_auth: false # when true username & password are automatically sent from session
|
124
164
|
username: 'TEXT',
|
@@ -132,6 +172,7 @@ class ThingsController < ApplicationController
|
|
132
172
|
post: ['query QUERY_PARAM'], # could be an array or a single string in a 'name value' format
|
133
173
|
redirect_delay: NUMBER,
|
134
174
|
javascript_delay: NUMBER,
|
175
|
+
window_status: 'TEXT', # wait to render until some JS sets window.status to the given string
|
135
176
|
image_quality: NUMBER,
|
136
177
|
no_pdf_compression: true,
|
137
178
|
zoom: FLOAT,
|
@@ -144,21 +185,31 @@ class ThingsController < ApplicationController
|
|
144
185
|
enable_plugins: true,
|
145
186
|
disable_internal_links: true,
|
146
187
|
disable_external_links: true,
|
188
|
+
keep_relative_links: true,
|
147
189
|
print_media_type: true,
|
190
|
+
|
191
|
+
# define as true the key 'disable_local_file_access' or 'enable_local_file_access', not both
|
192
|
+
disable_local_file_access: true,
|
193
|
+
enable_local_file_access: false, # must be true when using wkhtmltopdf > 0.12.6
|
194
|
+
|
148
195
|
disable_smart_shrinking: true,
|
149
196
|
use_xserver: true,
|
150
|
-
background: false, #
|
197
|
+
background: false, # background needs to be true to enable background colors to render
|
151
198
|
no_background: true,
|
199
|
+
no_stop_slow_scripts: false,
|
152
200
|
viewport_size: 'TEXT', # available only with use_xserver or patched QT
|
153
201
|
extra: '', # directly inserted into the command to wkhtmltopdf
|
202
|
+
raise_on_all_errors: nil, # raise error for any stderr output. Such as missing media, image assets
|
203
|
+
log_level: 'info', # Available values: none, error, warn, or info - only available with wkhtmltopdf 0.12.5+
|
204
|
+
quiet: false, # `false` is same as `log_level: 'info'`, `true` is same as `log_level: 'none'`
|
154
205
|
outline: { outline: true,
|
155
206
|
outline_depth: LEVEL },
|
156
207
|
margin: { top: SIZE, # default 10 (mm)
|
157
208
|
bottom: SIZE,
|
158
209
|
left: SIZE,
|
159
210
|
right: SIZE },
|
160
|
-
header: { html: { template: 'users/header
|
161
|
-
layout: 'pdf_plain
|
211
|
+
header: { html: { template: 'users/header', # use :template OR :url
|
212
|
+
layout: 'pdf_plain', # optional, use 'pdf_plain' for a pdf_plain.html.pdf.erb file, defaults to main layout
|
162
213
|
url: 'www.example.com',
|
163
214
|
locals: { foo: @bar }},
|
164
215
|
center: 'TEXT',
|
@@ -169,8 +220,8 @@ class ThingsController < ApplicationController
|
|
169
220
|
spacing: REAL,
|
170
221
|
line: true,
|
171
222
|
content: 'HTML CONTENT ALREADY RENDERED'}, # optionally you can pass plain html already rendered (useful if using pdf_from_string)
|
172
|
-
footer: { html: { template:'shared/footer
|
173
|
-
layout: 'pdf_plain.html', # optional, use 'pdf_plain
|
223
|
+
footer: { html: { template:'shared/footer', # use :template OR :url
|
224
|
+
layout: 'pdf_plain.html', # optional, use 'pdf_plain' for a pdf_plain.html.pdf.erb file, defaults to main layout
|
174
225
|
url: 'www.example.com',
|
175
226
|
locals: { foo: @bar }},
|
176
227
|
center: 'TEXT',
|
@@ -206,7 +257,8 @@ class ThingsController < ApplicationController
|
|
206
257
|
disable_links: true,
|
207
258
|
disable_toc_links: true,
|
208
259
|
disable_back_links:true,
|
209
|
-
xsl_style_sheet: 'file.xsl'} # optional XSLT stylesheet to use for styling table of contents
|
260
|
+
xsl_style_sheet: 'file.xsl'}, # optional XSLT stylesheet to use for styling table of contents
|
261
|
+
progress: proc { |output| puts output } # proc called when console output changes
|
210
262
|
end
|
211
263
|
end
|
212
264
|
end
|
@@ -214,6 +266,19 @@ end
|
|
214
266
|
```
|
215
267
|
By default, it will render without a layout (layout: false) and the template for the current controller and action.
|
216
268
|
|
269
|
+
#### wkhtmltopdf Binary Options
|
270
|
+
|
271
|
+
Some of the options above are being passed to `wkhtmltopdf` binary. They can be used to control the options used in Webkit rendering before generating the PDF.
|
272
|
+
|
273
|
+
Examples of those options are:
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
print_media_type: true # Passes `--print-media-type`
|
277
|
+
no_background: true # Passes `--no-background`
|
278
|
+
```
|
279
|
+
|
280
|
+
You can see the complete list of options under "Global Options" in wkhtmltopdf usage [docs](http://wkhtmltopdf.org/usage/wkhtmltopdf.txt).
|
281
|
+
|
217
282
|
### Super Advanced Usage ###
|
218
283
|
|
219
284
|
If you need to just create a pdf and not display it:
|
@@ -230,25 +295,69 @@ pdf = WickedPdf.new.pdf_from_url('https://github.com/mileszs/wicked_pdf')
|
|
230
295
|
|
231
296
|
# create a pdf from string using templates, layouts and content option for header or footer
|
232
297
|
pdf = WickedPdf.new.pdf_from_string(
|
233
|
-
render_to_string('templates/pdf
|
298
|
+
render_to_string('templates/pdf', layout: 'pdfs/layout_pdf.html'),
|
234
299
|
footer: {
|
235
|
-
content: render_to_string(
|
300
|
+
content: render_to_string(
|
301
|
+
'templates/footer',
|
302
|
+
layout: 'pdfs/layout_pdf.html'
|
303
|
+
)
|
304
|
+
}
|
305
|
+
)
|
306
|
+
|
307
|
+
# It is possible to use footer/header templates without a layout, in that case you need to provide a valid HTML document
|
308
|
+
pdf = WickedPdf.new.pdf_from_string(
|
309
|
+
render_to_string('templates/full_pdf_template'),
|
310
|
+
header: {
|
311
|
+
content: render_to_string('templates/full_header_template')
|
236
312
|
}
|
237
313
|
)
|
238
314
|
|
239
315
|
# or from your controller, using views & templates and all wicked_pdf options as normal
|
240
|
-
pdf = render_to_string pdf: "some_file_name", template: "templates/pdf
|
316
|
+
pdf = render_to_string pdf: "some_file_name", template: "templates/pdf", encoding: "UTF-8"
|
241
317
|
|
242
318
|
# then save to a file
|
243
319
|
save_path = Rails.root.join('pdfs','filename.pdf')
|
244
320
|
File.open(save_path, 'wb') do |file|
|
245
321
|
file << pdf
|
246
322
|
end
|
323
|
+
|
324
|
+
# you can also track progress on your PDF generation, such as when using it from within a Resque job
|
325
|
+
class PdfJob
|
326
|
+
def perform
|
327
|
+
blk = proc { |output|
|
328
|
+
match = output.match(/\[.+\] Page (?<current_page>\d+) of (?<total_pages>\d+)/)
|
329
|
+
if match
|
330
|
+
current_page = match[:current_page].to_i
|
331
|
+
total_pages = match[:total_pages].to_i
|
332
|
+
message = "Generated #{current_page} of #{total_pages} pages"
|
333
|
+
at current_page, total_pages, message
|
334
|
+
end
|
335
|
+
}
|
336
|
+
WickedPdf.new.pdf_from_string(html, progress: blk)
|
337
|
+
end
|
338
|
+
end
|
247
339
|
```
|
248
340
|
If you need to display utf encoded characters, add this to your pdf views or layouts:
|
249
341
|
```html
|
250
342
|
<meta charset="utf-8" />
|
251
343
|
```
|
344
|
+
If you need to return a PDF in a controller with Rails in API mode:
|
345
|
+
```ruby
|
346
|
+
pdf_html = ActionController::Base.new.render_to_string(template: 'controller_name/action_name', layout: 'pdf')
|
347
|
+
pdf = WickedPdf.new.pdf_from_string(pdf_html)
|
348
|
+
send_data pdf, filename: 'file.pdf'
|
349
|
+
```
|
350
|
+
### Page Breaks
|
351
|
+
|
352
|
+
You can control page breaks with CSS.
|
353
|
+
|
354
|
+
Add a few styles like this to your stylesheet or page:
|
355
|
+
```css
|
356
|
+
div.alwaysbreak { page-break-before: always; }
|
357
|
+
div.nobreak:before { clear:both; }
|
358
|
+
div.nobreak { page-break-inside: avoid; }
|
359
|
+
```
|
360
|
+
|
252
361
|
### Page Numbering
|
253
362
|
|
254
363
|
A bit of javascript can help you number your pages. Create a template or header/footer file with this:
|
@@ -291,7 +400,7 @@ If you would like to have WickedPdf automatically generate PDF views for all (or
|
|
291
400
|
require 'wicked_pdf'
|
292
401
|
config.middleware.use WickedPdf::Middleware
|
293
402
|
```
|
294
|
-
If you want to turn on or off the middleware for certain
|
403
|
+
If you want to turn on or off the middleware for certain URLs, use the `:only` or `:except` conditions like so:
|
295
404
|
```ruby
|
296
405
|
# conditions can be plain strings or regular expressions, and you can supply only one or an array
|
297
406
|
config.middleware.use WickedPdf::Middleware, {}, only: '/invoice'
|
@@ -299,14 +408,45 @@ config.middleware.use WickedPdf::Middleware, {}, except: [ %r[^/admin], '/secret
|
|
299
408
|
```
|
300
409
|
If you use the standard `render pdf: 'some_pdf'` in your app, you will want to exclude those actions from the middleware.
|
301
410
|
|
411
|
+
|
412
|
+
### Include in an email as an attachment
|
413
|
+
|
414
|
+
To include a rendered pdf file in an email you can do the following:
|
415
|
+
|
416
|
+
```ruby
|
417
|
+
attachments['attachment.pdf'] = WickedPdf.new.pdf_from_string(
|
418
|
+
render_to_string('link_to_view.pdf.erb', layout: 'pdf')
|
419
|
+
)
|
420
|
+
```
|
421
|
+
|
422
|
+
This will render the pdf to a string and include it in the email. This is very slow so make sure you schedule your email delivery in a job.
|
423
|
+
|
302
424
|
### Further Reading
|
303
425
|
|
426
|
+
Mike Ackerman's post [How To Create PDFs in Rails](https://www.viget.com/articles/how-to-create-pdfs-in-rails)
|
427
|
+
|
304
428
|
Andreas Happe's post [Generating PDFs from Ruby on Rails](http://www.snikt.net/blog/2012/04/26/wicked-pdf/)
|
305
429
|
|
306
430
|
JESii's post [WickedPDF, wkhtmltopdf, and Heroku...a tricky combination](http://www.nubyrubyrailstales.com/2013/06/wickedpdf-wkhtmltopdf-and-herokua.html)
|
307
431
|
|
432
|
+
Berislav Babic's post [Send PDF attachments from Rails with WickedPdf and ActionMailer](http://berislavbabic.com/send-pdf-attachments-from-rails-with-wickedpdf-and-actionmailer/)
|
433
|
+
|
434
|
+
Corsego's 2021 post [Complete guide to generating PDFs with gem wicked_pdf](https://blog.corsego.com/gem-wicked-pdf)
|
435
|
+
|
436
|
+
PDFTron's post [How to Generate PDFs With Ruby on Rails](https://www.pdftron.com/blog/rails/how-to-generate-pdf-with-ruby-on-rails/)
|
437
|
+
|
308
438
|
StackOverflow [questions with the tag "wicked-pdf"](http://stackoverflow.com/questions/tagged/wicked-pdf)
|
309
439
|
|
440
|
+
### Screencasts
|
441
|
+
|
442
|
+
* SupeRails Screencast [EN]
|
443
|
+
|
444
|
+
[![Ruby on Rails #17 generate, save, send PDFs with gem wicked_pdf](https://i3.ytimg.com/vi/tFvtwEmW-GE/hqdefault.jpg)](https://youtu.be/tFvtwEmW-GE)
|
445
|
+
|
446
|
+
* codigofacilito Screencast [ES]
|
447
|
+
|
448
|
+
[![Generar PDF con Ruby on Rails - Tutorial](https://i3.ytimg.com/vi/jeWM_gusmJc/hqdefault.jpg)](https://youtu.be/jeWM_gusmJc)
|
449
|
+
|
310
450
|
### Debugging
|
311
451
|
|
312
452
|
Now you can use a debug param on the URL that shows you the content of the pdf in plain html to design it faster.
|
@@ -317,12 +457,30 @@ http://localhost:3001/CONTROLLER/X.pdf?debug
|
|
317
457
|
|
318
458
|
However, the wicked_pdf_* helpers will use file:/// paths for assets when using :show_as_html, and your browser's cross-domain safety feature will kick in, and not render them. To get around this, you can load your assets like so in your templates:
|
319
459
|
```html
|
320
|
-
|
460
|
+
<%= params.key?('debug') ? image_tag('foo') : wicked_pdf_image_tag('foo') %>
|
321
461
|
```
|
322
462
|
|
323
463
|
#### Gotchas
|
324
464
|
|
325
|
-
If one image from your HTML cannot be found (relative or wrong path for
|
465
|
+
If one image from your HTML cannot be found (relative or wrong path for example), others images with right paths **may not** be displayed in the output PDF as well (it seems to be an issue with wkhtmltopdf).
|
466
|
+
|
467
|
+
wkhtmltopdf may render at different resolutions on different platforms. For example, Linux prints at 75 dpi (native for WebKit) while on Windows it's at the desktop's DPI (which is normally 96 dpi). [Use `:zoom => 0.78125`](https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2184) (75/96) to match Linux rendering to Windows.
|
468
|
+
|
469
|
+
### Security considerations
|
470
|
+
|
471
|
+
WickedPdf renders page content on the server by saving HTML and assets to temporary files on disk, then executing `wkhtmltopdf` to convert that HTML to a PDF file.
|
472
|
+
|
473
|
+
It is highly recommended if you allow user-generated HTML/CSS/JS to be converted to PDF, you sanitize it first, or at least disallow requesting content from internal IP addresses and hostnames.
|
474
|
+
|
475
|
+
For example, these could potentially leak internal AWS metadata:
|
476
|
+
```html
|
477
|
+
<iframe src="http://169.254.169.254/latest/meta-data/"></iframe>
|
478
|
+
<object data="http://169.254.169.254/latest/meta-data/" type="text/html">
|
479
|
+
```
|
480
|
+
|
481
|
+
Thank you to Adam Gold from [Snyk](https://snyk.io) for reporting this.
|
482
|
+
We are considering adding host allow & block lists and/or potentially HTML element sanitizing.
|
483
|
+
Please open an issue or PR to help us out with this.
|
326
484
|
|
327
485
|
### Inspiration
|
328
486
|
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require 'rails/version'
|
|
5
5
|
require 'bundler/gem_tasks'
|
6
6
|
|
7
7
|
desc 'Default: run unit tests.'
|
8
|
-
task :default => [
|
8
|
+
task :default => %i[setup_and_run_tests rubocop]
|
9
9
|
|
10
10
|
desc 'Test the wicked_pdf plugin.'
|
11
11
|
Rake::TestTask.new(:test) do |t|
|
@@ -17,16 +17,13 @@ end
|
|
17
17
|
|
18
18
|
desc 'Run RuboCop'
|
19
19
|
task :rubocop do
|
20
|
-
return unless RUBY_VERSION > '1.9.2'
|
21
20
|
require 'rubocop/rake_task'
|
22
21
|
RuboCop::RakeTask.new
|
23
22
|
end
|
24
23
|
|
25
24
|
desc 'Setup and run all tests'
|
26
25
|
task :setup_and_run_tests do
|
27
|
-
unless File.exist?('test/dummy/config/environment.rb')
|
28
|
-
Rake::Task[:dummy_generate].invoke
|
29
|
-
end
|
26
|
+
Rake::Task[:dummy_generate].invoke unless File.exist?('test/dummy/config/environment.rb')
|
30
27
|
Rake::Task[:test].invoke
|
31
28
|
end
|
32
29
|
|
@@ -34,18 +31,24 @@ desc 'Generate dummy application for test cases'
|
|
34
31
|
task :dummy_generate do
|
35
32
|
Rake::Task[:dummy_remove].invoke
|
36
33
|
puts 'Creating dummy application to run tests'
|
37
|
-
|
38
|
-
system(
|
39
|
-
|
34
|
+
system('rails new test/dummy --database=sqlite3')
|
35
|
+
system('touch test/dummy/db/schema.rb')
|
36
|
+
FileUtils.cp 'test/fixtures/database.yml', 'test/dummy/config/'
|
40
37
|
FileUtils.rm_r Dir.glob('test/dummy/test/*')
|
38
|
+
|
39
|
+
# rails 6 needs this to be present before start:
|
40
|
+
FileUtils.mkdir_p('test/dummy/app/assets/config')
|
41
|
+
FileUtils.mkdir_p('test/dummy/app/assets/javascripts')
|
42
|
+
FileUtils.cp 'test/fixtures/manifest.js', 'test/dummy/app/assets/config/'
|
43
|
+
FileUtils.cp 'test/fixtures/wicked.js', 'test/dummy/app/assets/javascripts/'
|
41
44
|
end
|
42
45
|
|
43
46
|
desc 'Remove dummy application'
|
44
47
|
task :dummy_remove do
|
45
|
-
FileUtils.rm_r Dir.glob('test/dummy
|
48
|
+
FileUtils.rm_r Dir.glob('test/dummy/')
|
46
49
|
end
|
47
50
|
|
48
|
-
desc 'Generate documentation for the wicked_pdf
|
51
|
+
desc 'Generate documentation for the wicked_pdf gem.'
|
49
52
|
RDoc::Task.new(:rdoc) do |rdoc|
|
50
53
|
rdoc.rdoc_dir = 'rdoc'
|
51
54
|
rdoc.title = 'WickedPdf'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'bootsnap' # required to run `rake test` in Rails 6.1
|
4
|
+
gem 'bundler', '~>2'
|
5
|
+
gem 'rails', '~>6.1.0'
|
6
|
+
gem 'webpacker'
|
7
|
+
gem 'rdoc'
|
8
|
+
gem 'sprockets', '~>3.0'
|
9
|
+
gem 'sqlite3', '~> 1.4'
|
10
|
+
gem 'rubocop', '1.11.0'
|
11
|
+
|
12
|
+
gemspec :path => '../'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'bootsnap' # required to run `rake test` in Rails 7.0
|
4
|
+
gem 'bundler', '~>2'
|
5
|
+
gem 'rails', '~>7.0.0'
|
6
|
+
gem 'sprockets-rails'
|
7
|
+
gem 'rdoc'
|
8
|
+
gem 'sprockets', '~>3.0'
|
9
|
+
gem 'sqlite3', '~> 1.4'
|
10
|
+
gem 'rubocop', '1.11.0'
|
11
|
+
|
12
|
+
gemspec :path => '../'
|
@@ -15,7 +15,16 @@ WickedPdf.config = {
|
|
15
15
|
# or
|
16
16
|
# exe_path: Gem.bin_path('wkhtmltopdf-binary', 'wkhtmltopdf')
|
17
17
|
|
18
|
+
# Needed for wkhtmltopdf 0.12.6+ to use many wicked_pdf asset helpers
|
19
|
+
# enable_local_file_access: true,
|
20
|
+
|
18
21
|
# Layout file to be used for all PDFs
|
19
22
|
# (but can be overridden in `render :pdf` calls)
|
20
23
|
# layout: 'pdf.html',
|
24
|
+
|
25
|
+
# Using wkhtmltopdf without an X server can be achieved by enabling the
|
26
|
+
# 'use_xvfb' flag. This will wrap all wkhtmltopdf commands around the
|
27
|
+
# 'xvfb-run' command, in order to simulate an X server.
|
28
|
+
#
|
29
|
+
# use_xvfb: true,
|
21
30
|
}
|
@@ -1,11 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
def copy_initializer
|
7
|
-
copy_file 'wicked_pdf.rb', 'config/initializers/wicked_pdf.rb'
|
8
|
-
end
|
1
|
+
# Rails generator invoked with 'rails generate wicked_pdf'
|
2
|
+
class WickedPdfGenerator < Rails::Generators::Base
|
3
|
+
source_root(File.expand_path(File.dirname(__FILE__) + '/../../generators/wicked_pdf/templates'))
|
4
|
+
def copy_initializer
|
5
|
+
copy_file 'wicked_pdf.rb', 'config/initializers/wicked_pdf.rb'
|
9
6
|
end
|
10
|
-
|
11
7
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class WickedPdf
|
2
|
+
class Binary
|
3
|
+
EXE_NAME = 'wkhtmltopdf'.freeze
|
4
|
+
|
5
|
+
attr_reader :path, :default_version
|
6
|
+
|
7
|
+
def initialize(binary_path, default_version = WickedPdf::DEFAULT_BINARY_VERSION)
|
8
|
+
@path = binary_path || find_binary_path
|
9
|
+
@default_version = default_version
|
10
|
+
|
11
|
+
raise "Location of #{EXE_NAME} unknown" if @path.empty?
|
12
|
+
raise "Bad #{EXE_NAME}'s path: #{@path}" unless File.exist?(@path)
|
13
|
+
raise "#{EXE_NAME} is not executable" unless File.executable?(@path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def version
|
17
|
+
@version ||= retrieve_binary_version
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_version_string(version_info)
|
21
|
+
match_data = /wkhtmltopdf\s*(\d*\.\d*\.\d*\w*)/.match(version_info)
|
22
|
+
if match_data && (match_data.length == 2)
|
23
|
+
Gem::Version.new(match_data[1])
|
24
|
+
else
|
25
|
+
default_version
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def xvfb_run_path
|
30
|
+
path = possible_binary_locations.map { |l| File.expand_path("#{l}/xvfb-run") }.find { |location| File.exist?(location) }
|
31
|
+
raise StandardError, 'Could not find binary xvfb-run on the system.' unless path
|
32
|
+
|
33
|
+
path
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def retrieve_binary_version
|
39
|
+
_stdin, stdout, _stderr = Open3.popen3(@path + ' -V')
|
40
|
+
parse_version_string(stdout.gets(nil))
|
41
|
+
rescue StandardError
|
42
|
+
default_version
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_binary_path
|
46
|
+
exe_path ||= WickedPdf.config[:exe_path] unless WickedPdf.config.empty?
|
47
|
+
exe_path ||= possible_which_path
|
48
|
+
exe_path ||= possible_binary_locations.map { |l| File.expand_path("#{l}/#{EXE_NAME}") }.find { |location| File.exist?(location) }
|
49
|
+
exe_path || ''
|
50
|
+
end
|
51
|
+
|
52
|
+
def possible_which_path
|
53
|
+
detected_path = (defined?(Bundler) ? Bundler.which('wkhtmltopdf') : `which wkhtmltopdf`).chomp
|
54
|
+
detected_path.present? && detected_path
|
55
|
+
rescue StandardError
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def possible_binary_locations
|
60
|
+
possible_locations = (ENV['PATH'].split(':') + %w[/usr/bin /usr/local/bin]).uniq
|
61
|
+
possible_locations += %w[~/bin] if ENV.key?('HOME')
|
62
|
+
possible_locations
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -80,7 +80,7 @@ class WickedPdf
|
|
80
80
|
|
81
81
|
def set_request_to_render_as_pdf(env)
|
82
82
|
@render_pdf = true
|
83
|
-
%w
|
83
|
+
%w[PATH_INFO REQUEST_URI].each { |e| env[e] = env[e].sub(%r{\.pdf\b}, '') }
|
84
84
|
env['HTTP_ACCEPT'] = concat(env['HTTP_ACCEPT'], Rack::Mime.mime_type('.html'))
|
85
85
|
env['Rack-Middleware-WickedPdf'] = 'true'
|
86
86
|
end
|