@@ -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(); | |||
} |
@@ -183,4 +183,7 @@ public class PDFPage extends PDFResourceContext { | |||
put("Tabs", value); | |||
} | |||
public void setMetadata(PDFMetadata meta) { | |||
put("Metadata", meta.makeReference()); | |||
} | |||
} |
@@ -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) { |
@@ -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); | |||
} | |||
@@ -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()); | |||
} | |||
} | |||
@@ -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(); |
@@ -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) { |
@@ -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()); | |||
} | |||
} |
@@ -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> |
@@ -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> |