Browse Source

FOP-3127: Allow XMP at PDF page level

tags/2_9
Simon Steiner 1 year ago
parent
commit
b7eeb45bea

+ 2
- 1
fop-core/src/main/java/org/apache/fop/fo/extensions/xmp/AbstractMetadataElement.java View File

@@ -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();
}

+ 3
- 0
fop-core/src/main/java/org/apache/fop/pdf/PDFPage.java View File

@@ -183,4 +183,7 @@ public class PDFPage extends PDFResourceContext {
put("Tabs", value);
}

public void setMetadata(PDFMetadata meta) {
put("Metadata", meta.makeReference());
}
}

+ 11
- 0
fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java View File

@@ -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) {

+ 5
- 0
fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java View File

@@ -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);
}


+ 3
- 0
fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java View File

@@ -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());
}
}


+ 11
- 0
fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java View File

@@ -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();

+ 27
- 0
fop-core/src/main/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java View File

@@ -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) {

+ 119
- 0
fop-core/src/test/java/org/apache/fop/pdf/PDFPageXMPTestCase.java View File

@@ -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());
}
}

+ 38
- 0
fop-core/src/test/resources/org/apache/fop/pdf/PDFPageXMP.fo View File

@@ -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>

BIN
fop/lib/xmlgraphics-commons-svn-trunk.jar View File


+ 69
- 0
fop/test/layoutengine/standard-testcases/pdf-page-xmp.xml View File

@@ -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>

Loading…
Cancel
Save