super-max-pkg 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/super-max-pkg.gemspec +12 -0
- data/wicked_pdf-2.8.2/CHANGELOG.md +254 -0
- data/wicked_pdf-2.8.2/Gemfile +3 -0
- data/wicked_pdf-2.8.2/LICENSE.txt +22 -0
- data/wicked_pdf-2.8.2/README.md +504 -0
- data/wicked_pdf-2.8.2/Rakefile +58 -0
- data/wicked_pdf-2.8.2/gemfiles/5.0.gemfile +8 -0
- data/wicked_pdf-2.8.2/gemfiles/5.1.gemfile +8 -0
- data/wicked_pdf-2.8.2/gemfiles/5.2.gemfile +9 -0
- data/wicked_pdf-2.8.2/gemfiles/6.0.gemfile +10 -0
- data/wicked_pdf-2.8.2/gemfiles/6.1.gemfile +11 -0
- data/wicked_pdf-2.8.2/gemfiles/7.0.gemfile +11 -0
- data/wicked_pdf-2.8.2/generators/wicked_pdf/templates/wicked_pdf.rb +30 -0
- data/wicked_pdf-2.8.2/generators/wicked_pdf/wicked_pdf_generator.rb +7 -0
- data/wicked_pdf-2.8.2/init.rb +2 -0
- data/wicked_pdf-2.8.2/lib/generators/wicked_pdf_generator.rb +7 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf/binary.rb +65 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf/middleware.rb +92 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf/option_parser.rb +230 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf/pdf_helper.rb +128 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf/progress.rb +33 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf/railtie.rb +17 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf/tempfile.rb +43 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf/version.rb +3 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf/wicked_pdf_helper/assets.rb +332 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf/wicked_pdf_helper.rb +36 -0
- data/wicked_pdf-2.8.2/lib/wicked_pdf.rb +132 -0
- data/wicked_pdf-2.8.2/test/fixtures/database.yml +4 -0
- data/wicked_pdf-2.8.2/test/fixtures/document_with_long_line.html +16 -0
- data/wicked_pdf-2.8.2/test/fixtures/manifest.js +3 -0
- data/wicked_pdf-2.8.2/test/fixtures/subdirectory/nested.js +1 -0
- data/wicked_pdf-2.8.2/test/fixtures/wicked.css +1 -0
- data/wicked_pdf-2.8.2/test/fixtures/wicked.js +1 -0
- data/wicked_pdf-2.8.2/test/functional/pdf_helper_test.rb +96 -0
- data/wicked_pdf-2.8.2/test/functional/wicked_pdf_helper_assets_test.rb +252 -0
- data/wicked_pdf-2.8.2/test/functional/wicked_pdf_helper_test.rb +28 -0
- data/wicked_pdf-2.8.2/test/test_helper.rb +40 -0
- data/wicked_pdf-2.8.2/test/unit/wicked_pdf_binary_test.rb +26 -0
- data/wicked_pdf-2.8.2/test/unit/wicked_pdf_option_parser_test.rb +133 -0
- data/wicked_pdf-2.8.2/test/unit/wicked_pdf_test.rb +100 -0
- data/wicked_pdf-2.8.2/test/unit/wkhtmltopdf_location_test.rb +48 -0
- data/wicked_pdf-2.8.2/wicked_pdf.gemspec +42 -0
- metadata +83 -0
|
@@ -0,0 +1,7 @@
|
|
|
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'
|
|
6
|
+
end
|
|
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
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
class WickedPdf
|
|
2
|
+
class Middleware
|
|
3
|
+
def initialize(app, options = {}, conditions = {})
|
|
4
|
+
@app = app
|
|
5
|
+
@options = (WickedPdf.config || {}).merge(options)
|
|
6
|
+
@conditions = conditions
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(env)
|
|
10
|
+
@request = Rack::Request.new(env)
|
|
11
|
+
@render_pdf = false
|
|
12
|
+
|
|
13
|
+
set_request_to_render_as_pdf(env) if render_as_pdf?
|
|
14
|
+
status, headers, response = @app.call(env)
|
|
15
|
+
|
|
16
|
+
if rendering_pdf? && headers['Content-Type'] =~ /text\/html|application\/xhtml\+xml/
|
|
17
|
+
body = response.respond_to?(:body) ? response.body : response.join
|
|
18
|
+
body = body.join if body.is_a?(Array)
|
|
19
|
+
|
|
20
|
+
body = WickedPdf.new(@options[:wkhtmltopdf]).pdf_from_string(translate_paths(body, env), @options)
|
|
21
|
+
|
|
22
|
+
response = [body]
|
|
23
|
+
|
|
24
|
+
# Do not cache PDFs
|
|
25
|
+
headers.delete('ETag')
|
|
26
|
+
headers.delete('Cache-Control')
|
|
27
|
+
|
|
28
|
+
headers['Content-Length'] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
|
|
29
|
+
headers['Content-Type'] = 'application/pdf'
|
|
30
|
+
if @options.fetch(:disposition, '') == 'attachment'
|
|
31
|
+
headers['Content-Disposition'] = 'attachment'
|
|
32
|
+
headers['Content-Transfer-Encoding'] = 'binary'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
[status, headers, response]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# Change relative paths to absolute
|
|
42
|
+
def translate_paths(body, env)
|
|
43
|
+
# Host with protocol
|
|
44
|
+
root = WickedPdf.config[:root_url] || "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}/"
|
|
45
|
+
|
|
46
|
+
body.gsub(/(href|src)=(['"])\/([^\"']*|[^"']*)['"]/, '\1=\2' + root + '\3\2')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def rendering_pdf?
|
|
50
|
+
@render_pdf
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def render_as_pdf?
|
|
54
|
+
request_path_is_pdf = @request.path.match(%r{\.pdf$})
|
|
55
|
+
|
|
56
|
+
if request_path_is_pdf && @conditions[:only]
|
|
57
|
+
rules = [@conditions[:only]].flatten
|
|
58
|
+
rules.any? do |pattern|
|
|
59
|
+
if pattern.is_a?(Regexp)
|
|
60
|
+
@request.fullpath =~ pattern
|
|
61
|
+
else
|
|
62
|
+
@request.path[0, pattern.length] == pattern
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
elsif request_path_is_pdf && @conditions[:except]
|
|
66
|
+
rules = [@conditions[:except]].flatten
|
|
67
|
+
rules.map do |pattern|
|
|
68
|
+
if pattern.is_a?(Regexp)
|
|
69
|
+
return false if @request.fullpath =~ pattern
|
|
70
|
+
elsif @request.path[0, pattern.length] == pattern
|
|
71
|
+
return false
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
return true
|
|
76
|
+
else
|
|
77
|
+
request_path_is_pdf
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def set_request_to_render_as_pdf(env)
|
|
82
|
+
@render_pdf = true
|
|
83
|
+
%w[PATH_INFO REQUEST_URI].each { |e| env[e] = env[e].sub(%r{\.pdf\b}, '') }
|
|
84
|
+
env['HTTP_ACCEPT'] = concat(env['HTTP_ACCEPT'], Rack::Mime.mime_type('.html'))
|
|
85
|
+
env['Rack-Middleware-WickedPdf'] = 'true'
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def concat(accepts, type)
|
|
89
|
+
(accepts || '').split(',').unshift(type).compact.join(',')
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
class WickedPdf
|
|
2
|
+
class OptionParser
|
|
3
|
+
BINARY_VERSION_WITHOUT_DASHES = Gem::Version.new('0.12.0')
|
|
4
|
+
|
|
5
|
+
attr_reader :binary_version, :hf_tempfiles
|
|
6
|
+
|
|
7
|
+
def initialize(binary_version = WickedPdf::DEFAULT_BINARY_VERSION)
|
|
8
|
+
@binary_version = binary_version
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def parse(options)
|
|
12
|
+
[
|
|
13
|
+
parse_extra(options),
|
|
14
|
+
parse_others(options),
|
|
15
|
+
parse_global(options),
|
|
16
|
+
parse_outline(options.delete(:outline)),
|
|
17
|
+
parse_header_footer(:header => options.delete(:header),
|
|
18
|
+
:footer => options.delete(:footer),
|
|
19
|
+
:layout => options[:layout]),
|
|
20
|
+
parse_cover(options.delete(:cover)),
|
|
21
|
+
parse_toc(options.delete(:toc)),
|
|
22
|
+
parse_basic_auth(options)
|
|
23
|
+
].flatten
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def valid_option(name)
|
|
27
|
+
if binary_version < BINARY_VERSION_WITHOUT_DASHES
|
|
28
|
+
"--#{name}"
|
|
29
|
+
else
|
|
30
|
+
name
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def parse_extra(options)
|
|
37
|
+
return [] if options[:extra].nil?
|
|
38
|
+
return options[:extra].split if options[:extra].respond_to?(:split)
|
|
39
|
+
|
|
40
|
+
options[:extra]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def parse_basic_auth(options)
|
|
44
|
+
if options[:basic_auth]
|
|
45
|
+
user, passwd = Base64.decode64(options[:basic_auth]).split(':')
|
|
46
|
+
['--username', user, '--password', passwd]
|
|
47
|
+
else
|
|
48
|
+
[]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def parse_header_footer(options)
|
|
53
|
+
r = []
|
|
54
|
+
unless options.blank?
|
|
55
|
+
%i[header footer].collect do |hf|
|
|
56
|
+
next if options[hf].blank?
|
|
57
|
+
|
|
58
|
+
opt_hf = options[hf]
|
|
59
|
+
r += make_options(opt_hf, %i[center font_name left right], hf.to_s)
|
|
60
|
+
r += make_options(opt_hf, %i[font_size spacing], hf.to_s, :numeric)
|
|
61
|
+
r += make_options(opt_hf, [:line], hf.to_s, :boolean)
|
|
62
|
+
if options[hf] && options[hf][:content]
|
|
63
|
+
@hf_tempfiles = [] unless defined?(@hf_tempfiles)
|
|
64
|
+
@hf_tempfiles.push(tf = File.new(Dir::Tmpname.create(["wicked_#{hf}_pdf", '.html']) {}, 'w'))
|
|
65
|
+
tf.write options[hf][:content]
|
|
66
|
+
tf.flush
|
|
67
|
+
options[hf][:html] = {}
|
|
68
|
+
options[hf][:html][:url] = "file:///#{tf.path}"
|
|
69
|
+
end
|
|
70
|
+
unless opt_hf[:html].blank?
|
|
71
|
+
r += make_option("#{hf}-html", opt_hf[:html][:url]) unless opt_hf[:html][:url].blank?
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
r
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def parse_cover(argument)
|
|
79
|
+
arg = argument.to_s
|
|
80
|
+
return [] if arg.blank?
|
|
81
|
+
|
|
82
|
+
# Filesystem path or URL - hand off to wkhtmltopdf
|
|
83
|
+
if argument.is_a?(Pathname) || (arg[0, 4] == 'http')
|
|
84
|
+
[valid_option('cover'), arg]
|
|
85
|
+
else # HTML content
|
|
86
|
+
@hf_tempfiles ||= []
|
|
87
|
+
@hf_tempfiles << tf = WickedPdf::Tempfile.new('wicked_cover_pdf.html')
|
|
88
|
+
tf.write arg
|
|
89
|
+
tf.flush
|
|
90
|
+
[valid_option('cover'), tf.path]
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def parse_toc(options)
|
|
95
|
+
return [] if options.nil?
|
|
96
|
+
|
|
97
|
+
r = [valid_option('toc')]
|
|
98
|
+
unless options.blank?
|
|
99
|
+
r += make_options(options, %i[font_name header_text], 'toc')
|
|
100
|
+
r += make_options(options, [:xsl_style_sheet])
|
|
101
|
+
r += make_options(options, %i[depth
|
|
102
|
+
header_fs
|
|
103
|
+
text_size_shrink
|
|
104
|
+
l1_font_size
|
|
105
|
+
l2_font_size
|
|
106
|
+
l3_font_size
|
|
107
|
+
l4_font_size
|
|
108
|
+
l5_font_size
|
|
109
|
+
l6_font_size
|
|
110
|
+
l7_font_size
|
|
111
|
+
level_indentation
|
|
112
|
+
l1_indentation
|
|
113
|
+
l2_indentation
|
|
114
|
+
l3_indentation
|
|
115
|
+
l4_indentation
|
|
116
|
+
l5_indentation
|
|
117
|
+
l6_indentation
|
|
118
|
+
l7_indentation], 'toc', :numeric)
|
|
119
|
+
r += make_options(options, %i[no_dots
|
|
120
|
+
disable_links
|
|
121
|
+
disable_back_links], 'toc', :boolean)
|
|
122
|
+
r += make_options(options, %i[disable_dotted_lines
|
|
123
|
+
disable_toc_links], nil, :boolean)
|
|
124
|
+
end
|
|
125
|
+
r
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def parse_outline(options)
|
|
129
|
+
r = []
|
|
130
|
+
unless options.blank?
|
|
131
|
+
r = make_options(options, [:outline], '', :boolean)
|
|
132
|
+
r += make_options(options, [:outline_depth], '', :numeric)
|
|
133
|
+
end
|
|
134
|
+
r
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def parse_margins(options)
|
|
138
|
+
make_options(options, %i[top bottom left right], 'margin', :numeric)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def parse_global(options)
|
|
142
|
+
r = []
|
|
143
|
+
unless options.blank?
|
|
144
|
+
r += make_options(options, %i[orientation
|
|
145
|
+
dpi
|
|
146
|
+
page_size
|
|
147
|
+
page_width
|
|
148
|
+
title
|
|
149
|
+
log_level])
|
|
150
|
+
r += make_options(options, %i[lowquality
|
|
151
|
+
grayscale
|
|
152
|
+
no_pdf_compression
|
|
153
|
+
quiet], '', :boolean)
|
|
154
|
+
r += make_options(options, %i[image_dpi
|
|
155
|
+
image_quality
|
|
156
|
+
page_height], '', :numeric)
|
|
157
|
+
r += parse_margins(options.delete(:margin))
|
|
158
|
+
end
|
|
159
|
+
r
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def parse_others(options)
|
|
163
|
+
r = []
|
|
164
|
+
unless options.blank?
|
|
165
|
+
r += make_options(options, %i[proxy
|
|
166
|
+
username
|
|
167
|
+
password
|
|
168
|
+
encoding
|
|
169
|
+
user_style_sheet
|
|
170
|
+
viewport_size
|
|
171
|
+
window_status
|
|
172
|
+
allow])
|
|
173
|
+
r += make_options(options, %i[cookie
|
|
174
|
+
post], '', :name_value)
|
|
175
|
+
r += make_options(options, %i[redirect_delay
|
|
176
|
+
zoom
|
|
177
|
+
page_offset
|
|
178
|
+
javascript_delay], '', :numeric)
|
|
179
|
+
r += make_options(options, %i[book
|
|
180
|
+
default_header
|
|
181
|
+
disable_javascript
|
|
182
|
+
enable_plugins
|
|
183
|
+
disable_internal_links
|
|
184
|
+
disable_external_links
|
|
185
|
+
keep_relative_links
|
|
186
|
+
print_media_type
|
|
187
|
+
disable_local_file_access
|
|
188
|
+
enable_local_file_access
|
|
189
|
+
disable_smart_shrinking
|
|
190
|
+
use_xserver
|
|
191
|
+
no_background
|
|
192
|
+
images
|
|
193
|
+
no_images
|
|
194
|
+
no_stop_slow_scripts], '', :boolean)
|
|
195
|
+
end
|
|
196
|
+
r
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def make_options(options, names, prefix = '', type = :string)
|
|
200
|
+
return [] if options.nil?
|
|
201
|
+
|
|
202
|
+
names.collect do |o|
|
|
203
|
+
if options[o].blank?
|
|
204
|
+
[]
|
|
205
|
+
else
|
|
206
|
+
make_option("#{prefix.blank? ? '' : prefix + '-'}#{o}",
|
|
207
|
+
options[o],
|
|
208
|
+
type)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def make_option(name, value, type = :string)
|
|
214
|
+
return value.collect { |v| make_option(name, v, type) } if value.is_a?(Array)
|
|
215
|
+
|
|
216
|
+
if type == :name_value
|
|
217
|
+
parts = value.to_s.split(' ')
|
|
218
|
+
["--#{name.tr('_', '-')}", *parts]
|
|
219
|
+
elsif type == :boolean
|
|
220
|
+
if value
|
|
221
|
+
["--#{name.tr('_', '-')}"]
|
|
222
|
+
else
|
|
223
|
+
[]
|
|
224
|
+
end
|
|
225
|
+
else
|
|
226
|
+
["--#{name.tr('_', '-')}", value.to_s]
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
class WickedPdf
|
|
2
|
+
module PdfHelper
|
|
3
|
+
def self.prepended(base)
|
|
4
|
+
# Protect from trying to augment modules that appear
|
|
5
|
+
# as the result of adding other gems.
|
|
6
|
+
return if base != ActionController::Base
|
|
7
|
+
|
|
8
|
+
base.class_eval do
|
|
9
|
+
after_action :clean_temp_files
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render(*args)
|
|
14
|
+
options = args.first
|
|
15
|
+
if options.is_a?(Hash) && options.key?(:pdf)
|
|
16
|
+
render_with_wicked_pdf(options)
|
|
17
|
+
else
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render_to_string(*args)
|
|
23
|
+
options = args.first
|
|
24
|
+
if options.is_a?(Hash) && options.key?(:pdf)
|
|
25
|
+
render_to_string_with_wicked_pdf(options)
|
|
26
|
+
else
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def render_with_wicked_pdf(options)
|
|
32
|
+
raise ArgumentError, 'missing keyword: pdf' unless options.is_a?(Hash) && options.key?(:pdf)
|
|
33
|
+
|
|
34
|
+
options[:basic_auth] = set_basic_auth(options)
|
|
35
|
+
make_and_send_pdf(options.delete(:pdf), (WickedPdf.config || {}).merge(options))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def render_to_string_with_wicked_pdf(options)
|
|
39
|
+
raise ArgumentError, 'missing keyword: pdf' unless options.is_a?(Hash) && options.key?(:pdf)
|
|
40
|
+
|
|
41
|
+
options[:basic_auth] = set_basic_auth(options)
|
|
42
|
+
options.delete :pdf
|
|
43
|
+
make_pdf((WickedPdf.config || {}).merge(options))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def set_basic_auth(options = {})
|
|
49
|
+
options[:basic_auth] ||= WickedPdf.config.fetch(:basic_auth) { false }
|
|
50
|
+
return unless options[:basic_auth] && request.env['HTTP_AUTHORIZATION']
|
|
51
|
+
|
|
52
|
+
request.env['HTTP_AUTHORIZATION'].split(' ').last
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def clean_temp_files
|
|
56
|
+
return unless defined?(@hf_tempfiles)
|
|
57
|
+
|
|
58
|
+
@hf_tempfiles.each(&:close)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def make_pdf(options = {})
|
|
62
|
+
render_opts = {
|
|
63
|
+
:template => options[:template],
|
|
64
|
+
:layout => options[:layout],
|
|
65
|
+
:formats => options[:formats],
|
|
66
|
+
:handlers => options[:handlers],
|
|
67
|
+
:assigns => options[:assigns]
|
|
68
|
+
}
|
|
69
|
+
render_opts[:inline] = options[:inline] if options[:inline]
|
|
70
|
+
render_opts[:locals] = options[:locals] if options[:locals]
|
|
71
|
+
render_opts[:file] = options[:file] if options[:file]
|
|
72
|
+
html_string = render_to_string(render_opts)
|
|
73
|
+
options = prerender_header_and_footer(options)
|
|
74
|
+
w = WickedPdf.new(options[:wkhtmltopdf])
|
|
75
|
+
w.pdf_from_string(html_string, options)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def make_and_send_pdf(pdf_name, options = {})
|
|
79
|
+
options[:wkhtmltopdf] ||= nil
|
|
80
|
+
options[:layout] ||= false
|
|
81
|
+
options[:template] ||= File.join(controller_path, action_name)
|
|
82
|
+
options[:disposition] ||= 'inline'
|
|
83
|
+
if options[:show_as_html]
|
|
84
|
+
render_opts = {
|
|
85
|
+
:template => options[:template],
|
|
86
|
+
:layout => options[:layout],
|
|
87
|
+
:formats => options[:formats],
|
|
88
|
+
:handlers => options[:handlers],
|
|
89
|
+
:assigns => options[:assigns],
|
|
90
|
+
:content_type => 'text/html'
|
|
91
|
+
}
|
|
92
|
+
render_opts[:inline] = options[:inline] if options[:inline]
|
|
93
|
+
render_opts[:locals] = options[:locals] if options[:locals]
|
|
94
|
+
render_opts[:file] = options[:file] if options[:file]
|
|
95
|
+
render(render_opts)
|
|
96
|
+
else
|
|
97
|
+
pdf_content = make_pdf(options)
|
|
98
|
+
File.open(options[:save_to_file], 'wb') { |file| file << pdf_content } if options[:save_to_file]
|
|
99
|
+
send_data(pdf_content, :filename => pdf_name + '.pdf', :type => 'application/pdf', :disposition => options[:disposition]) unless options[:save_only]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Given an options hash, prerenders content for the header and footer sections
|
|
104
|
+
# to temp files and return a new options hash including the URLs to these files.
|
|
105
|
+
def prerender_header_and_footer(options)
|
|
106
|
+
%i[header footer].each do |hf|
|
|
107
|
+
next unless options[hf] && options[hf][:html] && options[hf][:html][:template]
|
|
108
|
+
|
|
109
|
+
@hf_tempfiles = [] unless defined?(@hf_tempfiles)
|
|
110
|
+
@hf_tempfiles.push(tf = WickedPdf::Tempfile.new("wicked_#{hf}_pdf.html"))
|
|
111
|
+
options[hf][:html][:layout] ||= options[:layout]
|
|
112
|
+
render_opts = {
|
|
113
|
+
:template => options[hf][:html][:template],
|
|
114
|
+
:layout => options[hf][:html][:layout],
|
|
115
|
+
:formats => options[hf][:html][:formats],
|
|
116
|
+
:handlers => options[hf][:html][:handlers],
|
|
117
|
+
:assigns => options[hf][:html][:assigns]
|
|
118
|
+
}
|
|
119
|
+
render_opts[:locals] = options[hf][:html][:locals] if options[hf][:html][:locals]
|
|
120
|
+
render_opts[:file] = options[hf][:html][:file] if options[:file]
|
|
121
|
+
tf.write render_to_string(render_opts)
|
|
122
|
+
tf.flush
|
|
123
|
+
options[hf][:html][:url] = "file:///#{tf.path}"
|
|
124
|
+
end
|
|
125
|
+
options
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class WickedPdf
|
|
2
|
+
module Progress
|
|
3
|
+
require 'pty' if RbConfig::CONFIG['target_os'] !~ /mswin|mingw/ && RUBY_ENGINE != 'truffleruby' # no support for windows and truffleruby
|
|
4
|
+
require 'English'
|
|
5
|
+
|
|
6
|
+
def track_progress?(options)
|
|
7
|
+
options[:progress] && !(on_windows? || RUBY_ENGINE == 'truffleruby')
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def invoke_with_progress(command, options)
|
|
11
|
+
output = []
|
|
12
|
+
begin
|
|
13
|
+
PTY.spawn(command.join(' ')) do |stdout, _stdin, pid|
|
|
14
|
+
begin
|
|
15
|
+
stdout.sync
|
|
16
|
+
stdout.each_line("\r") do |line|
|
|
17
|
+
output << line.chomp
|
|
18
|
+
options[:progress].call(line) if options[:progress]
|
|
19
|
+
end
|
|
20
|
+
rescue Errno::EIO # rubocop:disable Lint/HandleExceptions
|
|
21
|
+
# child process is terminated, this is expected behaviour
|
|
22
|
+
ensure
|
|
23
|
+
::Process.wait pid
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
rescue PTY::ChildExited
|
|
27
|
+
puts 'The child process exited!'
|
|
28
|
+
end
|
|
29
|
+
err = output.join('\n')
|
|
30
|
+
raise "#{command} failed (exitstatus 0). Output was: #{err}" unless $CHILD_STATUS && $CHILD_STATUS.exitstatus.zero?
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'wicked_pdf/pdf_helper'
|
|
2
|
+
require 'wicked_pdf/wicked_pdf_helper'
|
|
3
|
+
require 'wicked_pdf/wicked_pdf_helper/assets'
|
|
4
|
+
|
|
5
|
+
class WickedPdf
|
|
6
|
+
if defined?(Rails.env)
|
|
7
|
+
class WickedRailtie < Rails::Railtie
|
|
8
|
+
initializer 'wicked_pdf.register', :after => 'remotipart.controller_helper' do |_app|
|
|
9
|
+
ActiveSupport.on_load(:action_controller) { ActionController::Base.send :prepend, PdfHelper }
|
|
10
|
+
ActiveSupport.on_load(:action_view) { include WickedPdfHelper::Assets }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
Mime::Type.register('application/pdf', :pdf) if Mime::Type.lookup_by_extension(:pdf).nil?
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'tempfile'
|
|
2
|
+
require 'stringio'
|
|
3
|
+
|
|
4
|
+
class WickedPdf
|
|
5
|
+
class Tempfile < ::Tempfile
|
|
6
|
+
def initialize(filename, temp_dir = nil)
|
|
7
|
+
temp_dir ||= Dir.tmpdir
|
|
8
|
+
extension = File.extname(filename)
|
|
9
|
+
basename = File.basename(filename, extension)
|
|
10
|
+
super([basename, extension], temp_dir)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def write_in_chunks(input_string)
|
|
14
|
+
binmode
|
|
15
|
+
string_io = StringIO.new(input_string)
|
|
16
|
+
write(string_io.read(chunk_size)) until string_io.eof?
|
|
17
|
+
close
|
|
18
|
+
self
|
|
19
|
+
rescue Errno::EINVAL => e
|
|
20
|
+
raise e, file_too_large_message
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def read_in_chunks
|
|
24
|
+
rewind
|
|
25
|
+
binmode
|
|
26
|
+
chunks = []
|
|
27
|
+
chunks << read(chunk_size) until eof?
|
|
28
|
+
chunks.join
|
|
29
|
+
rescue Errno::EINVAL => e
|
|
30
|
+
raise e, file_too_large_message
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def chunk_size
|
|
36
|
+
1024 * 1024
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def file_too_large_message
|
|
40
|
+
'The HTML file is too large! Try reducing the size or using the return_file option instead.'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|