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