wicked_pdf 1.1.0 → 2.8.0
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 +62 -0
- data/.rubocop_todo.yml +81 -39
- data/CHANGELOG.md +194 -45
- data/README.md +136 -28
- data/Rakefile +12 -9
- data/gemfiles/5.0.gemfile +3 -1
- 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 +11 -0
- data/gemfiles/7.0.gemfile +11 -0
- data/generators/wicked_pdf/templates/wicked_pdf.rb +14 -5
- 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 +230 -0
- data/lib/wicked_pdf/pdf_helper.rb +35 -42
- data/lib/wicked_pdf/progress.rb +33 -0
- data/lib/wicked_pdf/railtie.rb +6 -44
- data/lib/wicked_pdf/tempfile.rb +33 -3
- data/lib/wicked_pdf/version.rb +1 -1
- data/lib/wicked_pdf/wicked_pdf_helper/assets.rb +200 -17
- data/lib/wicked_pdf/wicked_pdf_helper.rb +2 -1
- data/lib/wicked_pdf.rb +64 -275
- data/test/fixtures/database.yml +4 -0
- data/test/fixtures/manifest.js +3 -0
- data/test/functional/pdf_helper_test.rb +71 -0
- data/test/functional/wicked_pdf_helper_assets_test.rb +61 -0
- data/test/test_helper.rb +13 -8
- data/test/unit/wicked_pdf_binary_test.rb +26 -0
- data/test/unit/wicked_pdf_option_parser_test.rb +133 -0
- data/test/unit/wicked_pdf_test.rb +25 -146
- data/test/unit/wkhtmltopdf_location_test.rb +48 -0
- data/wicked_pdf.gemspec +20 -13
- metadata +79 -36
- data/.travis.yml +0 -57
- data/gemfiles/2.3.gemfile +0 -10
- data/gemfiles/3.0.gemfile +0 -12
- data/gemfiles/3.1.gemfile +0 -13
- data/gemfiles/3.2.gemfile +0 -12
- 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,17 +1,99 @@
|
|
1
|
-
require '
|
1
|
+
require 'net/http'
|
2
|
+
require 'delegate'
|
3
|
+
require 'stringio'
|
2
4
|
|
3
5
|
class WickedPdf
|
4
6
|
module WickedPdfHelper
|
5
7
|
module Assets
|
6
8
|
ASSET_URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/
|
7
9
|
|
10
|
+
class MissingAsset < StandardError; end
|
11
|
+
|
12
|
+
class MissingLocalAsset < MissingAsset
|
13
|
+
attr_reader :path
|
14
|
+
|
15
|
+
def initialize(path)
|
16
|
+
@path = path
|
17
|
+
super("Could not find asset '#{path}'")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class MissingRemoteAsset < MissingAsset
|
22
|
+
attr_reader :url, :response
|
23
|
+
|
24
|
+
def initialize(url, response)
|
25
|
+
@url = url
|
26
|
+
@response = response
|
27
|
+
super("Could not fetch asset '#{url}': server responded with #{response.code} #{response.message}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class PropshaftAsset < SimpleDelegator
|
32
|
+
def content_type
|
33
|
+
super.to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
content
|
38
|
+
end
|
39
|
+
|
40
|
+
def filename
|
41
|
+
path.to_s
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class SprocketsEnvironment
|
46
|
+
def self.instance
|
47
|
+
@instance ||= Sprockets::Railtie.build_environment(Rails.application)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.find_asset(*args)
|
51
|
+
instance.find_asset(*args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class LocalAsset
|
56
|
+
attr_reader :path
|
57
|
+
|
58
|
+
def initialize(path)
|
59
|
+
@path = path
|
60
|
+
end
|
61
|
+
|
62
|
+
def content_type
|
63
|
+
Mime::Type.lookup_by_extension(File.extname(path).delete('.'))
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
IO.read(path)
|
68
|
+
end
|
69
|
+
|
70
|
+
def filename
|
71
|
+
path.to_s
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
8
75
|
def wicked_pdf_asset_base64(path)
|
9
76
|
asset = find_asset(path)
|
10
|
-
raise
|
77
|
+
raise MissingLocalAsset, path if asset.nil?
|
78
|
+
|
11
79
|
base64 = Base64.encode64(asset.to_s).gsub(/\s+/, '')
|
12
80
|
"data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
|
13
81
|
end
|
14
82
|
|
83
|
+
# Using `image_tag` with URLs when generating PDFs (specifically large PDFs with lots of pages) can cause buffer/stack overflows.
|
84
|
+
#
|
85
|
+
def wicked_pdf_url_base64(url)
|
86
|
+
response = Net::HTTP.get_response(URI(url))
|
87
|
+
|
88
|
+
if response.is_a?(Net::HTTPSuccess)
|
89
|
+
base64 = Base64.encode64(response.body).gsub(/\s+/, '')
|
90
|
+
"data:#{response.content_type};base64,#{Rack::Utils.escape(base64)}"
|
91
|
+
else
|
92
|
+
Rails.logger.warn("[wicked_pdf] #{response.code} #{response.message}: #{url}")
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
15
97
|
def wicked_pdf_stylesheet_link_tag(*sources)
|
16
98
|
stylesheet_contents = sources.collect do |source|
|
17
99
|
source = WickedPdfHelper.add_extension(source, 'css')
|
@@ -27,6 +109,33 @@ class WickedPdf
|
|
27
109
|
end.html_safe
|
28
110
|
end
|
29
111
|
|
112
|
+
def wicked_pdf_stylesheet_pack_tag(*sources)
|
113
|
+
return unless defined?(Webpacker)
|
114
|
+
|
115
|
+
if running_in_development?
|
116
|
+
stylesheet_pack_tag(*sources)
|
117
|
+
else
|
118
|
+
css_text = sources.collect do |source|
|
119
|
+
source = WickedPdfHelper.add_extension(source, 'css')
|
120
|
+
wicked_pdf_stylesheet_link_tag(webpacker_source_url(source))
|
121
|
+
end.join("\n")
|
122
|
+
css_text.respond_to?(:html_safe) ? css_text.html_safe : css_text
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def wicked_pdf_javascript_pack_tag(*sources)
|
127
|
+
return unless defined?(Webpacker)
|
128
|
+
|
129
|
+
if running_in_development?
|
130
|
+
javascript_pack_tag(*sources)
|
131
|
+
else
|
132
|
+
sources.collect do |source|
|
133
|
+
source = WickedPdfHelper.add_extension(source, 'js')
|
134
|
+
"<script type='text/javascript'>#{read_asset(webpacker_source_url(source))}</script>"
|
135
|
+
end.join("\n").html_safe
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
30
139
|
def wicked_pdf_image_tag(img, options = {})
|
31
140
|
image_tag wicked_pdf_asset_path(img), options
|
32
141
|
end
|
@@ -51,6 +160,16 @@ class WickedPdf
|
|
51
160
|
end
|
52
161
|
end
|
53
162
|
|
163
|
+
def wicked_pdf_asset_pack_path(asset)
|
164
|
+
return unless defined?(Webpacker)
|
165
|
+
|
166
|
+
if running_in_development?
|
167
|
+
asset_pack_path(asset)
|
168
|
+
else
|
169
|
+
wicked_pdf_asset_path webpacker_source_url(asset)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
54
173
|
private
|
55
174
|
|
56
175
|
# borrowed from actionpack/lib/action_view/helpers/asset_url_helper.rb
|
@@ -68,15 +187,25 @@ class WickedPdf
|
|
68
187
|
end
|
69
188
|
else
|
70
189
|
asset = find_asset(source)
|
71
|
-
|
190
|
+
if asset
|
191
|
+
# older versions need pathname, Sprockets 4 supports only filename
|
192
|
+
asset.respond_to?(:filename) ? asset.filename : asset.pathname
|
193
|
+
else
|
194
|
+
File.join(Rails.public_path, source)
|
195
|
+
end
|
72
196
|
end
|
73
197
|
end
|
74
198
|
|
75
199
|
def find_asset(path)
|
76
200
|
if Rails.application.assets.respond_to?(:find_asset)
|
77
|
-
Rails.application.assets.find_asset(path)
|
201
|
+
Rails.application.assets.find_asset(path, :base_path => Rails.application.root.to_s)
|
202
|
+
elsif defined?(Propshaft::Assembly) && Rails.application.assets.is_a?(Propshaft::Assembly)
|
203
|
+
PropshaftAsset.new(Rails.application.assets.load_path.find(path))
|
204
|
+
elsif Rails.application.respond_to?(:assets_manifest)
|
205
|
+
asset_path = File.join(Rails.application.assets_manifest.dir, Rails.application.assets_manifest.assets[path])
|
206
|
+
LocalAsset.new(asset_path) if File.file?(asset_path)
|
78
207
|
else
|
79
|
-
|
208
|
+
SprocketsEnvironment.find_asset(path, :base_path => Rails.application.root.to_s)
|
80
209
|
end
|
81
210
|
end
|
82
211
|
|
@@ -93,27 +222,43 @@ class WickedPdf
|
|
93
222
|
end
|
94
223
|
|
95
224
|
def precompiled_or_absolute_asset?(source)
|
96
|
-
Rails.configuration.assets
|
225
|
+
!Rails.configuration.respond_to?(:assets) ||
|
226
|
+
Rails.configuration.assets.compile == false ||
|
97
227
|
source.to_s[0] == '/' ||
|
98
228
|
source.to_s.match(/\Ahttps?\:\/\//)
|
99
229
|
end
|
100
230
|
|
101
231
|
def read_asset(source)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
232
|
+
asset = find_asset(source)
|
233
|
+
return asset.to_s.force_encoding('UTF-8') if asset
|
234
|
+
|
235
|
+
unless precompiled_or_absolute_asset?(source)
|
236
|
+
raise MissingLocalAsset, source if WickedPdf.config[:raise_on_missing_assets]
|
237
|
+
|
238
|
+
return
|
239
|
+
end
|
240
|
+
|
241
|
+
pathname = asset_pathname(source)
|
242
|
+
if pathname =~ URI_REGEXP
|
243
|
+
read_from_uri(pathname)
|
244
|
+
elsif File.file?(pathname)
|
245
|
+
IO.read(pathname)
|
246
|
+
elsif WickedPdf.config[:raise_on_missing_assets]
|
247
|
+
raise MissingLocalAsset, pathname if WickedPdf.config[:raise_on_missing_assets]
|
111
248
|
end
|
112
249
|
end
|
113
250
|
|
114
251
|
def read_from_uri(uri)
|
115
|
-
|
116
|
-
|
252
|
+
response = Net::HTTP.get_response(URI(uri))
|
253
|
+
|
254
|
+
unless response.is_a?(Net::HTTPSuccess)
|
255
|
+
raise MissingRemoteAsset.new(uri, response) if WickedPdf.config[:raise_on_missing_assets]
|
256
|
+
|
257
|
+
return
|
258
|
+
end
|
259
|
+
|
260
|
+
asset = response.body
|
261
|
+
asset.force_encoding('UTF-8') if asset
|
117
262
|
asset = gzip(asset) if WickedPdf.config[:expect_gzipped_remote_assets]
|
118
263
|
asset
|
119
264
|
end
|
@@ -125,6 +270,44 @@ class WickedPdf
|
|
125
270
|
rescue Zlib::GzipFile::Error
|
126
271
|
nil
|
127
272
|
end
|
273
|
+
|
274
|
+
def webpacker_source_url(source)
|
275
|
+
return unless webpacker_version
|
276
|
+
|
277
|
+
# In Webpacker 3.2.0 asset_pack_url is introduced
|
278
|
+
if webpacker_version >= '3.2.0'
|
279
|
+
if (host = Rails.application.config.asset_host)
|
280
|
+
asset_pack_path(source, :host => host)
|
281
|
+
else
|
282
|
+
asset_pack_url(source)
|
283
|
+
end
|
284
|
+
else
|
285
|
+
source_path = asset_pack_path(source)
|
286
|
+
# Remove last slash from root path
|
287
|
+
root_url[0...-1] + source_path
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def running_in_development?
|
292
|
+
return unless webpacker_version
|
293
|
+
|
294
|
+
# :dev_server method was added in webpacker 3.0.0
|
295
|
+
if Webpacker.respond_to?(:dev_server)
|
296
|
+
Webpacker.dev_server.running?
|
297
|
+
else
|
298
|
+
Rails.env.development? || Rails.env.test?
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def webpacker_version
|
303
|
+
if defined?(Shakapacker)
|
304
|
+
require 'shakapacker/version'
|
305
|
+
Shakapacker::VERSION
|
306
|
+
elsif defined?(Webpacker)
|
307
|
+
require 'webpacker/version'
|
308
|
+
Webpacker::VERSION
|
309
|
+
end
|
310
|
+
end
|
128
311
|
end
|
129
312
|
end
|
130
313
|
end
|
@@ -23,8 +23,9 @@ class WickedPdf
|
|
23
23
|
|
24
24
|
def wicked_pdf_javascript_src_tag(jsfile, options = {})
|
25
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.
|
26
27
|
src = "file:///#{WickedPdfHelper.root_path.join('public', 'javascripts', jsfile)}"
|
27
|
-
content_tag('script', '', { 'type' =>
|
28
|
+
content_tag('script', '', { 'type' => type, 'src' => path_to_javascript(src) }.merge(options))
|
28
29
|
end
|
29
30
|
|
30
31
|
def wicked_pdf_javascript_include_tag(*sources)
|
data/lib/wicked_pdf.rb
CHANGED
@@ -1,48 +1,52 @@
|
|
1
1
|
# wkhtml2pdf Ruby interface
|
2
|
-
# http://
|
2
|
+
# http://wkhtmltopdf.org/
|
3
3
|
|
4
4
|
require 'logger'
|
5
5
|
require 'digest/md5'
|
6
6
|
require 'rbconfig'
|
7
|
+
require 'open3'
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
else
|
11
|
-
require 'open3'
|
12
|
-
end
|
13
|
-
|
14
|
-
begin
|
15
|
-
require 'active_support/core_ext/module/attribute_accessors'
|
16
|
-
rescue LoadError
|
17
|
-
require 'active_support/core_ext/class/attribute_accessors'
|
18
|
-
end
|
19
|
-
|
20
|
-
begin
|
21
|
-
require 'active_support/core_ext/object/blank'
|
22
|
-
rescue LoadError
|
23
|
-
require 'active_support/core_ext/blank'
|
24
|
-
end
|
9
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
10
|
+
require 'active_support/core_ext/object/blank'
|
25
11
|
|
26
12
|
require 'wicked_pdf/version'
|
27
13
|
require 'wicked_pdf/railtie'
|
14
|
+
require 'wicked_pdf/option_parser'
|
28
15
|
require 'wicked_pdf/tempfile'
|
16
|
+
require 'wicked_pdf/binary'
|
29
17
|
require 'wicked_pdf/middleware'
|
18
|
+
require 'wicked_pdf/progress'
|
30
19
|
|
31
20
|
class WickedPdf
|
32
21
|
DEFAULT_BINARY_VERSION = Gem::Version.new('0.9.9')
|
33
|
-
BINARY_VERSION_WITHOUT_DASHES = Gem::Version.new('0.12.0')
|
34
|
-
EXE_NAME = 'wkhtmltopdf'.freeze
|
35
22
|
@@config = {}
|
36
|
-
cattr_accessor :config
|
37
|
-
|
23
|
+
cattr_accessor :config, :silence_deprecations
|
24
|
+
|
25
|
+
include Progress
|
26
|
+
|
27
|
+
def self.config=(config)
|
28
|
+
::Kernel.warn 'WickedPdf.config= is deprecated and will be removed in future versions. Use WickedPdf.configure instead.' unless @@silence_deprecations
|
29
|
+
|
30
|
+
@@config = config
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.configure
|
34
|
+
config = OpenStruct.new(@@config)
|
35
|
+
yield config
|
36
|
+
|
37
|
+
@@config.merge! config.to_h
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.clear_config
|
41
|
+
@@config = {}
|
42
|
+
end
|
38
43
|
|
39
44
|
def initialize(wkhtmltopdf_binary_path = nil)
|
40
|
-
@
|
41
|
-
|
42
|
-
raise "Bad #{EXE_NAME}'s path: #{@exe_path}" unless File.exist?(@exe_path)
|
43
|
-
raise "#{EXE_NAME} is not executable" unless File.executable?(@exe_path)
|
45
|
+
@binary = Binary.new(wkhtmltopdf_binary_path, DEFAULT_BINARY_VERSION)
|
46
|
+
end
|
44
47
|
|
45
|
-
|
48
|
+
def binary_version
|
49
|
+
@binary.version
|
46
50
|
end
|
47
51
|
|
48
52
|
def pdf_from_html_file(filepath, options = {})
|
@@ -52,51 +56,58 @@ class WickedPdf
|
|
52
56
|
def pdf_from_string(string, options = {})
|
53
57
|
options = options.dup
|
54
58
|
options.merge!(WickedPdf.config) { |_key, option, _config| option }
|
55
|
-
string_file =
|
56
|
-
string_file.
|
57
|
-
string_file.
|
58
|
-
string_file.close
|
59
|
-
|
60
|
-
pdf = pdf_from_html_file(string_file.path, options)
|
61
|
-
pdf
|
59
|
+
string_file = WickedPdf::Tempfile.new('wicked_pdf.html', options[:temp_path])
|
60
|
+
string_file.write_in_chunks(string)
|
61
|
+
pdf_from_html_file(string_file.path, options)
|
62
62
|
ensure
|
63
|
-
|
63
|
+
if options[:delete_temporary_files] && string_file
|
64
|
+
string_file.close!
|
65
|
+
elsif string_file
|
66
|
+
string_file.close
|
67
|
+
end
|
64
68
|
end
|
65
69
|
|
66
|
-
def pdf_from_url(url, options = {})
|
70
|
+
def pdf_from_url(url, options = {}) # rubocop:disable Metrics/CyclomaticComplexity
|
67
71
|
# merge in global config options
|
68
72
|
options.merge!(WickedPdf.config) { |_key, option, _config| option }
|
69
|
-
generated_pdf_file =
|
70
|
-
command = [@
|
71
|
-
command
|
72
|
-
command +=
|
73
|
+
generated_pdf_file = WickedPdf::Tempfile.new('wicked_pdf_generated_file.pdf', options[:temp_path])
|
74
|
+
command = [@binary.path]
|
75
|
+
command.unshift(@binary.xvfb_run_path) if options[:use_xvfb]
|
76
|
+
command += option_parser.parse(options)
|
73
77
|
command << url
|
74
78
|
command << generated_pdf_file.path.to_s
|
75
79
|
|
76
80
|
print_command(command.inspect) if in_development_mode?
|
77
81
|
|
78
|
-
|
79
|
-
|
82
|
+
if track_progress?(options)
|
83
|
+
invoke_with_progress(command, options)
|
84
|
+
else
|
85
|
+
_out, err, status = Open3.capture3(*command)
|
86
|
+
err = [status.to_s, err].join("\n") if !err.empty? || !status.success?
|
80
87
|
end
|
81
88
|
if options[:return_file]
|
82
89
|
return_file = options.delete(:return_file)
|
83
90
|
return generated_pdf_file
|
84
91
|
end
|
85
|
-
|
86
|
-
generated_pdf_file.
|
87
|
-
|
92
|
+
|
93
|
+
pdf = generated_pdf_file.read_in_chunks
|
94
|
+
|
95
|
+
raise "Error generating PDF\n Command Error: #{err}" if options[:raise_on_all_errors] && !err.empty?
|
88
96
|
raise "PDF could not be generated!\n Command Error: #{err}" if pdf && pdf.rstrip.empty?
|
97
|
+
|
89
98
|
pdf
|
90
|
-
rescue => e
|
99
|
+
rescue StandardError => e
|
91
100
|
raise "Failed to execute:\n#{command}\nError: #{e}"
|
92
101
|
ensure
|
102
|
+
clean_temp_files
|
93
103
|
generated_pdf_file.close! if generated_pdf_file && !return_file
|
94
104
|
end
|
95
105
|
|
96
106
|
private
|
97
107
|
|
98
108
|
def in_development_mode?
|
99
|
-
return Rails.env == 'development' if defined?(Rails)
|
109
|
+
return Rails.env == 'development' if defined?(Rails.env)
|
110
|
+
|
100
111
|
RAILS_ENV == 'development' if defined?(RAILS_ENV)
|
101
112
|
end
|
102
113
|
|
@@ -105,238 +116,16 @@ class WickedPdf
|
|
105
116
|
end
|
106
117
|
|
107
118
|
def print_command(cmd)
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
def retrieve_binary_version
|
112
|
-
_stdin, stdout, _stderr = Open3.popen3(@exe_path + ' -V')
|
113
|
-
@binary_version = parse_version(stdout.gets(nil))
|
114
|
-
rescue
|
115
|
-
DEFAULT_BINARY_VERSION
|
116
|
-
end
|
117
|
-
|
118
|
-
def parse_version(version_info)
|
119
|
-
match_data = /wkhtmltopdf\s*(\d*\.\d*\.\d*\w*)/.match(version_info)
|
120
|
-
if match_data && (2 == match_data.length)
|
121
|
-
Gem::Version.new(match_data[1])
|
122
|
-
else
|
123
|
-
DEFAULT_BINARY_VERSION
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def parse_options(options)
|
128
|
-
[
|
129
|
-
parse_extra(options),
|
130
|
-
parse_others(options),
|
131
|
-
parse_global(options),
|
132
|
-
parse_outline(options.delete(:outline)),
|
133
|
-
parse_header_footer(:header => options.delete(:header),
|
134
|
-
:footer => options.delete(:footer),
|
135
|
-
:layout => options[:layout]),
|
136
|
-
parse_cover(options.delete(:cover)),
|
137
|
-
parse_toc(options.delete(:toc)),
|
138
|
-
parse_basic_auth(options)
|
139
|
-
].flatten
|
140
|
-
end
|
141
|
-
|
142
|
-
def parse_extra(options)
|
143
|
-
return [] if options[:extra].nil?
|
144
|
-
return options[:extra].split if options[:extra].respond_to?(:split)
|
145
|
-
options[:extra]
|
119
|
+
Rails.logger.debug '[wicked_pdf]: ' + cmd
|
146
120
|
end
|
147
121
|
|
148
|
-
def
|
149
|
-
|
150
|
-
user, passwd = Base64.decode64(options[:basic_auth]).split(':')
|
151
|
-
['--username', user, '--password', passwd]
|
152
|
-
else
|
153
|
-
[]
|
154
|
-
end
|
122
|
+
def option_parser
|
123
|
+
@option_parser ||= OptionParser.new(binary_version)
|
155
124
|
end
|
156
125
|
|
157
|
-
def
|
158
|
-
|
159
|
-
return value.collect { |v| make_option(name, v, type) }
|
160
|
-
end
|
161
|
-
if type == :name_value
|
162
|
-
parts = value.to_s.split(' ')
|
163
|
-
["--#{name.tr('_', '-')}", *parts]
|
164
|
-
elsif type == :boolean
|
165
|
-
if value
|
166
|
-
["--#{name.tr('_', '-')}"]
|
167
|
-
else
|
168
|
-
[]
|
169
|
-
end
|
170
|
-
else
|
171
|
-
["--#{name.tr('_', '-')}", value.to_s]
|
172
|
-
end
|
173
|
-
end
|
126
|
+
def clean_temp_files
|
127
|
+
return unless option_parser.hf_tempfiles.present?
|
174
128
|
|
175
|
-
|
176
|
-
if binary_version < BINARY_VERSION_WITHOUT_DASHES
|
177
|
-
"--#{name}"
|
178
|
-
else
|
179
|
-
name
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def make_options(options, names, prefix = '', type = :string)
|
184
|
-
return [] if options.nil?
|
185
|
-
names.collect do |o|
|
186
|
-
if options[o].blank?
|
187
|
-
[]
|
188
|
-
else
|
189
|
-
make_option("#{prefix.blank? ? '' : prefix + '-'}#{o}",
|
190
|
-
options[o],
|
191
|
-
type)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
def parse_header_footer(options)
|
197
|
-
r = []
|
198
|
-
[:header, :footer].collect do |hf|
|
199
|
-
next if options[hf].blank?
|
200
|
-
opt_hf = options[hf]
|
201
|
-
r += make_options(opt_hf, [:center, :font_name, :left, :right], hf.to_s)
|
202
|
-
r += make_options(opt_hf, [:font_size, :spacing], hf.to_s, :numeric)
|
203
|
-
r += make_options(opt_hf, [:line], hf.to_s, :boolean)
|
204
|
-
if options[hf] && options[hf][:content]
|
205
|
-
@hf_tempfiles = [] unless defined?(@hf_tempfiles)
|
206
|
-
@hf_tempfiles.push(tf = WickedPdfTempfile.new("wicked_#{hf}_pdf.html"))
|
207
|
-
tf.write options[hf][:content]
|
208
|
-
tf.flush
|
209
|
-
options[hf][:html] = {}
|
210
|
-
options[hf][:html][:url] = "file:///#{tf.path}"
|
211
|
-
end
|
212
|
-
unless opt_hf[:html].blank?
|
213
|
-
r += make_option("#{hf}-html", opt_hf[:html][:url]) unless opt_hf[:html][:url].blank?
|
214
|
-
end
|
215
|
-
end unless options.blank?
|
216
|
-
r
|
217
|
-
end
|
218
|
-
|
219
|
-
def parse_cover(argument)
|
220
|
-
arg = argument.to_s
|
221
|
-
return [] if arg.blank?
|
222
|
-
# Filesystem path or URL - hand off to wkhtmltopdf
|
223
|
-
if argument.is_a?(Pathname) || (arg[0, 4] == 'http')
|
224
|
-
[valid_option('cover'), arg]
|
225
|
-
else # HTML content
|
226
|
-
@hf_tempfiles ||= []
|
227
|
-
@hf_tempfiles << tf = WickedPdfTempfile.new('wicked_cover_pdf.html')
|
228
|
-
tf.write arg
|
229
|
-
tf.flush
|
230
|
-
[valid_option('cover'), tf.path]
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
def parse_toc(options)
|
235
|
-
return [] if options.nil?
|
236
|
-
r = [valid_option('toc')]
|
237
|
-
unless options.blank?
|
238
|
-
r += make_options(options, [:font_name, :header_text], 'toc')
|
239
|
-
r += make_options(options, [:xsl_style_sheet])
|
240
|
-
r += make_options(options, [:depth,
|
241
|
-
:header_fs,
|
242
|
-
:text_size_shrink,
|
243
|
-
:l1_font_size,
|
244
|
-
:l2_font_size,
|
245
|
-
:l3_font_size,
|
246
|
-
:l4_font_size,
|
247
|
-
:l5_font_size,
|
248
|
-
:l6_font_size,
|
249
|
-
:l7_font_size,
|
250
|
-
:level_indentation,
|
251
|
-
:l1_indentation,
|
252
|
-
:l2_indentation,
|
253
|
-
:l3_indentation,
|
254
|
-
:l4_indentation,
|
255
|
-
:l5_indentation,
|
256
|
-
:l6_indentation,
|
257
|
-
:l7_indentation], 'toc', :numeric)
|
258
|
-
r += make_options(options, [:no_dots,
|
259
|
-
:disable_links,
|
260
|
-
:disable_back_links], 'toc', :boolean)
|
261
|
-
r += make_options(options, [:disable_dotted_lines,
|
262
|
-
:disable_toc_links], nil, :boolean)
|
263
|
-
end
|
264
|
-
r
|
265
|
-
end
|
266
|
-
|
267
|
-
def parse_outline(options)
|
268
|
-
r = []
|
269
|
-
unless options.blank?
|
270
|
-
r = make_options(options, [:outline], '', :boolean)
|
271
|
-
r += make_options(options, [:outline_depth], '', :numeric)
|
272
|
-
end
|
273
|
-
r
|
274
|
-
end
|
275
|
-
|
276
|
-
def parse_margins(options)
|
277
|
-
make_options(options, [:top, :bottom, :left, :right], 'margin', :numeric)
|
278
|
-
end
|
279
|
-
|
280
|
-
def parse_global(options)
|
281
|
-
r = []
|
282
|
-
unless options.blank?
|
283
|
-
r += make_options(options, [:orientation,
|
284
|
-
:dpi,
|
285
|
-
:page_size,
|
286
|
-
:page_width,
|
287
|
-
:title])
|
288
|
-
r += make_options(options, [:lowquality,
|
289
|
-
:grayscale,
|
290
|
-
:no_pdf_compression], '', :boolean)
|
291
|
-
r += make_options(options, [:image_dpi,
|
292
|
-
:image_quality,
|
293
|
-
:page_height], '', :numeric)
|
294
|
-
r += parse_margins(options.delete(:margin))
|
295
|
-
end
|
296
|
-
r
|
297
|
-
end
|
298
|
-
|
299
|
-
def parse_others(options)
|
300
|
-
r = []
|
301
|
-
unless options.blank?
|
302
|
-
r += make_options(options, [:proxy,
|
303
|
-
:username,
|
304
|
-
:password,
|
305
|
-
:encoding,
|
306
|
-
:user_style_sheet,
|
307
|
-
:viewport_size,
|
308
|
-
:window_status])
|
309
|
-
r += make_options(options, [:cookie,
|
310
|
-
:post], '', :name_value)
|
311
|
-
r += make_options(options, [:redirect_delay,
|
312
|
-
:zoom,
|
313
|
-
:page_offset,
|
314
|
-
:javascript_delay], '', :numeric)
|
315
|
-
r += make_options(options, [:book,
|
316
|
-
:default_header,
|
317
|
-
:disable_javascript,
|
318
|
-
:enable_plugins,
|
319
|
-
:disable_internal_links,
|
320
|
-
:disable_external_links,
|
321
|
-
:print_media_type,
|
322
|
-
:disable_smart_shrinking,
|
323
|
-
:use_xserver,
|
324
|
-
:no_background,
|
325
|
-
:no_stop_slow_scripts], '', :boolean)
|
326
|
-
end
|
327
|
-
r
|
328
|
-
end
|
329
|
-
|
330
|
-
def find_wkhtmltopdf_binary_path
|
331
|
-
possible_locations = (ENV['PATH'].split(':') + %w(/usr/bin /usr/local/bin ~/bin)).uniq
|
332
|
-
exe_path ||= WickedPdf.config[:exe_path] unless WickedPdf.config.empty?
|
333
|
-
exe_path ||= begin
|
334
|
-
detected_path = (defined?(Bundler) ? `bundle exec which wkhtmltopdf` : `which wkhtmltopdf`).chomp
|
335
|
-
detected_path.present? && detected_path
|
336
|
-
rescue
|
337
|
-
nil
|
338
|
-
end
|
339
|
-
exe_path ||= possible_locations.map { |l| File.expand_path("#{l}/#{EXE_NAME}") }.find { |location| File.exist?(location) }
|
340
|
-
exe_path || ''
|
129
|
+
option_parser.hf_tempfiles.each { |file| File.delete(file) }
|
341
130
|
end
|
342
131
|
end
|