Lei: could you please take a look? Thanks. :-)
If this is fine, then I guess the next step would be actually exposing some "pdf signature" object, so you can get the Nth object when the API added in this change says there are such objects.
To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.
Is this going to lead into having a family of APIs for digital signatures? Do we want put them in a separate header in the public/ directory?
Can two_signatures.pdf be smaller and still have all the document features needed to test digital signatures like the current ~100 KB one?
I imagine trying to handwrite two_signatures.pdf is way too difficult. If we are checking in a PDF, can the CL description explain how it's generated?
Is this going to lead into having a family of APIs for digital signatures? Do we want put them in a separate header in the public/ directory?
Yes, I imagine some 5 more functions till signatures can be verified by client code. One to get the Nth signature, then to access a few properties. Done.
Can two_signatures.pdf be smaller and still have all the document features needed to test digital signatures like the current ~100 KB one?
I had integration testing in mind, in which case a hexdump of a PKCS#7 signature blob is around 50KB. I tested with Acrobat and LibreOffice, both create signatures with around that size.
I imagine trying to handwrite two_signatures.pdf is way too difficult. If we are checking in a PDF, can the CL description explain how it's generated?
Actually we can handwrite it for unit testing purposes, given that the PKCS#7 blob can be simply a stub. This instantly solves the size concern as well. Done.
For the reference, I created a self-signed CA + two x509 signing certificates using the openssl commandline tool, then signed an empty PDF with LibreOffice, that's how I created the test file in patch set 1. (Could do the same with Acrobat as well, the output would be quite similar.) But based on the above, this is not needed after all.
Also, FPDF_GetSignatureCount() now returns -1 if the document argument is nullptr.
Patch Set 1:
> I imagine trying to handwrite two_signatures.pdf is way too difficult. If we are checking in a PDF, can the CL description explain how it's generated?
Actually we can handwrite it for unit testing purposes, given that the PKCS#7 blob can be simply a stub. This instantly solves the size concern as well. Done.
Nice. This is definitely one of the fancier .in files. Glad it works.
Also, FPDF_GetSignatureCount() now returns -1 if the document argument is nullptr.
Make sense. Thanks.
Patch set 2:Commit-Queue +1
9 comments:
File fpdfsdk/fpdf_doc_embeddertest.cpp:
Patch Set #2, Line 486: TEST_F(FPDFDocEmbedderTest, GetSignatureCount) {
I would similarly move these into its own test file.
Patch Set #2, Line 298: FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document) {
And separate fpdfsdk/fpdf_signature.cpp.
Patch Set #2, Line 322: GetStringFor
BTW, I'm hoping to add GetNameFor() later this week. Then we can switch to that here.
File fpdfsdk/fpdf_view_c_api_test.c:
Patch Set #2, Line 439: CHK(FPDF_GetSignatureCount);
And start a new section for the new file.
File testing/resources/two_signatures.in:
Patch Set #2, Line 45: %% 0 -> 10, 30 -> 40
This comment is a bit mysterious. Care to elaborate?
Patch Set #2, Line 46: {{object 5 0}} <<
Does this section require the objects to be ordered as 5, 6, 7, 3, 1?
Patch Set #2, Line 48: /Type/Sig
I would put /Type, /Filter, and /SubFilter first, and then just sort the rest.
Patch Set #2, Line 49: /SubFilter/ETSI.CAdES.detached
Space these out like the top portion of the file.
Patch Set #2, Line 51: [ 0 10 30 10 ]
[0 10 30 10]
To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.
I think I addressed all of your points, please let me know if something is still missing. Thanks.
9 comments:
Patch Set #2, Line 486: TEST_F(FPDFDocEmbedderTest, GetSignatureCount) {
I would similarly move these into its own test file.
Patch Set #2, Line 298: FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document) {
And separate fpdfsdk/fpdf_signature.cpp.
Done
Patch Set #2, Line 322: GetStringFor
BTW, I'm hoping to add GetNameFor() later this week. Then we can switch to that here.
Patch Set #2, Line 439: CHK(FPDF_GetSignatureCount);
And start a new section for the new file.
Patch Set #2, Line 45: %% 0 -> 10, 30 -> 40
This comment is a bit mysterious. […]
What I meant to point out is that these are start offset + length pairs, not start offset + end offset ones. It's in the spec, but it can confusing if you just read the .in file. I've updated the comment to be more explicit.
Patch Set #2, Line 46: {{object 5 0}} <<
Does this section require the objects to be ordered as 5, 6, 7, 3, 1?
Not really. I just added the new objects, then went up the parents chain and updated the old objects to refer to the new ones. Sorted now.
Patch Set #2, Line 48: /Type/Sig
I would put /Type, /Filter, and /SubFilter first, and then just sort the rest.
Done for both signature objects.
Patch Set #2, Line 49: /SubFilter/ETSI.CAdES.detached
Space these out like the top portion of the file.
Done for both.
Patch Set #2, Line 51: [ 0 10 30 10 ]
[0 10 30 10]
Done for both.
To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.
Mostly good. Just nit picking.
9 comments:
File fpdfsdk/fpdf_signature.cpp:
BTW, I would recommend avoiding Hungarian notation in new code. Just go with Chromium style.
Patch Set #2, Line 322: GetStringFor
Ack
Patch Set #3, Line 47: #include "public/fpdf_signature.h"
Still needed?
Patch Set #3, Line 19: FPDF_LoadDocument
FPDF_LoadDocument()
File testing/resources/two_signatures.in:
Patch Set #3, Line 45: {{object 1 0}} <<
It'll be helpful to add a comment here and on line 103 to explain why there are repeated objects.
Patch Set #3, Line 48: /AcroForm<</Fields[7 0 R]/SigFlags 3>>
Expand this out as well?
/AcroForm <<
/Fields [7 0 R]
/SigFlags 3
>>
Can the objects in this section be grouped together?
space before
More spacing here and below.
To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.
I think I addressed all nits now.
9 comments:
BTW, I would recommend avoiding Hungarian notation in new code. Just go with Chromium style.
I've removed all of them in this file.
Patch Set #2, Line 322: GetStringFor
It's in https://pdfium.googlesource. […]
OK, I've rebased and now I use this.
Patch Set #3, Line 47: #include "public/fpdf_signature.h"
Still needed?
Oops, just a leftover. :-) Removed now.
Patch Set #3, Line 19: FPDF_LoadDocument
FPDF_LoadDocument()
Patch Set #3, Line 45: {{object 1 0}} <<
It'll be helpful to add a comment here and on line 103 to explain why there are repeated objects.
Done
Patch Set #3, Line 48: /AcroForm<</Fields[7 0 R]/SigFlags 3>>
Expand this out as well? […]
Done
Can the objects in this section be grouped together?
I assume you mean avoid empty lines inside the two incremental updates, I did that now.
space before
Done
More spacing here and below.
I think I fixed all cases where / was not preceded by a space and broke up all lines with <<.*>> in them.
To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.
Hopefully the last round of nits.
8 comments:
File fpdfsdk/fpdf_signature.cpp:
Patch Set #4, Line 21: acroForm
With a lowercase 'f' please, or acro_form.
Patch Set #4, Line 29: signatureCount
Chromium style is signature_count.
Patch Set #4, Line 30: fieldIndex
Maybe just go with "i" since there are no other indices. Or try and see if range-based for-loops works on CPDF_Array. i.e. for (const CPDF_Object* field : *fields)
File testing/resources/two_signatures.in:
Patch Set #4, Line 45: %% First incremental update adds an initial signature and update objects to refer to it.
Wrap at 80 columns.
spacing
spacing
Patch Set #4, Line 91: /AP<<
Spacing here and indenting on the next 2 lines.
spacing
To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.
1 comment:
File fpdfsdk/fpdf_signature.cpp:
if (!fieldDict)
continue;
if (fieldDict->GetNameFor("FT") != "Sig")
continue;
++signatureCount;
drive-by nit: As much as we like |continue| to avoid deep nesting, its probably clearer when written as
if (field_dict && field_dict->GetNameFor("FT") == "Sig")
++count;To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.
Hopefully the last round of nits.
All done, I believe.
9 comments:
With a lowercase 'f' please, or acro_form.
Ah, sorry. I was confused by what style to use in new code, but now I found https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++.md, which links the Google guide, and found you like foo_bar in new code. Will stick to that from now on. :-)
Patch Set #4, Line 29: signatureCount
Chromium style is signature_count.
Done
Patch Set #4, Line 30: fieldIndex
Maybe just go with "i" since there are no other indices. […]
invalid range expression of type 'const CPDF_Array *'; no viable 'begin' function available
So I went with "i" for now.
if (!fieldDict)
continue;
if (fieldDict->GetNameFor("FT") != "Sig")
continue;
++signatureCount;
drive-by nit: As much as we like |continue| to avoid deep nesting, its probably clearer when written […]
Done
File testing/resources/two_signatures.in:
Patch Set #4, Line 45: %% First incremental update adds an initial signature and update objects to refer to it.
Wrap at 80 columns.
Done
spacing
Done
spacing
Done
Patch Set #4, Line 91: /AP<<
Spacing here and indenting on the next 2 lines.
Done
spacing
Done
To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.
3 comments:
File fpdfsdk/fpdf_signature.cpp:
Patch Set #4, Line 30: fieldIndex
invalid range expression of type 'const CPDF_Array *'; no viable 'begin' function available […]
Ah, use CPDF_ArrayLocker.
File fpdfsdk/fpdf_signature.cpp:
Patch Set #5, Line 32: fieldDict
One more to change to snake_case.
File testing/resources/two_signatures.in:
Patch Set #5, Line 87: /FT /Sig
I typically put this after /Subtype, since it's the field type.
To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.
3 comments:
Patch Set #4, Line 30: fieldIndex
Ah, use CPDF_ArrayLocker.
Done
File fpdfsdk/fpdf_signature.cpp:
Patch Set #5, Line 32: fieldDict
One more to change to snake_case.
Patch Set #5, Line 87: /FT /Sig
I typically put this after /Subtype, since it's the field type.
Done here and for the other Widget as well.
To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.
LGTM! Thanks for addressing all the comments.
Patch set 6:Code-Review +1
Patch set 6:Commit-Queue +2
Chromium commit bot submitted this change.
Add FPDF_GetSignatureCount() API
This is the first step towards exposing enough information about digital
signatures so that client code (not pdfium itself) can evaluate if they
are valid or not.
Change-Id: I9b913521bd700bfadd8ca02f5c6fe49f7b63e31e
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/70830
Reviewed-by: Lei Zhang <the...@chromium.org>
Commit-Queue: Lei Zhang <the...@chromium.org>
---
M BUILD.gn
M fpdfsdk/BUILD.gn
A fpdfsdk/fpdf_signature.cpp
A fpdfsdk/fpdf_signature_embeddertest.cpp
M fpdfsdk/fpdf_view_c_api_test.c
A public/fpdf_signature.h
A testing/resources/two_signatures.in
A testing/resources/two_signatures.pdf
8 files changed, 444 insertions(+), 0 deletions(-)
diff --git a/BUILD.gn b/BUILD.gn
index 5bcb9cb..8bfe0ca55 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -122,6 +122,7 @@
"public/fpdf_progressive.h",
"public/fpdf_save.h",
"public/fpdf_searchex.h",
+ "public/fpdf_signature.h",
"public/fpdf_structtree.h",
"public/fpdf_sysfontinfo.h",
"public/fpdf_text.h",
diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn
index 081152e..93cfe5c 100644
--- a/fpdfsdk/BUILD.gn
+++ b/fpdfsdk/BUILD.gn
@@ -62,6 +62,7 @@
"fpdf_progressive.cpp",
"fpdf_save.cpp",
"fpdf_searchex.cpp",
+ "fpdf_signature.cpp",
"fpdf_structtree.cpp",
"fpdf_sysfontinfo.cpp",
"fpdf_text.cpp",
@@ -145,6 +146,7 @@
"fpdf_ppo_embeddertest.cpp",
"fpdf_save_embeddertest.cpp",
"fpdf_searchex_embeddertest.cpp",
+ "fpdf_signature_embeddertest.cpp",
"fpdf_structtree_embeddertest.cpp",
"fpdf_sysfontinfo_embeddertest.cpp",
"fpdf_text_embeddertest.cpp",
diff --git a/fpdfsdk/fpdf_signature.cpp b/fpdfsdk/fpdf_signature.cpp
new file mode 100644
index 0000000..0d898ef
--- /dev/null
+++ b/fpdfsdk/fpdf_signature.cpp
@@ -0,0 +1,38 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_signature.h"
+
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document) {
+ auto* doc = CPDFDocumentFromFPDFDocument(document);
+ if (!doc)
+ return -1;
+
+ CPDF_Dictionary* root = doc->GetRoot();
+ if (!root)
+ return 0;
+
+ const CPDF_Dictionary* acro_form = root->GetDictFor("AcroForm");
+ if (!acro_form)
+ return 0;
+
+ const CPDF_Array* fields = acro_form->GetArrayFor("Fields");
+ if (!fields)
+ return 0;
+
+ int signature_count = 0;
+ CPDF_ArrayLocker locker(fields);
+ for (const auto& field : locker) {
+ const CPDF_Dictionary* field_dict = field->GetDict();
+ if (field_dict && field_dict->GetNameFor("FT") == "Sig")
+ ++signature_count;
+ }
+
+ return signature_count;
+}
diff --git a/fpdfsdk/fpdf_signature_embeddertest.cpp b/fpdfsdk/fpdf_signature_embeddertest.cpp
new file mode 100644
index 0000000..fb8bb3a
--- /dev/null
+++ b/fpdfsdk/fpdf_signature_embeddertest.cpp
@@ -0,0 +1,21 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_signature.h"
+#include "testing/embedder_test.h"
+
+class FPDFSignatureEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFSignatureEmbedderTest, GetSignatureCount) {
+ EXPECT_TRUE(OpenDocument("two_signatures.pdf"));
+ EXPECT_EQ(2, FPDF_GetSignatureCount(document()));
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetSignatureCountZero) {
+ EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+ EXPECT_EQ(0, FPDF_GetSignatureCount(document()));
+
+ // Provide no document.
+ EXPECT_EQ(-1, FPDF_GetSignatureCount(nullptr));
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index f76d3d2..d04d1f8 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -24,6 +24,7 @@
#include "public/fpdf_progressive.h"
#include "public/fpdf_save.h"
#include "public/fpdf_searchex.h"
+#include "public/fpdf_signature.h"
#include "public/fpdf_structtree.h"
#include "public/fpdf_sysfontinfo.h"
#include "public/fpdf_text.h"
@@ -314,6 +315,9 @@
CHK(FPDFText_GetCharIndexFromTextIndex);
CHK(FPDFText_GetTextIndexFromCharIndex);
+ // fpdf_signature.h
+ CHK(FPDF_GetSignatureCount);
+
// fpdf_structtree.h
CHK(FPDF_StructElement_CountChildren);
CHK(FPDF_StructElement_GetAltText);
diff --git a/public/fpdf_signature.h b/public/fpdf_signature.h
new file mode 100644
index 0000000..7290bcb
--- /dev/null
+++ b/public/fpdf_signature.h
@@ -0,0 +1,28 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PUBLIC_FPDF_SIGNATURE_H_
+#define PUBLIC_FPDF_SIGNATURE_H_
+
+// NOLINTNEXTLINE(build/include)
+#include "fpdfview.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// Experimental API.
+// Function: FPDF_GetSignatureCount
+// Get total number of signatures in the document.
+// Parameters:
+// document - Handle to document. Returned by FPDF_LoadDocument().
+// Return value:
+// Total number of signatures in the document on success, -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+#endif // PUBLIC_FPDF_SIGNATURE_H_
diff --git a/testing/resources/two_signatures.in b/testing/resources/two_signatures.in
new file mode 100644
index 0000000..f18495f
--- /dev/null
+++ b/testing/resources/two_signatures.in
@@ -0,0 +1,155 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 300]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+{{object 5 0}} <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ /SubFilter /ETSI.CAdES.detached
+ /ByteRange [0 10 30 10]
+ /Contents <308006092A864886F70D010702A0803080020101>
+ /M (D:20200624093114+02'00')
+>>
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature1)
+ /V 5 0 R
+ /DV 5 0 R
+ /AP <<
+ /N 6 0 R
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+
+%% Second incremental update adds a next signature and update objects once again to refer to it.
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R 10 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R 10 0 R]
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ /SubFilter /ETSI.CAdES.detached
+ /ByteRange [0 40 50 10]
+ /Contents <308006092A864886F70D010702A080308002010131>
+ /M (D:20200624093118+02'00')
+>>
+endobj
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+{{object 10 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature2)
+ /V 8 0 R
+ /DV 8 0 R
+ /AP <<
+ /N 9 0 R
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/two_signatures.pdf b/testing/resources/two_signatures.pdf
new file mode 100644
index 0000000..9a033d6
--- /dev/null
+++ b/testing/resources/two_signatures.pdf
@@ -0,0 +1,195 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 300]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+ /Length 188
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000157 00000 n
+0000000226 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
+startxref
+466
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+5 0 obj <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ /SubFilter /ETSI.CAdES.detached
+ /ByteRange [0 10 30 10]
+ /Contents <308006092A864886F70D010702A0803080020101>
+ /M (D:20200624093114+02'00')
+>>
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+7 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature1)
+ /V 5 0 R
+ /DV 5 0 R
+ /AP <<
+ /N 6 0 R
+ >>
+>>
+endobj
+xref
+0 8
+0000000000 65535 f
+0000000726 00000 n
+0000000068 00000 n
+0000000835 00000 n
+0000000226 00000 n
+0000000998 00000 n
+0000001201 00000 n
+0000001303 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+>>
+startxref
+1475
+%%EOF
+
+%% Second incremental update adds a next signature and update objects once again to refer to it.
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R 10 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R 10 0 R]
+>>
+endobj
+8 0 obj <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ /SubFilter /ETSI.CAdES.detached
+ /ByteRange [0 40 50 10]
+ /Contents <308006092A864886F70D010702A080308002010131>
+ /M (D:20200624093118+02'00')
+>>
+endobj
+9 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+10 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature2)
+ /V 8 0 R
+ /DV 8 0 R
+ /AP <<
+ /N 9 0 R
+ >>
+>>
+endobj
+xref
+0 11
+0000000000 65535 f
+0000001801 00000 n
+0000000068 00000 n
+0000001917 00000 n
+0000000226 00000 n
+0000000998 00000 n
+0000001201 00000 n
+0000001303 00000 n
+0000002011 00000 n
+0000002216 00000 n
+0000002318 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 11
+>>
+startxref
+2491
+%%EOF
To view, visit change 70830. To unsubscribe, or for help writing mail filters, visit settings.