wicked_pdf 2.0.2 → 2.7.0

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.
data/gemfiles/5.0.gemfile CHANGED
@@ -1,8 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'rails', '~> 5.0.0'
3
4
  gem 'rdoc'
4
- gem 'sqlite3', '~> 1.3.6'
5
5
  gem 'sprockets', '~>3.0' # v4 strips newlines from assets causing tests to fail
6
- gem 'rails', '~> 5.0.0'
6
+ gem 'sqlite3', '~> 1.3.6'
7
7
 
8
8
  gemspec path: '../'
data/gemfiles/5.1.gemfile CHANGED
@@ -1,8 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'rails', '~> 5.1.0'
3
4
  gem 'rdoc'
4
- gem 'sqlite3', '~> 1.3.6'
5
5
  gem 'sprockets', '~>3.0' # v4 strips newlines from assets causing tests to fail
6
- gem 'rails', '~> 5.1.0'
6
+ gem 'sqlite3', '~> 1.3.6'
7
7
 
8
8
  gemspec path: '../'
data/gemfiles/5.2.gemfile CHANGED
@@ -1,10 +1,9 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'rdoc'
3
+ gem 'bootsnap' # required to run `rake test` in Rails 5.2
4
4
  gem 'rails', '~> 5.2'
5
- gem 'sqlite3', '~> 1.3.6'
5
+ gem 'rdoc'
6
6
  gem 'sprockets', '~>3.0' # v4 strips newlines from assets causing tests to fail
7
- gem 'bootsnap' # required to run `rake test` in Rails 5.2
8
- gem 'mocha', '= 1.3' # newer versions blow up
7
+ gem 'sqlite3', '~> 1.3.6'
9
8
 
10
9
  gemspec path: '../'
data/gemfiles/6.0.gemfile CHANGED
@@ -1,11 +1,10 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'bootsnap' # required to run `rake test` in Rails 6.0
3
4
  gem 'bundler', '~>2'
4
- gem 'rdoc'
5
5
  gem 'rails', '~>6.0.1'
6
- gem 'sqlite3', '~> 1.4'
6
+ gem 'rdoc'
7
7
  gem 'sprockets', '~>3.0'
8
- gem 'bootsnap' # required to run `rake test` in Rails 6.0
9
- gem 'mocha', '= 1.3' # newer versions blow up
8
+ gem 'sqlite3', '~> 1.4'
10
9
 
11
10
  gemspec path: '../'
@@ -0,0 +1,11 @@
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
+
11
+ gemspec :path => '../'
@@ -0,0 +1,11 @@
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
+
11
+ 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
  }
@@ -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,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
@@ -1,21 +1,5 @@
1
1
  class WickedPdf
2
2
  module PdfHelper
3
- def self.included(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
- alias_method_chain :render, :wicked_pdf
10
- alias_method_chain :render_to_string, :wicked_pdf
11
- if respond_to?(:after_action)
12
- after_action :clean_temp_files
13
- else
14
- after_filter :clean_temp_files
15
- end
16
- end
17
- end
18
-
19
3
  def self.prepended(base)
20
4
  # Protect from trying to augment modules that appear
21
5
  # as the result of adding other gems.
@@ -26,52 +10,52 @@ class WickedPdf
26
10
  end
27
11
  end
28
12
 
29
- def render(options = nil, *args, &block)
30
- render_with_wicked_pdf(options, *args, &block)
31
- end
32
-
33
- def render_to_string(options = nil, *args, &block)
34
- render_to_string_with_wicked_pdf(options, *args, &block)
35
- end
36
-
37
- def render_with_wicked_pdf(options = nil, *args, &block)
13
+ def render(*args)
14
+ options = args.first
38
15
  if options.is_a?(Hash) && options.key?(:pdf)
39
- options[:basic_auth] = set_basic_auth(options)
40
- make_and_send_pdf(options.delete(:pdf), (WickedPdf.config || {}).merge(options))
41
- elsif respond_to?(:render_without_wicked_pdf)
42
- # support alias_method_chain (module included)
43
- render_without_wicked_pdf(options, *args, &block)
16
+ render_with_wicked_pdf(options)
44
17
  else
45
- # support inheritance (module prepended)
46
- method(:render).super_method.call(options, *args, &block)
18
+ super
47
19
  end
48
20
  end
49
21
 
50
- def render_to_string_with_wicked_pdf(options = nil, *args, &block)
22
+ def render_to_string(*args)
23
+ options = args.first
51
24
  if options.is_a?(Hash) && options.key?(:pdf)
52
- options[:basic_auth] = set_basic_auth(options)
53
- options.delete :pdf
54
- make_pdf((WickedPdf.config || {}).merge(options))
55
- elsif respond_to?(:render_to_string_without_wicked_pdf)
56
- # support alias_method_chain (module included)
57
- render_to_string_without_wicked_pdf(options, *args, &block)
25
+ render_to_string_with_wicked_pdf(options)
58
26
  else
59
- # support inheritance (module prepended)
60
- method(:render_to_string).super_method.call(options, *args, &block)
27
+ super
61
28
  end
62
29
  end
63
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
+
64
46
  private
65
47
 
66
48
  def set_basic_auth(options = {})
67
49
  options[:basic_auth] ||= WickedPdf.config.fetch(:basic_auth) { false }
68
50
  return unless options[:basic_auth] && request.env['HTTP_AUTHORIZATION']
51
+
69
52
  request.env['HTTP_AUTHORIZATION'].split(' ').last
70
53
  end
71
54
 
72
55
  def clean_temp_files
73
56
  return unless defined?(@hf_tempfiles)
74
- @hf_tempfiles.each(&:close!)
57
+
58
+ @hf_tempfiles.each(&:close)
75
59
  end
76
60
 
77
61
  def make_pdf(options = {})
@@ -119,10 +103,11 @@ class WickedPdf
119
103
  # Given an options hash, prerenders content for the header and footer sections
120
104
  # to temp files and return a new options hash including the URLs to these files.
121
105
  def prerender_header_and_footer(options)
122
- [:header, :footer].each do |hf|
106
+ %i[header footer].each do |hf|
123
107
  next unless options[hf] && options[hf][:html] && options[hf][:html][:template]
108
+
124
109
  @hf_tempfiles = [] unless defined?(@hf_tempfiles)
125
- @hf_tempfiles.push(tf = WickedPdfTempfile.new("wicked_#{hf}_pdf.html"))
110
+ @hf_tempfiles.push(tf = WickedPdf::Tempfile.new("wicked_#{hf}_pdf.html"))
126
111
  options[hf][:html][:layout] ||= options[:layout]
127
112
  render_opts = {
128
113
  :template => options[hf][:html][:template],
@@ -6,21 +6,12 @@ class WickedPdf
6
6
  if defined?(Rails.env)
7
7
  class WickedRailtie < Rails::Railtie
8
8
  initializer 'wicked_pdf.register', :after => 'remotipart.controller_helper' do |_app|
9
- ActiveSupport.on_load(:action_controller) do
10
- if ActionController::Base.respond_to?(:prepend) &&
11
- Object.method(:new).respond_to?(:super_method)
12
- ActionController::Base.send :prepend, PdfHelper
13
- else
14
- ActionController::Base.send :include, PdfHelper
15
- end
16
- ActionView::Base.send :include, WickedPdfHelper::Assets
17
- end
9
+ ActiveSupport.on_load(:action_controller) { ActionController::Base.send :prepend, PdfHelper }
10
+ ActiveSupport.on_load(:action_view) { include WickedPdfHelper::Assets }
18
11
  end
19
12
  end
20
13
 
21
- if Mime::Type.lookup_by_extension(:pdf).nil?
22
- Mime::Type.register('application/pdf', :pdf)
23
- end
14
+ Mime::Type.register('application/pdf', :pdf) if Mime::Type.lookup_by_extension(:pdf).nil?
24
15
 
25
16
  end
26
17
  end
@@ -1,13 +1,43 @@
1
1
  require 'tempfile'
2
+ require 'stringio'
2
3
 
3
4
  class WickedPdf
4
- class WickedPdfTempfile < Tempfile
5
- # ensures the Tempfile's filename always keeps its extension
5
+ class Tempfile < ::Tempfile
6
6
  def initialize(filename, temp_dir = nil)
7
7
  temp_dir ||= Dir.tmpdir
8
8
  extension = File.extname(filename)
9
- basename = File.basename(filename, extension)
9
+ basename = File.basename(filename, extension)
10
10
  super([basename, extension], temp_dir)
11
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
+ output_string = ''
27
+ output_string << read(chunk_size) until eof?
28
+ output_string
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
12
42
  end
13
43
  end
@@ -1,3 +1,3 @@
1
1
  class WickedPdf
2
- VERSION = '2.0.2'.freeze
2
+ VERSION = '2.7.0'.freeze
3
3
  end