diff --git a/lib/rubrik/document.rb b/lib/rubrik/document.rb index 0698701..fe2cdd0 100644 --- a/lib/rubrik/document.rb +++ b/lib/rubrik/document.rb @@ -91,7 +91,15 @@ def add_signature_field modified_objects << {id: first_page_reference, value: new_first_page} end - (interactive_form[:Fields] ||= []) << signature_field_id + fields_entry = interactive_form[:Fields] + if fields_entry.is_a?(PDF::Reader::Reference) + new_fields_array = objects.fetch(fields_entry).dup + new_fields_array << signature_field_id + + modified_objects << {id: fields_entry, value: new_fields_array} + else + (interactive_form[:Fields] ||= []) << signature_field_id + end signature_value_id end diff --git a/test/rubrik/document_test.rb b/test/rubrik/document_test.rb index e6534e6..5edb886 100644 --- a/test/rubrik/document_test.rb +++ b/test/rubrik/document_test.rb @@ -189,5 +189,27 @@ def test_add_signature_field_with_indirect_annots ensure input&.close end + + def test_add_signature_field_with_indirect_fields + # Arrange + input = File.open(SupportPDF["indirect_fields"], "rb") + document = Document.new(input) + initial_number_of_objects = document.modified_objects.size + + # Act + result = document.add_signature_field + + # Assert + number_of_added_objects = document.modified_objects.size - initial_number_of_objects + assert_equal(4, number_of_added_objects) + + assert_pattern do + document.modified_objects => [{id: PDF::Reader::Reference, value: {Fields: fields_ref}}, *] + document.modified_objects => [*, {id: ^fields_ref, value: [signature_field_ref]}] + document.modified_objects => [*, {id: ^signature_field_ref, value: {FT: :Sig}}, *] + end + ensure + input&.close + end end end diff --git a/test/rubrik/sign_test.rb b/test/rubrik/sign_test.rb index cf2027f..e1f09dc 100644 --- a/test/rubrik/sign_test.rb +++ b/test/rubrik/sign_test.rb @@ -151,5 +151,41 @@ def test_document_with_inline_interactive_form input_pdf&.close expected_output&.close end + + def test_document_with_indirect_fields + # Arrange + input_pdf = File.open(SupportPDF["indirect_fields"], "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["indirect_fields.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 diff --git a/test/support/indirect_fields.expected.pdf b/test/support/indirect_fields.expected.pdf new file mode 100644 index 0000000..66ea92e Binary files /dev/null and b/test/support/indirect_fields.expected.pdf differ diff --git a/test/support/indirect_fields.pdf b/test/support/indirect_fields.pdf new file mode 100644 index 0000000..8819d62 Binary files /dev/null and b/test/support/indirect_fields.pdf differ