/**
* @return the default DOMImplementation when no specialized DOM is necessary.
*/
- protected DOMImplementation getDefaultDOMImplementation() {
+ public static DOMImplementation getDefaultDOMImplementation() {
DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
fact.setNamespaceAware(true);
fact.setValidating(false);
addElementMapping("org.apache.fop.fo.extensions.svg.SVGElementMapping");
addElementMapping("org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping");
addElementMapping("org.apache.fop.fo.extensions.ExtensionElementMapping");
+ addElementMapping("org.apache.fop.fo.extensions.xmp.XMPElementMapping");
+ addElementMapping("org.apache.fop.fo.extensions.xmp.RDFElementMapping");
addElementMapping("org.apache.fop.render.ps.extensions.PSExtensionElementMapping");
// add mappings from available services
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import org.apache.fop.fo.extensions.svg.SVGElementMapping;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.util.CharUtilities;
+import org.apache.fop.util.ContentHandlerFactory;
/**
* Base class for nodes in the XML tree
return null;
}
+ /**
+ * This method is overridden by extension elements and allows the extension element to return
+ * a ContentHandlerFactory. This factory can create ContentHandler implementations that handle
+ * foreign XML content by either building up a specific DOM, a Java object or something else.
+ * @return the ContentHandlerFactory or null if not applicable
+ */
+ public ContentHandlerFactory getContentHandlerFactory() {
+ return null;
+ }
+
}
import org.apache.fop.fo.ElementMapping.Maker;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.image.ImageFactory;
+import org.apache.fop.util.ContentHandlerFactory;
+import org.apache.fop.util.ContentHandlerFactory.ObjectSource;
+import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
*/
protected Root rootFObj = null;
- /**
- * Current formatting object being handled
- */
- protected FONode currentFObj = null;
-
- /**
- * Current propertyList for the node being handled.
- */
- protected PropertyList currentPropertyList;
-
+ /** Main DefaultHandler that handles the FO namespace. */
+ protected MainFOHandler mainFOHandler;
+
+ /** Current delegate ContentHandler to receive the SAX events */
+ protected ContentHandler delegate;
+
/**
* The class that handles formatting and rendering to a stream
* (mark-fop@inomial.com)
private boolean used = false;
+ private int depth;
+
/**
* FOTreeBuilder constructor
* @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
* @see org.xml.sax.ContentHandler#characters(char[], int, int)
*/
public void characters(char[] data, int start, int length)
- throws FOPException {
- if (currentFObj != null) {
- currentFObj.addCharacters(data, start, start + length,
- currentPropertyList, getEffectiveLocator());
- }
+ throws SAXException {
+ delegate.characters(data, start, length);
}
/**
log.debug("Building formatting object tree");
}
foEventHandler.startDocument();
+ this.mainFOHandler = new MainFOHandler();
+ this.mainFOHandler.startDocument();
+ this.delegate = this.mainFOHandler;
}
/**
* @see org.xml.sax.ContentHandler#endDocument()
*/
public void endDocument() throws SAXException {
+ this.delegate.endDocument();
rootFObj = null;
- currentFObj = null;
if (log.isDebugEnabled()) {
log.debug("Parsing of document complete");
}
*/
public void startElement(String namespaceURI, String localName, String rawName,
Attributes attlist) throws SAXException {
-
- /* the node found in the FO document */
- FONode foNode;
- PropertyList propertyList;
-
- // Check to ensure first node encountered is an fo:root
- if (rootFObj == null) {
- if (!namespaceURI.equals(FOElementMapping.URI)
- || !localName.equals("root")) {
- throw new SAXException(new ValidationException(
- "Error: First element must be the fo:root formatting object. Found "
- + FONode.getNodeString(namespaceURI, localName) + " instead."
- + " Please make sure you're producing a valid XSL-FO document."));
- }
- } else { // check that incoming node is valid for currentFObj
- if (namespaceURI.equals(FOElementMapping.URI)) {
- // currently no fox: elements to validate
- // || namespaceURI.equals(ExtensionElementMapping.URI) */) {
- try {
- currentFObj.validateChildNode(locator, namespaceURI, localName);
- } catch (ValidationException e) {
- throw e;
- }
- }
- }
-
- ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName);
-// log.debug("found a " + fobjMaker.toString());
-
- try {
- foNode = fobjMaker.make(currentFObj);
- propertyList = foNode.createPropertyList(currentPropertyList, foEventHandler);
- foNode.processNode(localName, getEffectiveLocator(), attlist, propertyList);
- foNode.startOfNode();
- } catch (IllegalArgumentException e) {
- throw new SAXException(e);
- }
-
- if (rootFObj == null) {
- rootFObj = (Root) foNode;
- rootFObj.setFOEventHandler(foEventHandler);
- } else {
- currentFObj.addChildNode(foNode);
- }
-
- currentFObj = foNode;
- if (propertyList != null) {
- currentPropertyList = propertyList;
- }
+ this.depth++;
+ delegate.startElement(namespaceURI, localName, rawName, attlist);
}
/**
* @see org.xml.sax.ContentHandler#endElement(String, String, String)
*/
public void endElement(String uri, String localName, String rawName)
- throws FOPException {
- if (currentFObj == null) {
- throw new IllegalStateException(
- "endElement() called for " + rawName + " where there is no current element.");
- } else if (!currentFObj.getLocalName().equals(localName)
- || !currentFObj.getNamespaceURI().equals(uri)) {
- log.warn("Mismatch: " + currentFObj.getLocalName()
- + " (" + currentFObj.getNamespaceURI()
- + ") vs. " + localName + " (" + uri + ")");
- }
- currentFObj.endOfNode();
-
- if (currentPropertyList.getFObj() == currentFObj) {
- currentPropertyList = currentPropertyList.getParentPropertyList();
- }
- if (currentFObj.getParent() == null) {
- log.debug("endElement for top-level " + currentFObj.getName());
+ throws SAXException {
+ this.delegate.endElement(uri, localName, rawName);
+ this.depth--;
+ if (depth == 0) {
+ if (delegate != mainFOHandler) {
+ //Return from sub-handler back to main handler
+ delegate.endDocument();
+ delegate = mainFOHandler;
+ delegate.endElement(uri, localName, rawName);
+ }
}
- currentFObj = currentFObj.getParent();
}
/**
}
}
+ /**
+ * Main DefaultHandler implementation which builds the FO tree.
+ */
+ private class MainFOHandler extends DefaultHandler {
+
+ /**
+ * Current formatting object being handled
+ */
+ protected FONode currentFObj = null;
+
+ /**
+ * Current propertyList for the node being handled.
+ */
+ protected PropertyList currentPropertyList;
+
+ /**
+ * SAX Handler for the start of an element
+ * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
+ */
+ public void startElement(String namespaceURI, String localName, String rawName,
+ Attributes attlist) throws SAXException {
+
+ /* the node found in the FO document */
+ FONode foNode;
+ PropertyList propertyList;
+
+ // Check to ensure first node encountered is an fo:root
+ if (rootFObj == null) {
+ if (!namespaceURI.equals(FOElementMapping.URI)
+ || !localName.equals("root")) {
+ throw new SAXException(new ValidationException(
+ "Error: First element must be the fo:root formatting object. Found "
+ + FONode.getNodeString(namespaceURI, localName) + " instead."
+ + " Please make sure you're producing a valid XSL-FO document."));
+ }
+ } else { // check that incoming node is valid for currentFObj
+ if (namespaceURI.equals(FOElementMapping.URI)) {
+ // currently no fox: elements to validate
+ // || namespaceURI.equals(ExtensionElementMapping.URI) */) {
+ try {
+ currentFObj.validateChildNode(locator, namespaceURI, localName);
+ } catch (ValidationException e) {
+ throw e;
+ }
+ }
+ }
+
+ ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName);
+
+ try {
+ foNode = fobjMaker.make(currentFObj);
+ propertyList = foNode.createPropertyList(currentPropertyList, foEventHandler);
+ foNode.processNode(localName, getEffectiveLocator(), attlist, propertyList);
+ foNode.startOfNode();
+ } catch (IllegalArgumentException e) {
+ throw new SAXException(e);
+ }
+
+ ContentHandlerFactory chFactory = foNode.getContentHandlerFactory();
+ if (chFactory != null) {
+ ContentHandler subHandler = chFactory.createContentHandler();
+ if (subHandler instanceof ObjectSource
+ && foNode instanceof ObjectBuiltListener) {
+ ((ObjectSource)subHandler).setObjectBuiltListener((ObjectBuiltListener)foNode);
+ }
+
+ subHandler.startDocument();
+ subHandler.startElement(namespaceURI, localName, rawName, attlist);
+ depth = 1;
+ delegate = subHandler;
+ }
+
+ if (rootFObj == null) {
+ rootFObj = (Root) foNode;
+ rootFObj.setFOEventHandler(foEventHandler);
+ } else {
+ currentFObj.addChildNode(foNode);
+ }
+
+ currentFObj = foNode;
+ if (propertyList != null) {
+ currentPropertyList = propertyList;
+ }
+ }
+
+ /**
+ * SAX Handler for the end of an element
+ * @see org.xml.sax.ContentHandler#endElement(String, String, String)
+ */
+ public void endElement(String uri, String localName, String rawName)
+ throws SAXException {
+ if (currentFObj == null) {
+ throw new IllegalStateException(
+ "endElement() called for " + rawName
+ + " where there is no current element.");
+ } else if (!currentFObj.getLocalName().equals(localName)
+ || !currentFObj.getNamespaceURI().equals(uri)) {
+ log.warn("Mismatch: " + currentFObj.getLocalName()
+ + " (" + currentFObj.getNamespaceURI()
+ + ") vs. " + localName + " (" + uri + ")");
+ }
+ currentFObj.endOfNode();
+
+ if (currentPropertyList.getFObj() == currentFObj) {
+ currentPropertyList = currentPropertyList.getParentPropertyList();
+ }
+ if (currentFObj.getParent() == null) {
+ log.debug("endElement for top-level " + currentFObj.getName());
+ }
+ currentFObj = currentFObj.getParent();
+ }
+
+ /**
+ * SAX Handler for characters
+ * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+ */
+ public void characters(char[] data, int start, int length)
+ throws FOPException {
+ if (currentFObj != null) {
+ currentFObj.addCharacters(data, start, start + length,
+ currentPropertyList, getEffectiveLocator());
+ }
+ }
+
+ public void endDocument() throws SAXException {
+ currentFObj = null;
+ }
+
+
+
+ }
+
}
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import org.apache.batik.dom.util.XMLSupport;
import org.apache.fop.apps.FOPException;
+import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.Attributes;
* Abstract class modelling generic, non-XSL-FO XML objects. Such objects are
* stored in a DOM.
*/
-public abstract class XMLObj extends FONode {
+public abstract class XMLObj extends FONode implements ObjectBuiltListener {
// temp reference for attributes
private Attributes attr = null;
this.doc = doc;
element = doc.createElementNS(getNamespaceURI(), name);
+ setAttributes(element, attr);
+ attr = null;
+ parent.appendChild(element);
+ }
+
+ private static void setAttributes(Element element, Attributes attr) {
for (int count = 0; count < attr.getLength(); count++) {
String rf = attr.getValue(count);
String qname = attr.getQName(count);
}
}
}
- attr = null;
- parent.appendChild(element);
}
-
+
/**
* Add the top-level element to the DOM document
* @param doc DOM document
*/
public void buildTopLevel(Document doc, Element svgRoot) {
// build up the info for the top level element
- for (int count = 0; count < attr.getLength(); count++) {
- String rf = attr.getValue(count);
- String qname = attr.getQName(count);
- int idx = qname.indexOf(":");
- if (idx == -1) {
- element.setAttribute(qname, rf);
- } else {
- String pref = qname.substring(0, idx);
- String tail = qname.substring(idx + 1);
- if (pref.equals("xmlns")) {
- ns.put(tail, rf);
- } else {
- element.setAttributeNS((String)ns.get(pref), tail, rf);
- }
- }
- }
+ setAttributes(element, attr);
}
/**
}
} catch (Exception e) {
+ //TODO this is ugly because there may be subsequent failures like NPEs
log.error("Error while trying to instantiate a DOM Document", e);
}
return doc;
element.appendChild(text);
}
+ /** @see org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener */
+ public void notifyObjectBuilt(Object obj) {
+ this.doc = (Document)obj;
+ }
+
}
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
+import org.apache.fop.util.ContentHandlerFactory;
+import org.apache.fop.util.DOMBuilderContentHandlerFactory;
import org.apache.batik.dom.svg.SVGOMDocument;
import org.apache.batik.dom.svg.SVGOMElement;
super(parent);
}
+ /**
+ * @see org.apache.fop.fo.FONode#getContentHandlerFactory()
+ */
+ public ContentHandlerFactory getContentHandlerFactory() {
+ return new DOMBuilderContentHandlerFactory(getNamespaceURI(),
+ SVGDOMImplementation.getDOMImplementation());
+ }
+
/**
* @see org.apache.fop.fo.FONode#processNode
*/
return 100;
}
}
+
}
--- /dev/null
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fo.extensions.xmp;
+
+import org.apache.fop.fo.ElementMapping;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.XMLObj;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.util.ContentHandlerFactory;
+import org.apache.fop.util.DOMBuilderContentHandlerFactory;
+
+/**
+ * Represents the top-level "RDF" element used by XMP metadata.
+ */
+public class RDFElement extends XMLObj {
+
+ private XMPMetadata attachment;
+
+ /**
+ * Main constructor.
+ * @param parent the parent formatting object
+ */
+ public RDFElement(FONode parent) {
+ super(parent);
+ }
+
+ /** @see org.apache.fop.fo.FONode#getNormalNamespacePrefix() */
+ public String getNormalNamespacePrefix() {
+ return "rdf";
+ }
+
+ /** @see org.apache.fop.fo.FONode#getNamespaceURI() */
+ public String getNamespaceURI() {
+ return XMPConstants.RDF_NAMESPACE;
+ }
+
+ /**
+ * @see org.apache.fop.fo.FONode#getContentHandlerFactory()
+ */
+ public ContentHandlerFactory getContentHandlerFactory() {
+ return new DOMBuilderContentHandlerFactory(getNamespaceURI(),
+ ElementMapping.getDefaultDOMImplementation());
+ }
+
+ /** @see org.apache.fop.fo.FONode#getExtensionAttachment() */
+ public ExtensionAttachment getExtensionAttachment() {
+ if (parent instanceof FObj) {
+ if (attachment == null) {
+ attachment = new XMPMetadata(doc);
+ }
+ return attachment;
+ } else {
+ return super.getExtensionAttachment();
+ }
+ }
+
+ /**
+ * @see org.apache.fop.fo.XMLObj#notifyObjectBuilt(java.lang.Object)
+ */
+ public void notifyObjectBuilt(Object obj) {
+ super.notifyObjectBuilt(obj);
+ attachment.setDocument(doc);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fo.extensions.xmp;
+
+import java.util.HashMap;
+
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.ElementMapping;
+
+import org.w3c.dom.DOMImplementation;
+
+/**
+ * Setup the element mapping for XMP metadata.
+ */
+public class RDFElementMapping extends ElementMapping {
+
+ /** Main constructor. */
+ public RDFElementMapping() {
+ namespaceURI = XMPConstants.RDF_NAMESPACE;
+ }
+
+ /** @see org.apache.fop.fo.ElementMapping#getDOMImplementation() */
+ public DOMImplementation getDOMImplementation() {
+ return getDefaultDOMImplementation();
+ }
+
+ /** @see org.apache.fop.fo.ElementMapping#initialize() */
+ protected void initialize() {
+ if (foObjs == null) {
+ foObjs = new HashMap();
+ foObjs.put("RDF", new RDFElementMaker());
+ }
+ }
+
+ static class RDFElementMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new RDFElement(parent);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fo.extensions.xmp;
+
+/**
+ * Constants used in XMP metadata.
+ */
+public interface XMPConstants {
+
+ /** Namespace URI for XMP */
+ String XMP_NAMESPACE = "adobe:ns:meta/";
+
+ /** Namespace URI for RDF */
+ String RDF_NAMESPACE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+
+ /** Namespace URI for Dublin Core */
+ String DUBLIN_CORE_NAMESPACE = "http://purl.org/dc/elements/1.1/";
+
+ /** Namespace URI for the XMP Basic Schema */
+ String XMP_BASIC_NAMESPACE = "http://ns.adobe.com/xap/1.0/";
+
+}
--- /dev/null
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fo.extensions.xmp;
+
+import java.util.HashMap;
+
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.ElementMapping;
+
+import org.w3c.dom.DOMImplementation;
+
+/**
+ * Setup the element mapping for XMP metadata.
+ */
+public class XMPElementMapping extends ElementMapping {
+
+ /** Main constructor. */
+ public XMPElementMapping() {
+ namespaceURI = XMPConstants.XMP_NAMESPACE;
+ }
+
+ /** @see org.apache.fop.fo.ElementMapping#getDOMImplementation() */
+ public DOMImplementation getDOMImplementation() {
+ return getDefaultDOMImplementation();
+ }
+
+ /** @see org.apache.fop.fo.ElementMapping#initialize() */
+ protected void initialize() {
+ if (foObjs == null) {
+ foObjs = new HashMap();
+ foObjs.put("xmpmeta", new XMPMetaElementMaker());
+ }
+ }
+
+ static class XMPMetaElementMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new XMPMetaElement(parent);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fo.extensions.xmp;
+
+import org.apache.fop.fo.ElementMapping;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.XMLObj;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.util.ContentHandlerFactory;
+import org.apache.fop.util.DOMBuilderContentHandlerFactory;
+
+/**
+ * Represents the top-level "xmpmeta" element used by XMP metadata.
+ */
+public class XMPMetaElement extends XMLObj {
+
+ private XMPMetadata attachment;
+
+ /**
+ * Main constructor.
+ * @param parent the parent formatting object
+ */
+ public XMPMetaElement(FONode parent) {
+ super(parent);
+ }
+
+ /** @see org.apache.fop.fo.FONode#getNormalNamespacePrefix() */
+ public String getNormalNamespacePrefix() {
+ return "x";
+ }
+
+ /** @see org.apache.fop.fo.FONode#getNamespaceURI() */
+ public String getNamespaceURI() {
+ return XMPConstants.XMP_NAMESPACE;
+ }
+
+ /**
+ * @see org.apache.fop.fo.FONode#getContentHandlerFactory()
+ */
+ public ContentHandlerFactory getContentHandlerFactory() {
+ return new DOMBuilderContentHandlerFactory(getNamespaceURI(),
+ ElementMapping.getDefaultDOMImplementation());
+ }
+
+ /** @see org.apache.fop.fo.FONode#getExtensionAttachment() */
+ public ExtensionAttachment getExtensionAttachment() {
+ if (parent instanceof FObj) {
+ if (attachment == null) {
+ attachment = new XMPMetadata(doc);
+ }
+ return attachment;
+ } else {
+ return super.getExtensionAttachment();
+ }
+ }
+
+ /**
+ * @see org.apache.fop.fo.XMLObj#notifyObjectBuilt(java.lang.Object)
+ */
+ public void notifyObjectBuilt(Object obj) {
+ super.notifyObjectBuilt(obj);
+ attachment.setDocument(doc);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fo.extensions.xmp;
+
+import java.io.Serializable;
+
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.util.DOM2SAX;
+import org.apache.fop.util.XMLizable;
+import org.w3c.dom.Document;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * This is the pass-through value object for the XMP metadata extension.
+ */
+public class XMPMetadata implements ExtensionAttachment, Serializable, XMLizable {
+
+ /** The category URI for this extension attachment. */
+ public static final String CATEGORY = XMPConstants.XMP_NAMESPACE;
+
+ private Document doc;
+ private boolean readOnly = true;
+
+ /**
+ * No-argument contructor.
+ */
+ public XMPMetadata() {
+ //nop
+ }
+
+ /**
+ * Default constructor.
+ * @param doc the DOM document containing the XMP metadata
+ */
+ public XMPMetadata(Document doc) {
+ this.doc = doc;
+ }
+
+ /** @return the DOM document containing the XMP metadata */
+ public Document getDocument() {
+ return this.doc;
+ }
+
+ /**
+ * Sets the DOM document containing the XMP metadata.
+ * @param document the DOM document
+ */
+ public void setDocument(Document document) {
+ this.doc = document;
+ }
+
+ /** @return true if the XMP metadata is marked read-only. */
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ /**
+ * Sets the flag that decides whether a metadata packet may be modified.
+ * @param readOnly true if the XMP metadata packet should be marked read-only.
+ */
+ public void setReadOnly(boolean readOnly) {
+ this.readOnly = readOnly;
+ }
+
+ /** @see org.apache.fop.fo.extensions.ExtensionAttachment#getCategory() */
+ public String getCategory() {
+ return CATEGORY;
+ }
+
+ /** @see org.apache.fop.util.XMLizable#toSAX(org.xml.sax.ContentHandler) */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ DOM2SAX.writeDocument(getDocument(), handler);
+ }
+
+}
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2004,2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* the version of PDF supported which is 1.4
*/
protected static final String PDF_VERSION = "1.4";
+
+ /** Integer constant to represent PDF 1.3 */
+ public static final int PDF_VERSION_1_3 = 3;
+ /** Integer constant to represent PDF 1.4 */
+ public static final int PDF_VERSION_1_4 = 4;
+
/**
* the encoding to use when converting strings to PDF commandos.
*/
*/
protected int xref;
+ /** Indicates what PDF version is active */
+ protected int pdfVersion = PDF_VERSION_1_4;
+
/**
* the /Root object
*/
this.info = getFactory().makeInfo(prod);
}
+ /**
+ * @return the integer representing the active PDF version (one of PDFDocument.PDF_VERSION_*)
+ */
+ public int getPDFVersion() {
+ return this.pdfVersion;
+ }
+
/**
* Returns the factory for PDF objects.
* @return PDFFactory the factory
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
+//W3C DOM
+import org.w3c.dom.Document;
+
// Apache libs
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
private PDFDocument document;
- private Log log = LogFactory.getLog("org.apache.fop.pdf");
+ private Log log = LogFactory.getLog(PDFFactory.class);
/**
* Creates a new PDFFactory.
public PDFRoot makeRoot(PDFPages pages) {
//Make a /Pages object. This object is written in the trailer.
PDFRoot pdfRoot = new PDFRoot(++this.document.objectcount, pages);
+ pdfRoot.setDocument(getDocument());
getDocument().addTrailerObject(pdfRoot);
return pdfRoot;
}
*/
public PDFPages makePages() {
PDFPages pdfPages = new PDFPages(++(this.document.objectcount));
+ pdfPages.setDocument(getDocument());
getDocument().addTrailerObject(pdfPages);
return pdfPages;
}
*/
public PDFResources makeResources() {
PDFResources pdfResources = new PDFResources(++this.document.objectcount);
+ pdfResources.setDocument(getDocument());
getDocument().addTrailerObject(pdfResources);
return pdfResources;
}
return pdfInfo;
}
+ /**
+ * Make a Metadata object.
+ * @param doc the DOM Document containing the XMP metadata.
+ * @param readOnly true if the metadata packet should be marked read-only
+ * @return the newly created Metadata object
+ */
+ public PDFMetadata makeMetadata(Document doc, boolean readOnly) {
+ PDFMetadata pdfMetadata = new PDFMetadata(doc, readOnly);
+ getDocument().registerObject(pdfMetadata);
+ return pdfMetadata;
+ }
+
/**
* Make a /Page object. The page is assigned an object number immediately
* so references can already be made. The page must be added to the
return obj;
}
-
-
}
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
public static final String TIFF_FILTER = "tiff";
/** Key for the filter used for fonts */
public static final String FONT_FILTER = "font";
+ /** Key for the filter used for metadata */
+ public static final String METADATA_FILTER = "metadata";
private List filters = new java.util.ArrayList();
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
this.producer = producer;
}
+ /** @return the creator of the document or null if not set */
+ public String getCreator() {
+ return this.creator;
+ }
+
/**
* set the creator string
*
this.title = t;
}
+ /** @return the author of the document or null if not set */
+ public String getAuthor() {
+ return this.author;
+ }
+
/**
* set the author string
*
this.author = a;
}
+ /** @return the subject of the document or null if not set */
+ public String getSubject() {
+ return this.subject;
+ }
+
/**
* set the subject string
*
this.subject = s;
}
+ /** @return the keywords for the document or null if not set */
+ public String getKeyword() {
+ return this.keywords;
+ }
+
/**
* set the keywords string
*
--- /dev/null
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.IOException;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.ElementMapping;
+import org.apache.fop.fo.extensions.xmp.XMPConstants;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Special PDFStream for Metadata.
+ * @since PDF 1.4
+ */
+public class PDFMetadata extends PDFStream {
+
+ private Document xmpMetadata;
+ private boolean readOnly = true;
+
+ /** @see org.apache.fop.pdf.PDFObject#PDFObject() */
+ public PDFMetadata(Document xmp, boolean readOnly) {
+ super();
+ if (xmp == null) {
+ throw new NullPointerException(
+ "DOM Document representing the metadata must no be null");
+ }
+ this.xmpMetadata = xmp;
+ this.readOnly = readOnly;
+ }
+
+ /** @see org.apache.fop.pdf.AbstractPDFStream#setupFilterList() */
+ protected void setupFilterList() {
+ if (!getFilterList().isInitialized()) {
+ getFilterList().addDefaultFilters(
+ getDocumentSafely().getFilterMap(),
+ PDFFilterList.METADATA_FILTER);
+ }
+ super.setupFilterList();
+ }
+
+ /** @see org.apache.fop.pdf.AbstractPDFStream#allowEncryption() */
+ protected boolean allowEncryption() {
+ return false; //XMP metadata packet must be scannable by non PDF-compatible readers
+ }
+
+ /**
+ * overload the base object method so we don't have to copy
+ * byte arrays around so much
+ * @see org.apache.fop.pdf.PDFObject#output(OutputStream)
+ */
+ protected int output(java.io.OutputStream stream)
+ throws java.io.IOException {
+ int length = super.output(stream);
+ this.xmpMetadata = null; //Release DOM when it's not used anymore
+ return length;
+ }
+
+ /** @see org.apache.fop.pdf.AbstractPDFStream#outputRawStreamData(java.io.OutputStream) */
+ protected void outputRawStreamData(OutputStream out) throws IOException {
+ final String encoding = "UTF-8";
+ out.write("<?xpacket begin=\"\uFEFF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n"
+ .getBytes(encoding));
+ try {
+ TransformerFactory tFactory = TransformerFactory.newInstance();
+ Transformer transformer = tFactory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
+ transformer.setOutputProperty(OutputKeys.INDENT, "no");
+ DOMSource src = new DOMSource(this.xmpMetadata);
+ StreamResult res = new StreamResult(out);
+ transformer.transform(src, res);
+ } catch (TransformerConfigurationException e) {
+ throw new IOException("Error setting up Transformer for XMP stream serialization: "
+ + e.getMessage());
+ } catch (TransformerException e) {
+ throw new IOException("Error while serializing XMP stream: "
+ + e.getMessage());
+ }
+ if (readOnly) {
+ out.write("\n<?xpacket end=\"r\"?>".getBytes(encoding));
+ } else {
+ //Create padding string (40 * 101 characters is more or less the recommended 4KB)
+ StringBuffer sb = new StringBuffer(101);
+ sb.append('\n');
+ for (int i = 0; i < 100; i++) {
+ sb.append(" ");
+ }
+ byte[] padding = sb.toString().getBytes(encoding);
+ for (int i = 0; i < 40; i++) {
+ out.write(padding);
+ }
+ out.write("\n<?xpacket end=\"w\"?>".getBytes(encoding));
+ }
+ }
+
+ /** @see org.apache.fop.pdf.AbstractPDFStream#buildStreamDict(String) */
+ protected String buildStreamDict(String lengthEntry) {
+ final String filterEntry = getFilterList().buildFilterDictEntries();
+ final StringBuffer sb = new StringBuffer(128);
+ sb.append(getObjectID());
+ sb.append("<< ");
+ sb.append("/Type /Metadata");
+ sb.append("\n/Subtype /XML");
+ sb.append("\n/Length " + lengthEntry);
+ sb.append("\n" + filterEntry);
+ sb.append("\n>>\n");
+ return sb.toString();
+ }
+
+ /**
+ * Creates an XMP document based on the settings on the PDF Document.
+ * @param pdfDoc the PDF Document
+ * @return a DOM document representing the requested XMP metadata
+ */
+ public static Document createXMPFromUserAgent(PDFDocument pdfDoc) {
+ DOMImplementation domImplementation = ElementMapping.getDefaultDOMImplementation();
+ Document doc = domImplementation.createDocument(
+ XMPConstants.XMP_NAMESPACE, "x:xmpmeta", null);
+ Element rdf = doc.createElementNS(XMPConstants.RDF_NAMESPACE, "rdf:RDF");
+ doc.getDocumentElement().appendChild(rdf);
+
+ Element desc, el;
+ PDFInfo info = pdfDoc.getInfo();
+ DateFormat pseudoISO8601DateFormat = new SimpleDateFormat(
+ "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS");
+
+ //Set creation date if not available, yet
+ if (info.getCreationDate() == null) {
+ Date d = new Date();
+ info.setCreationDate(d);
+ }
+
+ //Dublin Core
+ desc = doc.createElementNS(XMPConstants.RDF_NAMESPACE, "rdf:Description");
+ desc.setAttribute("about", "");
+ rdf.appendChild(desc);
+ if (info.getAuthor() != null) {
+ el = doc.createElementNS(XMPConstants.DUBLIN_CORE_NAMESPACE, "dc:creator");
+ desc.appendChild(el);
+ el.appendChild(doc.createTextNode(info.getAuthor()));
+ }
+ if (info.getTitle() != null) {
+ el = doc.createElementNS(XMPConstants.DUBLIN_CORE_NAMESPACE, "dc:title");
+ desc.appendChild(el);
+ el.appendChild(doc.createTextNode(info.getTitle()));
+ }
+ if (info.getSubject() != null) {
+ el = doc.createElementNS(XMPConstants.DUBLIN_CORE_NAMESPACE, "dc:subject");
+ desc.appendChild(el);
+ el.appendChild(doc.createTextNode(info.getSubject()));
+ }
+ el = doc.createElementNS(XMPConstants.DUBLIN_CORE_NAMESPACE, "dc:date");
+ desc.appendChild(el);
+ el.appendChild(doc.createTextNode(pseudoISO8601DateFormat.format(info.getCreationDate())));
+
+ //XMP Basic Schema
+ desc = doc.createElementNS(XMPConstants.RDF_NAMESPACE, "rdf:Description");
+ desc.setAttribute("about", "");
+ rdf.appendChild(desc);
+ el = doc.createElementNS(XMPConstants.XMP_BASIC_NAMESPACE, "xmp:createDate");
+ desc.appendChild(el);
+ el.appendChild(doc.createTextNode(pseudoISO8601DateFormat.format(info.getCreationDate())));
+ if (info.getCreator() != null) {
+ el = doc.createElementNS(XMPConstants.XMP_BASIC_NAMESPACE, "xmp:creatorTool");
+ desc.appendChild(el);
+ el.appendChild(doc.createTextNode(info.getCreator()));
+ }
+
+
+ return doc;
+ }
+
+
+}
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2004,2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
private PDFOutline outline;
+ /** Optional Metadata object */
+ private PDFMetadata metadata;
+
private int pageMode = PAGEMODE_USENONE;
/**
public PDFOutline getRootOutline() {
return outline;
}
+
+ /**
+ * Set the optional Metadata object.
+ * @param meta the Metadata object
+ * @since PDF 1.4
+ */
+ public void setMetadata(PDFMetadata meta) {
+ this.metadata = meta;
+ }
+
+ /**
+ * @return the Metadata object if set, null otherwise.
+ * @since PDF 1.4
+ */
+ public PDFMetadata getMetadata() {
+ return this.metadata;
+ }
/**
* @see org.apache.fop.pdf.PDFObject#toPDFString()
break;
}
}
+ if (getMetadata() != null
+ && getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) {
+ p.append("/Metadata " + getMetadata().referencePDF() + "\n");
+ }
p.append(">>\nendobj\n");
return p.toString();
}
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.area.CTM;
import org.apache.fop.area.LineArea;
-import org.apache.fop.area.Page;
+import org.apache.fop.area.OffDocumentExtensionAttachment;
import org.apache.fop.area.PageViewport;
import org.apache.fop.area.RegionViewport;
import org.apache.fop.area.Trait;
import org.apache.fop.pdf.PDFFilterList;
import org.apache.fop.pdf.PDFInfo;
import org.apache.fop.pdf.PDFLink;
+import org.apache.fop.pdf.PDFMetadata;
import org.apache.fop.pdf.PDFNumber;
import org.apache.fop.pdf.PDFOutline;
import org.apache.fop.pdf.PDFPage;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.RendererContext;
import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.fo.extensions.xmp.XMPMetadata;
/*
todo:
// render Bookmark-Tree
if (odi instanceof BookmarkData) {
renderBookmarkTree((BookmarkData) odi);
+ } else if (odi instanceof OffDocumentExtensionAttachment) {
+ ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)odi).getAttachment();
+ if (XMPMetadata.CATEGORY.equals(attachment.getCategory())) {
+ renderXMPMetadata((XMPMetadata)attachment);
+ }
}
}
}
}
+ private void renderXMPMetadata(XMPMetadata metadata) {
+ PDFMetadata pdfMetadata = pdfDoc.getFactory().makeMetadata(
+ metadata.getDocument(), metadata.isReadOnly());
+ pdfDoc.getRoot().setMetadata(pdfMetadata);
+ }
+
/** @see org.apache.fop.render.Renderer#getGraphics2DAdapter() */
public Graphics2DAdapter getGraphics2DAdapter() {
return new PDFGraphics2DAdapter(this);
info.setTitle(str);
}
}
+ if (pdfDoc.getRoot().getMetadata() == null) {
+ //If at this time no XMP metadata for the overall document has been set, create it
+ //from the PDFInfo object.
+ Document xmp = PDFMetadata.createXMPFromUserAgent(pdfDoc);
+ PDFMetadata pdfMetadata = pdfDoc.getFactory().makeMetadata(
+ xmp, true);
+ pdfDoc.getRoot().setMetadata(pdfMetadata);
+ }
}
/**
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.fop.area.AreaTreeParser;
import org.apache.fop.util.ContentHandlerFactory;
+import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
private Attributes lastAttributes;
private PSSetupCode returnedObject;
+ private ObjectBuiltListener listener;
/** @see org.xml.sax.helpers.DefaultHandler */
public void startElement(String uri, String localName, String qName, Attributes attributes)
content.append(ch, start, length);
}
- /** @see org.apache.fop.area.AreaTreeParser.ObjectSource#getObject() */
+ /**
+ * @see org.xml.sax.helpers.DefaultHandler#endDocument()
+ */
+ public void endDocument() throws SAXException {
+ if (listener != null) {
+ listener.notifyObjectBuilt(getObject());
+ }
+ }
+
+ /**
+ * @see org.apache.fop.util.ContentHandlerFactory.ObjectSource#getObject()
+ */
public Object getObject() {
return returnedObject;
}
+ /**
+ * @see org.apache.fop.util.ContentHandlerFactory.ObjectSource
+ */
+ public void setObjectBuiltListener(ObjectBuiltListener listener) {
+ this.listener = listener;
+ }
+
}
package org.apache.fop.render.xml;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.apache.fop.render.Renderer;
import org.apache.fop.render.XMLHandler;
import org.apache.fop.render.RendererContext;
+import org.apache.fop.util.DOM2SAX;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Attr;
-import org.xml.sax.SAXException;
import org.xml.sax.ContentHandler;
-import org.xml.sax.ext.LexicalHandler;
-import org.xml.sax.helpers.AttributesImpl;
/**
* XML handler for the XML renderer.
/** Key for getting the TransformerHandler from the RendererContext */
public static final String HANDLER = "handler";
- /** Logging instance */
- private static Log log = LogFactory.getLog(XMLXMLHandler.class);
-
- private AttributesImpl atts = new AttributesImpl();
-
/** @see org.apache.fop.render.XMLHandler */
public void handleXML(RendererContext context,
org.w3c.dom.Document doc, String ns) throws Exception {
ContentHandler handler = (ContentHandler) context.getProperty(HANDLER);
- writeDocument(doc, handler);
- }
-
- /**
- * Writes the given document using the given TransformerHandler.
- * @param doc DOM document
- * @param handler TransformerHandler to write to
- * @throws SAXException In case of a problem while writing XML
- */
- public void writeDocument(Document doc,
- ContentHandler handler) throws SAXException {
- for (Node n = doc.getFirstChild(); n != null;
- n = n.getNextSibling()) {
- writeNode(n, handler);
- }
- }
-
- /**
- * Writes a node using the given writer.
- * @param node node to serialize
- * @param handler ContentHandler to write to
- * @throws SAXException In case of a problem while writing XML
- */
- public void writeNode(Node node, ContentHandler handler) throws SAXException {
- char[] ca;
- switch (node.getNodeType()) {
- case Node.ELEMENT_NODE:
- atts.clear();
-
- if (node.hasAttributes()) {
- NamedNodeMap attr = node.getAttributes();
- int len = attr.getLength();
- for (int i = 0; i < len; i++) {
- Attr a = (Attr) attr.item(i);
- atts.addAttribute("", a.getNodeName(), a.getNodeName(),
- "CDATA", a.getNodeValue());
- }
- }
- handler.startElement(node.getNamespaceURI(),
- node.getLocalName(), node.getLocalName(), atts);
-
- Node c = node.getFirstChild();
- if (c != null) {
- for (; c != null; c = c.getNextSibling()) {
- writeNode(c, handler);
- }
- }
- handler.endElement(node.getNamespaceURI(), node.getNodeName(), node.getNodeName());
- break;
- case Node.TEXT_NODE:
- ca = node.getNodeValue().toCharArray();
- handler.characters(ca, 0, ca.length);
- break;
- case Node.CDATA_SECTION_NODE:
- ca = node.getNodeValue().toCharArray();
- if (handler instanceof LexicalHandler) {
- LexicalHandler lh = (LexicalHandler)handler;
- lh.startCDATA();
- handler.characters(ca, 0, ca.length);
- lh.endCDATA();
- } else {
- handler.characters(ca, 0, ca.length);
- }
- break;
- case Node.ENTITY_REFERENCE_NODE:
- log.warn("Ignoring ENTITY_REFERENCE_NODE. NYI");
- /*
- writer.write("&");
- writer.write();
- writer.write(";");
- */
- break;
- case Node.PROCESSING_INSTRUCTION_NODE:
- handler.processingInstruction(node.getNodeName(), node.getNodeValue());
- break;
- case Node.COMMENT_NODE:
- ca = node.getNodeValue().toCharArray();
- if (handler instanceof LexicalHandler) {
- LexicalHandler lh = (LexicalHandler)handler;
- lh.comment(ca, 0, ca.length);
- }
- break;
- case Node.DOCUMENT_TYPE_NODE:
- break;
- default:
- throw new IllegalArgumentException("Unexpected node type ("
- + node.getNodeType() + ")");
- }
+ DOM2SAX.writeDocument(doc, handler);
}
/** @see org.apache.fop.render.XMLHandler#supportsRenderer(org.apache.fop.render.Renderer) */
package org.apache.fop.util;
+import java.util.EventListener;
+
import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
/**
* Factory interface implemented by classes that can instantiate ContentHandler subclasses which
/**
* @return a new ContentHandler to handle a SAX stream
+ * @throws SAXException if there's an error while preparing the ContentHandler
*/
- ContentHandler createContentHandler();
+ ContentHandler createContentHandler() throws SAXException;
/**
* Interface that ContentHandler implementations that parse Java objects from XML can implement
* @return the object parsed from the SAX stream (call valid after parsing)
*/
Object getObject();
+
+ /**
+ * Set a listener which gets notified when the object is fully built.
+ * @param listener the listener which gets notified
+ */
+ void setObjectBuiltListener(ObjectBuiltListener listener);
+ }
+
+ /**
+ * EventListener interface for objects which want to get notified when ContentHandler
+ * implementing the ObjectSource interface has finished parsing.
+ */
+ public interface ObjectBuiltListener extends EventListener {
+
+ /**
+ * Notifies the listener when the object is fully built.
+ * @param obj the newly built object
+ */
+ void notifyObjectBuilt(Object obj);
}
--- /dev/null
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.util;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Helper class that produces a SAX stream from a DOM Document.
+ */
+public class DOM2SAX {
+
+ /** Logging instance */
+ private static Log log = LogFactory.getLog(DOM2SAX.class);
+
+ /**
+ * Writes the given document using the given TransformerHandler.
+ * @param doc DOM document
+ * @param handler TransformerHandler to write to
+ * @throws SAXException In case of a problem while writing XML
+ */
+ public static void writeDocument(Document doc,
+ ContentHandler handler) throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ for (Node n = doc.getFirstChild(); n != null;
+ n = n.getNextSibling()) {
+ writeNode(n, handler, atts);
+ }
+ }
+
+ /**
+ * Writes a node using the given writer.
+ * @param node node to serialize
+ * @param handler ContentHandler to write to
+ * @param atts AttributesImpl instance that is reused during SAX event generation
+ * @throws SAXException In case of a problem while writing XML
+ */
+ private static void writeNode(Node node, ContentHandler handler, AttributesImpl atts)
+ throws SAXException {
+ char[] ca;
+ switch (node.getNodeType()) {
+ case Node.ELEMENT_NODE:
+ atts.clear();
+
+ if (node.hasAttributes()) {
+ NamedNodeMap attr = node.getAttributes();
+ int len = attr.getLength();
+ for (int i = 0; i < len; i++) {
+ Attr a = (Attr) attr.item(i);
+ atts.addAttribute("", a.getNodeName(), a.getNodeName(),
+ "CDATA", a.getNodeValue());
+ }
+ }
+ handler.startElement(node.getNamespaceURI(),
+ node.getLocalName(), node.getLocalName(), atts);
+
+ Node c = node.getFirstChild();
+ if (c != null) {
+ for (; c != null; c = c.getNextSibling()) {
+ writeNode(c, handler, atts);
+ }
+ }
+ handler.endElement(node.getNamespaceURI(), node.getNodeName(), node.getNodeName());
+ break;
+ case Node.TEXT_NODE:
+ ca = node.getNodeValue().toCharArray();
+ handler.characters(ca, 0, ca.length);
+ break;
+ case Node.CDATA_SECTION_NODE:
+ ca = node.getNodeValue().toCharArray();
+ if (handler instanceof LexicalHandler) {
+ LexicalHandler lh = (LexicalHandler)handler;
+ lh.startCDATA();
+ handler.characters(ca, 0, ca.length);
+ lh.endCDATA();
+ } else {
+ handler.characters(ca, 0, ca.length);
+ }
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ log.warn("Ignoring ENTITY_REFERENCE_NODE. NYI");
+ /*
+ writer.write("&");
+ writer.write();
+ writer.write(";");
+ */
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ handler.processingInstruction(node.getNodeName(), node.getNodeValue());
+ break;
+ case Node.COMMENT_NODE:
+ ca = node.getNodeValue().toCharArray();
+ if (handler instanceof LexicalHandler) {
+ LexicalHandler lh = (LexicalHandler)handler;
+ lh.comment(ca, 0, ca.length);
+ }
+ break;
+ case Node.DOCUMENT_TYPE_NODE:
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected node type ("
+ + node.getNodeType() + ")");
+ }
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.util;
+
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * ContentHandlerFactory which constructs ContentHandlers that build DOM Documents.
+ */
+public class DOMBuilderContentHandlerFactory implements ContentHandlerFactory {
+
+ private static SAXTransformerFactory tFactory
+ = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+
+ private String namespaceURI;
+ private DOMImplementation domImplementation;
+
+ /**
+ * Main Constructor
+ * @param namespaceURI the main namespace URI for the DOM to be parsed
+ * @param domImplementation the DOMImplementation to use for build the DOM
+ */
+ public DOMBuilderContentHandlerFactory(String namespaceURI,
+ DOMImplementation domImplementation) {
+ this.namespaceURI = namespaceURI;
+ this.domImplementation = domImplementation;
+ }
+
+ /** @see org.apache.fop.util.ContentHandlerFactory#getSupportedNamespaces() */
+ public String[] getSupportedNamespaces() {
+ return new String[] {namespaceURI};
+ }
+
+ /** @see org.apache.fop.util.ContentHandlerFactory#createContentHandler() */
+ public ContentHandler createContentHandler() throws SAXException {
+ return new Handler();
+ }
+
+ private class Handler extends DelegatingContentHandler
+ implements ContentHandlerFactory.ObjectSource {
+
+ private Document doc;
+ private ObjectBuiltListener obListener;
+
+ public Handler() throws SAXException {
+ super();
+ }
+
+ public Document getDocument() {
+ return this.doc;
+ }
+
+ /**
+ * @see org.apache.fop.util.ContentHandlerFactory.ObjectSource#getObject()
+ */
+ public Object getObject() {
+ return getDocument();
+ }
+
+ /**
+ * @see org.apache.fop.util.ContentHandlerFactory.ObjectSource
+ */
+ public void setObjectBuiltListener(ObjectBuiltListener listener) {
+ this.obListener = listener;
+ }
+
+ /**
+ * @see org.apache.fop.util.DelegatingContentHandler#startDocument()
+ */
+ public void startDocument() throws SAXException {
+ //Suppress startDocument() call if doc has not been set, yet. It will be done later.
+ if (doc != null) {
+ super.startDocument();
+ }
+ }
+
+ /**
+ * @see org.apache.fop.util.DelegatingContentHandler
+ */
+ public void startElement(String uri, String localName, String qName, Attributes atts)
+ throws SAXException {
+ if (doc == null) {
+ TransformerHandler handler;
+ try {
+ handler = tFactory.newTransformerHandler();
+ } catch (TransformerConfigurationException e) {
+ throw new SAXException("Error creating a new TransformerHandler", e);
+ }
+ doc = domImplementation.createDocument(namespaceURI, qName, null);
+ //It's easier to work with an empty document, so remove the root element
+ doc.removeChild(doc.getDocumentElement());
+ handler.setResult(new DOMResult(doc));
+ setDelegateContentHandler(handler);
+ setDelegateLexicalHandler(handler);
+ setDelegateDTDHandler(handler);
+ handler.startDocument();
+ }
+ super.startElement(uri, localName, qName, atts);
+ }
+
+ /**
+ * @see org.apache.fop.util.DelegatingContentHandler#endDocument()
+ */
+ public void endDocument() throws SAXException {
+ super.endDocument();
+ if (obListener != null) {
+ obListener.notifyObjectBuilt(getObject());
+ }
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.util;
+
+import java.io.IOException;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * SAX 2 Event Handler which simply delegates all calls to another ContentHandler. Subclasses can
+ * do additional processing. This class is the passive counterpart to XMLFilterImpl.
+ * <p>
+ * The ContentHandler is the only instance that is required. All others (DTDHandler,
+ * EntityResolver, LexicalHandler and ErrorHandler) may be ignored.
+ *
+ */
+public class DelegatingContentHandler
+ implements EntityResolver, DTDHandler, ContentHandler, LexicalHandler, ErrorHandler {
+
+ private ContentHandler delegate;
+ private EntityResolver entityResolver;
+ private DTDHandler dtdHandler;
+ private LexicalHandler lexicalHandler;
+ private ErrorHandler errorHandler;
+
+ /**
+ * Main constructor.
+ */
+ public DelegatingContentHandler() {
+ //nop
+ }
+
+ /**
+ * @return the delegate that all ContentHandler events are forwarded to
+ */
+ public ContentHandler getDelegateContentHandler() {
+ return this.delegate;
+ }
+
+ /**
+ * Sets the delegate ContentHandler that all events are forwarded to.
+ * @param handler the delegate instance
+ */
+ public void setDelegateContentHandler(ContentHandler handler) {
+ this.delegate = handler;
+ }
+
+ /**
+ * Sets the delegate EntityResolver.
+ * @param resolver the delegate instance
+ */
+ public void setDelegateEntityResolver(EntityResolver resolver) {
+ this.entityResolver = resolver;
+ }
+
+ /**
+ * Sets the delegate DTDHandler.
+ * @param handler the delegate instance
+ */
+ public void setDelegateDTDHandler(DTDHandler handler) {
+ this.dtdHandler = handler;
+ }
+
+ /**
+ * Sets the delegate LexicalHandler.
+ * @param handler the delegate instance
+ */
+ public void setDelegateLexicalHandler(LexicalHandler handler) {
+ this.lexicalHandler = handler;
+ }
+
+ /**
+ * Sets the delegate ErrorHandler.
+ * @param handler the delegate instance
+ */
+ public void setDelegateErrorHandler(ErrorHandler handler) {
+ this.errorHandler = handler;
+ }
+
+ // ==== EntityResolver
+
+ /**
+ * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String)
+ */
+ public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+ if (entityResolver != null) {
+ return entityResolver.resolveEntity(publicId, systemId);
+ } else {
+ return null;
+ }
+ }
+
+ // ==== DTDHandler
+
+ /**
+ * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void notationDecl(String name, String publicId, String systemId) throws SAXException {
+ if (dtdHandler != null) {
+ dtdHandler.notationDecl(name, publicId, systemId);
+ }
+ }
+
+ /**
+ * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void unparsedEntityDecl(String name, String publicId, String systemId,
+ String notationName) throws SAXException {
+ if (dtdHandler != null) {
+ dtdHandler.unparsedEntityDecl(name, publicId, systemId, notationName);
+ }
+ }
+
+ // ==== ContentHandler
+
+ /**
+ * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
+ */
+ public void setDocumentLocator(Locator locator) {
+ delegate.setDocumentLocator(locator);
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#startDocument()
+ */
+ public void startDocument() throws SAXException {
+ delegate.startDocument();
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#endDocument()
+ */
+ public void endDocument() throws SAXException {
+ delegate.endDocument();
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
+ */
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ delegate.startPrefixMapping(prefix, uri);
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
+ */
+ public void endPrefixMapping(String prefix) throws SAXException {
+ delegate.endPrefixMapping(prefix);
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+ */
+ public void startElement(String uri, String localName, String qName,
+ Attributes atts) throws SAXException {
+ delegate.startElement(uri, localName, qName, atts);
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ delegate.endElement(uri, localName, qName);
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+ */
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ delegate.characters(ch, start, length);
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+ */
+ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+ delegate.ignorableWhitespace(ch, start, length);
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String)
+ */
+ public void processingInstruction(String target, String data) throws SAXException {
+ delegate.processingInstruction(target, data);
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
+ */
+ public void skippedEntity(String name) throws SAXException {
+ delegate.skippedEntity(name);
+ }
+
+ // ==== LexicalHandler
+
+ /**
+ * @see org.xml.sax.ext.LexicalHandler#startDTD(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void startDTD(String name, String publicId, String systemId) throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.startDTD(name, publicId, systemId);
+ }
+
+ }
+
+ /**
+ * @see org.xml.sax.ext.LexicalHandler#endDTD()
+ */
+ public void endDTD() throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.endDTD();
+ }
+ }
+
+ /**
+ * @see org.xml.sax.ext.LexicalHandler#startEntity(java.lang.String)
+ */
+ public void startEntity(String name) throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.startEntity(name);
+ }
+ }
+
+ /**
+ * @see org.xml.sax.ext.LexicalHandler#endEntity(java.lang.String)
+ */
+ public void endEntity(String name) throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.endEntity(name);
+ }
+ }
+
+ /**
+ * @see org.xml.sax.ext.LexicalHandler#startCDATA()
+ */
+ public void startCDATA() throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.startCDATA();
+ }
+ }
+
+ /**
+ * @see org.xml.sax.ext.LexicalHandler#endCDATA()
+ */
+ public void endCDATA() throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.endCDATA();
+ }
+ }
+
+ /**
+ * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int)
+ */
+ public void comment(char[] ch, int start, int length) throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.comment(ch, start, length);
+ }
+ }
+
+ // ==== ErrorHandler
+
+ /**
+ * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
+ */
+ public void warning(SAXParseException exception) throws SAXException {
+ if (errorHandler != null) {
+ errorHandler.warning(exception);
+ }
+ }
+
+ /**
+ * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
+ */
+ public void error(SAXParseException exception) throws SAXException {
+ if (errorHandler != null) {
+ errorHandler.error(exception);
+ }
+ }
+
+ /**
+ * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
+ */
+ public void fatalError(SAXParseException exception) throws SAXException {
+ if (errorHandler != null) {
+ errorHandler.fatalError(exception);
+ }
+ }
+
+}
<changes>
<release version="FOP Trunk">
+ <action context="Code" dev="JM" type="add">
+ Initial support for XMP metadata (PDF 1.4) under fo:declarations.
+ </action>
<action context="Code" dev="AD" type="add">
Added support for the background-position shorthand property.
</action>