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
@@ -1,120 +1,242 @@
1
- require 'open-uri'
1
+ require 'net/http'
2
+ require 'delegate'
2
3
 
3
- module WickedPdfHelper
4
- module Assets
5
- ASSET_URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/
4
+ class WickedPdf
5
+ module WickedPdfHelper
6
+ module Assets
7
+ ASSET_URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/
6
8
 
7
- def wicked_pdf_asset_base64(path)
8
- asset = Rails.application.assets.find_asset(path)
9
- throw "Could not find asset '#{path}'" if asset.nil?
10
- base64 = Base64.encode64(asset.to_s).gsub(/\s+/, '')
11
- "data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
12
- end
9
+ class PropshaftAsset < SimpleDelegator
10
+ def content_type
11
+ super.to_s
12
+ end
13
+
14
+ def to_s
15
+ content
16
+ end
17
+
18
+ def filename
19
+ path.to_s
20
+ end
21
+ end
22
+
23
+ def wicked_pdf_asset_base64(path)
24
+ asset = find_asset(path)
25
+ raise "Could not find asset '#{path}'" if asset.nil?
13
26
 
14
- def wicked_pdf_stylesheet_link_tag(*sources)
15
- stylesheet_contents = sources.collect do |source|
16
- source = WickedPdfHelper.add_extension(source, 'css')
17
- "<style type='text/css'>#{read_asset(source)}</style>"
18
- end.join("\n")
27
+ base64 = Base64.encode64(asset.to_s).gsub(/\s+/, '')
28
+ "data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
29
+ end
30
+
31
+ # Using `image_tag` with URLs when generating PDFs (specifically large PDFs with lots of pages) can cause buffer/stack overflows.
32
+ #
33
+ def wicked_pdf_url_base64(url)
34
+ response = Net::HTTP.get_response(URI(url))
19
35
 
20
- stylesheet_contents.gsub(ASSET_URL_REGEX) do
21
- if Regexp.last_match[1].starts_with?('data:')
22
- "url(#{Regexp.last_match[1]})"
36
+ if response.is_a?(Net::HTTPSuccess)
37
+ base64 = Base64.encode64(response.body).gsub(/\s+/, '')
38
+ "data:#{response.content_type};base64,#{Rack::Utils.escape(base64)}"
23
39
  else
24
- "url(#{wicked_pdf_asset_path(Regexp.last_match[1])})"
40
+ Rails.logger.warn("[wicked_pdf] #{response.code} #{response.message}: #{url}")
41
+ nil
25
42
  end
26
- end.html_safe
27
- end
43
+ end
28
44
 
29
- def wicked_pdf_image_tag(img, options = {})
30
- image_tag wicked_pdf_asset_path(img), options
31
- end
45
+ def wicked_pdf_stylesheet_link_tag(*sources)
46
+ stylesheet_contents = sources.collect do |source|
47
+ source = WickedPdfHelper.add_extension(source, 'css')
48
+ "<style type='text/css'>#{read_asset(source)}</style>"
49
+ end.join("\n")
50
+
51
+ stylesheet_contents.gsub(ASSET_URL_REGEX) do
52
+ if Regexp.last_match[1].starts_with?('data:')
53
+ "url(#{Regexp.last_match[1]})"
54
+ else
55
+ "url(#{wicked_pdf_asset_path(Regexp.last_match[1])})"
56
+ end
57
+ end.html_safe
58
+ end
32
59
 
33
- def wicked_pdf_javascript_src_tag(jsfile, options = {})
34
- jsfile = WickedPdfHelper.add_extension(jsfile, 'js')
35
- javascript_include_tag wicked_pdf_asset_path(jsfile), options
36
- end
60
+ def wicked_pdf_stylesheet_pack_tag(*sources)
61
+ return unless defined?(Webpacker)
37
62
 
38
- def wicked_pdf_javascript_include_tag(*sources)
39
- sources.collect do |source|
40
- source = WickedPdfHelper.add_extension(source, 'js')
41
- "<script type='text/javascript'>#{read_asset(source)}</script>"
42
- end.join("\n").html_safe
43
- end
63
+ if running_in_development?
64
+ stylesheet_pack_tag(*sources)
65
+ else
66
+ css_text = sources.collect do |source|
67
+ source = WickedPdfHelper.add_extension(source, 'css')
68
+ wicked_pdf_stylesheet_link_tag(webpacker_source_url(source))
69
+ end.join("\n")
70
+ css_text.respond_to?(:html_safe) ? css_text.html_safe : css_text
71
+ end
72
+ end
73
+
74
+ def wicked_pdf_javascript_pack_tag(*sources)
75
+ return unless defined?(Webpacker)
44
76
 
45
- def wicked_pdf_asset_path(asset)
46
- if (pathname = asset_pathname(asset).to_s) =~ URI_REGEXP
47
- pathname
48
- else
49
- "file:///#{pathname}"
77
+ if running_in_development?
78
+ javascript_pack_tag(*sources)
79
+ else
80
+ sources.collect do |source|
81
+ source = WickedPdfHelper.add_extension(source, 'js')
82
+ "<script type='text/javascript'>#{read_asset(webpacker_source_url(source))}</script>"
83
+ end.join("\n").html_safe
84
+ end
85
+ end
86
+
87
+ def wicked_pdf_image_tag(img, options = {})
88
+ image_tag wicked_pdf_asset_path(img), options
50
89
  end
51
- end
52
90
 
53
- private
91
+ def wicked_pdf_javascript_src_tag(jsfile, options = {})
92
+ jsfile = WickedPdfHelper.add_extension(jsfile, 'js')
93
+ javascript_include_tag wicked_pdf_asset_path(jsfile), options
94
+ end
54
95
 
55
- # borrowed from actionpack/lib/action_view/helpers/asset_url_helper.rb
56
- URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
96
+ def wicked_pdf_javascript_include_tag(*sources)
97
+ sources.collect do |source|
98
+ source = WickedPdfHelper.add_extension(source, 'js')
99
+ "<script type='text/javascript'>#{read_asset(source)}</script>"
100
+ end.join("\n").html_safe
101
+ end
57
102
 
58
- def asset_pathname(source)
59
- if precompiled_or_absolute_asset?(source)
60
- asset = asset_path(source)
61
- pathname = prepend_protocol(asset)
62
- if pathname =~ URI_REGEXP
63
- # asset_path returns an absolute URL using asset_host if asset_host is set
103
+ def wicked_pdf_asset_path(asset)
104
+ if (pathname = asset_pathname(asset).to_s) =~ URI_REGEXP
64
105
  pathname
65
106
  else
66
- File.join(Rails.public_path, asset.sub(/\A#{Rails.application.config.action_controller.relative_url_root}/, ''))
107
+ "file:///#{pathname}"
67
108
  end
68
- else
69
- asset = Rails.application.assets.find_asset(source)
70
- asset ? asset.pathname : File.join(Rails.public_path, source)
71
109
  end
72
- end
73
110
 
74
- # will prepend a http or default_protocol to a protocol relative URL
75
- # or when no protcol is set.
76
- def prepend_protocol(source)
77
- protocol = WickedPdf.config[:default_protocol] || 'http'
78
- if source[0, 2] == '//'
79
- source = [protocol, ':', source].join
80
- elsif source[0] != '/' && !source[0, 8].include?('://')
81
- source = [protocol, '://', source].join
111
+ def wicked_pdf_asset_pack_path(asset)
112
+ return unless defined?(Webpacker)
113
+
114
+ if running_in_development?
115
+ asset_pack_path(asset)
116
+ else
117
+ wicked_pdf_asset_path webpacker_source_url(asset)
118
+ end
82
119
  end
83
- source
84
- end
85
120
 
86
- def precompiled_or_absolute_asset?(source)
87
- Rails.configuration.assets.compile == false ||
88
- source.to_s[0] == '/' ||
89
- source.to_s.match(/\Ahttps?\:\/\//)
90
- end
121
+ private
122
+
123
+ # borrowed from actionpack/lib/action_view/helpers/asset_url_helper.rb
124
+ URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
125
+
126
+ def asset_pathname(source)
127
+ if precompiled_or_absolute_asset?(source)
128
+ asset = asset_path(source)
129
+ pathname = prepend_protocol(asset)
130
+ if pathname =~ URI_REGEXP
131
+ # asset_path returns an absolute URL using asset_host if asset_host is set
132
+ pathname
133
+ else
134
+ File.join(Rails.public_path, asset.sub(/\A#{Rails.application.config.action_controller.relative_url_root}/, ''))
135
+ end
136
+ else
137
+ asset = find_asset(source)
138
+ if asset
139
+ # older versions need pathname, Sprockets 4 supports only filename
140
+ asset.respond_to?(:filename) ? asset.filename : asset.pathname
141
+ else
142
+ File.join(Rails.public_path, source)
143
+ end
144
+ end
145
+ end
91
146
 
92
- def read_asset(source)
93
- if precompiled_or_absolute_asset?(source)
94
- pathname = asset_pathname(source)
95
- if pathname =~ URI_REGEXP
96
- read_from_uri(pathname)
97
- elsif File.file?(pathname)
98
- IO.read(pathname)
147
+ def find_asset(path)
148
+ if Rails.application.assets.respond_to?(:find_asset)
149
+ Rails.application.assets.find_asset(path, :base_path => Rails.application.root.to_s)
150
+ elsif defined?(Propshaft::Assembly) && Rails.application.assets.is_a?(Propshaft::Assembly)
151
+ PropshaftAsset.new(Rails.application.assets.load_path.find(path))
152
+ else
153
+ Sprockets::Railtie.build_environment(Rails.application).find_asset(path, :base_path => Rails.application.root.to_s)
99
154
  end
100
- else
101
- Rails.application.assets.find_asset(source).to_s
102
155
  end
103
- end
104
156
 
105
- def read_from_uri(uri)
106
- encoding = ':UTF-8' if RUBY_VERSION > '1.8'
107
- asset = open(uri, "r#{encoding}", &:read)
108
- asset = gzip(asset) if WickedPdf.config[:expect_gzipped_remote_assets]
109
- asset
110
- end
157
+ # will prepend a http or default_protocol to a protocol relative URL
158
+ # or when no protcol is set.
159
+ def prepend_protocol(source)
160
+ protocol = WickedPdf.config[:default_protocol] || 'http'
161
+ if source[0, 2] == '//'
162
+ source = [protocol, ':', source].join
163
+ elsif source[0] != '/' && !source[0, 8].include?('://')
164
+ source = [protocol, '://', source].join
165
+ end
166
+ source
167
+ end
168
+
169
+ def precompiled_or_absolute_asset?(source)
170
+ !Rails.configuration.respond_to?(:assets) ||
171
+ Rails.configuration.assets.compile == false ||
172
+ source.to_s[0] == '/' ||
173
+ source.to_s.match(/\Ahttps?\:\/\//)
174
+ end
175
+
176
+ def read_asset(source)
177
+ if precompiled_or_absolute_asset?(source)
178
+ pathname = asset_pathname(source)
179
+ if pathname =~ URI_REGEXP
180
+ read_from_uri(pathname)
181
+ elsif File.file?(pathname)
182
+ IO.read(pathname)
183
+ end
184
+ else
185
+ find_asset(source).to_s
186
+ end
187
+ end
188
+
189
+ def read_from_uri(uri)
190
+ asset = Net::HTTP.get(URI(uri))
191
+ asset.force_encoding('UTF-8') if asset
192
+ asset = gzip(asset) if WickedPdf.config[:expect_gzipped_remote_assets]
193
+ asset
194
+ end
195
+
196
+ def gzip(asset)
197
+ stringified_asset = StringIO.new(asset)
198
+ gzipper = Zlib::GzipReader.new(stringified_asset)
199
+ gzipper.read
200
+ rescue Zlib::GzipFile::Error
201
+ nil
202
+ end
203
+
204
+ def webpacker_source_url(source)
205
+ return unless webpacker_version
206
+
207
+ # In Webpacker 3.2.0 asset_pack_url is introduced
208
+ if webpacker_version >= '3.2.0'
209
+ if (host = Rails.application.config.asset_host)
210
+ asset_pack_path(source, :host => host)
211
+ else
212
+ asset_pack_url(source)
213
+ end
214
+ else
215
+ source_path = asset_pack_path(source)
216
+ # Remove last slash from root path
217
+ root_url[0...-1] + source_path
218
+ end
219
+ end
111
220
 
112
- def gzip(asset)
113
- stringified_asset = StringIO.new(asset)
114
- gzipper = Zlib::GzipReader.new(stringified_asset)
115
- gzipper.read
116
- rescue Zlib::GzipFile::Error
117
- nil
221
+ def running_in_development?
222
+ return unless webpacker_version
223
+
224
+ # :dev_server method was added in webpacker 3.0.0
225
+ if Webpacker.respond_to?(:dev_server)
226
+ Webpacker.dev_server.running?
227
+ else
228
+ Rails.env.development? || Rails.env.test?
229
+ end
230
+ end
231
+
232
+ def webpacker_version
233
+ return unless defined?(Webpacker)
234
+
235
+ # If webpacker is used, need to check for version
236
+ require 'webpacker/version'
237
+
238
+ Webpacker::VERSION
239
+ end
118
240
  end
119
241
  end
120
242
  end
@@ -1,33 +1,36 @@
1
- module WickedPdfHelper
2
- def self.root_path
3
- String === Rails.root ? Pathname.new(Rails.root) : Rails.root
4
- end
1
+ class WickedPdf
2
+ module WickedPdfHelper
3
+ def self.root_path
4
+ String === Rails.root ? Pathname.new(Rails.root) : Rails.root
5
+ end
5
6
 
6
- def self.add_extension(filename, extension)
7
- filename.to_s.split('.').include?(extension) ? filename : "#{filename}.#{extension}"
8
- end
7
+ def self.add_extension(filename, extension)
8
+ filename.to_s.split('.').include?(extension) ? filename : "#{filename}.#{extension}"
9
+ end
9
10
 
10
- def wicked_pdf_stylesheet_link_tag(*sources)
11
- css_dir = WickedPdfHelper.root_path.join('public', 'stylesheets')
12
- css_text = sources.collect do |source|
13
- source = WickedPdfHelper.add_extension(source, 'css')
14
- "<style type='text/css'>#{File.read(css_dir.join(source))}</style>"
15
- end.join("\n")
16
- css_text.respond_to?(:html_safe) ? css_text.html_safe : css_text
17
- end
11
+ def wicked_pdf_stylesheet_link_tag(*sources)
12
+ css_dir = WickedPdfHelper.root_path.join('public', 'stylesheets')
13
+ css_text = sources.collect do |source|
14
+ source = WickedPdfHelper.add_extension(source, 'css')
15
+ "<style type='text/css'>#{File.read(css_dir.join(source))}</style>"
16
+ end.join("\n")
17
+ css_text.respond_to?(:html_safe) ? css_text.html_safe : css_text
18
+ end
18
19
 
19
- def wicked_pdf_image_tag(img, options = {})
20
- image_tag "file:///#{WickedPdfHelper.root_path.join('public', 'images', img)}", options
21
- end
20
+ def wicked_pdf_image_tag(img, options = {})
21
+ image_tag "file:///#{WickedPdfHelper.root_path.join('public', 'images', img)}", options
22
+ end
22
23
 
23
- def wicked_pdf_javascript_src_tag(jsfile, options = {})
24
- jsfile = WickedPdfHelper.add_extension(jsfile, 'js')
25
- src = "file:///#{WickedPdfHelper.root_path.join('public', 'javascripts', jsfile)}"
26
- content_tag('script', '', { 'type' => Mime::JS, 'src' => path_to_javascript(src) }.merge(options))
27
- end
24
+ def wicked_pdf_javascript_src_tag(jsfile, options = {})
25
+ jsfile = WickedPdfHelper.add_extension(jsfile, 'js')
26
+ type = ::Mime.respond_to?(:[]) ? ::Mime[:js] : ::Mime::JS # ::Mime[:js] cannot be used in Rails 2.3.
27
+ src = "file:///#{WickedPdfHelper.root_path.join('public', 'javascripts', jsfile)}"
28
+ content_tag('script', '', { 'type' => type, 'src' => path_to_javascript(src) }.merge(options))
29
+ end
28
30
 
29
- def wicked_pdf_javascript_include_tag(*sources)
30
- js_text = sources.collect { |source| wicked_pdf_javascript_src_tag(source, {}) }.join("\n")
31
- js_text.respond_to?(:html_safe) ? js_text.html_safe : js_text
31
+ def wicked_pdf_javascript_include_tag(*sources)
32
+ js_text = sources.collect { |source| wicked_pdf_javascript_src_tag(source, {}) }.join("\n")
33
+ js_text.respond_to?(:html_safe) ? js_text.html_safe : js_text
34
+ end
32
35
  end
33
36
  end