Skip to content

Commit

Permalink
Handle inline Interactive Forms
Browse files Browse the repository at this point in the history
  • Loading branch information
tomascco committed Nov 30, 2023
1 parent c7ec32e commit 7fa8e91
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 2 deletions.
19 changes: 17 additions & 2 deletions lib/rubrik/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,22 @@ def fetch_or_create_interactive_form!
root_ref = objects.trailer[:Root]
root = T.let(objects.fetch(root_ref), T::Hash[Symbol, T.untyped])

if root.key?(:AcroForm)
interactive_form_value = root[:AcroForm]
case interactive_form_value
when PDF::Reader::Reference
form_id = root[:AcroForm]

modified_objects << {id: form_id, value: objects.fetch(form_id).dup}
else
when Hash
interactive_form_id = assign_new_object_id!

modified_objects << {id: interactive_form_id, value: interactive_form_value.dup}

new_root = root.dup
new_root[:AcroForm] = interactive_form_id

modified_objects << {id: root_ref, value: new_root}
when NilClass
interactive_form_id = assign_new_object_id!
modified_objects << {id: interactive_form_id, value: {Fields: []}}

Expand All @@ -131,6 +142,10 @@ def fetch_or_create_interactive_form!
updated_root[:AcroForm] = interactive_form_id

modified_objects << {id: root_ref, value: updated_root}
else
raise Error.new(
"Expected dictionary, reference or nil but got " \
"#{interactive_form_value.class} on AcroForm entry.")
end

interactive_form[:SigFlags] = 3 # dont modify, append only
Expand Down
35 changes: 35 additions & 0 deletions test/rubrik/document_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,41 @@ def test_initialize_document_with_interactive_form
input&.close
end

def test_initialize_document_with_unexpected_interactive_form_input
# Arrange
input = File.open(SupportPDF["unexpected_value_interactive_form"], "rb")

# Act + Assert
assert_raises("Expected dictionary, reference or nil but got Array on AcroForm entry.") do
Document.new(input)
end
ensure
input&.close
end

def test_initialize_document_with_inline_interactive_form
# Arrange
input = File.open(SupportPDF["inline_interactive_form"], "rb")

# Act
document = Document.new(input)

# Assert
assert_equal(input, document.send(:io))
assert_equal(5, document.last_object_id)
assert_kind_of(PDF::Reader::ObjectHash, document.objects)

root_ref = PDF::Reader::Reference.new(1, 0)
assert_pattern do
document.modified_objects => [
{id: PDF::Reader::Reference, value: {Fields: [], SigFlags: 3, NeedAppearances: true}},
{id: ^root_ref, value: Hash}
]
end
ensure
input&.close
end

def test_add_signature_field
# Arrange
input = File.open(SupportPDF["with_interactive_form"], "rb")
Expand Down
36 changes: 36 additions & 0 deletions test/rubrik/sign_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,41 @@ def test_document_with_indirect_annots
output_pdf&.close
File.delete(output_pdf.path) if output_pdf
end

def test_document_with_inline_interactive_form
# Arrange
input_pdf = File.open(SupportPDF["inline_interactive_form"], "rb")
output_pdf = StringIO.new
certificate_file = File.open("test/support/demo_cert.pem", "rb")

private_key = OpenSSL::PKey::RSA.new(certificate_file, "")
certificate_file.rewind
certificate = OpenSSL::X509::Certificate.new(certificate_file)

# Act
Sign.call(input_pdf, output_pdf, private_key:, certificate:)

# Assert
expected_output = File.open(SupportPDF["inline_interactive_form.expected"], "rb")

expected_output.readlines.zip(output_pdf.readlines).each do |(expected_line, actual_line)|
# We must erase the signature because it is timestampped
if actual_line&.match?("/Type /Sig")
actual_line.sub!(/<[a-f0-9]+>/, "")
expected_line.sub!(/<[a-f0-9]+>/, "")
# The signature field name is also random
elsif actual_line&.match?(/Signature-[a-f0-9]{4}/)
actual_line.sub!(/Signature-[a-f0-9]{4}/, "")
expected_line.sub!(/Signature-[a-f0-9]{4}/, "")
end

assert_equal(expected_line, actual_line)
end
ensure
certificate_file&.close
output_pdf&.close
input_pdf&.close
expected_output&.close
end
end
end
Binary file added test/support/inline_interactive_form.expected.pdf
Binary file not shown.
Binary file added test/support/inline_interactive_form.pdf
Binary file not shown.
Binary file added test/support/unexpected_value_interactive_form.pdf
Binary file not shown.

0 comments on commit 7fa8e91

Please sign in to comment.