Skip to content

Commit 19beec7

Browse files
SnipxiText-CI
authored andcommitted
Fix append mode for hybrid-reference documents
iText used to refer to regular xref section by /Prev key in the xref stream which is "not meaningful in hybrid-reference files" according to the specification. In full compression mode after the changes in this commit first an xref stream is written and then a regular xref section referring to the xref stream. For safety, regular objects, not the ones from object streams, are also enumerated in the last regular xref section of the stamped document. DEVSIX-2080 Autoported commit. Original commit hash: [68411e67b]
1 parent 3708ba5 commit 19beec7

File tree

5 files changed

+108
-56
lines changed

5 files changed

+108
-56
lines changed

itext.tests/itext.sign.tests/itext/signatures/sign/PdfSignatureAppearanceTest.cs

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ source product.
4141
4242
*/
4343
using System;
44+
using System.Collections.Generic;
4445
using System.IO;
4546
using Org.BouncyCastle.Crypto;
4647
using Org.BouncyCastle.X509;
@@ -160,6 +161,52 @@ public virtual void TextAutoscaleTest06() {
160161
AssertAppearanceFontSize(dest, 6.26f);
161162
}
162163

164+
/// <exception cref="System.IO.IOException"/>
165+
/// <exception cref="Org.BouncyCastle.Security.GeneralSecurityException"/>
166+
/// <exception cref="System.Exception"/>
167+
[NUnit.Framework.Test]
168+
public virtual void TestSigningInAppendModeWithHybridDocument() {
169+
String src = sourceFolder + "hybrid.pdf";
170+
String dest = destinationFolder + "signed_hybrid.pdf";
171+
String cmp = sourceFolder + "cmp_signed_hybrid.pdf";
172+
PdfSigner signer = new PdfSigner(new PdfReader(src), new FileStream(dest, FileMode.Create), new StampingProperties
173+
().UseAppendMode());
174+
PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
175+
appearance.SetLayer2FontSize(13.8f).SetPageRect(new Rectangle(36, 748, 200, 100)).SetPageNumber(1).SetReason
176+
("Test").SetLocation("Nagpur");
177+
signer.SetFieldName("Sign1");
178+
signer.SetCertificationLevel(PdfSigner.NOT_CERTIFIED);
179+
IExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256);
180+
signer.SignDetached(pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CADES);
181+
// Make sure iText can open the document
182+
new PdfDocument(new PdfReader(dest)).Close();
183+
// Assert that the document can be rendered correctly
184+
NUnit.Framework.Assert.IsNull(new CompareTool().CompareVisually(dest, cmp, destinationFolder, "diff_", GetIgnoredAreaTestMap
185+
(new Rectangle(36, 748, 200, 100))));
186+
}
187+
188+
/// <exception cref="Org.BouncyCastle.Security.GeneralSecurityException"/>
189+
/// <exception cref="System.IO.IOException"/>
190+
/// <exception cref="System.Exception"/>
191+
[NUnit.Framework.Test]
192+
public virtual void FontColorTest01() {
193+
String fileName = "fontColorTest01.pdf";
194+
String dest = destinationFolder + fileName;
195+
Rectangle rect = new Rectangle(36, 648, 100, 50);
196+
String src = sourceFolder + "simpleDocument.pdf";
197+
PdfSigner signer = new PdfSigner(new PdfReader(src), new FileStream(dest, FileMode.Create), new StampingProperties
198+
());
199+
// Creating the appearance
200+
signer.GetSignatureAppearance().SetLayer2FontColor(ColorConstants.RED).SetLayer2Text("Verified and signed by me."
201+
).SetPageRect(rect);
202+
signer.SetFieldName("Signature1");
203+
// Creating the signature
204+
IExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256);
205+
signer.SignDetached(pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CADES);
206+
NUnit.Framework.Assert.IsNull(new CompareTool().CompareVisually(dest, sourceFolder + "cmp_" + fileName, destinationFolder
207+
, "diff_"));
208+
}
209+
163210
/// <exception cref="System.IO.IOException"/>
164211
/// <exception cref="Org.BouncyCastle.Security.GeneralSecurityException"/>
165212
private void TestSignatureAppearanceAutoscale(String dest, Rectangle rect, PdfSignatureAppearance.RenderingMode
@@ -198,26 +245,10 @@ private static void AssertAppearanceFontSize(String filename, float expectedFont
198245
.Format("Font size: exptected {0}, found {1}", expectedFontSize, fontSize));
199246
}
200247

201-
/// <exception cref="Org.BouncyCastle.Security.GeneralSecurityException"/>
202-
/// <exception cref="System.IO.IOException"/>
203-
/// <exception cref="System.Exception"/>
204-
[NUnit.Framework.Test]
205-
public virtual void FontColorTest01() {
206-
String fileName = "fontColorTest01.pdf";
207-
String dest = destinationFolder + fileName;
208-
Rectangle rect = new Rectangle(36, 648, 100, 50);
209-
String src = sourceFolder + "simpleDocument.pdf";
210-
PdfSigner signer = new PdfSigner(new PdfReader(src), new FileStream(dest, FileMode.Create), new StampingProperties
211-
());
212-
// Creating the appearance
213-
signer.GetSignatureAppearance().SetLayer2FontColor(ColorConstants.RED).SetLayer2Text("Verified and signed by me."
214-
).SetPageRect(rect);
215-
signer.SetFieldName("Signature1");
216-
// Creating the signature
217-
IExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256);
218-
signer.SignDetached(pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CADES);
219-
NUnit.Framework.Assert.IsNull(new CompareTool().CompareVisually(dest, sourceFolder + "cmp_" + fileName, destinationFolder
220-
, "diff_"));
248+
private static IDictionary<int, IList<Rectangle>> GetIgnoredAreaTestMap(Rectangle ignoredArea) {
249+
IDictionary<int, IList<Rectangle>> result = new Dictionary<int, IList<Rectangle>>();
250+
result.Put(1, JavaUtil.ArraysAsList(ignoredArea));
251+
return result;
221252
}
222253
}
223254
}

itext/itext.kernel/itext/kernel/pdf/PdfXrefTable.cs

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -216,41 +216,14 @@ protected internal virtual void WriteXrefTableAndTrailer(PdfDocument document, P
216216
}
217217
}
218218
}
219-
IList<int> sections = new List<int>();
220-
int first = 0;
221-
int len = 0;
222-
for (int i = 0; i < Size(); i++) {
223-
PdfIndirectReference reference = xref[i];
224-
if (document.properties.appendMode && reference != null && !reference.CheckState(PdfObject.MODIFIED)) {
225-
reference = null;
226-
}
227-
if (reference == null) {
228-
if (len > 0) {
229-
sections.Add(first);
230-
sections.Add(len);
231-
}
232-
len = 0;
233-
}
234-
else {
235-
if (len > 0) {
236-
len++;
237-
}
238-
else {
239-
first = i;
240-
len = 1;
241-
}
242-
}
243-
}
244-
if (len > 0) {
245-
sections.Add(first);
246-
sections.Add(len);
247-
}
219+
IList<int> sections = CreateSections(document, false);
248220
if (document.properties.appendMode && sections.Count == 0) {
249221
// no modifications.
250222
xref = null;
251223
return;
252224
}
253225
long startxref = writer.GetCurrentPos();
226+
long xRefStmPos = -1;
254227
if (writer.IsFullCompression()) {
255228
PdfStream xrefStream = (PdfStream)new PdfStream().MakeIndirect(document);
256229
xrefStream.MakeIndirect(document);
@@ -269,15 +242,16 @@ protected internal virtual void WriteXrefTableAndTrailer(PdfDocument document, P
269242
foreach (int? section in sections) {
270243
index.Add(new PdfNumber((int)section));
271244
}
272-
if (document.properties.appendMode) {
245+
if (document.properties.appendMode && !document.reader.hybridXref) {
246+
// "not meaningful in hybrid-reference files"
273247
PdfNumber lastXref = new PdfNumber(document.reader.GetLastXref());
274248
xrefStream.Put(PdfName.Prev, lastXref);
275249
}
276250
xrefStream.Put(PdfName.Index, index);
277251
iText.Kernel.Pdf.PdfXrefTable xrefTable = document.GetXref();
278252
for (int k = 0; k < sections.Count; k += 2) {
279-
first = (int)sections[k];
280-
len = (int)sections[k + 1];
253+
int first = (int)sections[k];
254+
int len = (int)sections[k + 1];
281255
for (int i = first; i < first + len; i++) {
282256
PdfIndirectReference reference = xrefTable.Get(i);
283257
if (reference.IsFree()) {
@@ -300,13 +274,23 @@ protected internal virtual void WriteXrefTableAndTrailer(PdfDocument document, P
300274
}
301275
}
302276
xrefStream.Flush();
277+
xRefStmPos = startxref;
303278
}
304-
else {
279+
// For documents with hybrid cross-reference table, i.e. containing xref streams as well as regular xref sections,
280+
// we write additional regular xref section at the end of the document because the /Prev reference from
281+
// xref stream to a regular xref section doesn't seem to be valid
282+
bool needsRegularXref = !writer.IsFullCompression() || document.properties.appendMode && document.reader.hybridXref;
283+
if (needsRegularXref) {
284+
startxref = writer.GetCurrentPos();
305285
writer.WriteString("xref\n");
306286
iText.Kernel.Pdf.PdfXrefTable xrefTable = document.GetXref();
287+
if (xRefStmPos != -1) {
288+
// Get rid of all objects from object stream. This is done for hybrid documents
289+
sections = CreateSections(document, true);
290+
}
307291
for (int k = 0; k < sections.Count; k += 2) {
308-
first = (int)sections[k];
309-
len = (int)sections[k + 1];
292+
int first = (int)sections[k];
293+
int len = (int)sections[k + 1];
310294
writer.WriteInteger(first).WriteSpace().WriteInteger(len).WriteByte((byte)'\n');
311295
for (int i = first; i < first + len; i++) {
312296
PdfIndirectReference reference = xrefTable.Get(i);
@@ -330,6 +314,9 @@ protected internal virtual void WriteXrefTableAndTrailer(PdfDocument document, P
330314
trailer.Remove(PdfName.Length);
331315
trailer.Put(PdfName.Size, new PdfNumber(this.Size()));
332316
trailer.Put(PdfName.ID, fileId);
317+
if (xRefStmPos != -1) {
318+
trailer.Put(PdfName.XRefStm, new PdfNumber(xRefStmPos));
319+
}
333320
if (crypto != null) {
334321
trailer.Put(PdfName.Encrypt, crypto);
335322
}
@@ -357,6 +344,40 @@ internal virtual void Clear() {
357344
count = 1;
358345
}
359346

347+
private IList<int> CreateSections(PdfDocument document, bool dropObjectsFromObjectStream) {
348+
IList<int> sections = new List<int>();
349+
int first = 0;
350+
int len = 0;
351+
for (int i = 0; i < Size(); i++) {
352+
PdfIndirectReference reference = xref[i];
353+
if (document.properties.appendMode && reference != null && (!reference.CheckState(PdfObject.MODIFIED) || dropObjectsFromObjectStream
354+
&& reference.GetObjStreamNumber() != 0)) {
355+
reference = null;
356+
}
357+
if (reference == null) {
358+
if (len > 0) {
359+
sections.Add(first);
360+
sections.Add(len);
361+
}
362+
len = 0;
363+
}
364+
else {
365+
if (len > 0) {
366+
len++;
367+
}
368+
else {
369+
first = i;
370+
len = 1;
371+
}
372+
}
373+
}
374+
if (len > 0) {
375+
sections.Add(first);
376+
sections.Add(len);
377+
}
378+
return sections;
379+
}
380+
360381
/// <summary>Gets size of the offset.</summary>
361382
/// <remarks>Gets size of the offset. Max size is 2^40, i.e. 1 Tb.</remarks>
362383
private int GetOffsetSize(long startxref) {

port-hash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1a4910165bfaddba81a44cf05371fadaa19ff8b8
1+
68411e67bd5a0cbf9cdff2a8cc2e59aaa67f8bb5

0 commit comments

Comments
 (0)