lib/pdfkit/pdfkit.rb in pdfkit-0.6.2 vs lib/pdfkit/pdfkit.rb in pdfkit-0.7.0
- old
+ new
@@ -1,9 +1,8 @@
require 'shellwords'
class PDFKit
-
class NoExecutableError < StandardError
def initialize
msg = "No wkhtmltopdf executable found at #{PDFKit.configuration.wkhtmltopdf}\n"
msg << ">> Please install wkhtmltopdf - https://github.com/pdfkit/PDFKit/wiki/Installing-WKHTMLTOPDF"
super(msg)
@@ -31,22 +30,21 @@
raise NoExecutableError.new unless File.exists?(PDFKit.configuration.wkhtmltopdf)
end
def command(path = nil)
- args = [executable]
- args += @options.to_a.flatten.compact
+ args = @options.to_a.flatten.compact
if @source.html?
args << '-' # Get HTML from stdin
else
args << @source.to_s
end
args << (path || '-') # Write to file or stdout
- args.shelljoin
+ [executable, args.shelljoin].join ' '
end
def executable
default = PDFKit.configuration.wkhtmltopdf
return default if default !~ /^\// # its not a path, so nothing we can do
@@ -63,132 +61,135 @@
invoke = command(path)
result = IO.popen(invoke, "wb+") do |pdf|
pdf.puts(@source.to_s) if @source.html?
pdf.close_write
- pdf.gets(nil)
+ pdf.gets(nil) if path.nil?
end
- result = File.read(path) if path
- # $? is thread safe per http://stackoverflow.com/questions/2164887/thread-safe-external-process-in-ruby-plus-checking-exitstatus
- raise "command failed (exitstatus=#{$?.exitstatus}): #{invoke}" if result.to_s.strip.empty? or !successful?($?)
+ # $? is thread safe per
+ # http://stackoverflow.com/questions/2164887/thread-safe-external-process-in-ruby-plus-checking-exitstatus
+ raise "command failed (exitstatus=#{$?.exitstatus}): #{invoke}" if empty_result?(path, result) or !successful?($?)
return result
end
def to_file(path)
self.to_pdf(path)
File.new(path)
end
protected
- # Pulled from:
- # https://github.com/wkhtmltopdf/wkhtmltopdf/blob/ebf9b6cfc4c58a31349fb94c568b254fac37b3d3/README_WKHTMLTOIMAGE#L27
- REPEATABLE_OPTIONS = %w[--allow --cookie --custom-header --post --post-file --run-script]
+ # Pulled from:
+ # https://github.com/wkhtmltopdf/wkhtmltopdf/blob/ebf9b6cfc4c58a31349fb94c568b254fac37b3d3/README_WKHTMLTOIMAGE#L27
+ REPEATABLE_OPTIONS = %w[--allow --cookie --custom-header --post --post-file --run-script]
- def find_options_in_meta(content)
- # Read file if content is a File
- content = content.read if content.is_a?(File)
+ def find_options_in_meta(content)
+ # Read file if content is a File
+ content = content.read if content.is_a?(File)
- found = {}
- content.scan(/<meta [^>]*>/) do |meta|
- if meta.match(/name=["']#{PDFKit.configuration.meta_tag_prefix}/)
- name = meta.scan(/name=["']#{PDFKit.configuration.meta_tag_prefix}([^"']*)/)[0][0].split
- found[name] = meta.scan(/content=["']([^"'\\]+)["']/)[0][0]
- end
+ found = {}
+ content.scan(/<meta [^>]*>/) do |meta|
+ if meta.match(/name=["']#{PDFKit.configuration.meta_tag_prefix}/)
+ name = meta.scan(/name=["']#{PDFKit.configuration.meta_tag_prefix}([^"']*)/)[0][0].split
+ found[name] = meta.scan(/content=["']([^"'\\]+)["']/)[0][0]
end
-
- tuple_keys = found.keys.select { |k| k.is_a? Array }
- tuple_keys.each do |key|
- value = found.delete key
- new_key = key.shift
- found[new_key] ||= {}
- found[new_key][key] = value
- end
-
- found
end
- def style_tag_for(stylesheet)
- "<style>#{File.read(stylesheet)}</style>"
+ tuple_keys = found.keys.select { |k| k.is_a? Array }
+ tuple_keys.each do |key|
+ value = found.delete key
+ new_key = key.shift
+ found[new_key] ||= {}
+ found[new_key][key] = value
end
- def append_stylesheets
- raise ImproperSourceError.new('Stylesheets may only be added to an HTML source') if stylesheets.any? && !@source.html?
+ found
+ end
- stylesheets.each do |stylesheet|
- if @source.to_s.match(/<\/head>/)
- @source = Source.new(@source.to_s.gsub(/(<\/head>)/) {|s| style_tag_for(stylesheet) + s })
- else
- @source.to_s.insert(0, style_tag_for(stylesheet))
- end
+ def style_tag_for(stylesheet)
+ "<style>#{File.read(stylesheet)}</style>"
+ end
+
+ def append_stylesheets
+ raise ImproperSourceError.new('Stylesheets may only be added to an HTML source') if stylesheets.any? && !@source.html?
+
+ stylesheets.each do |stylesheet|
+ if @source.to_s.match(/<\/head>/)
+ @source = Source.new(@source.to_s.gsub(/(<\/head>)/) {|s| style_tag_for(stylesheet) + s })
+ else
+ @source.to_s.insert(0, style_tag_for(stylesheet))
end
end
+ end
- def normalize_options(options)
- normalized_options = {}
+ def normalize_options(options)
+ normalized_options = {}
- options.each do |key, value|
- next if !value
+ options.each do |key, value|
+ next if !value
- # The actual option for wkhtmltopdf
- normalized_key = "--#{normalize_arg key}"
+ # The actual option for wkhtmltopdf
+ normalized_key = "--#{normalize_arg key}"
- # If the option is repeatable, attempt to normalize all values
- if REPEATABLE_OPTIONS.include? normalized_key
- normalize_repeatable_value(value) do |normalized_key_piece, normalized_value|
- normalized_options[[normalized_key, normalized_key_piece]] = normalized_value
- end
- else # Otherwise, just normalize it like usual
- normalized_options[normalized_key] = normalize_value(value)
+ # If the option is repeatable, attempt to normalize all values
+ if REPEATABLE_OPTIONS.include? normalized_key
+ normalize_repeatable_value(value) do |normalized_key_piece, normalized_value|
+ normalized_options[[normalized_key, normalized_key_piece]] = normalized_value
end
+ else # Otherwise, just normalize it like usual
+ normalized_options[normalized_key] = normalize_value(value)
end
-
- normalized_options
end
- def normalize_arg(arg)
- arg.to_s.downcase.gsub(/[^a-z0-9]/,'-')
- end
+ normalized_options
+ end
- def normalize_value(value)
- case value
- when TrueClass, 'true' #ie, ==true, see http://www.ruby-doc.org/core-1.9.3/TrueClass.html
- nil
- when Hash
- value.to_a.flatten.collect{|x| normalize_value(x)}.compact
- when Array
- value.flatten.collect{|x| x.to_s}
- else
- value.to_s
- end
+ def normalize_arg(arg)
+ arg.to_s.downcase.gsub(/[^a-z0-9]/,'-')
+ end
+
+ def normalize_value(value)
+ case value
+ when TrueClass, 'true' #ie, ==true, see http://www.ruby-doc.org/core-1.9.3/TrueClass.html
+ nil
+ when Hash
+ value.to_a.flatten.collect{|x| normalize_value(x)}.compact
+ when Array
+ value.flatten.collect{|x| x.to_s}
+ else
+ value.to_s
end
+ end
- def normalize_repeatable_value(value)
- case value
- when Hash, Array
- value.each do |(key, value)|
- yield [normalize_value(key), normalize_value(value)]
- end
- else
- [normalize_value(value), '']
+ def normalize_repeatable_value(value)
+ case value
+ when Hash, Array
+ value.each do |(key, val)|
+ yield [normalize_value(key), normalize_value(val)]
end
+ else
+ [normalize_value(value), '']
end
+ end
- def successful?(status)
- return true if status.success?
+ def successful?(status)
+ return true if status.success?
- # Some of the codes: https://code.google.com/p/wkhtmltopdf/issues/detail?id=1088
- # returned when assets are missing (404): https://code.google.com/p/wkhtmltopdf/issues/detail?id=548
- return true if status.exitstatus == 2 && error_handling?
+ # Some of the codes: https://code.google.com/p/wkhtmltopdf/issues/detail?id=1088
+ # returned when assets are missing (404): https://code.google.com/p/wkhtmltopdf/issues/detail?id=548
+ return true if status.exitstatus == 2 && error_handling?
- false
- end
+ false
+ end
- def error_handling?
- @options.key?('--ignore-load-errors') ||
- # wkhtmltopdf v0.10.0 beta4 replaces ignore-load-errors with load-error-handling
- # https://code.google.com/p/wkhtmltopdf/issues/detail?id=55
- %w(skip ignore).include?(@options['--load-error-handling'])
- end
+ def empty_result?(path, result)
+ (path && File.size(path) == 0) || (path.nil? && result.to_s.strip.empty?)
+ end
+ def error_handling?
+ @options.key?('--ignore-load-errors') ||
+ # wkhtmltopdf v0.10.0 beta4 replaces ignore-load-errors with load-error-handling
+ # https://code.google.com/p/wkhtmltopdf/issues/detail?id=55
+ %w(skip ignore).include?(@options['--load-error-handling'])
+ end
end