Skip to content

Commit 4177fb4

Browse files
author
Eugene Bochilo
committed
Introduce lazy initialization for some of pdfDocument fields
DEVSIX-2164
1 parent 8197cb7 commit 4177fb4

File tree

7 files changed

+139
-33
lines changed

7 files changed

+139
-33
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public static PdfAConformanceLevel getConformanceLevel(XMPMeta meta) {
120120
try {
121121
conformanceXmpProperty = meta.getProperty(XMPConst.NS_PDFA_ID, XMPConst.CONFORMANCE);
122122
partXmpProperty = meta.getProperty(XMPConst.NS_PDFA_ID, XMPConst.PART);
123-
} catch (XMPException exc) {
123+
} catch (XMPException ignored) {
124124
}
125125
if (conformanceXmpProperty == null || partXmpProperty == null) {
126126
return null;

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

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -665,11 +665,18 @@ public void removePage(int pageNum) {
665665

666666
/**
667667
* Gets document information dictionary.
668+
* {@link PdfDocument#info} is lazy initialized. It will be initialized during the first call of this method.
668669
*
669670
* @return document information dictionary.
670671
*/
671672
public PdfDocumentInfo getDocumentInfo() {
672673
checkClosingStatus();
674+
if (info == null) {
675+
PdfObject infoDict = trailer.get(PdfName.Info);
676+
info = new PdfDocumentInfo(
677+
infoDict instanceof PdfDictionary ? (PdfDictionary) infoDict : new PdfDictionary(), this);
678+
XmpMetaInfoConverter.appendMetadataToInfo(xmpMetadata, info);
679+
}
673680
return info;
674681
}
675682

@@ -840,7 +847,7 @@ public void close() {
840847
// In PDF 2.0, all the values except CreationDate and ModDate are deprecated. Remove them now
841848
if (pdfVersion.compareTo(PdfVersion.PDF_2_0) >= 0) {
842849
for (PdfName deprecatedKey : PdfDocumentInfo.PDF20_DEPRECATED_KEYS) {
843-
info.getPdfObject().remove(deprecatedKey);
850+
getDocumentInfo().getPdfObject().remove(deprecatedKey);
844851
}
845852
}
846853
if (getXmpMetadata() != null) {
@@ -893,8 +900,8 @@ public void close() {
893900
}
894901

895902

896-
if (info.getPdfObject().isModified()) {
897-
info.getPdfObject().flush(false);
903+
if (getDocumentInfo().getPdfObject().isModified()) {
904+
getDocumentInfo().getPdfObject().flush(false);
898905
}
899906
flushFonts();
900907

@@ -942,7 +949,7 @@ public void close() {
942949
tryFlushTagStructure(false);
943950
}
944951
catalog.getPdfObject().flush(false);
945-
info.getPdfObject().flush(false);
952+
getDocumentInfo().getPdfObject().flush(false);
946953
flushFonts();
947954

948955
if (writer.crypto != null) {
@@ -978,7 +985,7 @@ public void close() {
978985
// entries existing in the trailer object and corresponding fields. This inconsistency
979986
// may appear when user gets trailer and explicitly sets new root or info dictionaries.
980987
trailer.put(PdfName.Root, catalog.getPdfObject());
981-
trailer.put(PdfName.Info, info.getPdfObject());
988+
trailer.put(PdfName.Info, getDocumentInfo().getPdfObject());
982989

983990

984991
//By this time original and modified document ids should always be not null due to initializing in
@@ -1932,14 +1939,13 @@ protected void open(PdfVersion newPdfVersion) {
19321939
PdfStream xmpMetadataStream = catalog.getPdfObject().getAsStream(PdfName.Metadata);
19331940
if (xmpMetadataStream != null) {
19341941
xmpMetadata = xmpMetadataStream.getBytes();
1935-
try {
1936-
reader.pdfAConformanceLevel = PdfAConformanceLevel.getConformanceLevel(XMPMetaFactory.parseFromBuffer(xmpMetadata));
1937-
} catch (XMPException ignored) {
1942+
if (!this.getClass().equals(PdfDocument.class)) {
1943+
// TODO DEVSIX-5292 If somebody extends PdfDocument we have to initialize document info
1944+
// and conformance level to provide compatibility. This code block shall be removed
1945+
reader.getPdfAConformanceLevel();
1946+
getDocumentInfo();
19381947
}
19391948
}
1940-
PdfObject infoDict = trailer.get(PdfName.Info);
1941-
info = new PdfDocumentInfo(infoDict instanceof PdfDictionary ? (PdfDictionary) infoDict : new PdfDictionary(), this);
1942-
XmpMetaInfoConverter.appendMetadataToInfo(xmpMetadata, info);
19431949

19441950
PdfDictionary str = catalog.getPdfObject().getAsDictionary(PdfName.StructTreeRoot);
19451951
if (str != null) {
@@ -1965,10 +1971,10 @@ protected void open(PdfVersion newPdfVersion) {
19651971
info = new PdfDocumentInfo(this).addCreationDate();
19661972
}
19671973
updateProducerInInfoDictionary();
1968-
info.addModDate();
1974+
getDocumentInfo().addModDate();
19691975
trailer = new PdfDictionary();
19701976
trailer.put(PdfName.Root, catalog.getPdfObject().getIndirectReference());
1971-
trailer.put(PdfName.Info, info.getPdfObject().getIndirectReference());
1977+
trailer.put(PdfName.Info, getDocumentInfo().getPdfObject().getIndirectReference());
19721978

19731979
if (reader != null) {
19741980
// If the reader's trailer contains an ID entry, let's copy it over to the new trailer
@@ -2094,7 +2100,7 @@ protected void updateXmpMetadata() {
20942100
*/
20952101
protected XMPMeta updateDefaultXmpMetadata() throws XMPException {
20962102
XMPMeta xmpMeta = XMPMetaFactory.parseFromBuffer(getXmpMetadata(true));
2097-
XmpMetaInfoConverter.appendDocumentInfoToMetadata(info, xmpMeta);
2103+
XmpMetaInfoConverter.appendDocumentInfoToMetadata(getDocumentInfo(), xmpMeta);
20982104

20992105
if (isTagged() && writer.properties.addUAXmpMetadata && !isXmpMetaHasProperty(xmpMeta, XMPConst.NS_PDFUA_ID, XMPConst.PART)) {
21002106
xmpMeta.setPropertyInteger(XMPConst.NS_PDFUA_ID, XMPConst.PART, 1, new PropertyOptions(PropertyOptions.SEPARATE_NODE));
@@ -2204,17 +2210,18 @@ boolean hasAcroForm() {
22042210

22052211
private void updateProducerInInfoDictionary() {
22062212
String producer = null;
2213+
PdfDictionary documentInfoObject = getDocumentInfo().getPdfObject();
22072214
if (reader == null) {
22082215
producer = versionInfo.getVersion();
22092216
} else {
2210-
if (info.getPdfObject().containsKey(PdfName.Producer)) {
2211-
final PdfString producerPdfStr = info.getPdfObject().getAsString(PdfName.Producer);
2217+
if (documentInfoObject.containsKey(PdfName.Producer)) {
2218+
final PdfString producerPdfStr = documentInfoObject.getAsString(PdfName.Producer);
22122219
producer = producerPdfStr == null ? null : producerPdfStr.toUnicodeString();
22132220
}
22142221
producer = addModifiedPostfix(producer);
22152222
}
22162223

2217-
info.getPdfObject().put(PdfName.Producer, new PdfString(producer, PdfEncodings.UNICODE_BIG));
2224+
documentInfoObject.put(PdfName.Producer, new PdfString(producer, PdfEncodings.UNICODE_BIG));
22182225
}
22192226

22202227
/**

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ This file is part of the iText (R) project.
6464
import java.io.InputStream;
6565
import java.io.Serializable;
6666
import java.util.Map;
67+
68+
import com.itextpdf.kernel.xmp.XMPException;
69+
import com.itextpdf.kernel.xmp.XMPMetaFactory;
6770
import org.slf4j.Logger;
6871
import org.slf4j.LoggerFactory;
6972

@@ -605,13 +608,24 @@ public int getCryptoMode() {
605608
}
606609

607610
/**
608-
* Gets the declared Pdf/A conformance level of the source document that is being read.
611+
* Gets the declared PDF/A conformance level of the source document that is being read.
609612
* Note that this information is provided via XMP metadata and is not verified by iText.
613+
* {@link PdfReader#pdfAConformanceLevel} is lazy initialized.
614+
* It will be initialized during the first call of this method.
610615
*
611-
* @return conformance level of the source document, or {@code null} if no Pdf/A
616+
* @return conformance level of the source document, or {@code null} if no PDF/A
612617
* conformance level information is specified.
613618
*/
614619
public PdfAConformanceLevel getPdfAConformanceLevel() {
620+
if (pdfAConformanceLevel == null) {
621+
if (pdfDocument != null && pdfDocument.getXmpMetadata() != null) {
622+
try {
623+
pdfAConformanceLevel = PdfAConformanceLevel.getConformanceLevel(
624+
XMPMetaFactory.parseFromBuffer(pdfDocument.getXmpMetadata()));
625+
} catch (XMPException ignored) {
626+
}
627+
}
628+
}
615629
return pdfAConformanceLevel;
616630
}
617631

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public void baseReading01() throws IOException {
127127
int total = 817;
128128
int flushedExpected = 0;
129129
// link annots, line annots, actions and images: one hundred of each
130-
int notReadExpected = 401;
130+
int notReadExpected = 402;
131131

132132
test("baseReading01.pdf", DocMode.READING, FlushMode.NONE, PagesOp.READ,
133133
total, flushedExpected, notReadExpected);
@@ -137,7 +137,7 @@ public void baseReading01() throws IOException {
137137
public void releaseDeepReading01() throws IOException {
138138
int total = 817;
139139
int flushedExpected = 0;
140-
int notReadExpected = 803;
140+
int notReadExpected = 804;
141141

142142
test("releaseDeepReading01.pdf", DocMode.READING, FlushMode.RELEASE_DEEP, PagesOp.READ,
143143
total, flushedExpected, notReadExpected);

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

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ This file is part of the iText (R) project.
4646
import com.itextpdf.io.LogMessageConstant;
4747
import com.itextpdf.io.font.AdobeGlyphList;
4848
import com.itextpdf.io.source.ByteArrayOutputStream;
49+
import com.itextpdf.kernel.PdfException;
4950
import com.itextpdf.kernel.font.PdfType3Font;
5051
import com.itextpdf.kernel.pdf.layer.PdfLayer;
5152
import com.itextpdf.kernel.pdf.layer.PdfOCProperties;
@@ -69,6 +70,8 @@ This file is part of the iText (R) project.
6970

7071
@Category(UnitTest.class)
7172
public class PdfDocumentUnitTest extends ExtendedITextTest {
73+
public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/pdf/PdfDocumentUnitTest/";
74+
7275
@Rule
7376
public ExpectedException junitExpectedException = ExpectedException.none();
7477

@@ -235,6 +238,98 @@ public void copyPagesFlushedResources() throws IOException {
235238
}
236239
}
237240

241+
@Test
242+
public void pdfDocumentInstanceNoWriterInfoAndConformanceLevelInitialization() throws IOException {
243+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + "pdfWithMetadata.pdf"));
244+
245+
Assert.assertNull(pdfDocument.info);
246+
Assert.assertNull(pdfDocument.reader.pdfAConformanceLevel);
247+
248+
pdfDocument.close();
249+
250+
Assert.assertNull(pdfDocument.info);
251+
Assert.assertNull(pdfDocument.reader.pdfAConformanceLevel);
252+
}
253+
254+
@Test
255+
public void pdfDocumentInstanceWriterInfoAndConformanceLevelInitialization() throws IOException {
256+
PdfDocument pdfDocument = new PdfDocument(
257+
new PdfReader(sourceFolder + "pdfWithMetadata.pdf"), new PdfWriter(new ByteArrayOutputStream()));
258+
259+
Assert.assertNotNull(pdfDocument.info);
260+
Assert.assertNull(pdfDocument.reader.pdfAConformanceLevel);
261+
262+
pdfDocument.close();
263+
264+
Assert.assertNotNull(pdfDocument.info);
265+
Assert.assertNull(pdfDocument.reader.pdfAConformanceLevel);
266+
}
267+
268+
@Test
269+
public void extendedPdfDocumentNoWriterInfoAndConformanceLevelInitialization() throws IOException {
270+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + "pdfWithMetadata.pdf")) {
271+
// This class instance extends pdfDocument
272+
};
273+
274+
// TODO DEVSIX-5292 These fields shouldn't be initialized during the document's opening
275+
Assert.assertNotNull(pdfDocument.info);
276+
Assert.assertNotNull(pdfDocument.reader.pdfAConformanceLevel);
277+
278+
pdfDocument.close();
279+
280+
Assert.assertNotNull(pdfDocument.info);
281+
Assert.assertNotNull(pdfDocument.reader.pdfAConformanceLevel);
282+
}
283+
284+
@Test
285+
public void extendedPdfDocumentWriterInfoAndConformanceLevelInitialization() throws IOException {
286+
PdfDocument pdfDocument = new PdfDocument(
287+
new PdfReader(sourceFolder + "pdfWithMetadata.pdf"), new PdfWriter(new ByteArrayOutputStream())) {
288+
// This class instance extends pdfDocument
289+
};
290+
291+
Assert.assertNotNull(pdfDocument.info);
292+
// TODO DEVSIX-5292 pdfAConformanceLevel shouldn't be initialized during the document's opening
293+
Assert.assertNotNull(pdfDocument.reader.pdfAConformanceLevel);
294+
295+
pdfDocument.close();
296+
297+
Assert.assertNotNull(pdfDocument.info);
298+
Assert.assertNotNull(pdfDocument.reader.pdfAConformanceLevel);
299+
}
300+
301+
@Test
302+
public void getDocumentInfoAlreadyClosedTest() throws IOException {
303+
junitExpectedException.expect(PdfException.class);
304+
305+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + "pdfWithMetadata.pdf"));
306+
pdfDocument.close();
307+
308+
pdfDocument.getDocumentInfo();
309+
310+
Assert.fail();
311+
}
312+
313+
@Test
314+
public void getDocumentInfoNotInitializedTest() throws IOException {
315+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + "pdfWithMetadata.pdf"));
316+
317+
Assert.assertNull(pdfDocument.info);
318+
Assert.assertNotNull(pdfDocument.getDocumentInfo());
319+
320+
pdfDocument.close();
321+
}
322+
323+
@Test
324+
public void getPdfAConformanceLevelNotInitializedTest() throws IOException {
325+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + "pdfWithMetadata.pdf"));
326+
327+
Assert.assertNull(pdfDocument.reader.pdfAConformanceLevel);
328+
Assert.assertNotNull(pdfDocument.reader.getPdfAConformanceLevel());
329+
330+
pdfDocument.close();
331+
}
332+
238333
private static void assertLayerNames(PdfDocument outDocument, List<String> layerNames) {
239334
Assert.assertNotNull(outDocument.getCatalog());
240335
PdfOCProperties ocProperties = outDocument.getCatalog().getOCProperties(true);

pdfa/src/main/java/com/itextpdf/pdfa/PdfADocument.java

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,7 @@ public PdfADocument(PdfReader reader, PdfWriter writer) {
153153
public PdfADocument(PdfReader reader, PdfWriter writer, StampingProperties properties) {
154154
super(reader, writer, properties);
155155

156-
byte[] existingXmpMetadata = getXmpMetadata();
157-
if (existingXmpMetadata == null) {
158-
throw new PdfAConformanceException(PdfAConformanceException.DOCUMENT_TO_READ_FROM_SHALL_BE_A_PDFA_CONFORMANT_FILE_WITH_VALID_XMP_METADATA);
159-
}
160-
XMPMeta meta;
161-
try {
162-
meta = XMPMetaFactory.parseFromBuffer(existingXmpMetadata);
163-
} catch (XMPException exc) {
164-
throw new PdfAConformanceException(PdfAConformanceException.DOCUMENT_TO_READ_FROM_SHALL_BE_A_PDFA_CONFORMANT_FILE_WITH_VALID_XMP_METADATA);
165-
}
166-
PdfAConformanceLevel conformanceLevel = PdfAConformanceLevel.getConformanceLevel(meta);
156+
PdfAConformanceLevel conformanceLevel = reader.getPdfAConformanceLevel();
167157
if (conformanceLevel == null) {
168158
throw new PdfAConformanceException(PdfAConformanceException.DOCUMENT_TO_READ_FROM_SHALL_BE_A_PDFA_CONFORMANT_FILE_WITH_VALID_XMP_METADATA);
169159
}

0 commit comments

Comments
 (0)