Skip to content

Commit eb84ff9

Browse files
yulian-gaponenkoitext-teamcity
authored andcommitted
Fix issues with free references writing to xref table
- avoid rereading deleted objects; - update generation number only on objects deletion; - always write all free references to xref table (even in append mode). DEVSIX-1428 Autoported commit. Original commit hash: [57c22ebed]
1 parent c93468a commit eb84ff9

File tree

6 files changed

+87
-72
lines changed

6 files changed

+87
-72
lines changed

itext.tests/itext.kernel.tests/itext/kernel/pdf/FreeReferencesTest.cs

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,11 @@ public virtual void FreeReferencesTest01() {
2525
+ @out));
2626
pdfDocument.Close();
2727
String[] xrefString = ExtractXrefTableAsStrings(@out);
28-
String[] expected = new String[] { "xref\n" + "0 5\n" + "0000000010 65535 f \n" + "0000000269 00000 n \n"
29-
+ "0000000561 00000 n \n" + "0000000314 00000 n \n" + "0000000011 65535 f \n" + "10 5\n" + "0000000000 00001 f \n"
30-
+ "0000000000 00002 f \n" + "0000000133 00000 n \n" + "0000000015 00000 n \n" + "0000000613 00000 n \n"
31-
};
32-
// TODO first xref shall have no subsections
33-
// TODO linked list of refs is invalid
28+
String[] expected = new String[] { "xref\n" + "0 15\n" + "0000000004 65535 f \n" + "0000000269 00000 n \n"
29+
+ "0000000561 00000 n \n" + "0000000314 00000 n \n" + "0000000005 65535 f \n" + "0000000006 00000 f \n"
30+
+ "0000000007 00000 f \n" + "0000000008 00000 f \n" + "0000000009 00000 f \n" + "0000000010 00000 f \n"
31+
+ "0000000011 00000 f \n" + "0000000000 00001 f \n" + "0000000133 00000 n \n" + "0000000015 00000 n \n"
32+
+ "0000000613 00000 n \n" };
3433
NUnit.Framework.Assert.AreEqual(expected, xrefString);
3534
}
3635

@@ -42,12 +41,13 @@ public virtual void FreeReferencesTest02() {
4241
PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + src), new PdfWriter(destinationFolder
4342
+ @out), new StampingProperties().UseAppendMode());
4443
pdfDocument.Close();
45-
// TODO exception is thrown on attempt to read free reference
4644
String[] xrefString = ExtractXrefTableAsStrings(@out);
4745
String[] expected = new String[] { "xref\n" + "0 5\n" + "0000000010 65535 f \n" + "0000000269 00000 n \n"
4846
+ "0000000569 00000 n \n" + "0000000314 00000 n \n" + "0000000000 65535 f \n" + "10 5\n" + "0000000011 00000 f \n"
4947
+ "0000000000 00001 f \n" + "0000000133 00000 n \n" + "0000000015 00000 n \n" + "0000000480 00000 n \n"
50-
, "xref\n" + "3 1\n" + "0000000995 00000 n \n" };
48+
, "xref\n" + "0 1\n" + "0000000004 65535 f \n" + "3 9\n" + "0000000995 00000 n \n" + "0000000005 65535 f \n"
49+
+ "0000000006 00000 f \n" + "0000000007 00000 f \n" + "0000000008 00000 f \n" + "0000000009 00000 f \n"
50+
+ "0000000010 00000 f \n" + "0000000011 00000 f \n" + "0000000000 00001 f \n" };
5151
// Append mode, no possibility to fix subsections in first xref
5252
NUnit.Framework.Assert.AreEqual(expected, xrefString);
5353
}
@@ -65,9 +65,9 @@ public virtual void FreeReferencesTest03() {
6565
String[] expected = new String[] { "xref\n" + "0 7\n" + "0000000000 65535 f \n" + "0000000265 00000 n \n"
6666
+ "0000000564 00000 n \n" + "0000000310 00000 n \n" + "0000000132 00000 n \n" + "0000000015 00001 n \n"
6767
+ "0000000476 00000 n \n", "xref\n" + "0 1\n" + "0000000005 65535 n \n" + "3 3\n" + "0000000923 00000 n \n"
68-
+ "0000001170 00000 n \n" + "0000000000 00002 f \n" + "7 1\n" + "0000001303 00000 n \n", "xref\n" + "1 3\n"
69-
+ "0000001706 00000 n \n" + "0000001998 00000 n \n" + "0000001751 00000 n \n" + "8 2\n" + "0000002055 00000 n \n"
70-
+ "0000002156 00000 n \n" };
68+
+ "0000001170 00000 n \n" + "0000000000 00002 f \n" + "7 1\n" + "0000001303 00000 n \n", "xref\n" + "0 4\n"
69+
+ "0000000005 65535 f \n" + "0000001706 00000 n \n" + "0000001998 00000 n \n" + "0000001751 00000 n \n"
70+
+ "5 1\n" + "0000000000 00002 f \n" + "8 2\n" + "0000002055 00000 n \n" + "0000002156 00000 n \n" };
7171
NUnit.Framework.Assert.AreEqual(expected, xrefString);
7272
}
7373

@@ -84,8 +84,12 @@ public virtual void FreeReferencesTest04() {
8484
contentsRef.SetFree();
8585
PdfObject freedContentsRefRefersTo = contentsRef.GetRefersTo();
8686
NUnit.Framework.Assert.IsNull(freedContentsRefRefersTo);
87-
// TODO assertion fails. Free reference should not be reread, see freeReferencesTest02
8887
pdfDocument.Close();
88+
String[] xrefString = ExtractXrefTableAsStrings(@out);
89+
String[] expected = new String[] { "xref\n" + "0 7\n" + "0000000005 65535 f \n" + "0000000133 00000 n \n"
90+
+ "0000000425 00000 n \n" + "0000000178 00000 n \n" + "0000000015 00000 n \n" + "0000000000 00001 f \n"
91+
+ "0000000476 00000 n \n" };
92+
NUnit.Framework.Assert.AreEqual(expected, xrefString);
8993
}
9094

9195
/// <exception cref="System.IO.IOException"/>
@@ -97,10 +101,11 @@ public virtual void FreeReferencesTest05() {
97101
+ @out));
98102
pdfDocument.Close();
99103
String[] xrefString = ExtractXrefTableAsStrings(@out);
100-
String[] expected = new String[] { "xref\n" + "0 4\n" + "0000000000 65535 f \n" + "0000000269 00000 n \n"
101-
+ "0000000561 00000 n \n" + "0000000314 00000 n \n" + "11 3\n" + "0000000133 00000 n \n" + "0000000015 00000 n \n"
102-
+ "0000000613 00000 n \n" };
103-
// TODO first xref shall have no subsections
104+
String[] expected = new String[] { "xref\n" + "0 14\n" + "0000000004 65535 f \n" + "0000000269 00000 n \n"
105+
+ "0000000561 00000 n \n" + "0000000314 00000 n \n" + "0000000005 00000 f \n" + "0000000006 00000 f \n"
106+
+ "0000000007 00000 f \n" + "0000000008 00000 f \n" + "0000000009 00000 f \n" + "0000000010 00000 f \n"
107+
+ "0000000000 00000 f \n" + "0000000133 00000 n \n" + "0000000015 00000 n \n" + "0000000613 00000 n \n"
108+
};
104109
NUnit.Framework.Assert.AreEqual(expected, xrefString);
105110
}
106111

@@ -115,7 +120,9 @@ public virtual void FreeReferencesTest06() {
115120
String[] xrefString = ExtractXrefTableAsStrings(@out);
116121
String[] expected = new String[] { "xref\n" + "0 4\n" + "0000000000 65535 f \n" + "0000000269 00000 n \n"
117122
+ "0000000569 00000 n \n" + "0000000314 00000 n \n" + "11 3\n" + "0000000133 00000 n \n" + "0000000015 00000 n \n"
118-
+ "0000000480 00000 n \n", "xref\n" + "3 1\n" + "0000000935 00000 n \n" };
123+
+ "0000000480 00000 n \n", "xref\n" + "0 1\n" + "0000000004 65535 f \n" + "3 8\n" + "0000000935 00000 n \n"
124+
+ "0000000005 00000 f \n" + "0000000006 00000 f \n" + "0000000007 00000 f \n" + "0000000008 00000 f \n"
125+
+ "0000000009 00000 f \n" + "0000000010 00000 f \n" + "0000000000 00000 f \n" };
119126
// Append mode, no possibility to fix subsections in first xref
120127
NUnit.Framework.Assert.AreEqual(expected, xrefString);
121128
}
@@ -135,6 +142,29 @@ public virtual void FreeReferencesTest07() {
135142
NUnit.Framework.Assert.AreEqual(expected, xrefString);
136143
}
137144

145+
/// <exception cref="System.IO.IOException"/>
146+
[NUnit.Framework.Test]
147+
public virtual void FreeReferencesTest08() {
148+
String src = "simpleDoc.pdf";
149+
String @out = "freeReferencesTest08.pdf";
150+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + src), new PdfWriter(destinationFolder
151+
+ @out), new StampingProperties().UseAppendMode());
152+
PdfObject contentsObj = pdfDocument.GetPage(1).GetPdfObject().Remove(PdfName.Contents);
153+
pdfDocument.GetPage(1).SetModified();
154+
NUnit.Framework.Assert.IsTrue(contentsObj is PdfIndirectReference);
155+
PdfIndirectReference contentsRef = (PdfIndirectReference)contentsObj;
156+
contentsRef.SetFree();
157+
PdfObject freedContentsRefRefersTo = contentsRef.GetRefersTo();
158+
NUnit.Framework.Assert.IsNull(freedContentsRefRefersTo);
159+
pdfDocument.Close();
160+
String[] xrefString = ExtractXrefTableAsStrings(@out);
161+
String[] expected = new String[] { "xref\n" + "0 7\n" + "0000000000 65535 f \n" + "0000000265 00000 n \n"
162+
+ "0000000564 00000 n \n" + "0000000310 00000 n \n" + "0000000132 00000 n \n" + "0000000015 00000 n \n"
163+
+ "0000000476 00000 n \n", "xref\n" + "0 1\n" + "0000000005 65535 f \n" + "3 3\n" + "0000000923 00000 n \n"
164+
+ "0000001170 00000 n \n" + "0000000000 00001 f \n" };
165+
NUnit.Framework.Assert.AreEqual(expected, xrefString);
166+
}
167+
138168
/// <exception cref="System.IO.IOException"/>
139169
private String[] ExtractXrefTableAsStrings(String @out) {
140170
byte[] outPdfBytes = ReadFile(destinationFolder + @out);

itext.tests/itext.kernel.tests/itext/kernel/pdf/PdfXrefTableTest.cs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class PdfXrefTableTest : ExtendedITextTest {
1717

1818
[NUnit.Framework.OneTimeSetUp]
1919
public static void BeforeClass() {
20-
ITextTest.CreateOrClearDestinationFolder(destinationFolder);
20+
CreateOrClearDestinationFolder(destinationFolder);
2121
}
2222

2323
/// <exception cref="System.IO.IOException"/>
@@ -31,11 +31,10 @@ public virtual void TestCreateAndUpdateXMP() {
3131
// create XMP metadata
3232
pdfDocument.Close();
3333
pdfDocument = new PdfDocument(new PdfReader(created), new PdfWriter(updated));
34-
pdfDocument.Close();
35-
pdfDocument = new PdfDocument(new PdfReader(updated));
3634
PdfXrefTable xref = pdfDocument.GetXref();
37-
PdfIndirectReference freeRef = xref.Get(xref.Size() - 2);
38-
// 6
35+
PdfIndirectReference ref0 = xref.Get(0);
36+
PdfIndirectReference freeRef = xref.Get(6);
37+
pdfDocument.Close();
3938
/*
4039
Current xref structure:
4140
xref
@@ -50,9 +49,8 @@ 0000000015 00000 n
5049
0000000561 00000 n
5150
*/
5251
NUnit.Framework.Assert.IsTrue(freeRef.IsFree());
53-
NUnit.Framework.Assert.AreEqual(xref.Get(0).offsetOrIndex, freeRef.objNr);
52+
NUnit.Framework.Assert.AreEqual(ref0.offsetOrIndex, freeRef.objNr);
5453
NUnit.Framework.Assert.AreEqual(1, freeRef.genNr);
55-
pdfDocument.Close();
5654
}
5755

5856
/// <exception cref="System.IO.IOException"/>
@@ -69,13 +67,11 @@ public virtual void TestCreateAndUpdateTwiceXMP() {
6967
pdfDocument = new PdfDocument(new PdfReader(created), new PdfWriter(updated));
7068
pdfDocument.Close();
7169
pdfDocument = new PdfDocument(new PdfReader(updated), new PdfWriter(updatedAgain));
72-
pdfDocument.Close();
73-
pdfDocument = new PdfDocument(new PdfReader(updatedAgain));
7470
PdfXrefTable xref = pdfDocument.GetXref();
75-
PdfIndirectReference freeRef1 = xref.Get(xref.Size() - 3);
76-
// 6
77-
PdfIndirectReference freeRef2 = xref.Get(xref.Size() - 2);
78-
// 7
71+
PdfIndirectReference ref0 = xref.Get(0);
72+
PdfIndirectReference freeRef1 = xref.Get(6);
73+
PdfIndirectReference freeRef2 = xref.Get(7);
74+
pdfDocument.Close();
7975
/*
8076
Current xref structure:
8177
xref
@@ -86,13 +82,13 @@ 0000000510 00000 n
8682
0000000263 00000 n
8783
0000000088 00000 n
8884
0000000015 00000 n
89-
0000000007 00002 f % this is object 6; 7 refers to free object 7; note generation number
85+
0000000007 00001 f % this is object 6; 7 refers to free object 7; note generation number
9086
0000000000 00001 f % this is object 7; 0 refers to free object 0; note generation number
9187
0000000561 00000 n
9288
*/
9389
NUnit.Framework.Assert.IsTrue(freeRef1.IsFree());
94-
NUnit.Framework.Assert.AreEqual(xref.Get(0).offsetOrIndex, freeRef1.objNr);
95-
NUnit.Framework.Assert.AreEqual(2, freeRef1.genNr);
90+
NUnit.Framework.Assert.AreEqual(ref0.offsetOrIndex, freeRef1.objNr);
91+
NUnit.Framework.Assert.AreEqual(1, freeRef1.genNr);
9692
NUnit.Framework.Assert.IsTrue(freeRef2.IsFree());
9793
NUnit.Framework.Assert.AreEqual(freeRef1.offsetOrIndex, freeRef2.objNr);
9894
NUnit.Framework.Assert.AreEqual(1, freeRef2.genNr);

itext/itext.kernel/itext/kernel/pdf/PdfIndirectReference.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,8 @@ protected internal virtual PdfReader GetReader() {
254254
return null;
255255
}
256256

257-
// NOTE In append mode object could be OriginalObjectStream, but not Modified,
258-
// so information about this reference would not be added to the new Cross-Reference table.
259-
// In stamp mode without append the reference will be free.
260257
protected internal virtual bool IsFree() {
261-
return CheckState(FREE) || CheckState(ORIGINAL_OBJECT_STREAM);
258+
return CheckState(FREE);
262259
}
263260

264261
protected internal override PdfObject NewInstance() {

itext/itext.kernel/itext/kernel/pdf/PdfReader.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -917,12 +917,7 @@ protected internal virtual PdfDictionary ReadXrefSection() {
917917
reference.ClearState(PdfObject.READING);
918918
}
919919
else {
920-
if (reference.objNr == 0 && pos != 0L) {
921-
reference.SetIndex(pos);
922-
}
923-
else {
924-
continue;
925-
}
920+
continue;
926921
}
927922
}
928923
if (tokens.TokenValueEqualsTo(PdfTokenizer.N)) {
@@ -936,7 +931,7 @@ protected internal virtual PdfDictionary ReadXrefSection() {
936931
else {
937932
if (tokens.TokenValueEqualsTo(PdfTokenizer.F)) {
938933
if (xref.Get(num) == null) {
939-
reference.SetFree();
934+
xref.FreeReference(reference, true);
940935
xref.Add(reference);
941936
}
942937
}

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

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public PdfXrefTable()
6868
}
6969

7070
public PdfXrefTable(int capacity) {
71+
// TODO not using this collection for now
7172
if (capacity < 1) {
7273
capacity = INITIAL_CAPACITY;
7374
}
@@ -116,16 +117,20 @@ internal virtual PdfIndirectReference CreateNewIndirectReference(PdfDocument doc
116117
}
117118

118119
protected internal virtual void FreeReference(PdfIndirectReference reference) {
120+
FreeReference(reference, false);
121+
}
122+
123+
internal virtual void FreeReference(PdfIndirectReference reference, bool readingFreeReference) {
124+
reference.SetOffset(0);
119125
reference.SetState(PdfObject.FREE);
120126
if (!reference.CheckState(PdfObject.FLUSHED)) {
121127
if (reference.refersTo != null) {
122128
reference.refersTo.SetIndirectReference(null).SetState(PdfObject.MUST_BE_INDIRECT);
123129
reference.refersTo = null;
124130
}
125-
if (reference.GetGenNumber() < MAX_GENERATION) {
126-
freeReferences.Add(reference.GetObjNumber());
127-
EnsureCount(Math.Max(this.count, reference.GetObjNumber()));
128-
}
131+
}
132+
if (!readingFreeReference && reference.GetGenNumber() < MAX_GENERATION) {
133+
reference.genNr++;
129134
}
130135
}
131136

@@ -140,10 +145,6 @@ protected internal virtual void SetCapacity(int capacity) {
140145
protected internal virtual void WriteXrefTableAndTrailer(PdfDocument document, PdfObject fileId, PdfObject
141146
crypto) {
142147
PdfWriter writer = document.GetWriter();
143-
// Increment generation number for all freed references.
144-
foreach (int? objNr in freeReferences) {
145-
xref[(int)objNr].genNr++;
146-
}
147148
for (int i = count; i > 0; --i) {
148149
PdfIndirectReference lastRef = xref[i];
149150
if (lastRef == null || (lastRef.IsFree() && lastRef.GetGenNumber() == 0) || (!lastRef.CheckState(PdfObject
@@ -154,17 +155,25 @@ protected internal virtual void WriteXrefTableAndTrailer(PdfDocument document, P
154155
break;
155156
}
156157
}
158+
int lastFreeObjNr = 0;
159+
for (int i = count; i >= 0; --i) {
160+
PdfIndirectReference @ref = xref[i];
161+
if (@ref == null) {
162+
@ref = ((PdfIndirectReference)new PdfIndirectReference(document, i, 0).SetState(PdfObject.FREE));
163+
xref[i] = @ref;
164+
}
165+
if (@ref.IsFree()) {
166+
@ref.SetOffset(lastFreeObjNr);
167+
lastFreeObjNr = i;
168+
}
169+
}
157170
IList<int> sections = new List<int>();
158171
int first = 0;
159172
int len = 1;
160-
if (document.IsAppendMode()) {
161-
first = 1;
162-
len = 0;
163-
}
164173
for (int i = 1; i < Size(); i++) {
165174
PdfIndirectReference reference = xref[i];
166175
if (reference != null) {
167-
if (document.properties.appendMode && !reference.CheckState(PdfObject.MODIFIED)) {
176+
if (document.properties.appendMode && !reference.CheckState(PdfObject.MODIFIED) && !reference.IsFree()) {
168177
reference = null;
169178
}
170179
}
@@ -189,7 +198,7 @@ protected internal virtual void WriteXrefTableAndTrailer(PdfDocument document, P
189198
sections.Add(first);
190199
sections.Add(len);
191200
}
192-
if (document.properties.appendMode && sections.Count == 0) {
201+
if (document.properties.appendMode && sections.Count == 2 && sections[0] == 0 && sections[1] == 1) {
193202
// no modifications.
194203
xref = null;
195204
return;
@@ -261,18 +270,7 @@ protected internal virtual void WriteXrefTableAndTrailer(PdfDocument document, P
261270
writer.WriteInteger(first).WriteSpace().WriteInteger(len).WriteByte((byte)'\n');
262271
for (int i = first; i < first + len; i++) {
263272
PdfIndirectReference reference = xrefTable.Get(i);
264-
StringBuilder off = new StringBuilder("0000000000");
265-
if (reference.IsFree()) {
266-
if (!freeReferences.IsEmpty()) {
267-
off.Append(freeReferences.PollFirst());
268-
}
269-
}
270-
else {
271-
/* if (freeReferences.isEmpty()), then we are at the
272-
last free reference. Its referral value must be object 0.
273-
*/
274-
off.Append(reference.GetOffset());
275-
}
273+
StringBuilder off = new StringBuilder("0000000000").Append(reference.GetOffset());
276274
StringBuilder gen = new StringBuilder("00000").Append(reference.GetGenNumber());
277275
writer.WriteString(off.JSubstring(off.Length - 10, off.Length)).WriteSpace().WriteString(gen.JSubstring(gen
278276
.Length - 5, gen.Length)).WriteSpace();
@@ -303,7 +301,6 @@ last free reference. Its referral value must be object 0.
303301
writer.Write(document.GetTrailer());
304302
writer.Write('\n');
305303
}
306-
freeReferences.Clear();
307304
WriteKeyInfo(writer);
308305
writer.WriteString("startxref\n").WriteLong(startxref).WriteString("\n%%EOF\n");
309306
xref = null;

port-hash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
f66f0b42f981f9e55c085bd283460205fe9d81de
1+
57c22ebed0df0d237deb1e8f426e36bd877b78ee

0 commit comments

Comments
 (0)