Skip to content

Commit fa74bf4

Browse files
yulian-gaponenkoiText-CI
authored andcommitted
Fix smart-mode serialization content cluttering by internal buffer out-of-range bytes
DEVSIX-2883 Autoported commit. Original commit hash: [fea9a1a1b]
1 parent da95c1e commit fa74bf4

File tree

9 files changed

+183
-6
lines changed

9 files changed

+183
-6
lines changed

itext.tests/itext.kernel.tests/itext/kernel/pdf/PdfCopyTest.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ source product.
4141
4242
*/
4343
using System;
44+
using System.IO;
4445
using iText.IO.Source;
4546
using iText.IO.Util;
4647
using iText.Kernel.Utils;
@@ -227,5 +228,33 @@ public virtual void CopyIndirectInheritablePageEntriesTest01() {
227228
outputDoc.Close();
228229
NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(dest, cmp, destinationFolder, "diff_"));
229230
}
231+
232+
/// <exception cref="System.IO.IOException"/>
233+
[NUnit.Framework.Test]
234+
public virtual void CopySelfContainedObject() {
235+
ByteArrayOutputStream inputBytes = new ByteArrayOutputStream();
236+
PdfDocument prepInputDoc = new PdfDocument(new PdfWriter(inputBytes));
237+
PdfDictionary selfContainedDict = new PdfDictionary();
238+
PdfName randDictName = PdfName.Sound;
239+
PdfName randEntry1 = PdfName.R;
240+
PdfName randEntry2 = PdfName.S;
241+
selfContainedDict.Put(randEntry1, selfContainedDict);
242+
selfContainedDict.Put(randEntry2, selfContainedDict);
243+
prepInputDoc.AddNewPage().Put(randDictName, selfContainedDict.MakeIndirect(prepInputDoc));
244+
prepInputDoc.Close();
245+
PdfDocument srcDoc = new PdfDocument(new PdfReader(new MemoryStream(inputBytes.ToArray())));
246+
PdfDocument destDoc = new PdfDocument(new PdfWriter(destinationFolder + "copySelfContainedObject.pdf"));
247+
srcDoc.CopyPagesTo(1, 1, destDoc);
248+
PdfDictionary destPageObj = destDoc.GetFirstPage().GetPdfObject();
249+
PdfDictionary destSelfContainedDict = destPageObj.GetAsDictionary(randDictName);
250+
PdfDictionary destSelfContainedDictR = destSelfContainedDict.GetAsDictionary(randEntry1);
251+
PdfDictionary destSelfContainedDictS = destSelfContainedDict.GetAsDictionary(randEntry2);
252+
NUnit.Framework.Assert.AreEqual(destSelfContainedDict.GetIndirectReference(), destSelfContainedDictR.GetIndirectReference
253+
());
254+
NUnit.Framework.Assert.AreEqual(destSelfContainedDict.GetIndirectReference(), destSelfContainedDictS.GetIndirectReference
255+
());
256+
destDoc.Close();
257+
srcDoc.Close();
258+
}
230259
}
231260
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
using System;
2+
using iText.Kernel.Font;
3+
using iText.Kernel.Pdf.Canvas;
4+
using iText.Kernel.Utils;
5+
using iText.Test;
6+
7+
namespace iText.Kernel.Pdf {
8+
public class SmartModeTest : ExtendedITextTest {
9+
public static readonly String destinationFolder = NUnit.Framework.TestContext.CurrentContext.TestDirectory
10+
+ "/test/itext/kernel/pdf/SmartModeTest/";
11+
12+
public static readonly String sourceFolder = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
13+
.CurrentContext.TestDirectory) + "/resources/itext/kernel/pdf/SmartModeTest/";
14+
15+
[NUnit.Framework.OneTimeSetUp]
16+
public static void BeforeClass() {
17+
CreateOrClearDestinationFolder(destinationFolder);
18+
}
19+
20+
/// <exception cref="System.IO.IOException"/>
21+
/// <exception cref="System.Exception"/>
22+
[NUnit.Framework.Test]
23+
public virtual void SmartModeSameResourcesCopyingAndFlushing() {
24+
String outFile = destinationFolder + "smartModeSameResourcesCopyingAndFlushing.pdf";
25+
String cmpFile = sourceFolder + "cmp_smartModeSameResourcesCopyingAndFlushing.pdf";
26+
String[] srcFiles = new String[] { sourceFolder + "indirectResourcesStructure.pdf", sourceFolder + "indirectResourcesStructure2.pdf"
27+
};
28+
PdfDocument outputDoc = new PdfDocument(new PdfWriter(outFile, new WriterProperties().UseSmartMode()));
29+
foreach (String srcFile in srcFiles) {
30+
PdfDocument sourceDoc = new PdfDocument(new PdfReader(srcFile));
31+
sourceDoc.CopyPagesTo(1, sourceDoc.GetNumberOfPages(), outputDoc);
32+
sourceDoc.Close();
33+
outputDoc.FlushCopiedObjects(sourceDoc);
34+
}
35+
outputDoc.Close();
36+
PdfDocument assertDoc = new PdfDocument(new PdfReader(outFile));
37+
PdfIndirectReference page1ResFontObj = assertDoc.GetPage(1).GetPdfObject().GetAsDictionary(PdfName.Resources
38+
).GetAsDictionary(PdfName.Font).GetIndirectReference();
39+
PdfIndirectReference page2ResFontObj = assertDoc.GetPage(2).GetPdfObject().GetAsDictionary(PdfName.Resources
40+
).GetAsDictionary(PdfName.Font).GetIndirectReference();
41+
PdfIndirectReference page3ResFontObj = assertDoc.GetPage(3).GetPdfObject().GetAsDictionary(PdfName.Resources
42+
).GetAsDictionary(PdfName.Font).GetIndirectReference();
43+
NUnit.Framework.Assert.IsTrue(page1ResFontObj.Equals(page2ResFontObj));
44+
NUnit.Framework.Assert.IsTrue(page1ResFontObj.Equals(page3ResFontObj));
45+
assertDoc.Close();
46+
NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outFile, cmpFile, destinationFolder));
47+
}
48+
49+
/// <exception cref="System.IO.IOException"/>
50+
[NUnit.Framework.Test]
51+
public virtual void SmartModeSameResourcesCopyingModifyingAndFlushing() {
52+
String outFile = destinationFolder + "smartModeSameResourcesCopyingModifyingAndFlushing.pdf";
53+
String[] srcFiles = new String[] { sourceFolder + "indirectResourcesStructure.pdf", sourceFolder + "indirectResourcesStructure2.pdf"
54+
};
55+
bool exceptionCaught = false;
56+
PdfDocument outputDoc = new PdfDocument(new PdfWriter(outFile, new WriterProperties().UseSmartMode()));
57+
int lastPageNum = 1;
58+
PdfFont font = PdfFontFactory.CreateFont();
59+
foreach (String srcFile in srcFiles) {
60+
PdfDocument sourceDoc = new PdfDocument(new PdfReader(srcFile));
61+
sourceDoc.CopyPagesTo(1, sourceDoc.GetNumberOfPages(), outputDoc);
62+
sourceDoc.Close();
63+
int i;
64+
for (i = lastPageNum; i <= outputDoc.GetNumberOfPages(); ++i) {
65+
PdfCanvas canvas;
66+
try {
67+
canvas = new PdfCanvas(outputDoc.GetPage(i));
68+
}
69+
catch (NullReferenceException) {
70+
// Smart mode makes it possible to share objects coming from different source documents.
71+
// Flushing one object documents might make it impossible to modify further copied objects.
72+
NUnit.Framework.Assert.AreEqual(2, i);
73+
exceptionCaught = true;
74+
break;
75+
}
76+
canvas.BeginText().MoveText(36, 36).SetFontAndSize(font, 12).ShowText("Page " + i).EndText();
77+
}
78+
lastPageNum = i;
79+
if (exceptionCaught) {
80+
break;
81+
}
82+
outputDoc.FlushCopiedObjects(sourceDoc);
83+
}
84+
if (!exceptionCaught) {
85+
NUnit.Framework.Assert.Fail();
86+
}
87+
}
88+
89+
/// <exception cref="System.IO.IOException"/>
90+
/// <exception cref="System.Exception"/>
91+
[NUnit.Framework.Test]
92+
public virtual void SmartModeSameResourcesCopyingModifyingAndFlushing_ensureObjectFresh() {
93+
String outFile = destinationFolder + "smartModeSameResourcesCopyingModifyingAndFlushing_ensureObjectFresh.pdf";
94+
String cmpFile = sourceFolder + "cmp_smartModeSameResourcesCopyingModifyingAndFlushing_ensureObjectFresh.pdf";
95+
String[] srcFiles = new String[] { sourceFolder + "indirectResourcesStructure.pdf", sourceFolder + "indirectResourcesStructure2.pdf"
96+
};
97+
PdfDocument outputDoc = new PdfDocument(new PdfWriter(outFile, new WriterProperties().UseSmartMode()));
98+
int lastPageNum = 1;
99+
PdfFont font = PdfFontFactory.CreateFont();
100+
foreach (String srcFile in srcFiles) {
101+
PdfDocument sourceDoc = new PdfDocument(new PdfReader(srcFile));
102+
for (int i = 1; i <= sourceDoc.GetNumberOfPages(); ++i) {
103+
PdfDictionary srcRes = sourceDoc.GetPage(i).GetPdfObject().GetAsDictionary(PdfName.Resources);
104+
// Ensures that objects copied to the output document are fresh,
105+
// i.e. are not reused from already copied objects cache.
106+
bool ensureObjectIsFresh = true;
107+
// it's crucial to copy first inner objects and then the container object!
108+
foreach (PdfObject v in srcRes.Values()) {
109+
if (v.GetIndirectReference() != null) {
110+
// We are not interested in returned copied objects instances, they will be picked up by
111+
// general copying mechanism from copied objects cache by default.
112+
v.CopyTo(outputDoc, ensureObjectIsFresh);
113+
}
114+
}
115+
if (srcRes.GetIndirectReference() != null) {
116+
srcRes.CopyTo(outputDoc, ensureObjectIsFresh);
117+
}
118+
}
119+
sourceDoc.CopyPagesTo(1, sourceDoc.GetNumberOfPages(), outputDoc);
120+
sourceDoc.Close();
121+
int i_1;
122+
for (i_1 = lastPageNum; i_1 <= outputDoc.GetNumberOfPages(); ++i_1) {
123+
PdfPage page = outputDoc.GetPage(i_1);
124+
PdfCanvas canvas = new PdfCanvas(page);
125+
canvas.BeginText().MoveText(36, 36).SetFontAndSize(font, 12).ShowText("Page " + i_1).EndText();
126+
}
127+
lastPageNum = i_1;
128+
outputDoc.FlushCopiedObjects(sourceDoc);
129+
}
130+
outputDoc.Close();
131+
PdfDocument assertDoc = new PdfDocument(new PdfReader(outFile));
132+
PdfIndirectReference page1ResFontObj = assertDoc.GetPage(1).GetPdfObject().GetAsDictionary(PdfName.Resources
133+
).GetAsDictionary(PdfName.Font).GetIndirectReference();
134+
PdfIndirectReference page2ResFontObj = assertDoc.GetPage(2).GetPdfObject().GetAsDictionary(PdfName.Resources
135+
).GetAsDictionary(PdfName.Font).GetIndirectReference();
136+
PdfIndirectReference page3ResFontObj = assertDoc.GetPage(3).GetPdfObject().GetAsDictionary(PdfName.Resources
137+
).GetAsDictionary(PdfName.Font).GetIndirectReference();
138+
NUnit.Framework.Assert.IsFalse(page1ResFontObj.Equals(page2ResFontObj));
139+
NUnit.Framework.Assert.IsFalse(page1ResFontObj.Equals(page3ResFontObj));
140+
NUnit.Framework.Assert.IsFalse(page2ResFontObj.Equals(page3ResFontObj));
141+
assertDoc.Close();
142+
NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outFile, cmpFile, destinationFolder));
143+
}
144+
}
145+
}

itext/itext.kernel/itext/kernel/pdf/PdfDocument.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1198,7 +1198,10 @@ public virtual IList<PdfPage> CopyPagesTo(IList<int> pagesToCopy, iText.Kernel.P
11981198
/// <summary>Flush all copied objects and remove them from copied cache.</summary>
11991199
/// <remarks>
12001200
/// Flush all copied objects and remove them from copied cache.
1201-
/// Note, if you will copy objects from the same document, doublicated objects will be created.
1201+
/// <p>
1202+
/// Note, if you will copy objects from the same document, duplicated objects will be created.
1203+
/// That's why usually this method is meant to be used when all copying from source document is finished.
1204+
/// For other cases one can also consider other flushing mechanisms, e.g. pages-based flushing.
12021205
/// </remarks>
12031206
/// <param name="sourceDoc">source document</param>
12041207
public virtual void FlushCopiedObjects(iText.Kernel.Pdf.PdfDocument sourceDoc) {

itext/itext.kernel/itext/kernel/pdf/SmartModePdfObjectsSerializer.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,6 @@ public virtual SerializedObjectContent SerializeObject(PdfObject obj) {
9898
return new SerializedObjectContent(content);
9999
}
100100

101-
private class SelfReferenceException : Exception {
102-
}
103-
104101
/// <exception cref="iText.Kernel.Pdf.SmartModePdfObjectsSerializer.SelfReferenceException"/>
105102
private void SerObject(PdfObject obj, ByteBuffer bb, int level, IDictionary<PdfIndirectReference, byte[]>
106103
serializedCache) {
@@ -165,7 +162,7 @@ private void SerObject(PdfObject obj, ByteBuffer bb, int level, IDictionary<PdfI
165162
// PdfNull case is also here
166163
if (savedBb != null) {
167164
serializedCache.Put(reference, bb.ToByteArray());
168-
savedBb.Append(bb.GetInternalBuffer());
165+
savedBb.Append(bb.GetInternalBuffer(), 0, bb.Size());
169166
}
170167
}
171168

@@ -205,5 +202,8 @@ private bool IsKeyRefersBack(PdfDictionary dic, PdfName key) {
205202
return key.Equals(PdfName.P) && (dic.Get(key).IsIndirectReference() || dic.Get(key).IsDictionary()) || key
206203
.Equals(PdfName.Parent);
207204
}
205+
206+
private class SelfReferenceException : Exception {
207+
}
208208
}
209209
}

port-hash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
b50863a0d49b6660e15aa942d766bf701d21913c
1+
fea9a1a1b05e2db13a36b1b74aa42b0c75ae198a

0 commit comments

Comments
 (0)