class PdfForms::PdftkWrapper

Wraps calls to PdfTk

Constants

ALLOWED_OPTIONS
PDFTK

Attributes

options[R]
pdftk[R]

Public Class Methods

new(*args) click to toggle source

Initializes a new wrapper instance. Pdftk will be autodetected from PATH: PdftkWrapper.new(:flatten => true, :encrypt => true, :encrypt_options => 'allow Printing')

The pdftk binary may also be explecitly specified: PdftkWrapper.new('/usr/bin/pdftk', :flatten => true, :encrypt => true, :encrypt_options => 'allow Printing')

Besides the options shown above, the drop_xfa or drop_xmp options are also supported.

# File lib/pdf_forms/pdftk_wrapper.rb, line 29
def initialize(*args)
  pdftk, options = normalize_args *args
  @pdftk = Cliver.detect! pdftk
  raise "pdftk executable #{@pdftk} not found" unless call_pdftk('-h') && $?.success?
  @options = options
end

Public Instance Methods

call_pdftk(*args) click to toggle source

returns the commands output, check general execution success with $?.success?

# File lib/pdf_forms/pdftk_wrapper.rb, line 87
def call_pdftk(*args)
  SafeShell.execute pdftk, *(args.flatten)
end
cat(*args) click to toggle source

concatenate documents, can optionally specify page ranges

args: in_file1, {in_file2 => [“1-2”, “4-10”]}, … , in_file_n, output

# File lib/pdf_forms/pdftk_wrapper.rb, line 94
def cat(*args)
  in_files = []
  page_ranges = []
  file_handle = "A"
  output = normalize_path args.pop

  args.flatten.compact.each do |in_file|
    if in_file.is_a? Hash
      path = in_file.keys.first
      page_ranges.push *in_file.values.first.map {|range| "#{file_handle}#{range}"}
    else
      path = in_file
      page_ranges.push "#{file_handle}"
    end
    in_files.push "#{file_handle}=#{normalize_path(path)}"
    file_handle.next!
  end

  call_pdftk in_files, 'cat', page_ranges, 'output', output
end
fill_form(template, destination, data = {}, fill_options = {}) click to toggle source

pdftk.fill_form '/path/to/form.pdf', '/path/to/destination.pdf', :field1 => 'value 1'

# File lib/pdf_forms/pdftk_wrapper.rb, line 37
def fill_form(template, destination, data = {}, fill_options = {})
  q_template = normalize_path(template)
  q_destination = normalize_path(destination)
  fdf = data_format(data)
  tmp = Tempfile.new('pdf_forms-fdf')
  tmp.close
  fdf.save_to tmp.path
  fill_options = {:tmp_path => tmp.path}.merge(fill_options)

  args = [ q_template, 'fill_form', normalize_path(tmp.path), 'output', q_destination ]
  result = call_pdftk(*(append_options(args, fill_options)))

  unless File.readable?(destination) && File.size(destination) > 0
    fdf_path = nil
    begin
      fdf_path = File.join(File.dirname(tmp.path), "#{Time.now.strftime '%Y%m%d%H%M%S'}.fdf")
      fdf.save_to fdf_path
    rescue Exception
      fdf_path = "#{$!}\n#{$!.backtrace.join("\n")}"
    end
    raise PdftkError.new("failed to fill form with command\n#{pdftk} #{args.flatten.compact.join ' '}\ncommand output was:\n#{result}\nfailing form data has been saved to #{fdf_path}")
  end
ensure
  tmp.unlink if tmp
end
get_field_names(template) click to toggle source

get field names for template

Initialize the object with utf8_fields: true to get utf8 encoded field names.

# File lib/pdf_forms/pdftk_wrapper.rb, line 81
def get_field_names(template)
  read(template).fields.map{|f| f.name}
end
get_fields(template) click to toggle source

Get field metadata for template

Initialize the object with utf8_fields: true to get utf8 encoded field metadata.

# File lib/pdf_forms/pdftk_wrapper.rb, line 73
def get_fields(template)
  read(template).fields
end
multistamp(primary_file, stamp_file, output) click to toggle source

applies each page of the stamp PDF to the corresponding page of the input PDF args: primary_file, stamp_file, output

# File lib/pdf_forms/pdftk_wrapper.rb, line 124
def multistamp(primary_file, stamp_file, output)
  call_pdftk primary_file, 'multistamp', stamp_file, 'output', output
end
read(path) click to toggle source

pdftk.read '/path/to/form.pdf' returns an instance of PdfForms::Pdf representing the given template

# File lib/pdf_forms/pdftk_wrapper.rb, line 65
def read(path)
  Pdf.new path, self, options
end
stamp(primary_file, stamp_file, output) click to toggle source

stamp one pdf with another

args: primary_file, stamp_file, output

# File lib/pdf_forms/pdftk_wrapper.rb, line 118
def stamp(primary_file, stamp_file, output)
  call_pdftk primary_file, 'stamp', stamp_file, 'output', output
end

Private Instance Methods

append_options(args, local_options = {}) click to toggle source
# File lib/pdf_forms/pdftk_wrapper.rb, line 146
def append_options(args, local_options = {})
  return args if options.empty? && local_options.empty?
  args = args.dup
  ALLOWED_OPTIONS.each do |option|
    if option_or_global(option, local_options)
      args << option.to_s
    end
  end
  if option_or_global(:encrypt, local_options)
    encrypt_pass = option_or_global(:encrypt_password, local_options)
    encrypt_pass ||= SecureRandom.urlsafe_base64
    encrypt_options = option_or_global(:encrypt_options, local_options)
    encrypt_options = encrypt_options.split if String === encrypt_options
    args << ['encrypt_128bit', 'owner_pw', encrypt_pass, encrypt_options]
  end
  args.flatten!
  args.compact!
  args
end
data_format(data) click to toggle source
# File lib/pdf_forms/pdftk_wrapper.rb, line 130
def data_format(data)
  data_format = options[:data_format] || 'Fdf'
  PdfForms.const_get(data_format).new(data)
end
normalize_args(*args) click to toggle source
# File lib/pdf_forms/pdftk_wrapper.rb, line 166
def normalize_args(*args)
  args = args.dup
  pdftk = PDFTK
  options = {}
  if first_arg = args.shift
    case first_arg
    when String
      pdftk = first_arg
      options = args.shift || {}
      raise ArgumentError.new("expected hash, got #{options.class.name}") unless Hash === options
    when Hash
      options = first_arg
    else
      raise ArgumentError.new("expected string or hash, got #{first_arg.class.name}")
    end
  end
  [pdftk, options]
end
option_or_global(attrib, local = {}) click to toggle source
# File lib/pdf_forms/pdftk_wrapper.rb, line 135
def option_or_global(attrib, local = {})
  local[attrib] || options[attrib]
end