Skip to content

Commit 92f7133

Browse files
Merge branch 'hotfix/xref_sections' into hotfix/xref_free_refs
2 parents a9e8695 + ff82dd0 commit 92f7133

File tree

6 files changed

+143
-38
lines changed

6 files changed

+143
-38
lines changed

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfDocument.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -800,12 +800,14 @@ public void close() {
800800
flushFonts();
801801
writer.flushWaitingObjects();
802802
// flush unused objects
803-
if (isFlushUnusedObjects()) {
804-
for (int i = 0; i < xref.size(); i++) {
805-
PdfIndirectReference indirectReference = xref.get(i);
806-
if (!indirectReference.isFree() && !indirectReference.checkState(PdfObject.FLUSHED)) {
803+
for (int i = 0; i < xref.size(); i++) {
804+
PdfIndirectReference indirectReference = xref.get(i);
805+
if (indirectReference != null && !indirectReference.isFree() && !indirectReference.checkState(PdfObject.FLUSHED)) {
806+
if (isFlushUnusedObjects()) {
807807
PdfObject object = indirectReference.getRefersTo();
808808
object.flush();
809+
} else {
810+
indirectReference.setFree();
809811
}
810812
}
811813
}

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfReader.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,8 @@ protected PdfDictionary readXrefSection() throws IOException {
830830
} else if (reference.checkState(PdfObject.READING) && reference.getGenNumber() == gen) {
831831
reference.setOffset(pos);
832832
reference.clearState(PdfObject.READING);
833+
} else if (reference.objNr == 0 && pos != 0L) {
834+
reference.setIndex(pos);
833835
} else {
834836
continue;
835837
}

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfXrefTable.java

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,8 @@ public PdfIndirectReference get(int index) {
116116
* @return created indirect reference.
117117
*/
118118
protected PdfIndirectReference createNextIndirectReference(PdfDocument document) {
119-
PdfIndirectReference reference;
120-
if (freeReferences.size() > 0) {
121-
int num = (int) freeReferences.pollFirst();
122-
reference = xref[num];
123-
if (reference == null) {
124-
reference = new PdfIndirectReference(document, num);
125-
xref[num] = reference;
126-
}
127-
reference.setOffset(0);
128-
reference.clearState(PdfObject.FREE);
129-
} else {
130-
reference = new PdfIndirectReference(document, ++count);
131-
add(reference);
132-
}
119+
PdfIndirectReference reference = new PdfIndirectReference(document, ++count);
120+
add(reference);
133121
return reference.setState(PdfObject.MODIFIED);
134122
}
135123

@@ -141,7 +129,6 @@ PdfIndirectReference createNewIndirectReference(PdfDocument document) {
141129
}
142130

143131
protected void freeReference(PdfIndirectReference reference) {
144-
reference.setOffset(0);
145132
reference.setState(PdfObject.FREE);
146133
if (!reference.checkState(PdfObject.FLUSHED)) {
147134
if (reference.refersTo != null) {
@@ -151,9 +138,7 @@ protected void freeReference(PdfIndirectReference reference) {
151138
if (reference.getGenNumber() < MAX_GENERATION) {
152139
freeReferences.add(reference.getObjNumber());
153140
ensureCount(Math.max(this.count, reference.getObjNumber()));
154-
xref[reference.getObjNumber()] = null;
155141
}
156-
157142
}
158143
}
159144

@@ -170,18 +155,10 @@ protected void setCapacity(int capacity) {
170155
*/
171156
protected void writeXrefTableAndTrailer(PdfDocument document, PdfObject fileId, PdfObject crypto) throws IOException {
172157
PdfWriter writer = document.getWriter();
173-
if (document.isAppendMode()) {
174-
// Increment generation number for all freed references.
175-
for (Integer objNr : freeReferences) {
176-
xref[(int) objNr].genNr++;
177-
}
178-
} else {
179-
for (Integer objNr : freeReferences) {
180-
xref[(int) objNr] = null;
181-
}
158+
// Increment generation number for all freed references.
159+
for (Integer objNr : freeReferences) {
160+
xref[(int) objNr].genNr++;
182161
}
183-
freeReferences.clear();
184-
185162

186163
for (int i = count; i > 0; --i) {
187164
PdfIndirectReference lastRef = xref[i];
@@ -205,9 +182,7 @@ protected void writeXrefTableAndTrailer(PdfDocument document, PdfObject fileId,
205182
for (int i = 1; i < size(); i++) {
206183
PdfIndirectReference reference = xref[i];
207184
if (reference != null) {
208-
if ((document.properties.appendMode && !reference.checkState(PdfObject.MODIFIED)) ||
209-
(reference.isFree() && reference.getGenNumber() == 0) ||
210-
(!reference.checkState(PdfObject.FLUSHED))) {
185+
if (document.properties.appendMode && !reference.checkState(PdfObject.MODIFIED)) {
211186
reference = null;
212187
}
213188
}
@@ -298,7 +273,17 @@ protected void writeXrefTableAndTrailer(PdfDocument document, PdfObject fileId,
298273
for (int i = first; i < first + len; i++) {
299274
PdfIndirectReference reference = xrefTable.get(i);
300275

301-
StringBuilder off = new StringBuilder("0000000000").append(reference.getOffset());
276+
StringBuilder off = new StringBuilder("0000000000");
277+
if (reference.isFree()) {
278+
if (!freeReferences.isEmpty()) {
279+
off.append(freeReferences.pollFirst());
280+
}
281+
/* if (freeReferences.isEmpty()), then we are at the
282+
last free reference. Its referral value must be object 0.
283+
*/
284+
} else {
285+
off.append(reference.getOffset());
286+
}
302287
StringBuilder gen = new StringBuilder("00000").append(reference.getGenNumber());
303288
writer.writeString(off.substring(off.length() - 10, off.length())).writeSpace().
304289
writeString(gen.substring(gen.length() - 5, gen.length())).writeSpace();
@@ -327,6 +312,7 @@ protected void writeXrefTableAndTrailer(PdfDocument document, PdfObject fileId,
327312
writer.write(document.getTrailer());
328313
writer.write('\n');
329314
}
315+
freeReferences.clear();
330316
writeKeyInfo(writer);
331317
writer.writeString("startxref\n").
332318
writeLong(startxref).

kernel/src/test/java/com/itextpdf/kernel/pdf/PdfPagesTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public void randomObjectPagesTest() throws IOException {
171171
PdfPage testPage = document.removePage(1000);
172172
Assert.assertTrue(testPage.getPdfObject().getIndirectReference() == null);
173173
document.addPage(1000, testPage);
174-
Assert.assertTrue(testPage.getPdfObject().getIndirectReference().getObjNumber() < xrefSize);
174+
Assert.assertTrue(testPage.getPdfObject().getIndirectReference().getObjNumber() == xrefSize);
175175

176176
for (int i = 0; i < pages.length; i++) {
177177
Assert.assertEquals("Remove page", true, document.removePage(pages[i]));

kernel/src/test/java/com/itextpdf/kernel/pdf/PdfReaderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ public void pagesTest01() throws IOException {
375375

376376
Assert.assertTrue(testPage.getPdfObject().getIndirectReference() == null);
377377
document.addPage(1000, testPage);
378-
Assert.assertTrue(testPage.getPdfObject().getIndirectReference().getObjNumber() < xrefSize);
378+
Assert.assertTrue(testPage.getPdfObject().getIndirectReference().getObjNumber() == xrefSize);
379379

380380
for (int i = 1; i < document.getNumberOfPages() + 1; i++) {
381381
PdfPage page = document.getPage(i);
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* To change this license header, choose License Headers in Project Properties.
3+
* To change this template file, choose Tools | Templates
4+
* and open the template in the editor.
5+
*/
6+
package com.itextpdf.kernel.pdf;
7+
8+
import com.itextpdf.test.ExtendedITextTest;
9+
import static com.itextpdf.test.ITextTest.createOrClearDestinationFolder;
10+
import com.itextpdf.test.annotations.type.IntegrationTest;
11+
12+
import java.io.IOException;
13+
14+
import org.junit.BeforeClass;
15+
import org.junit.Test;
16+
import org.junit.experimental.categories.Category;
17+
18+
import junit.framework.Assert;
19+
20+
/**
21+
*
22+
* @author benoit
23+
*/
24+
@Category(IntegrationTest.class)
25+
public class PdfXrefTableTest extends ExtendedITextTest {
26+
27+
public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/pdf/PdfXrefTableTest/";
28+
public static final String destinationFolder = "./target/test/com/itextpdf/kernel/pdf/PdfXrefTableTest/";
29+
30+
@BeforeClass
31+
public static void beforeClass() {
32+
createOrClearDestinationFolder(destinationFolder);
33+
}
34+
35+
@Test
36+
public void testCreateAndUpdateXMP() throws IOException {
37+
String created = destinationFolder + "testCreateAndUpdateXMP_create.pdf";
38+
String updated = destinationFolder + "testCreateAndUpdateXMP_update.pdf";
39+
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(created));
40+
pdfDocument.addNewPage();
41+
42+
pdfDocument.getXmpMetadata(true); // create XMP metadata
43+
pdfDocument.close();
44+
45+
pdfDocument = new PdfDocument(new PdfReader(created), new PdfWriter(updated));
46+
pdfDocument.close();
47+
pdfDocument = new PdfDocument(new PdfReader(updated));
48+
PdfXrefTable xref = pdfDocument.getXref();
49+
PdfIndirectReference freeRef = xref.get(xref.size() - 2); // 6
50+
51+
/*
52+
Current xref structure:
53+
xref
54+
0 8
55+
0000000006 65535 f % this is object 0; 6 refers to free object 6
56+
0000000203 00000 n
57+
0000000510 00000 n
58+
0000000263 00000 n
59+
0000000088 00000 n
60+
0000000015 00000 n
61+
0000000000 00001 f % this is object 6; 0 refers to free object 0; note generation number
62+
0000000561 00000 n
63+
*/
64+
65+
Assert.assertTrue(freeRef.isFree());
66+
Assert.assertEquals(xref.get(0).offsetOrIndex, freeRef.objNr);
67+
Assert.assertEquals(1, freeRef.genNr);
68+
pdfDocument.close();
69+
}
70+
71+
72+
@Test
73+
public void testCreateAndUpdateTwiceXMP() throws IOException {
74+
String created = destinationFolder + "testCreateAndUpdateTwiceXMP_create.pdf";
75+
String updated = destinationFolder + "testCreateAndUpdateTwiceXMP_update.pdf";
76+
String updatedAgain = destinationFolder + "testCreateAndUpdateTwiceXMP_updatedAgain.pdf";
77+
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(created));
78+
pdfDocument.addNewPage();
79+
80+
pdfDocument.getXmpMetadata(true); // create XMP metadata
81+
pdfDocument.close();
82+
83+
pdfDocument = new PdfDocument(new PdfReader(created), new PdfWriter(updated));
84+
pdfDocument.close();
85+
pdfDocument = new PdfDocument(new PdfReader(updated), new PdfWriter(updatedAgain));
86+
pdfDocument.close();
87+
pdfDocument = new PdfDocument(new PdfReader(updatedAgain));
88+
PdfXrefTable xref = pdfDocument.getXref();
89+
PdfIndirectReference freeRef1 = xref.get(xref.size() - 3); // 6
90+
PdfIndirectReference freeRef2 = xref.get(xref.size() - 2); // 7
91+
92+
/*
93+
Current xref structure:
94+
xref
95+
0 9
96+
0000000006 65535 f % this is object 0; 6 refers to free object 6
97+
0000000203 00000 n
98+
0000000510 00000 n
99+
0000000263 00000 n
100+
0000000088 00000 n
101+
0000000015 00000 n
102+
0000000007 00002 f % this is object 6; 7 refers to free object 7; note generation number
103+
0000000000 00001 f % this is object 7; 0 refers to free object 0; note generation number
104+
0000000561 00000 n
105+
*/
106+
107+
Assert.assertTrue(freeRef1.isFree());
108+
Assert.assertEquals(xref.get(0).offsetOrIndex, freeRef1.objNr);
109+
Assert.assertEquals(2, freeRef1.genNr);
110+
Assert.assertTrue(freeRef2.isFree());
111+
Assert.assertEquals(freeRef1.offsetOrIndex, freeRef2.objNr);
112+
Assert.assertEquals(1, freeRef2.genNr);
113+
pdfDocument.close();
114+
}
115+
}

0 commit comments

Comments
 (0)