From: Simon Steiner Date: Tue, 4 Apr 2023 10:07:14 +0000 (+0100) Subject: FOP-3127: Allow XMP at PDF page level X-Git-Tag: 2_9~22 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b7eeb45bea9fa9fbc462a338c84d65876da263c7;p=xmlgraphics-fop.git FOP-3127: Allow XMP at PDF page level --- 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 properties; private List 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 collections = new Stack(); 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 = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "split\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + ""; + + @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 @@ + + + + + + + + + + + + + + + + + + split + + + + + + + + + + + + + + + SLIDE 1 + + + \ No newline at end of file diff --git a/fop/lib/xmlgraphics-commons-svn-trunk.jar b/fop/lib/xmlgraphics-commons-svn-trunk.jar index 74ca9a7a4..8c9b59232 100644 Binary files a/fop/lib/xmlgraphics-commons-svn-trunk.jar and b/fop/lib/xmlgraphics-commons-svn-trunk.jar differ 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 @@ + + + + + +

+ This test checks the PDF dictionary extensions. +

+
+ + + + + + + + + + + + + + + + + + + split + + + + + + + + + + + + + + + SLIDE 1 + + + + + + + +