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
@@ -1,120 +1,242 @@
|
|
1
|
-
require '
|
1
|
+
require 'net/http'
|
2
|
+
require 'delegate'
|
2
3
|
|
3
|
-
|
4
|
-
module
|
5
|
-
|
4
|
+
class WickedPdf
|
5
|
+
module WickedPdfHelper
|
6
|
+
module Assets
|
7
|
+
ASSET_URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
21
|
-
|
22
|
-
"
|
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
|
-
"
|
40
|
+
Rails.logger.warn("[wicked_pdf] #{response.code} #{response.message}: #{url}")
|
41
|
+
nil
|
25
42
|
end
|
26
|
-
end
|
27
|
-
end
|
43
|
+
end
|
28
44
|
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
34
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
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
|
-
|
56
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def self.add_extension(filename, extension)
|
8
|
+
filename.to_s.split('.').include?(extension) ? filename : "#{filename}.#{extension}"
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
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
|