Skip to content

Commit 89de8b4

Browse files
author
dmitry.radchuk
committed
Save XMP metadata in canonical format for PDF/A documents.
DEVSIX-6355
1 parent 1f198bd commit 89de8b4

File tree

5 files changed

+82
-2
lines changed

5 files changed

+82
-2
lines changed

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ public class PdfDocument implements IEventDispatcher, Closeable {
173173
final PdfXrefTable xref = new PdfXrefTable();
174174
protected FingerPrint fingerPrint;
175175

176+
protected SerializeOptions serializeOptions = new SerializeOptions();
177+
176178
protected final StampingProperties properties;
177179

178180
protected PdfStructTreeRoot structTreeRoot;
@@ -334,6 +336,7 @@ protected void setXmpMetadata(byte[] xmpMetadata) {
334336
* @throws XMPException on serialization errors
335337
*/
336338
public void setXmpMetadata(XMPMeta xmpMeta, SerializeOptions serializeOptions) throws XMPException {
339+
this.serializeOptions = serializeOptions;
337340
setXmpMetadata(XMPMetaFactory.serializeToBuffer(xmpMeta, serializeOptions));
338341
}
339342

@@ -345,7 +348,6 @@ public void setXmpMetadata(XMPMeta xmpMeta, SerializeOptions serializeOptions) t
345348
* @throws XMPException on serialization errors
346349
*/
347350
public void setXmpMetadata(XMPMeta xmpMeta) throws XMPException {
348-
SerializeOptions serializeOptions = new SerializeOptions();
349351
serializeOptions.setPadding(2000);
350352
setXmpMetadata(xmpMeta, serializeOptions);
351353
}
@@ -1869,6 +1871,24 @@ public SequenceId getDocumentIdWrapper() {
18691871
return documentId;
18701872
}
18711873

1874+
/**
1875+
* Sets a persistent XMP metadata serialization options.
1876+
*
1877+
* @param serializeOptions serialize options
1878+
*/
1879+
public void setSerializeOptions(SerializeOptions serializeOptions) {
1880+
this.serializeOptions = serializeOptions;
1881+
}
1882+
1883+
/**
1884+
* Gets a persistent XMP metadata serialization options.
1885+
*
1886+
* @return serialize options
1887+
*/
1888+
public SerializeOptions getSerializeOptions() {
1889+
return this.serializeOptions;
1890+
}
1891+
18721892
/**
18731893
* Gets list of indirect references.
18741894
*

kernel/src/main/java/com/itextpdf/kernel/xmp/impl/XMPSerializerRDF.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,9 @@ private void declareNamespace(String prefix, String namespace, Set<String> usedP
909909
return;
910910
}
911911
}
912-
912+
if (prefix.isEmpty()) {
913+
return;
914+
}
913915
if (!usedPrefixes.contains(prefix))
914916
{
915917
writeNewline();

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ This file is part of the iText (R) project.
5858
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
5959
import com.itextpdf.kernel.pdf.xobject.PdfImageXObject;
6060
import com.itextpdf.kernel.utils.CompareTool;
61+
import com.itextpdf.kernel.xmp.options.SerializeOptions;
6162
import com.itextpdf.test.ExtendedITextTest;
6263
import com.itextpdf.test.LogLevelConstants;
6364
import com.itextpdf.test.annotations.LogMessage;
@@ -606,6 +607,14 @@ public void rootCannotBeReferenceFromTrailerTest() throws IOException {
606607
Assert.assertEquals(KernelExceptionMessageConstant.CORRUPTED_ROOT_ENTRY_IN_TRAILER, e.getMessage());
607608
}
608609

610+
@Test
611+
public void setSerializeOptionsTest() {
612+
PdfDocument document = new PdfDocument(new PdfWriter(new ByteArrayOutputStream()));
613+
SerializeOptions options = new SerializeOptions().setUseCanonicalFormat(true);
614+
document.setSerializeOptions(options);
615+
Assert.assertEquals(options, document.getSerializeOptions());
616+
}
617+
609618
private static class IgnoreTagStructurePdfDocument extends PdfDocument {
610619

611620
IgnoreTagStructurePdfDocument(PdfReader reader) {

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,17 @@ This file is part of the iText (R) project.
4343
package com.itextpdf.pdfa;
4444

4545
import com.itextpdf.kernel.pdf.PdfAConformanceLevel;
46+
import com.itextpdf.kernel.pdf.PdfDocument;
4647
import com.itextpdf.kernel.pdf.PdfOutputIntent;
48+
import com.itextpdf.kernel.pdf.PdfReader;
4749
import com.itextpdf.kernel.pdf.PdfWriter;
4850
import com.itextpdf.kernel.utils.CompareTool;
51+
import com.itextpdf.kernel.xmp.XMPConst;
52+
import com.itextpdf.kernel.xmp.XMPException;
53+
import com.itextpdf.kernel.xmp.XMPMeta;
54+
import com.itextpdf.kernel.xmp.XMPMetaFactory;
55+
import com.itextpdf.kernel.xmp.options.PropertyOptions;
56+
import com.itextpdf.kernel.xmp.options.SerializeOptions;
4957
import com.itextpdf.test.ExtendedITextTest;
5058
import com.itextpdf.test.annotations.type.IntegrationTest;
5159
import java.io.FileInputStream;
@@ -108,4 +116,45 @@ public void keywordsInfoTestPdfA2b() throws IOException, InterruptedException {
108116
Assert.assertNull(ct.compareXmp(outFile, cmpFile, true));
109117
}
110118

119+
@Test
120+
public void saveAndReadDocumentWithCanonicalXmpMetadata() throws IOException, XMPException {
121+
String outFile = destinationFolder + "saveAndReadDocumentWithCanonicalXmpMetadata.pdf";
122+
String cmpFile = cmpFolder + "cmp_saveAndReadDocumentWithCanonicalXmpMetadata.pdf";
123+
PdfAConformanceLevel conformanceLevel = PdfAConformanceLevel.PDF_A_2B;
124+
PdfOutputIntent outputIntent;
125+
126+
try (InputStream is = new FileInputStream(sourceFolder + "sRGB Color Space Profile.icm")) {
127+
outputIntent = new PdfOutputIntent("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", is);
128+
}
129+
130+
try (PdfADocument doc = new PdfADocument(new PdfWriter(outFile), conformanceLevel, outputIntent)) {
131+
doc.addNewPage();
132+
XMPMeta xmp = XMPMetaFactory.create();
133+
xmp.setProperty(XMPConst.NS_PDFA_ID, XMPConst.PART, conformanceLevel.getPart(), new PropertyOptions().setSchemaNode(true));
134+
xmp.setProperty(XMPConst.NS_PDFA_ID, XMPConst.CONFORMANCE, conformanceLevel.getConformance(), new PropertyOptions().setSchemaNode(true));
135+
SerializeOptions options = new SerializeOptions().setUseCanonicalFormat(true).setUseCompactFormat(false);
136+
doc.setXmpMetadata(xmp, options);
137+
doc.setTagged();
138+
}
139+
// Closing document and reopening it to flush it XMP metadata ModifyDate
140+
try (PdfDocument doc = new PdfDocument(new PdfReader(outFile));
141+
PdfDocument cmpDoc = new PdfDocument(new PdfReader(cmpFile))) {
142+
byte[] rdf = doc.getXmpMetadata();
143+
byte[] expectedRdf = cmpDoc.getXmpMetadata();
144+
// Comparing angle brackets, since it's the main difference between canonical and compact format.
145+
Assert.assertEquals(count(expectedRdf, (byte)'<'), count(rdf, (byte)'<'));
146+
Assert.assertNull(new CompareTool().compareXmp(cmpFile, outFile, true));
147+
}
148+
}
149+
150+
private int count(byte[] array, byte b) {
151+
int counter = 0;
152+
for (byte each : array) {
153+
if (each == b) {
154+
counter++;
155+
}
156+
}
157+
return counter;
158+
}
159+
111160
}

0 commit comments

Comments
 (0)