lib/hexapdf/cli/form.rb in hexapdf-0.40.0 vs lib/hexapdf/cli/form.rb in hexapdf-0.41.0
- old
+ new
@@ -74,10 +74,14 @@
@generate_template = true
end
options.on('--flatten', 'Flatten the form fields') do
@flatten = true
end
+ options.on("--[no-]fill-read-only-fields", "Allow filling in fields that are " \
+ "marked as read only. Default: false") do |read_only|
+ @fill_read_only_fields = read_only
+ end
options.on("--[no-]viewer-override", "Let the PDF viewer override the visual " \
"appearance. Default: use setting from input PDF") do |need_appearances|
@need_appearances = need_appearances
end
options.on("--[no-]incremental-save", "Append the changes instead of rewriting the " \
@@ -88,10 +92,11 @@
@password = nil
@fill = false
@flatten = false
@generate_template = false
@template = nil
+ @fill_read_only_fields = false
@need_appearances = nil
@incremental = true
end
def execute(in_file, out_file = nil) #:nodoc:
@@ -113,10 +118,11 @@
if @template
fill_form_with_template(doc)
else
fill_form(doc)
end
+ doc.acro_form.recalculate_fields
end
if @flatten && !doc.acro_form.flatten.empty?
$stderr.puts "Warning: Not all form fields could be flattened"
doc.catalog.delete(:AcroForm)
doc.delete(doc.acro_form)
@@ -124,12 +130,16 @@
elsif @generate_template
unsupported_fields = [:signature_field, :password_field]
each_field(doc) do |_, _, field, _|
next if unsupported_fields.include?(field.concrete_field_type)
name = field.full_field_name.gsub(':', "\\:")
- Array(field.field_value).each do |val|
- puts "#{name}: #{val.to_s.gsub(/(\r|\r\n|\n)/, '\1 ')}"
+ if field.field_value
+ Array(field.field_value).each do |val|
+ puts "#{name}: #{val.to_s.gsub(/(\r|\r\n|\n)/, '\1 ')}"
+ end
+ else
+ puts "#{name}: "
end
end
else
list_form_fields(doc)
end
@@ -139,59 +149,67 @@
private
# Lists all terminal form fields.
def list_form_fields(doc)
current_page_index = -1
- each_field(doc) do |_page, page_index, field, widget|
+ each_field(doc, with_seen: true) do |_page, page_index, field, widget|
if current_page_index != page_index
puts "Page #{page_index + 1}"
current_page_index = page_index
end
field_name = field.full_field_name +
(field.alternate_field_name ? " (#{field.alternate_field_name})" : '')
concrete_field_type = field.concrete_field_type
nice_field_type = concrete_field_type.to_s.split('_').map(&:capitalize).join(' ')
+ size = "(#{widget[:Rect].width.round(3)}x#{widget[:Rect].height.round(3)})"
position = "(#{widget[:Rect].left}, #{widget[:Rect].bottom})"
field_value = if !field.field_value || concrete_field_type != :signature_field
field.field_value.inspect
else
sig = field.field_value
temp = "#{sig.signer_name} (#{sig.signing_time})"
temp << " (#{sig.signing_reason})" if sig.signing_reason
temp
end
- puts " #{field_name}"
+ flags = field_flags(field)
+ puts " #{field_name}" << (flags.empty? ? '' : " (#{flags.join(', ')})")
if command_parser.verbosity_info?
- printf(" └─ %-22s | %-20s\n", nice_field_type, position)
+ printf(" └─ %-22s | %-20s\n", nice_field_type, "#{size} #{position}")
end
puts " └─ #{field_value}"
if command_parser.verbosity_info?
if field.field_type == :Ch
puts " └─ Options: #{field.option_items.map(&:inspect).join(', ')}"
elsif concrete_field_type == :radio_button || concrete_field_type == :check_box
puts " └─ Options: #{([:Off] + field.allowed_values).map(&:to_s).join(', ')}"
end
+ puts " └─ Widget OID: #{widget.oid},#{widget.gen}"
+ if field != widget
+ puts " └─ Field OID: #{field.oid},#{field.gen}"
+ end
end
end
end
# Fills out the form by interactively asking the user for field values.
def fill_form(doc)
current_page_index = -1
each_field(doc) do |_page, page_index, field, _widget|
+ next if field.flagged?(:read_only) && !@fill_read_only_fields
if current_page_index != page_index
puts "Page #{page_index + 1}"
current_page_index = page_index
end
field_name = field.full_field_name +
(field.alternate_field_name ? " (#{field.alternate_field_name})" : '')
concrete_field_type = field.concrete_field_type
- puts " #{field_name}"
+ flags = field_flags(field)
+ puts " #{field_name}" << (flags.empty? ? '' : " (#{flags.join(', ')})")
puts " └─ Current value: #{field.field_value.inspect}"
if field.field_type == :Ch
puts " └─ Possible values: #{field.option_items.map(&:to_s).join(', ')}"
elsif concrete_field_type == :radio_button
@@ -219,10 +237,15 @@
data = parse_template
form = doc.acro_form
data.each do |name, value|
field = form.field_by_name(name)
raise Error, "Field '#{name}' not found in input PDF" unless field
+ if field.flagged?(:read_only) && !@fill_read_only_fields
+ puts "Ignoring field '#{name}' because it is read only and --fill-read-only-fields " \
+ "is no set"
+ next
+ end
apply_field_value(field, value)
end
end
# Parses the data from the given template file.
@@ -273,24 +296,30 @@
rescue StandardError
raise Error, "Error while setting '#{field.full_field_name}': #{$!.message}"
end
# Iterates over all non-push button fields in page order. If a field appears on multiple
- # pages, it is only yielded on the first page.
- def each_field(doc) # :yields: page, page_index, field
+ # pages, it is only yielded on the first page if +with_seen+ is +false.
+ def each_field(doc, with_seen: false) # :yields: page, page_index, field
seen = {}
doc.pages.each_with_index do |page, page_index|
page.each_annotation do |annotation|
next unless annotation[:Subtype] == :Widget
field = annotation.form_field
next if field.concrete_field_type == :push_button
- unless seen[field.full_field_name]
+ if with_seen || !seen[field.full_field_name]
yield(page, page_index, field, annotation)
seen[field.full_field_name] = true
end
end
end
+ end
+
+ # Returns an array with the flags "read only" and "required" if they are set.
+ def field_flags(field)
+ [field.flagged?(:read_only) ? "read only" : nil,
+ field.flagged?(:required) ? "required" : nil].compact
end
end
end