diff options
author | Simon Steiner <ssteiner@apache.org> | 2023-04-04 11:07:14 +0100 |
---|---|---|
committer | Simon Steiner <ssteiner@apache.org> | 2023-04-04 11:07:14 +0100 |
commit | b7eeb45bea9fa9fbc462a338c84d65876da263c7 (patch) | |
tree | 6b20323d3b311932cdef591333deb8293f1e562d | |
parent | 45f68e349e3f870fee5d4604fcc8115938484c52 (diff) | |
download | xmlgraphics-fop-b7eeb45bea9fa9fbc462a338c84d65876da263c7.tar.gz xmlgraphics-fop-b7eeb45bea9fa9fbc462a338c84d65876da263c7.zip |
FOP-3127: Allow XMP at PDF page level
11 files changed, 288 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 diff --git a/fop/lib/xmlgraphics-commons-svn-trunk.jar b/fop/lib/xmlgraphics-commons-svn-trunk.jar Binary files differindex 74ca9a7a4..8c9b59232 100644 --- a/fop/lib/xmlgraphics-commons-svn-trunk.jar +++ b/fop/lib/xmlgraphics-commons-svn-trunk.jar diff --git a/fop/test/layoutengine/standard-testcases/pdf-page-xmp.xml b/fop/test/layoutengine/standard-testcases/pdf-page-xmp.xml new file mode 100644 index 000000000..922ef27d5 --- /dev/null +++ b/fop/test/layoutengine/standard-testcases/pdf-page-xmp.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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$ --> +<testcase> + <info> + <p> + This test checks the PDF dictionary extensions. + </p> + </info> + <fo> + <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> + </fo> + <if-checks xmlns:if="http://xmlgraphics.apache.org/fop/intermediate" xmlns:pdf="apache:fop:extensions:pdf" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:abc="http://www.abc.de/abc/"> + <eval expected="val" xpath="//if:page[@name=1]/if:page-header/pdf:page//rdf:Description[1]/@abc:def"/> + </if-checks> +</testcase> |