adzap-wicked_pdf 2.0.0.beta5 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0e3c7a80eafff7408d840f346daf69875aa08ddf4732dcce26b8d41a293df84
4
- data.tar.gz: 04fe3eb6fbe869899c46faa88eec83ec4abbf2aa7c3dbbb904cda40415a434e3
3
+ metadata.gz: df55b441686f92b7087e24ccc0311fdb2c2653a6a07ee8a661f5168ba403462c
4
+ data.tar.gz: c9016439d082b975e932d1912f84434375897d06a17905065b5cbbf18e1f2354
5
5
  SHA512:
6
- metadata.gz: a1f3607db97564707b60ff9427de9c24aa5112144665d53ace67fa667216bea616ddbe78e91a94abaebbee85086b8118d8f36722f00d32532b3a4f7963fa6e7a
7
- data.tar.gz: 62dba9bf7aa036f6f5c4f249190ccee3f4879914a163f22c42e4c2b6fcb33c2b096b6d858a38e10881d9d39262f2af1a7dd2d715fc05a636b3d3561863897a9c
6
+ metadata.gz: 7a7568160aadb93576e342cb0185b44854658029c3f46a4fe50c5a3b69b9ef36d06692679760353f535981c2f603637af705e458cc887dce331506763d341b59
7
+ data.tar.gz: 739be6b379f0834f488c6db9aad4ecc5561838a981b11f5589f54ad6d1d4c7b5b202373518cdce66c68f69a78e6d9b40e5f153e31f94e68d22c7d9d8301551d5
data/.rubocop.yml CHANGED
@@ -1,9 +1,14 @@
1
1
  inherit_from: .rubocop_todo.yml
2
2
 
3
3
  AllCops:
4
- TargetRubyVersion: 2.3
4
+ TargetRubyVersion: 3.4
5
+ NewCops: disable
6
+ SuggestExtensions: false
5
7
  Exclude:
6
8
  - 'test/dummy/**/*'
9
+ - 'spike/**/*'
10
+ - 'gemfiles/**/*'
11
+ - 'vendor/**/*'
7
12
 
8
13
  Metrics/BlockLength:
9
14
  Exclude:
@@ -19,4 +24,4 @@ Style/SymbolArray:
19
24
  EnforcedStyle: brackets
20
25
 
21
26
  Style/SafeNavigation:
22
- Enabled: false
27
+ Enabled: false
data/.rubocop_todo.yml CHANGED
@@ -28,7 +28,7 @@ Metrics/CyclomaticComplexity:
28
28
 
29
29
  # Offense count: 92
30
30
  # Configuration parameters: AllowURI, URISchemes.
31
- Metrics/LineLength:
31
+ Layout/LineLength:
32
32
  Max: 563
33
33
 
34
34
  # Offense count: 14
@@ -38,7 +38,7 @@ Metrics/MethodLength:
38
38
 
39
39
  # Offense count: 4
40
40
  Metrics/PerceivedComplexity:
41
- Max: 12
41
+ Max: 14
42
42
 
43
43
  # Offense count: 2
44
44
  Naming/AccessorMethodName:
@@ -1,5 +1,5 @@
1
1
  class WickedPdfGenerator < Rails::Generators::Base
2
- source_root(File.expand_path(File.dirname(__FILE__) + '/../../generators/wicked_pdf/templates'))
2
+ source_root(File.expand_path("#{File.dirname(__FILE__)}/../../generators/wicked_pdf/templates"))
3
3
  def copy_initializer
4
4
  copy_file 'wicked_pdf.rb', 'config/initializers/wicked_pdf.rb'
5
5
  end
@@ -15,6 +15,7 @@ module WickedPdf
15
15
  def wicked_pdf_asset_base64(path)
16
16
  asset = find_asset(path)
17
17
  raise "Could not find asset '#{path}'" if asset.nil?
18
+
18
19
  base64 = Base64.encode64(asset.to_s).gsub(/\s+/, '')
19
20
  "data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
20
21
  end
@@ -95,7 +96,7 @@ module WickedPdf
95
96
  # will prepend a http or default_protocol to a protocol relative URL
96
97
  # or when no protcol is set.
97
98
  def prepend_protocol(source)
98
- protocol = WickedPdf.config[:default_protocol] || 'http'
99
+ protocol = WickedPdf.config.default_protocol || 'http'
99
100
  if source[0, 2] == '//'
100
101
  source = [protocol, ':', source].join
101
102
  elsif source[0] != '/' && !source[0, 8].include?('://')
@@ -107,7 +108,7 @@ module WickedPdf
107
108
  def precompiled_or_absolute_asset?(source)
108
109
  Rails.configuration.assets.compile == false ||
109
110
  source.to_s[0] == '/' ||
110
- source.to_s.match(/\Ahttps?\:\/\//)
111
+ source.to_s.match(/\Ahttps?:\/\//)
111
112
  end
112
113
 
113
114
  def read_asset(source)
@@ -125,7 +126,7 @@ module WickedPdf
125
126
 
126
127
  def read_from_uri(uri)
127
128
  asset = Net::HTTP.get(URI(uri))
128
- asset = gzip(asset) if WickedPdf.config[:expect_gzipped_remote_assets]
129
+ asset = gzip(asset) if WickedPdf.config.expect_gzipped_remote_assets
129
130
  asset
130
131
  end
131
132
 
@@ -5,6 +5,21 @@ module WickedPdf
5
5
 
6
6
  attr_reader :path, :version
7
7
 
8
+ # Shared instance, so the version check (`wkhtmltopdf -V` subprocess) runs
9
+ # once instead of once per render. Re-resolved whenever config.exe_path
10
+ # changes — including back to nil (automatic path detection).
11
+ def self.default
12
+ configured_path = WickedPdf.config.exe_path
13
+ @default = nil if @default && @default_configured_path != configured_path
14
+ @default_configured_path = configured_path
15
+ @default ||= new
16
+ end
17
+
18
+ def self.reset_default!
19
+ @default = nil
20
+ @default_configured_path = nil
21
+ end
22
+
8
23
  def initialize(binary_path = nil)
9
24
  @path = binary_path || find_binary_path
10
25
  @version = retrieve_binary_version
@@ -27,14 +42,15 @@ module WickedPdf
27
42
  private
28
43
 
29
44
  def retrieve_binary_version
30
- _stdin, stdout, _stderr = Open3.popen3(@path + ' -V')
31
- parse_version_string(stdout.gets(nil))
45
+ # argv form: no shell parsing, so paths with spaces work.
46
+ stdout, = Open3.capture3(@path, '-V')
47
+ parse_version_string(stdout)
32
48
  rescue StandardError
33
49
  MINIMUM_BINARY_VERSION
34
50
  end
35
51
 
36
52
  def find_binary_path
37
- return WickedPdf.config[:exe_path] if WickedPdf.config[:exe_path]
53
+ return WickedPdf.config.exe_path if WickedPdf.config.exe_path
38
54
 
39
55
  begin
40
56
  detected_path = (defined?(Bundler) ? Bundler.which('wkhtmltopdf') : `which wkhtmltopdf`).chomp
@@ -2,14 +2,14 @@ module WickedPdf
2
2
  class Command
3
3
  attr_reader :binary, :option_parser
4
4
 
5
- def initialize(binary: Binary.new, option_parser: OptionParser.new)
5
+ def initialize(binary: Binary.default, option_parser: OptionParser.new)
6
6
  @binary = binary
7
7
  @option_parser = option_parser
8
8
  end
9
9
 
10
10
  def execute(options, *args)
11
11
  command = [binary.path]
12
- command << '--enable-local-file-access' # FIXME should be set as config
12
+ command += global_argv
13
13
  command += option_parser.parse(options)
14
14
  command += args
15
15
 
@@ -18,28 +18,168 @@ module WickedPdf
18
18
  if track_progress?(options)
19
19
  Progress.new(options[:progress]).execute(command)
20
20
  else
21
- begin
22
- err = Open3.popen3(*command) do |_stdin, _stdout, stderr|
23
- stderr.read
24
- end
21
+ err = run_process(command)
22
+
23
+ raise GenerationError, "Error generating PDF\n Command Error: #{err}" if options[:raise_on_all_errors] && !err.empty?
24
+
25
+ err
26
+ end
27
+ end
28
+
29
+ # Runs several conversions through shared wkhtmltopdf processes using
30
+ # --read-args-from-stdin, avoiding one process boot per document.
31
+ #
32
+ # Each job is a Hash with:
33
+ # options: wkhtmltopdf options for this conversion only
34
+ # url: input URL (file:///... for local files)
35
+ # output_path: file the PDF is written to
36
+ #
37
+ # Jobs are split into `concurrency` contiguous slices, each processed by
38
+ # its own wkhtmltopdf process. wkhtmltopdf aborts a batch at the first
39
+ # failing conversion, so the failed job is retried as a normal single
40
+ # invocation (for a proper error) and the batch resumed with the rest.
41
+ # Raises WickedPdf::GenerationError if a job cannot be rendered.
42
+ #
43
+ # All argv compilation happens here on the calling thread: OptionParser
44
+ # is not thread-safe (it accumulates tempfile state for inline
45
+ # header/footer content), so worker threads only ever see frozen strings.
46
+ def execute_batch(jobs, concurrency: 1)
47
+ return if jobs.empty?
48
+
49
+ compiled = jobs.map { |job| BatchJob.new(job, batch_argv(job)) }
50
+
51
+ slices = compiled.each_slice(batch_slice_size(jobs.size, concurrency)).to_a
52
+ errors = Array.new(slices.size)
53
+
54
+ slices.each_with_index.map do |slice, index|
55
+ Thread.new do
56
+ process_batch_slice(slice)
25
57
  rescue StandardError => e
26
- raise "Failed to execute:\n#{command}\nError: #{e}"
58
+ errors[index] = e
27
59
  end
60
+ end.each(&:join)
28
61
 
29
- raise "Error generating PDF\n Command Error: #{err}" if options[:raise_on_all_errors] && !err.empty?
30
- err
62
+ error = errors.compact.first
63
+ raise error if error
64
+ end
65
+
66
+ # A batch job with its argv compiled up front (thread-safe to run).
67
+ BatchJob = Struct.new(:job, :argv) do
68
+ def output_path
69
+ job[:output_path]
70
+ end
71
+
72
+ def line
73
+ @line ||= argv.join(' ')
31
74
  end
32
75
  end
33
76
 
34
77
  private
35
78
 
79
+ def batch_slice_size(job_count, concurrency)
80
+ concurrency = concurrency.clamp(1, job_count)
81
+ (job_count.to_f / concurrency).ceil
82
+ end
83
+
84
+ def process_batch_slice(batch_jobs)
85
+ pending = batch_jobs
86
+ until pending.empty?
87
+ run_batch_process(pending.map(&:line))
88
+ pending = pending.drop_while { |batch_job| batch_output_complete?(batch_job) }
89
+ break if pending.empty?
90
+
91
+ retry_batch_job(pending.shift)
92
+ end
93
+ end
94
+
95
+ # A failed conversion aborts its batch process, leaving this job and all
96
+ # jobs after it unrendered. Retrying it individually either renders it
97
+ # (transient failure) or produces a proper single-invocation error.
98
+ def retry_batch_job(batch_job)
99
+ command = [binary.path] + batch_job.argv.map { |token| unquote_batch_token(token) }
100
+ print_command(command.inspect) if in_development_mode?
101
+
102
+ err = run_process(command)
103
+ return if batch_output_complete?(batch_job)
104
+
105
+ raise GenerationError, "Error generating PDF for #{batch_job.output_path}\n Command Error: #{err}"
106
+ end
107
+
108
+ # wkhtmltopdf aborts a failing batch conversion partway, which can leave
109
+ # a non-empty but truncated file behind. Only a well-formed PDF (header
110
+ # and end-of-file marker) counts as done; anything else is retried.
111
+ def batch_output_complete?(batch_job)
112
+ size = File.size?(batch_job.output_path)
113
+ return false unless size
114
+
115
+ File.open(batch_job.output_path, 'rb') do |file|
116
+ return false unless file.read(5) == '%PDF-'
117
+
118
+ file.seek([size - 64, 0].max)
119
+ file.read.include?('%%EOF')
120
+ end
121
+ end
122
+
123
+ def batch_argv(job)
124
+ argv = global_argv
125
+ argv += option_parser.parse(job[:options].dup)
126
+ argv += [job[:url], job[:output_path]]
127
+ argv.map { |arg| batch_token(arg.to_s) }
128
+ end
129
+
130
+ # Flags controlled by gem configuration rather than per-render options.
131
+ def global_argv
132
+ WickedPdf.config.enable_local_file_access ? ['--enable-local-file-access'] : []
133
+ end
134
+
135
+ # wkhtmltopdf tokenises each stdin line itself: spaces separate arguments
136
+ # unless quoted. Only quoted-space handling is verified, so reject
137
+ # characters with unknown escaping semantics rather than corrupt a line.
138
+ def batch_token(arg)
139
+ raise ArgumentError, "Unsupported character in wkhtmltopdf batch argument: #{arg.inspect}" if arg.match?(/["\n\r]/)
140
+
141
+ arg.include?(' ') ? %("#{arg}") : arg
142
+ end
143
+
144
+ # Reverses batch_token quoting when a compiled token is passed directly
145
+ # as a process argument instead of through the stdin line protocol.
146
+ def unquote_batch_token(token)
147
+ token.start_with?('"') && token.end_with?('"') ? token[1..-2] : token
148
+ end
149
+
150
+ def run_process(command)
151
+ Open3.popen3(*command) do |_stdin, _stdout, stderr|
152
+ stderr.read
153
+ end
154
+ rescue StandardError => e
155
+ raise Error, "Failed to execute:\n#{command}\nError: #{e}"
156
+ end
157
+
158
+ def run_batch_process(lines)
159
+ print_command(lines.inspect) if in_development_mode?
160
+
161
+ err = nil
162
+ Open3.popen3(binary.path, '--read-args-from-stdin') do |stdin, stdout, stderr, wait_thr|
163
+ stdout_reader = Thread.new { stdout.read }
164
+ stderr_reader = Thread.new { stderr.read }
165
+ lines.each { |line| stdin.puts(line) }
166
+ stdin.close
167
+ stdout_reader.value
168
+ err = stderr_reader.value
169
+ wait_thr.value
170
+ end
171
+ err
172
+ rescue StandardError => e
173
+ raise Error, "Failed to execute wkhtmltopdf batch:\n#{lines.inspect}\nError: #{e}"
174
+ end
175
+
36
176
  def in_development_mode?
37
177
  defined?(Rails.env) && Rails.env.development?
38
178
  end
39
179
 
40
180
  def print_command(cmd)
41
181
  # TODO: if no Rails what then?
42
- Rails.logger.debug '[wicked_pdf]: ' + cmd
182
+ Rails.logger.debug "[wicked_pdf]: #{cmd}"
43
183
  end
44
184
 
45
185
  def track_progress?(options)
@@ -0,0 +1,60 @@
1
+ module WickedPdf
2
+ # Global gem configuration, set via WickedPdf.configure or by assigning a
3
+ # Hash to WickedPdf.config (kept for backward compatibility).
4
+ #
5
+ # Two kinds of values live here:
6
+ #
7
+ # * Typed settings, declared as accessors below, which control the gem
8
+ # itself (binary location, middleware URLs, asset handling).
9
+ # * default_options, a Hash of wkhtmltopdf render options (:footer,
10
+ # :orientation, :dpi, ...) merged under every render's own options.
11
+ # Unknown keys assigned through a Hash or #[]= land here.
12
+ class Configuration
13
+ SETTINGS = [
14
+ :exe_path,
15
+ :basic_auth,
16
+ :root_url,
17
+ :default_protocol,
18
+ :expect_gzipped_remote_assets,
19
+ :enable_local_file_access
20
+ ].freeze
21
+
22
+ attr_accessor(*SETTINGS)
23
+ attr_reader :default_options
24
+
25
+ def self.from_hash(hash)
26
+ new.tap do |config|
27
+ (hash || {}).each { |key, value| config[key] = value }
28
+ end
29
+ end
30
+
31
+ def initialize
32
+ @basic_auth = false
33
+ @enable_local_file_access = true
34
+ @default_options = {}
35
+ end
36
+
37
+ def []=(key, value)
38
+ if setting?(key)
39
+ public_send("#{key}=", value)
40
+ else
41
+ default_options[key.to_sym] = value
42
+ end
43
+ end
44
+
45
+ def [](key)
46
+ setting?(key) ? public_send(key) : default_options[key.to_sym]
47
+ end
48
+
49
+ def to_h
50
+ settings = SETTINGS.to_h { |key| [key, public_send(key)] }
51
+ settings.compact.merge(default_options)
52
+ end
53
+
54
+ private
55
+
56
+ def setting?(key)
57
+ SETTINGS.include?(key.to_sym)
58
+ end
59
+ end
60
+ end
@@ -10,7 +10,7 @@ module WickedPdf
10
10
 
11
11
  def pdf_from_string(string, options = {})
12
12
  options = options.dup
13
- options.merge!(WickedPdf.config) { |_key, option, _config| option }
13
+ options.merge!(WickedPdf.config.default_options) { |_key, option, _config| option }
14
14
  string_file = WickedPdf::Tempfile.new('wicked_pdf.html', options[:temp_path])
15
15
  string_file.write_in_chunks(string)
16
16
 
@@ -22,21 +22,52 @@ module WickedPdf
22
22
  string_file.close! if string_file
23
23
  end
24
24
 
25
+ # Renders many HTML strings via Command#execute_batch (shared wkhtmltopdf
26
+ # processes instead of one boot per document). Items are Hashes of
27
+ # { html: String, options: Hash }; each item's options apply to that
28
+ # conversion only. Returns the PDF strings in item order.
29
+ def pdfs_from_strings(items, concurrency: 1)
30
+ tempfiles = []
31
+
32
+ jobs = items.map do |item|
33
+ options = (item[:options] || {}).dup
34
+ options.merge!(WickedPdf.config.default_options) { |_key, option, _config| option }
35
+
36
+ input = WickedPdf::Tempfile.new('wicked_pdf_batch.html', options[:temp_path])
37
+ tempfiles << input
38
+ input.write_in_chunks(item.fetch(:html))
39
+
40
+ output = WickedPdf::Tempfile.new('wicked_pdf_batch.pdf', options[:temp_path])
41
+ tempfiles << output
42
+
43
+ { options: options, url: "file:///#{input.path}", output_path: output.path, output_file: output }
44
+ end
45
+
46
+ @command.execute_batch(jobs, concurrency: concurrency)
47
+
48
+ jobs.map do |job|
49
+ pdf = job[:output_file].read_in_chunks
50
+ raise GenerationError, "PDF could not be generated!\n Output: #{job[:output_path]}" if pdf.rstrip.empty?
51
+
52
+ pdf
53
+ end
54
+ ensure
55
+ tempfiles.each(&:close!)
56
+ end
57
+
25
58
  def pdf_from_url(url, options = {})
26
59
  # merge in global config options
27
- options.merge!(WickedPdf.config) { |_key, option, _config| option }
60
+ options.merge!(WickedPdf.config.default_options) { |_key, option, _config| option }
28
61
  generated_pdf_file = WickedPdf::Tempfile.new('wicked_pdf_generated_file.pdf', options[:temp_path])
29
62
  return_file = options.delete(:return_file)
30
63
 
31
64
  err = @command.execute(options, url, generated_pdf_file.path.to_s)
32
65
 
33
- if return_file
34
- return generated_pdf_file
35
- end
66
+ return generated_pdf_file if return_file
36
67
 
37
68
  pdf = generated_pdf_file.read_in_chunks
38
69
 
39
- raise "PDF could not be generated!\n Command Error: #{err}" if pdf && pdf.rstrip.empty?
70
+ raise GenerationError, "PDF could not be generated!\n Command Error: #{err}" if pdf && pdf.rstrip.empty?
40
71
 
41
72
  pdf
42
73
  rescue Errno::EINVAL => e
@@ -2,7 +2,7 @@ module WickedPdf
2
2
  class Middleware
3
3
  def initialize(app, options = {}, conditions = {})
4
4
  @app = app
5
- @options = (WickedPdf.config || {}).merge(options)
5
+ @options = WickedPdf.config.default_options.merge(options)
6
6
  @conditions = conditions
7
7
  @command = command(options[:wkhtmltopdf])
8
8
  end
@@ -42,9 +42,9 @@ module WickedPdf
42
42
  # Change relative paths to absolute
43
43
  def translate_paths(body, env)
44
44
  # Host with protocol
45
- root = WickedPdf.config[:root_url] || "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}/"
45
+ root = WickedPdf.config.root_url || "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}/"
46
46
 
47
- body.gsub(/(href|src)=(['"])\/([^\"']*|[^"']*)['"]/, '\1=\2' + root + '\3\2')
47
+ body.gsub(/(href|src)=(['"])\/([^"']*|[^"']*)['"]/, "\\1=\\2#{root}\\3\\2")
48
48
  end
49
49
 
50
50
  def rendering_pdf?
@@ -73,7 +73,7 @@ module WickedPdf
73
73
  end
74
74
  end
75
75
 
76
- return true
76
+ true
77
77
  else
78
78
  request_path_is_pdf
79
79
  end
@@ -20,6 +20,7 @@ module WickedPdf
20
20
  def parse_extra(options)
21
21
  return [] if options[:extra].nil?
22
22
  return options[:extra].split if options[:extra].respond_to?(:split)
23
+
23
24
  options[:extra]
24
25
  end
25
26
 
@@ -37,6 +38,7 @@ module WickedPdf
37
38
  unless options.blank?
38
39
  [:header, :footer].collect do |hf|
39
40
  next if options[hf].blank?
41
+
40
42
  opt_hf = options[hf]
41
43
  r += make_options(opt_hf, [:center, :font_name, :left, :right], hf.to_s)
42
44
  r += make_options(opt_hf, [:font_size, :spacing], hf.to_s, :numeric)
@@ -49,9 +51,7 @@ module WickedPdf
49
51
  options[hf][:html] = {}
50
52
  options[hf][:html][:url] = "file:///#{tf.path}"
51
53
  end
52
- unless opt_hf[:html].blank?
53
- r += make_option("#{hf}-html", opt_hf[:html][:url]) unless opt_hf[:html][:url].blank?
54
- end
54
+ r += make_option("#{hf}-html", opt_hf[:html][:url]) if !opt_hf[:html].blank? && !opt_hf[:html][:url].blank?
55
55
  end
56
56
  end
57
57
  r
@@ -60,6 +60,7 @@ module WickedPdf
60
60
  def parse_cover(argument)
61
61
  arg = argument.to_s
62
62
  return [] if arg.blank?
63
+
63
64
  # Filesystem path or URL - hand off to wkhtmltopdf
64
65
  if argument.is_a?(Pathname) || (arg[0, 4] == 'http')
65
66
  ['cover', arg]
@@ -74,6 +75,7 @@ module WickedPdf
74
75
 
75
76
  def parse_toc(options)
76
77
  return [] if options.nil?
78
+
77
79
  r = ['toc']
78
80
  unless options.blank?
79
81
  r += make_options(options, [:font_name, :header_text], 'toc')
@@ -172,11 +174,12 @@ module WickedPdf
172
174
 
173
175
  def make_options(options, names, prefix = '', type = :string)
174
176
  return [] if options.nil?
177
+
175
178
  names.collect do |o|
176
179
  if options[o].blank?
177
180
  []
178
181
  else
179
- make_option("#{prefix.blank? ? '' : prefix + '-'}#{o}",
182
+ make_option("#{prefix.blank? ? '' : "#{prefix}-"}#{o}",
180
183
  options[o],
181
184
  type)
182
185
  end
@@ -184,9 +187,8 @@ module WickedPdf
184
187
  end
185
188
 
186
189
  def make_option(name, value, type = :string)
187
- if value.is_a?(Array)
188
- return value.collect { |v| make_option(name, v, type) }
189
- end
190
+ return value.collect { |v| make_option(name, v, type) } if value.is_a?(Array)
191
+
190
192
  if type == :name_value
191
193
  parts = value.to_s.split(' ')
192
194
  ["--#{name.tr('_', '-')}", *parts]
@@ -3,7 +3,7 @@ module WickedPdf
3
3
  def self.prepended(base)
4
4
  # Protect from trying to augment modules that appear
5
5
  # as the result of adding other gems.
6
- return if base != ActionController::Base
6
+ nil if base != ActionController::Base
7
7
  end
8
8
 
9
9
  def render_to_string(options = nil, *args, &block)
@@ -11,17 +11,15 @@ module WickedPdf
11
11
  output = []
12
12
  begin
13
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
- @callback.call(line) if @callback
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
14
+ stdout.sync
15
+ stdout.each_line("\r") do |line|
16
+ output << line.chomp
17
+ @callback.call(line) if @callback
24
18
  end
19
+ rescue Errno::EIO
20
+ # child process is terminated, this is expected behaviour
21
+ ensure
22
+ ::Process.wait pid
25
23
  end
26
24
  rescue PTY::ChildExited
27
25
  puts 'The child process exited!'
@@ -5,17 +5,15 @@ require 'wicked_pdf/asset_helper'
5
5
  module WickedPdf
6
6
  class Railtie < Rails::Railtie
7
7
  initializer 'wicked_pdf.register' do |_app|
8
- ActiveSupport.on_load(:action_controller) {
9
- ActionController::Base.send :prepend, PdfHelper
8
+ ActiveSupport.on_load(:action_controller) do
9
+ ActionController::Base.prepend PdfHelper
10
10
  ActionController::Renderers.add :pdf do |template, options|
11
11
  WickedPdf::Renderer.new(self).render(options.merge(pdf: template))
12
12
  end
13
- }
13
+ end
14
14
  ActiveSupport.on_load(:action_view) { include WickedPdf::AssetHelper }
15
15
  end
16
16
  end
17
17
  end
18
18
 
19
- if Mime::Type.lookup_by_extension(:pdf).nil?
20
- Mime::Type.register('application/pdf', :pdf)
21
- end
19
+ Mime::Type.register('application/pdf', :pdf) if Mime::Type.lookup_by_extension(:pdf).nil?
@@ -9,20 +9,46 @@ module WickedPdf
9
9
 
10
10
  def render(options)
11
11
  options[:basic_auth] = set_basic_auth(options)
12
- make_and_send_pdf(options.delete(:pdf), (WickedPdf.config || {}).merge(options))
12
+ make_and_send_pdf(options.delete(:pdf), WickedPdf.config.default_options.merge(options))
13
13
  end
14
14
 
15
15
  def render_to_string(options)
16
16
  options[:basic_auth] = set_basic_auth(options)
17
17
  options.delete :pdf
18
- make_pdf((WickedPdf.config || {}).merge(options))
18
+ make_pdf(WickedPdf.config.default_options.merge(options))
19
+ end
20
+
21
+ # Renders the template and any header/footer templates WITHOUT invoking
22
+ # wkhtmltopdf, returning { html:, options: } suitable for
23
+ # Document#pdfs_from_strings. Header/footer tempfiles referenced by the
24
+ # returned options stay alive until #cleanup is called, which must happen
25
+ # after the PDFs have been generated.
26
+ #
27
+ # Defaults (layout, template) match #render's `render :pdf` path, so a
28
+ # prepared document converts identically to a single render.
29
+ def prepare(options)
30
+ options = WickedPdf.config.default_options.merge(options)
31
+ options[:basic_auth] = set_basic_auth(options)
32
+ options.delete(:pdf)
33
+ options[:layout] ||= false
34
+ options[:template] ||= File.join(controller.controller_path, controller.action_name)
35
+
36
+ {
37
+ html: controller.render_to_string(render_options(options)),
38
+ options: prerender_header_and_footer(options)
39
+ }
40
+ end
41
+
42
+ def cleanup
43
+ clean_temp_files
19
44
  end
20
45
 
21
46
  private
22
47
 
23
48
  def set_basic_auth(options = {})
24
- options[:basic_auth] ||= WickedPdf.config.fetch(:basic_auth, false)
49
+ options[:basic_auth] ||= WickedPdf.config.basic_auth
25
50
  return unless options[:basic_auth] && controller.request.env['HTTP_AUTHORIZATION']
51
+
26
52
  controller.request.env['HTTP_AUTHORIZATION'].split(' ').last
27
53
  end
28
54
 
@@ -45,7 +71,7 @@ module WickedPdf
45
71
  else
46
72
  pdf_content = make_pdf(options)
47
73
  File.open(options[:save_to_file], 'wb') { |file| file << pdf_content } if options[:save_to_file]
48
- controller.send_data(pdf_content, :filename => pdf_name + '.pdf', :type => 'application/pdf', :disposition => options[:disposition]) unless options[:save_only]
74
+ controller.send_data(pdf_content, :filename => "#{pdf_name}.pdf", :type => 'application/pdf', :disposition => options[:disposition]) unless options[:save_only]
49
75
  end
50
76
  end
51
77