From 56142b7a2eb71e58df7bd295b1fad6120a79d5f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Co=C3=AAlho?= Date: Fri, 1 Dec 2023 18:32:38 +0000 Subject: [PATCH] Fix indirect Fields on Interactive Form --- lib/rubrik/document.rb | 10 +++++- test/rubrik/document_test.rb | 22 +++++++++++++ test/rubrik/sign_test.rb | 36 ++++++++++++++++++++++ test/support/indirect_fields.expected.pdf | Bin 0 -> 9468 bytes test/support/indirect_fields.pdf | Bin 0 -> 678 bytes 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 test/support/indirect_fields.expected.pdf create mode 100644 test/support/indirect_fields.pdf 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 0000000000000000000000000000000000000000..66ea92e01ec623a2e94bb2ef3eef56590a7a4f43 GIT binary patch literal 9468 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 Z_bLg;JnG*!zUq1JQVD-1lG|G){{(U)k>&sZ literal 0 HcmV?d00001 diff --git a/test/support/indirect_fields.pdf b/test/support/indirect_fields.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8819d6257462dfb97ad1844ccc4c385c18ba2d03 GIT binary patch literal 678 zcmZWnO>e?5487-9_<{t7^)sPGLP)T!JM0SqH^d>Mq3cu&>s*)bku+pH}Ty04WRp4h)5e1?-^1M$lwIcNlJViG>jg(D}!U z;%i