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.
Files changed (49) hide show
  1. checksums.yaml +5 -5
  2. data/.github/issue_template.md +15 -0
  3. data/.github/workflows/ci.yml +56 -0
  4. data/.rubocop.yml +60 -0
  5. data/.rubocop_todo.yml +83 -29
  6. data/CHANGELOG.md +182 -35
  7. data/README.md +188 -30
  8. data/Rakefile +13 -10
  9. data/gemfiles/5.0.gemfile +8 -0
  10. data/gemfiles/5.1.gemfile +8 -0
  11. data/gemfiles/5.2.gemfile +9 -0
  12. data/gemfiles/6.0.gemfile +10 -0
  13. data/gemfiles/6.1.gemfile +12 -0
  14. data/gemfiles/7.0.gemfile +12 -0
  15. data/generators/wicked_pdf/templates/wicked_pdf.rb +9 -0
  16. data/lib/generators/wicked_pdf_generator.rb +5 -9
  17. data/lib/wicked_pdf/binary.rb +65 -0
  18. data/lib/wicked_pdf/middleware.rb +1 -1
  19. data/lib/wicked_pdf/option_parser.rb +229 -0
  20. data/lib/wicked_pdf/pdf_helper.rb +99 -85
  21. data/lib/wicked_pdf/progress.rb +33 -0
  22. data/lib/wicked_pdf/railtie.rb +6 -33
  23. data/lib/wicked_pdf/tempfile.rb +38 -7
  24. data/lib/wicked_pdf/version.rb +1 -1
  25. data/lib/wicked_pdf/wicked_pdf_helper/assets.rb +213 -91
  26. data/lib/wicked_pdf/wicked_pdf_helper.rb +29 -26
  27. data/lib/wicked_pdf.rb +37 -272
  28. data/test/fixtures/database.yml +4 -0
  29. data/test/fixtures/manifest.js +3 -0
  30. data/test/fixtures/wicked.js +1 -0
  31. data/test/functional/pdf_helper_test.rb +74 -5
  32. data/test/functional/wicked_pdf_helper_assets_test.rb +27 -9
  33. data/test/functional/wicked_pdf_helper_test.rb +15 -13
  34. data/test/test_helper.rb +16 -7
  35. data/test/unit/wicked_pdf_binary_test.rb +26 -0
  36. data/test/unit/wicked_pdf_option_parser_test.rb +128 -0
  37. data/test/unit/wicked_pdf_test.rb +11 -149
  38. data/test/unit/wkhtmltopdf_location_test.rb +48 -0
  39. data/wicked_pdf.gemspec +21 -14
  40. metadata +68 -37
  41. data/.travis.yml +0 -66
  42. data/gemfiles/2.3.gemfile +0 -10
  43. data/gemfiles/3.0.gemfile +0 -7
  44. data/gemfiles/3.1.gemfile +0 -8
  45. data/gemfiles/3.2.gemfile +0 -7
  46. data/gemfiles/4.0.gemfile +0 -6
  47. data/gemfiles/4.1.gemfile +0 -6
  48. data/gemfiles/4.2.gemfile +0 -6
  49. data/gemfiles/rails_edge.gemfile +0 -6
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # Wicked PDF [![Build Status](https://secure.travis-ci.org/mileszs/wicked_pdf.png)](http://travis-ci.org/mileszs/wicked_pdf) [![Gem Version](https://badge.fury.io/rb/wicked_pdf.svg)](http://badge.fury.io/rb/wicked_pdf)
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 1.8.7 through 2.1; Rails 2 through 4.1_
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 (Linux, OSX, Windows) is through the gem [wkhtmltopdf-binary](https://github.com/steerio/wkhtmltopdf-binary).
30
- To install that, add a second gem
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 wrapper may trail in versions, at the moment it wraps the 0.9 version of `wkhtmltopdf` while there is 0.12 version available. Some of the advanced options listed below is not available with 0.9.
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, 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.
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
- #### CDN reference
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
- <!doctype html>
91
- <html>
92
- <head>
93
- <%= javascript_include_tag "http://code.jquery.com/jquery-1.10.0.min.js" %>
94
- <%= javascript_include_tag "http://code.jquery.com/ui/1.10.3/jquery-ui.min.js" %>
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
- The way to handle this for the asset pipeline on Heroku is to include these files in your asset precompile list, as follows:
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.pdf.erb',
112
- file: "#{Rails.root}/files/foo.erb"
113
- layout: 'pdf.html', # use 'pdf.html' for a pdf.html.erb file
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, # backround needs to be true to enable background colors to render
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.pdf.erb', # use :template OR :url
161
- layout: 'pdf_plain.html', # optional, use 'pdf_plain.html' for a pdf_plain.html.erb file, defaults to main layout
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.pdf.erb', # use :template OR :url
173
- layout: 'pdf_plain.html', # optional, use 'pdf_plain.html' for a pdf_plain.html.erb file, defaults to main layout
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.html.erb', layout: 'pdfs/layout_pdf'),
298
+ render_to_string('templates/pdf', layout: 'pdfs/layout_pdf.html'),
234
299
  footer: {
235
- content: render_to_string(layout: 'pdfs/layout_pdf')
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.html.erb", encoding: "UTF-8"
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 urls, use the `:only` or `:except` conditions like so:
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
- <%= params.key?('debug') ? image_tag('foo') : wicked_pdf_image_tag('foo') %>
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 ie), others images with right paths **may not** be displayed in the output PDF as well (it seems to be an issue with wkhtmltopdf).
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 => [:setup_and_run_tests, :rubocop]
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
- prefix = (Rails::VERSION::MAJOR == 2) ? '' : 'new '
38
- system("rails #{prefix}test/dummy")
39
- system("touch #{prefix}test/dummy/db/schema.rb")
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 plugin.'
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,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 5.0.0'
4
+ gem 'rdoc'
5
+ gem 'sprockets', '~>3.0' # v4 strips newlines from assets causing tests to fail
6
+ gem 'sqlite3', '~> 1.3.6'
7
+
8
+ gemspec path: '../'
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '~> 5.1.0'
4
+ gem 'rdoc'
5
+ gem 'sprockets', '~>3.0' # v4 strips newlines from assets causing tests to fail
6
+ gem 'sqlite3', '~> 1.3.6'
7
+
8
+ gemspec path: '../'
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'bootsnap' # required to run `rake test` in Rails 5.2
4
+ gem 'rails', '~> 5.2'
5
+ gem 'rdoc'
6
+ gem 'sprockets', '~>3.0' # v4 strips newlines from assets causing tests to fail
7
+ gem 'sqlite3', '~> 1.3.6'
8
+
9
+ gemspec path: '../'
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'bootsnap' # required to run `rake test` in Rails 6.0
4
+ gem 'bundler', '~>2'
5
+ gem 'rails', '~>6.0.1'
6
+ gem 'rdoc'
7
+ gem 'sprockets', '~>3.0'
8
+ gem 'sqlite3', '~> 1.4'
9
+
10
+ gemspec path: '../'
@@ -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
- if defined?(Rails) && Rails::VERSION::MAJOR != 2
2
-
3
- # Rails3 generator invoked with 'rails generate wicked_pdf'
4
- class WickedPdfGenerator < Rails::Generators::Base
5
- source_root(File.expand_path(File.dirname(__FILE__) + '/../../generators/wicked_pdf/templates'))
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(PATH_INFO REQUEST_URI).each { |e| env[e] = env[e].sub(%r{\.pdf\b}, '') }
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