aboutsummaryrefslogtreecommitdiffstats
path: root/fop-core
diff options
context:
space:
mode:
authorSimon Steiner <ssteiner@apache.org>2023-04-04 11:07:14 +0100
committerSimon Steiner <ssteiner@apache.org>2023-04-04 11:07:14 +0100
commitb7eeb45bea9fa9fbc462a338c84d65876da263c7 (patch)
tree6b20323d3b311932cdef591333deb8293f1e562d /fop-core
parent45f68e349e3f870fee5d4604fcc8115938484c52 (diff)
downloadxmlgraphics-fop-b7eeb45bea9fa9fbc462a338c84d65876da263c7.tar.gz
xmlgraphics-fop-b7eeb45bea9fa9fbc462a338c84d65876da263c7.zip
FOP-3127: Allow XMP at PDF page level
Diffstat (limited to 'fop-core')
-rw-r--r--fop-core/src/main/java/org/apache/fop/fo/extensions/xmp/AbstractMetadataElement.java3
-rw-r--r--fop-core/src/main/java/org/apache/fop/pdf/PDFPage.java3
-rw-r--r--fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java11
-rw-r--r--fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java5
-rw-r--r--fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java3
-rw-r--r--fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java11
-rw-r--r--fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java27
-rw-r--r--fop-core/src/test/java/org/apache/fop/pdf/PDFPageXMPTestCase.java119
-rw-r--r--fop-core/src/test/resources/org/apache/fop/pdf/PDFPageXMP.fo38
9 files changed, 219 insertions, 1 deletions
diff --git a/fop-core/src/main/java/org/apache/fop/fo/extensions/xmp/AbstractMetadataElement.java b/fop-core/src/main/java/org/apache/fop/fo/extensions/xmp/AbstractMetadataElement.java
index 8004be01b..c2af237c0 100644
--- a/fop-core/src/main/java/org/apache/fop/fo/extensions/xmp/AbstractMetadataElement.java
+++ b/fop-core/src/main/java/org/apache/fop/fo/extensions/xmp/AbstractMetadataElement.java
@@ -24,6 +24,7 @@ import org.apache.xmlgraphics.xmp.Metadata;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.render.pdf.extensions.PDFPageElement;
import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
@@ -51,7 +52,7 @@ public abstract class AbstractMetadataElement extends FONode implements ObjectBu
/** {@inheritDoc} */
public ExtensionAttachment getExtensionAttachment() {
- if (parent instanceof FObj) {
+ if (parent instanceof FObj || parent instanceof PDFPageElement) {
if (attachment == null) {
attachment = new XMPMetadata();
}
diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFPage.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFPage.java
index 156bf0354..8950d373b 100644
--- a/fop-core/src/main/java/org/apache/fop/pdf/PDFPage.java
+++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFPage.java
@@ -183,4 +183,7 @@ public class PDFPage extends PDFResourceContext {
put("Tabs", value);
}
+ public void setMetadata(PDFMetadata meta) {
+ put("Metadata", meta.makeReference());
+ }
}
diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
index 1f383a29e..f2cf5beff 100644
--- a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
+++ b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
@@ -47,6 +47,7 @@ import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
import org.apache.fop.accessibility.Accessibility;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.io.InternalResourceResolver;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.extensions.xmp.XMPMetadata;
import org.apache.fop.pdf.PDFAMode;
import org.apache.fop.pdf.PDFArray;
@@ -467,6 +468,7 @@ class PDFRenderingUtil {
assert extension instanceof PDFPageExtension;
if (((PDFPageExtension) extension).matchesPageNumber(currentPage.getPageIndex() + 1)) {
augmentDictionary(currentPage, extension);
+ renderExtension(currentPage, extension.getExtension());
}
} else if (type == PDFDictionaryType.Info) {
PDFInfo info = pdfDoc.getInfo();
@@ -490,6 +492,15 @@ class PDFRenderingUtil {
}
}
+ private void renderExtension(PDFPage currentPage, ExtensionAttachment extension) {
+ if (extension instanceof XMPMetadata) {
+ XMPMetadata metadata = (XMPMetadata) extension;
+ Metadata docXMP = metadata.getMetadata();
+ PDFMetadata pdfMetadata = pdfDoc.getFactory().makeMetadata(docXMP, metadata.isReadOnly());
+ currentPage.setMetadata(pdfMetadata);
+ }
+ }
+
private PDFDictionary augmentDictionary(PDFDictionary dictionary, PDFDictionaryExtension extension) {
for (PDFCollectionEntryExtension entry : extension.getEntries()) {
if (entry instanceof PDFDictionaryExtension) {
diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java b/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java
index e275bfbdc..9ea179337 100644
--- a/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java
+++ b/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java
@@ -23,6 +23,8 @@ import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
+import org.apache.xmlgraphics.util.XMLizable;
+
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.util.GenerationHelperContentHandler;
@@ -71,6 +73,9 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment {
for (PDFCollectionEntryExtension entry : dictionary.getEntries()) {
toSAX(handler, entry);
}
+ if (extension.getExtension() != null) {
+ ((XMLizable) extension.getExtension()).toSAX(handler);
+ }
handler.endElement(CATEGORY, ln, qn);
}
diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java b/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java
index d7b155e23..50fd319af 100644
--- a/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java
+++ b/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java
@@ -26,6 +26,7 @@ import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.fo.extensions.xmp.XMPMetaElement;
// CSOFF: LineLengthCheck
@@ -132,6 +133,8 @@ public class PDFDictionaryElement extends PDFCollectionEntryElement {
} else if (child instanceof PDFCollectionEntryElement) {
PDFCollectionEntryExtension entry = ((PDFCollectionEntryElement) child).getExtension();
extension.addEntry(entry);
+ } else if (child instanceof XMPMetaElement) {
+ extension.setExtension(child.getExtensionAttachment());
}
}
diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java b/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java
index d1367413e..8c96ef110 100644
--- a/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java
+++ b/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java
@@ -22,6 +22,8 @@ package org.apache.fop.render.pdf.extensions;
import java.util.List;
import java.util.Map;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
// CSOFF: LineLengthCheck
public class PDFDictionaryExtension extends PDFCollectionExtension {
@@ -34,6 +36,7 @@ public class PDFDictionaryExtension extends PDFCollectionExtension {
private PDFDictionaryType dictionaryType;
private Map<String, String> properties;
private List<PDFCollectionEntryExtension> entries;
+ private ExtensionAttachment extension;
PDFDictionaryExtension() {
this(PDFDictionaryType.Dictionary);
@@ -81,6 +84,14 @@ public class PDFDictionaryExtension extends PDFCollectionExtension {
return entries;
}
+ public void setExtension(ExtensionAttachment entry) {
+ extension = entry;
+ }
+
+ public ExtensionAttachment getExtension() {
+ return extension;
+ }
+
public PDFCollectionEntryExtension findEntry(String key) {
for (PDFCollectionEntryExtension entry : entries) {
String entryKey = entry.getKey();
diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java b/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
index 9579acc84..3aa8b4744 100644
--- a/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
+++ b/fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
@@ -29,6 +29,11 @@ import org.xml.sax.helpers.DefaultHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.xmp.XMPConstants;
+import org.apache.xmlgraphics.xmp.XMPHandler;
+
+import org.apache.fop.fo.extensions.xmp.XMPContentHandlerFactory;
+import org.apache.fop.fo.extensions.xmp.XMPMetadata;
import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
@@ -50,6 +55,7 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle
private Stack<PDFCollectionExtension> collections = new Stack<PDFCollectionExtension>();
private boolean captureContent;
private StringBuffer characters;
+ private XMPHandler xmpHandler;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
@@ -141,6 +147,11 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle
} else {
throw new SAXException("Unhandled element " + localName + " in namespace: " + uri);
}
+ } else if (XMPConstants.XMP_NAMESPACE.equals(uri) || xmpHandler != null) {
+ if (xmpHandler == null) {
+ xmpHandler = (XMPHandler) new XMPContentHandlerFactory().createContentHandler();
+ }
+ xmpHandler.startElement(uri, localName, qName, attributes);
} else {
log.warn("Unhandled element " + localName + " in namespace: " + uri);
}
@@ -154,11 +165,15 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle
}
characters.append(data, start, length);
}
+ if (xmpHandler != null) {
+ xmpHandler.characters(data, start, length);
+ }
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
+ setExtension();
if (PDFEmbeddedFileAttachment.ELEMENT.equals(localName)) {
String name = lastAttributes.getValue("filename");
String src = lastAttributes.getValue("src");
@@ -209,9 +224,21 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle
}
}
}
+ if (xmpHandler != null) {
+ xmpHandler.endElement(uri, localName, qName);
+ }
captureContent = false;
}
+ private void setExtension() {
+ if (xmpHandler != null) {
+ PDFPageExtension pdfPageExtension = (PDFPageExtension) collections.peek();
+ XMPMetadata wrapper = new XMPMetadata(xmpHandler.getMetadata());
+ pdfPageExtension.setExtension(wrapper);
+ xmpHandler = null;
+ }
+ }
+
@Override
public void endDocument() throws SAXException {
if (listener != null) {
diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFPageXMPTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFPageXMPTestCase.java
new file mode 100644
index 000000000..870e85998
--- /dev/null
+++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFPageXMPTestCase.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+package org.apache.fop.pdf;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.Fop;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.render.intermediate.IFDocumentHandler;
+import org.apache.fop.render.intermediate.IFParser;
+import org.apache.fop.render.intermediate.IFSerializer;
+import org.apache.fop.render.intermediate.IFUtil;
+
+public class PDFPageXMPTestCase {
+ private static final String XMP = "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n"
+ + "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n"
+ + "<rdf:Description xmlns:abc=\"http://www.abc.de/abc/\" abc:def=\"val\" rdf:about=\"\"/>\n"
+ + "<rdf:Description xmlns:pdfaExtension=\"http://www.aiim.org/pdfa/ns/extension/\" rdf:about=\"\">\n"
+ + "<pdfaExtension:schemas>\n"
+ + "<rdf:Bag>\n"
+ + "<rdf:li rdf:parseType=\"Resource\">\n"
+ + "<pdfaSchema:property xmlns:pdfaSchema=\"http://www.aiim.org/pdfa/ns/schema#\">\n"
+ + "<rdf:Seq>\n"
+ + "<rdf:li rdf:parseType=\"Resource\">\n"
+ + "<pdfaProperty:name xmlns:pdfaProperty=\"http://www.aiim.org/pdfa/ns/property#\">split</pdfaProperty:name>\n"
+ + "</rdf:li>\n"
+ + "</rdf:Seq>\n"
+ + "</pdfaSchema:property>\n"
+ + "</rdf:li>\n"
+ + "</rdf:Bag>\n"
+ + "</pdfaExtension:schemas>\n"
+ + "</rdf:Description>\n"
+ + "</rdf:RDF>\n"
+ + "</x:xmpmeta>";
+
+ @Test
+ public void textFO() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ foToOutput(out, MimeConstants.MIME_PDF);
+ Assert.assertTrue(out.toString().replace("\r", "").contains(XMP));
+ }
+
+ @Test
+ public void textIF() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ foToOutput(out, MimeConstants.MIME_FOP_IF);
+ out = iFToPDF(new ByteArrayInputStream(out.toByteArray()));
+ Assert.assertTrue(out.toString().replace("\r", "").contains(XMP));
+ }
+
+ private ByteArrayOutputStream iFToPDF(InputStream is) throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ FOUserAgent userAgent = getFopFactory().newFOUserAgent();
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ Source src = new StreamSource(is);
+ IFDocumentHandler documentHandler
+ = userAgent.getRendererFactory().createDocumentHandler(userAgent, MimeConstants.MIME_PDF);
+ documentHandler.setResult(new StreamResult(out));
+ IFUtil.setupFonts(documentHandler);
+ IFParser parser = new IFParser();
+ Result res = new SAXResult(parser.getContentHandler(documentHandler, userAgent));
+ transformer.transform(src, res);
+ return out;
+ }
+
+ private void foToOutput(ByteArrayOutputStream out, String mimeFopIf) throws Exception {
+ FopFactory fopFactory = getFopFactory();
+ FOUserAgent userAgent = fopFactory.newFOUserAgent();
+ if (mimeFopIf.equals(MimeConstants.MIME_FOP_IF)) {
+ IFSerializer serializer = new IFSerializer(new IFContext(userAgent));
+ IFDocumentHandler targetHandler
+ = userAgent.getRendererFactory().createDocumentHandler(userAgent, MimeConstants.MIME_PDF);
+ serializer.mimicDocumentHandler(targetHandler);
+ userAgent.setDocumentHandlerOverride(serializer);
+ }
+ Fop fop = fopFactory.newFop(mimeFopIf, userAgent, out);
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ Source src = new StreamSource(PDFPageXMPTestCase.class.getResource("PDFPageXMP.fo").openStream());
+ Result res = new SAXResult(fop.getDefaultHandler());
+ transformer.transform(src, res);
+ }
+
+ private FopFactory getFopFactory() {
+ return FopFactory.newInstance(new File(".").toURI());
+ }
+}
diff --git a/fop-core/src/test/resources/org/apache/fop/pdf/PDFPageXMP.fo b/fop-core/src/test/resources/org/apache/fop/pdf/PDFPageXMP.fo
new file mode 100644
index 000000000..6261862e8
--- /dev/null
+++ b/fop-core/src/test/resources/org/apache/fop/pdf/PDFPageXMP.fo
@@ -0,0 +1,38 @@
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:pdf="http://xmlgraphics.apache.org/fop/extensions/pdf">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="simple">
+ <fo:region-body/>
+ <fo:region-before/>
+ <fo:region-after/>
+ <pdf:page page-numbers="*">
+ <x:xmpmeta xmlns:x="adobe:ns:meta/">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:abc="http://www.abc.de/abc/">
+ <rdf:Description rdf:about="" abc:def="val"/>
+ <rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/"
+ xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#"
+ xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">
+ <pdfaExtension:schemas>
+ <rdf:Bag>
+ <rdf:li rdf:parseType="Resource">
+ <pdfaSchema:property>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <pdfaProperty:name>split</pdfaProperty:name>
+ </rdf:li>
+ </rdf:Seq>
+ </pdfaSchema:property>
+ </rdf:li>
+ </rdf:Bag>
+ </pdfaExtension:schemas>
+ </rdf:Description>
+ </rdf:RDF>
+ </x:xmpmeta>
+ </pdf:page>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="simple">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>SLIDE 1</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root> \ No newline at end of file