Skip to content

Commit 1965698

Browse files
committed
Hide PdfDocument#createXmpMetadata(). Add getXmpMetadata(boolean) for creating empty XMPMeta. Add WriterProperties#addXmpMetadata().
iText will create XMPMetadata implicity in case PDF/A. If users want to create XMPMetadata forPdfDocument, they may set additional WriterProperties#addXmpMetadata().
1 parent fb56876 commit 1965698

24 files changed

+108
-194
lines changed

io/src/main/java/com/itextpdf/io/LogMessageConstant.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ This file is part of the iText (R) project.
4747
/**
4848
* Class containing constants to be used in logging.
4949
*/
50-
public class LogMessageConstant {
50+
public final class LogMessageConstant {
5151

5252
/**
5353
* Log message.
@@ -173,4 +173,6 @@ public class LogMessageConstant {
173173
public static final String WRITER_ENCRYPTION_IS_IGNORED_APPEND = "Writer encryption will be ignored, because append mode is used. Document will preserve the original encryption (or will stay unencrypted)";
174174

175175
public static final String WRITER_ENCRYPTION_IS_IGNORED_PRESERVE = "Writer encryption will be ignored, because preservation of encryption is enabled. Document will preserve the original encryption (or will stay unencrypted)";
176+
177+
public static final String EXCEPTION_WHILE_UPDATING_XMPMETADATA = "Exception while updating XmpMetadata";
176178
}

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

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -275,16 +275,19 @@ public void setXmpMetadata(XMPMeta xmpMeta) throws XMPException {
275275
setXmpMetadata(xmpMeta, serializeOptions);
276276
}
277277

278-
public void createXmpMetadata() throws XMPException {
279-
checkClosingStatus();
280-
XMPMeta xmpMeta = XMPMetaFactory.create();
281-
xmpMeta.setObjectName(XMPConst.TAG_XMPMETA);
282-
xmpMeta.setObjectName("");
278+
protected void updateXmpMetadata() {
283279
try {
284-
xmpMeta.setProperty(XMPConst.NS_DC, PdfConst.Format, "application/pdf");
285-
xmpMeta.setProperty(XMPConst.NS_PDF, PdfConst.Producer, Version.getInstance().getVersion());
286-
} catch (XMPException ignored) {
280+
if (writer.properties.addXmpMetadata) {
281+
setXmpMetadata(createXmpMetadata());
282+
}
283+
} catch (XMPException e) {
284+
Logger logger = LoggerFactory.getLogger(PdfDocument.class);
285+
logger.error(LogMessageConstant.EXCEPTION_WHILE_UPDATING_XMPMETADATA, e);
287286
}
287+
}
288+
289+
protected XMPMeta createXmpMetadata() throws XMPException {
290+
XMPMeta xmpMeta = XMPMetaFactory.parseFromBuffer(getXmpMetadata(true));
288291
PdfDictionary docInfo = info.getPdfObject();
289292
if (docInfo != null) {
290293
PdfName key;
@@ -299,34 +302,73 @@ public void createXmpMetadata() throws XMPException {
299302
continue;
300303
value = ((PdfString) obj).toUnicodeString();
301304
if (PdfName.Title.equals(key)) {
302-
xmpMeta.setLocalizedText(XMPConst.NS_DC, PdfConst.Title, XMPConst.X_DEFAULT, XMPConst.X_DEFAULT, value);
305+
if (!IsXmpMetaHasLocalizedText(xmpMeta, XMPConst.NS_DC, PdfConst.Title)) {
306+
xmpMeta.setLocalizedText(XMPConst.NS_DC, PdfConst.Title, XMPConst.X_DEFAULT, XMPConst.X_DEFAULT, value);
307+
}
303308
} else if (PdfName.Author.equals(key)) {
304309
xmpMeta.appendArrayItem(XMPConst.NS_DC, PdfConst.Creator, new PropertyOptions(PropertyOptions.ARRAY_ORDERED), value, null);
305310
} else if (PdfName.Subject.equals(key)) {
306-
xmpMeta.setLocalizedText(XMPConst.NS_DC, PdfConst.Description, XMPConst.X_DEFAULT, XMPConst.X_DEFAULT, value);
311+
if (!IsXmpMetaHasLocalizedText(xmpMeta, XMPConst.NS_DC, PdfConst.Description)) {
312+
xmpMeta.setLocalizedText(XMPConst.NS_DC, PdfConst.Description, XMPConst.X_DEFAULT, XMPConst.X_DEFAULT, value);
313+
}
307314
} else if (PdfName.Keywords.equals(key)) {
308-
for (String v : value.split(",|;"))
309-
if (v.trim().length() > 0)
315+
for (String v : value.split(",|;")) {
316+
if (v.trim().length() > 0) {
310317
xmpMeta.appendArrayItem(XMPConst.NS_DC, PdfConst.Subject, new PropertyOptions(PropertyOptions.ARRAY), v.trim(), null);
311-
xmpMeta.setProperty(XMPConst.NS_PDF, PdfConst.Keywords, value);
318+
}
319+
}
320+
if (!isXmpMetaHasProperty(xmpMeta, XMPConst.NS_PDF, PdfConst.Keywords)) {
321+
xmpMeta.setProperty(XMPConst.NS_PDF, PdfConst.Keywords, value);
322+
}
312323
} else if (PdfName.Creator.equals(key)) {
313-
xmpMeta.setProperty(XMPConst.NS_XMP, PdfConst.CreatorTool, value);
324+
if (!isXmpMetaHasProperty(xmpMeta, XMPConst.NS_PDF, PdfConst.CreatorTool)) {
325+
xmpMeta.setProperty(XMPConst.NS_XMP, PdfConst.CreatorTool, value);
326+
}
314327
} else if (PdfName.Producer.equals(key)) {
315-
xmpMeta.setProperty(XMPConst.NS_PDF, PdfConst.Producer, value);
328+
if (!isXmpMetaHasProperty(xmpMeta, XMPConst.NS_PDF, PdfConst.Producer)) {
329+
xmpMeta.setProperty(XMPConst.NS_PDF, PdfConst.Producer, value);
330+
}
316331
} else if (PdfName.CreationDate.equals(key)) {
317-
xmpMeta.setProperty(XMPConst.NS_XMP, PdfConst.CreateDate, PdfDate.getW3CDate(value));
332+
if (!isXmpMetaHasProperty(xmpMeta, XMPConst.NS_XMP, PdfConst.CreateDate)) {
333+
xmpMeta.setProperty(XMPConst.NS_XMP, PdfConst.CreateDate, PdfDate.getW3CDate(value));
334+
}
318335
} else if (PdfName.ModDate.equals(key)) {
319-
xmpMeta.setProperty(XMPConst.NS_XMP, PdfConst.ModifyDate, PdfDate.getW3CDate(value));
336+
if (!isXmpMetaHasProperty(xmpMeta, XMPConst.NS_XMP, PdfConst.ModifyDate)) {
337+
xmpMeta.setProperty(XMPConst.NS_XMP, PdfConst.ModifyDate, PdfDate.getW3CDate(value));
338+
}
320339
}
321340
}
322341
}
323-
if (isTagged()) {
342+
if (isTagged() && !isXmpMetaHasProperty(xmpMeta, XMPConst.NS_PDFUA_ID, XMPConst.PART)) {
324343
xmpMeta.setPropertyInteger(XMPConst.NS_PDFUA_ID, XMPConst.PART, 1, new PropertyOptions(PropertyOptions.SEPARATE_NODE));
325344
}
326-
setXmpMetadata(xmpMeta);
345+
return xmpMeta;
327346
}
328347

348+
/**
349+
* Gets XMPMetadata.
350+
*/
329351
public byte[] getXmpMetadata() {
352+
return getXmpMetadata(false);
353+
}
354+
355+
/**
356+
* Gets XMPMetadata or create a new one.
357+
* @param createNew if true, create a new empty XMPMetadata if it did not present.
358+
* @return existed or newly created XMPMetadata byte array.
359+
*/
360+
public byte[] getXmpMetadata(boolean createNew) {
361+
if (xmpMetadata == null && createNew) {
362+
XMPMeta xmpMeta = XMPMetaFactory.create();
363+
xmpMeta.setObjectName(XMPConst.TAG_XMPMETA);
364+
xmpMeta.setObjectName("");
365+
try {
366+
xmpMeta.setProperty(XMPConst.NS_DC, PdfConst.Format, "application/pdf");
367+
xmpMeta.setProperty(XMPConst.NS_PDF, PdfConst.Producer, Version.getInstance().getVersion());
368+
setXmpMetadata(xmpMeta);
369+
} catch (XMPException ignored) {
370+
}
371+
}
330372
return xmpMetadata;
331373
}
332374

@@ -668,9 +710,11 @@ public void close() {
668710
isClosing = true;
669711
try {
670712
if (writer != null) {
671-
if (catalog.isFlushed())
713+
if (catalog.isFlushed()) {
672714
throw new PdfException(PdfException.CannotCloseDocumentWithAlreadyFlushedPdfCatalog);
673-
if (xmpMetadata != null) {
715+
}
716+
updateXmpMetadata();
717+
if (getXmpMetadata() != null) {
674718
PdfStream xmp = new PdfStream().makeIndirect(this);
675719
xmp.getOutputStream().write(xmpMetadata);
676720
xmp.put(PdfName.Type, PdfName.Metadata);
@@ -761,7 +805,6 @@ public void close() {
761805
}
762806

763807
}
764-
765808
byte[] originalFileID = null;
766809
if (crypto == null && writer.crypto != null) {
767810
originalFileID = writer.crypto.getDocumentId();
@@ -1651,6 +1694,14 @@ private void ensureTreeRootAddedToNames(PdfObject treeRoot, PdfName treeType) {
16511694
names.put(treeType, treeRoot);
16521695
}
16531696

1697+
private static boolean isXmpMetaHasProperty(XMPMeta xmpMeta, String schemaNS, String propName) throws XMPException {
1698+
return xmpMeta.getProperty(schemaNS, propName) != null;
1699+
}
1700+
1701+
private static boolean IsXmpMetaHasLocalizedText(XMPMeta xmpMeta, String schemaNS, String altTextName) throws XMPException {
1702+
return xmpMeta.getLocalizedText(schemaNS, altTextName, XMPConst.X_DEFAULT, XMPConst.X_DEFAULT) != null;
1703+
}
1704+
16541705
@SuppressWarnings("unused")
16551706
private byte[] getSerializedBytes() {
16561707
ByteArrayOutputStream bos = null;

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public class WriterProperties implements Serializable {
6464
*/
6565
protected boolean smartMode;
6666
protected boolean debugMode;
67+
protected boolean addXmpMetadata;
6768
protected PdfVersion pdfVersion;
6869
protected EncryptionProperties encryptionProperties;
6970

@@ -101,6 +102,15 @@ public WriterProperties useSmartMode() {
101102
return this;
102103
}
103104

105+
/**
106+
* If true, default XMPMetadata based on {@link PdfDocumentInfo} will be added.
107+
* @return this {@code WriterProperties} instance
108+
*/
109+
public WriterProperties addXmpMetadata() {
110+
this.addXmpMetadata = true;
111+
return this;
112+
}
113+
104114
/**
105115
* Defines the level of compression for the document.
106116
* See {@link CompressionConstants}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,11 @@ public void encryptWithPassword(String filename, int encryptionType, int compres
100100
String outFileName = destinationFolder + filename;
101101
int permissions = EncryptionConstants.ALLOW_SCREENREADERS;
102102
com.itextpdf.kernel.pdf.PdfWriter writer = new com.itextpdf.kernel.pdf.PdfWriter(new FileOutputStream(outFileName),
103-
new WriterProperties().setStandardEncryption(USER, OWNER, permissions, encryptionType));
103+
new WriterProperties().setStandardEncryption(USER, OWNER, permissions, encryptionType).addXmpMetadata());
104104
writer.setCompressionLevel(compression);
105105
PdfDocument document = new PdfDocument(writer);
106106
document.getDocumentInfo().setAuthor(author).
107107
setCreator(creator);
108-
document.createXmpMetadata();
109108
PdfPage page = document.addNewPage();
110109
page.getFirstContentStream().getOutputStream().writeBytes(("q\n" +
111110
"BT\n" +

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -747,10 +747,10 @@ public void stampingXmp1() throws IOException, XMPException {
747747
pdfDoc1.close();
748748

749749
PdfReader reader2 = new PdfReader(new FileInputStream(filename1));
750-
PdfWriter writer2 = new PdfWriter(new FileOutputStream(filename2), new WriterProperties().setFullCompressionMode(false));
750+
PdfWriter writer2 = new PdfWriter(new FileOutputStream(filename2),
751+
new WriterProperties().setFullCompressionMode(false).addXmpMetadata());
751752
PdfDocument pdfDoc2 = new PdfDocument(reader2, writer2);
752753
pdfDoc2.getDocumentInfo().setAuthor("Alexander Chingarev");
753-
pdfDoc2.createXmpMetadata();
754754
pdfDoc2.close();
755755

756756
PdfReader reader3 = new PdfReader(new FileInputStream(filename2));
@@ -792,11 +792,10 @@ public void stampingXmp2() throws IOException, XMPException {
792792
pdfDoc1.close();
793793

794794
PdfReader reader2 = new PdfReader(new FileInputStream(filename1));
795-
PdfWriter writer2 = new PdfWriter(new FileOutputStream(filename2), new WriterProperties().setFullCompressionMode(true));
795+
PdfWriter writer2 = new PdfWriter(new FileOutputStream(filename2),
796+
new WriterProperties().setFullCompressionMode(true).addXmpMetadata());
796797
PdfDocument pdfDoc2 = new PdfDocument(reader2, writer2);
797798
pdfDoc2.getDocumentInfo().setAuthor("Alexander Chingarev");
798-
pdfDoc2.getDocumentInfo().setAuthor("Alexander Chingarev");
799-
pdfDoc2.createXmpMetadata();
800799
pdfDoc2.close();
801800

802801
PdfReader reader3 = new PdfReader(new FileInputStream(filename2));

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,14 @@ public void createEmptyDocumentWithXmp() throws Exception {
3535

3636
String filename = "emptyDocumentWithXmp.pdf";
3737
FileOutputStream fos = new FileOutputStream(destinationFolder +filename);
38-
PdfWriter writer = new PdfWriter(fos);
38+
PdfWriter writer = new PdfWriter(fos, new WriterProperties().addXmpMetadata());
3939
PdfDocument pdfDoc = new PdfDocument(writer);
4040
pdfDoc.getDocumentInfo().setAuthor("Alexander Chingarev").
4141
setCreator("iText 6").
4242
setTitle("Empty iText 6 Document");
4343
pdfDoc.getDocumentInfo().getPdfObject().remove(PdfName.CreationDate);
4444
PdfPage page = pdfDoc.addNewPage();
4545
page.flush();
46-
pdfDoc.createXmpMetadata();
4746
pdfDoc.close();
4847
PdfReader reader = new PdfReader(destinationFolder +filename);
4948
PdfDocument pdfDocument = new PdfDocument(reader);

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

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ This file is part of the iText (R) project.
4444
*/
4545
package com.itextpdf.pdfa;
4646

47+
import com.itextpdf.io.LogMessageConstant;
4748
import com.itextpdf.kernel.font.PdfFont;
4849
import com.itextpdf.kernel.log.Counter;
4950
import com.itextpdf.kernel.log.CounterFactory;
@@ -73,6 +74,8 @@ This file is part of the iText (R) project.
7374
import com.itextpdf.pdfa.checker.PdfA2Checker;
7475
import com.itextpdf.pdfa.checker.PdfA3Checker;
7576
import com.itextpdf.pdfa.checker.PdfAChecker;
77+
import org.slf4j.Logger;
78+
import org.slf4j.LoggerFactory;
7679

7780
import java.io.IOException;
7881

@@ -203,17 +206,20 @@ public PdfAConformanceLevel getConformanceLevel() {
203206
}
204207

205208
@Override
206-
public void createXmpMetadata() throws XMPException {
207-
createXmpMetadata(checker.getConformanceLevel());
208-
}
209-
210-
public void createXmpMetadata(PdfAConformanceLevel conformanceLevel) throws XMPException {
211-
super.createXmpMetadata();
212-
XMPMeta xmpMeta = XMPMetaFactory.parseFromBuffer(getXmpMetadata());
213-
if (conformanceLevel != null) {
214-
addRdfDescription(xmpMeta, conformanceLevel);
209+
protected void updateXmpMetadata() {
210+
try {
211+
XMPMeta xmpMeta = createXmpMetadata();
212+
xmpMeta.setProperty(XMPConst.NS_PDFA_ID, XMPConst.PART, checker.getConformanceLevel().getPart());
213+
xmpMeta.setProperty(XMPConst.NS_PDFA_ID, XMPConst.CONFORMANCE, checker.getConformanceLevel().getConformance());
214+
if (this.isTagged()) {
215+
XMPMeta taggedExtensionMeta = XMPMetaFactory.parseFromString(PdfAXMPUtil.PDF_UA_EXTENSION);
216+
XMPUtils.appendProperties(taggedExtensionMeta, xmpMeta, true, false);
217+
}
218+
setXmpMetadata(xmpMeta);
219+
} catch (XMPException e) {
220+
Logger logger = LoggerFactory.getLogger(PdfADocument.class);
221+
logger.error(LogMessageConstant.EXCEPTION_WHILE_UPDATING_XMPMETADATA, e);
215222
}
216-
setXmpMetadata(xmpMeta);
217223
}
218224

219225
@Override
@@ -257,15 +263,6 @@ protected void setChecker(PdfAConformanceLevel conformanceLevel) {
257263
}
258264
}
259265

260-
protected void addRdfDescription(XMPMeta xmpMeta, PdfAConformanceLevel conformanceLevel) throws XMPException {
261-
xmpMeta.setProperty(XMPConst.NS_PDFA_ID, XMPConst.PART, conformanceLevel.getPart());
262-
xmpMeta.setProperty(XMPConst.NS_PDFA_ID, XMPConst.CONFORMANCE, conformanceLevel.getConformance());
263-
if (this.isTagged()) {
264-
XMPMeta taggedExtensionMeta = XMPMetaFactory.parseFromString(PdfAXMPUtil.PDF_UA_EXTENSION);
265-
XMPUtils.appendProperties(taggedExtensionMeta, xmpMeta, true, false);
266-
}
267-
}
268-
269266
protected void initTagStructureContext() {
270267
tagStructureContext = new TagStructureContext(this, getPdfVersionForPdfA(checker.getConformanceLevel()));
271268
}

pdfa/src/test/java/com/itextpdf/pdfa/PdfA1AcroFormCheckTest.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ public void acroFormCheck01() throws FileNotFoundException, XMPException {
4646
PdfWriter writer = new PdfWriter(new ByteArrayOutputStream());
4747
InputStream is = new FileInputStream(sourceFolder + "sRGB Color Space Profile.icm");
4848
PdfADocument doc = new PdfADocument(writer, PdfAConformanceLevel.PDF_A_1B, new PdfOutputIntent("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", is));
49-
doc.createXmpMetadata();
5049
doc.addNewPage();
5150
PdfDictionary acroForm = new PdfDictionary();
5251
acroForm.put(PdfName.NeedAppearances, new PdfBoolean(true));
@@ -63,7 +62,6 @@ public void acroFormCheck02() throws IOException, XMPException, InterruptedExcep
6362
PdfWriter writer = new PdfWriter(outPdf);
6463
InputStream is = new FileInputStream(sourceFolder + "sRGB Color Space Profile.icm");
6564
PdfADocument doc = new PdfADocument(writer, PdfAConformanceLevel.PDF_A_1B, new PdfOutputIntent("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", is));
66-
doc.createXmpMetadata();
6765
doc.addNewPage();
6866
PdfDictionary acroForm = new PdfDictionary();
6967
acroForm.put(PdfName.NeedAppearances, new PdfBoolean(false));
@@ -81,7 +79,6 @@ public void acroFormCheck03() throws IOException, XMPException, InterruptedExcep
8179
PdfWriter writer = new PdfWriter(outPdf);
8280
InputStream is = new FileInputStream(sourceFolder + "sRGB Color Space Profile.icm");
8381
PdfADocument doc = new PdfADocument(writer, PdfAConformanceLevel.PDF_A_1B, new PdfOutputIntent("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", is));
84-
doc.createXmpMetadata();
8582
doc.addNewPage();
8683
PdfDictionary acroForm = new PdfDictionary();
8784
doc.getCatalog().put(PdfName.AcroForm, acroForm);

0 commit comments

Comments
 (0)