diff options
Diffstat (limited to 'src')
25 files changed, 1558 insertions, 219 deletions
diff --git a/src/java/org/apache/fop/fo/ElementMapping.java b/src/java/org/apache/fop/fo/ElementMapping.java index 092a411f0..1142891b0 100644 --- a/src/java/org/apache/fop/fo/ElementMapping.java +++ b/src/java/org/apache/fop/fo/ElementMapping.java @@ -76,7 +76,7 @@ public abstract class ElementMapping { /** * @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); diff --git a/src/java/org/apache/fop/fo/ElementMappingRegistry.java b/src/java/org/apache/fop/fo/ElementMappingRegistry.java index 35224e7ae..ee8c7b86e 100644 --- a/src/java/org/apache/fop/fo/ElementMappingRegistry.java +++ b/src/java/org/apache/fop/fo/ElementMappingRegistry.java @@ -78,6 +78,8 @@ public class ElementMappingRegistry { 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 diff --git a/src/java/org/apache/fop/fo/FONode.java b/src/java/org/apache/fop/fo/FONode.java index 84d04b2c0..7663cb49b 100644 --- a/src/java/org/apache/fop/fo/FONode.java +++ b/src/java/org/apache/fop/fo/FONode.java @@ -1,5 +1,5 @@ /* - * 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. @@ -35,6 +35,7 @@ import org.apache.fop.fo.extensions.ExtensionElementMapping; 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 @@ -568,5 +569,15 @@ public abstract class FONode implements Cloneable { 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; + } + } diff --git a/src/java/org/apache/fop/fo/FOTreeBuilder.java b/src/java/org/apache/fop/fo/FOTreeBuilder.java index f674d58c8..d5d03b7b7 100644 --- a/src/java/org/apache/fop/fo/FOTreeBuilder.java +++ b/src/java/org/apache/fop/fo/FOTreeBuilder.java @@ -29,7 +29,11 @@ import org.apache.fop.area.AreaTreeHandler; 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; @@ -56,16 +60,12 @@ public class FOTreeBuilder extends DefaultHandler { */ 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) @@ -80,6 +80,8 @@ public class FOTreeBuilder extends DefaultHandler { private boolean used = false; + private int depth; + /** * FOTreeBuilder constructor * @param outputFormat the MIME type of the output format to use (ex. "application/pdf"). @@ -135,11 +137,8 @@ public class FOTreeBuilder extends DefaultHandler { * @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); } /** @@ -157,6 +156,9 @@ public class FOTreeBuilder extends DefaultHandler { log.debug("Building formatting object tree"); } foEventHandler.startDocument(); + this.mainFOHandler = new MainFOHandler(); + this.mainFOHandler.startDocument(); + this.delegate = this.mainFOHandler; } /** @@ -164,8 +166,8 @@ public class FOTreeBuilder extends DefaultHandler { * @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"); } @@ -181,55 +183,8 @@ public class FOTreeBuilder extends DefaultHandler { */ 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); } /** @@ -237,25 +192,17 @@ public class FOTreeBuilder extends DefaultHandler { * @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(); } /** @@ -309,5 +256,137 @@ public class FOTreeBuilder extends DefaultHandler { } } + /** + * 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; + } + + + + } + } diff --git a/src/java/org/apache/fop/fo/XMLObj.java b/src/java/org/apache/fop/fo/XMLObj.java index 55d8c2988..87cb004c3 100644 --- a/src/java/org/apache/fop/fo/XMLObj.java +++ b/src/java/org/apache/fop/fo/XMLObj.java @@ -1,5 +1,5 @@ /* - * 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. @@ -26,6 +26,7 @@ import javax.xml.parsers.DocumentBuilderFactory; 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; @@ -35,7 +36,7 @@ import org.xml.sax.Locator; * 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; @@ -112,6 +113,12 @@ public abstract class XMLObj extends FONode { 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); @@ -128,10 +135,8 @@ public abstract class XMLObj extends FONode { } } } - attr = null; - parent.appendChild(element); } - + /** * Add the top-level element to the DOM document * @param doc DOM document @@ -139,22 +144,7 @@ public abstract class XMLObj extends FONode { */ 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); } /** @@ -180,6 +170,7 @@ public abstract class XMLObj extends FONode { } } 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; @@ -214,5 +205,10 @@ public abstract class XMLObj extends FONode { element.appendChild(text); } + /** @see org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener */ + public void notifyObjectBuilt(Object obj) { + this.doc = (Document)obj; + } + } diff --git a/src/java/org/apache/fop/fo/extensions/svg/SVGElement.java b/src/java/org/apache/fop/fo/extensions/svg/SVGElement.java index c5d15fde7..47cc66340 100644 --- a/src/java/org/apache/fop/fo/extensions/svg/SVGElement.java +++ b/src/java/org/apache/fop/fo/extensions/svg/SVGElement.java @@ -22,6 +22,8 @@ package org.apache.fop.fo.extensions.svg; 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; @@ -59,6 +61,14 @@ public class SVGElement extends SVGObj { } /** + * @see org.apache.fop.fo.FONode#getContentHandlerFactory() + */ + public ContentHandlerFactory getContentHandlerFactory() { + return new DOMBuilderContentHandlerFactory(getNamespaceURI(), + SVGDOMImplementation.getDOMImplementation()); + } + + /** * @see org.apache.fop.fo.FONode#processNode */ public void processNode(String elementName, Locator locator, @@ -287,5 +297,6 @@ public class SVGElement extends SVGObj { return 100; } } + } diff --git a/src/java/org/apache/fop/fo/extensions/xmp/RDFElement.java b/src/java/org/apache/fop/fo/extensions/xmp/RDFElement.java new file mode 100644 index 000000000..ea8e48ba8 --- /dev/null +++ b/src/java/org/apache/fop/fo/extensions/xmp/RDFElement.java @@ -0,0 +1,82 @@ +/* + * 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); + } + +} diff --git a/src/java/org/apache/fop/fo/extensions/xmp/RDFElementMapping.java b/src/java/org/apache/fop/fo/extensions/xmp/RDFElementMapping.java new file mode 100644 index 000000000..566a6c647 --- /dev/null +++ b/src/java/org/apache/fop/fo/extensions/xmp/RDFElementMapping.java @@ -0,0 +1,57 @@ +/* + * 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); + } + } + +} diff --git a/src/java/org/apache/fop/fo/extensions/xmp/XMPConstants.java b/src/java/org/apache/fop/fo/extensions/xmp/XMPConstants.java new file mode 100644 index 000000000..b8b29988c --- /dev/null +++ b/src/java/org/apache/fop/fo/extensions/xmp/XMPConstants.java @@ -0,0 +1,38 @@ +/* + * 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/"; + +} diff --git a/src/java/org/apache/fop/fo/extensions/xmp/XMPElementMapping.java b/src/java/org/apache/fop/fo/extensions/xmp/XMPElementMapping.java new file mode 100644 index 000000000..230ea8151 --- /dev/null +++ b/src/java/org/apache/fop/fo/extensions/xmp/XMPElementMapping.java @@ -0,0 +1,57 @@ +/* + * 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); + } + } + +} diff --git a/src/java/org/apache/fop/fo/extensions/xmp/XMPMetaElement.java b/src/java/org/apache/fop/fo/extensions/xmp/XMPMetaElement.java new file mode 100644 index 000000000..4f39fb13b --- /dev/null +++ b/src/java/org/apache/fop/fo/extensions/xmp/XMPMetaElement.java @@ -0,0 +1,82 @@ +/* + * 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); + } + +} diff --git a/src/java/org/apache/fop/fo/extensions/xmp/XMPMetadata.java b/src/java/org/apache/fop/fo/extensions/xmp/XMPMetadata.java new file mode 100644 index 000000000..fd84b2412 --- /dev/null +++ b/src/java/org/apache/fop/fo/extensions/xmp/XMPMetadata.java @@ -0,0 +1,92 @@ +/* + * 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); + } + +} diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index 4d16dfa74..572e3be43 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -1,5 +1,5 @@ /* - * 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. @@ -62,7 +62,13 @@ public class PDFDocument { * 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. */ @@ -98,6 +104,9 @@ public class PDFDocument { */ protected int xref; + /** Indicates what PDF version is active */ + protected int pdfVersion = PDF_VERSION_1_4; + /** * the /Root object */ @@ -235,6 +244,13 @@ public class PDFDocument { } /** + * @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 */ diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 39b172417..6222cf1f0 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -29,6 +29,9 @@ import java.util.Map; 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; @@ -55,7 +58,7 @@ public class PDFFactory { private PDFDocument document; - private Log log = LogFactory.getLog("org.apache.fop.pdf"); + private Log log = LogFactory.getLog(PDFFactory.class); /** * Creates a new PDFFactory. @@ -86,6 +89,7 @@ public class 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; } @@ -97,6 +101,7 @@ public class PDFFactory { */ public PDFPages makePages() { PDFPages pdfPages = new PDFPages(++(this.document.objectcount)); + pdfPages.setDocument(getDocument()); getDocument().addTrailerObject(pdfPages); return pdfPages; } @@ -108,6 +113,7 @@ public class PDFFactory { */ public PDFResources makeResources() { PDFResources pdfResources = new PDFResources(++this.document.objectcount); + pdfResources.setDocument(getDocument()); getDocument().addTrailerObject(pdfResources); return pdfResources; } @@ -132,6 +138,18 @@ public class PDFFactory { } /** + * 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 * PDFDocument later using addObject(). @@ -1302,6 +1320,4 @@ public class PDFFactory { return obj; } - - } diff --git a/src/java/org/apache/fop/pdf/PDFFilterList.java b/src/java/org/apache/fop/pdf/PDFFilterList.java index 4554ea6c6..6ffda9da7 100644 --- a/src/java/org/apache/fop/pdf/PDFFilterList.java +++ b/src/java/org/apache/fop/pdf/PDFFilterList.java @@ -1,5 +1,5 @@ /* - * 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. @@ -49,6 +49,8 @@ public class PDFFilterList { 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(); diff --git a/src/java/org/apache/fop/pdf/PDFInfo.java b/src/java/org/apache/fop/pdf/PDFInfo.java index ce926d4da..7bf346753 100644 --- a/src/java/org/apache/fop/pdf/PDFInfo.java +++ b/src/java/org/apache/fop/pdf/PDFInfo.java @@ -1,5 +1,5 @@ /* - * 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. @@ -53,6 +53,11 @@ public class PDFInfo extends PDFObject { this.producer = producer; } + /** @return the creator of the document or null if not set */ + public String getCreator() { + return this.creator; + } + /** * set the creator string * @@ -76,6 +81,11 @@ public class PDFInfo extends PDFObject { this.title = t; } + /** @return the author of the document or null if not set */ + public String getAuthor() { + return this.author; + } + /** * set the author string * @@ -85,6 +95,11 @@ public class PDFInfo extends PDFObject { this.author = a; } + /** @return the subject of the document or null if not set */ + public String getSubject() { + return this.subject; + } + /** * set the subject string * @@ -94,6 +109,11 @@ public class PDFInfo extends PDFObject { this.subject = s; } + /** @return the keywords for the document or null if not set */ + public String getKeyword() { + return this.keywords; + } + /** * set the keywords string * diff --git a/src/java/org/apache/fop/pdf/PDFMetadata.java b/src/java/org/apache/fop/pdf/PDFMetadata.java new file mode 100644 index 000000000..d162ba032 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFMetadata.java @@ -0,0 +1,205 @@ +/* + * 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; + } + + +} diff --git a/src/java/org/apache/fop/pdf/PDFRoot.java b/src/java/org/apache/fop/pdf/PDFRoot.java index 2ac00cdf1..3d483f587 100644 --- a/src/java/org/apache/fop/pdf/PDFRoot.java +++ b/src/java/org/apache/fop/pdf/PDFRoot.java @@ -1,5 +1,5 @@ /* - * 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. @@ -53,6 +53,9 @@ public class PDFRoot extends PDFObject { */ private PDFOutline outline; + /** Optional Metadata object */ + private PDFMetadata metadata; + private int pageMode = PAGEMODE_USENONE; /** @@ -115,6 +118,23 @@ public class PDFRoot extends PDFObject { 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() @@ -144,6 +164,10 @@ public class PDFRoot extends PDFObject { break; } } + if (getMetadata() != null + && getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) { + p.append("/Metadata " + getMetadata().referencePDF() + "\n"); + } p.append(">>\nendobj\n"); return p.toString(); } diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java index ab2ae245b..d0f65d16f 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java @@ -41,7 +41,7 @@ import org.apache.fop.apps.FOUserAgent; 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; @@ -69,6 +69,7 @@ import org.apache.fop.pdf.PDFEncryptionManager; 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; @@ -82,6 +83,8 @@ import org.apache.fop.render.AbstractPathOrientedRenderer; 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: @@ -296,6 +299,11 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { // 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); + } } } @@ -340,6 +348,12 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { } } + 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); @@ -400,6 +414,14 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { 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); + } } /** diff --git a/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java b/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java index e22951236..401202dc0 100644 --- a/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java +++ b/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java @@ -20,8 +20,8 @@ package org.apache.fop.render.ps.extensions; 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; @@ -39,6 +39,7 @@ public class PSExtensionHandler extends 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) @@ -80,9 +81,27 @@ public class PSExtensionHandler extends DefaultHandler 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; + } + } diff --git a/src/java/org/apache/fop/render/xml/XMLXMLHandler.java b/src/java/org/apache/fop/render/xml/XMLXMLHandler.java index eb1f4946b..da44bc725 100644 --- a/src/java/org/apache/fop/render/xml/XMLXMLHandler.java +++ b/src/java/org/apache/fop/render/xml/XMLXMLHandler.java @@ -18,20 +18,12 @@ 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. @@ -41,104 +33,12 @@ public class XMLXMLHandler implements XMLHandler { /** 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) */ diff --git a/src/java/org/apache/fop/util/ContentHandlerFactory.java b/src/java/org/apache/fop/util/ContentHandlerFactory.java index da615cc54..a17d7a60f 100644 --- a/src/java/org/apache/fop/util/ContentHandlerFactory.java +++ b/src/java/org/apache/fop/util/ContentHandlerFactory.java @@ -18,7 +18,10 @@ 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 @@ -33,8 +36,9 @@ public interface ContentHandlerFactory { /** * @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 @@ -46,6 +50,25 @@ public interface ContentHandlerFactory { * @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); } diff --git a/src/java/org/apache/fop/util/DOM2SAX.java b/src/java/org/apache/fop/util/DOM2SAX.java new file mode 100644 index 000000000..a26e71429 --- /dev/null +++ b/src/java/org/apache/fop/util/DOM2SAX.java @@ -0,0 +1,133 @@ +/* + * 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() + ")"); + } + } + + +} diff --git a/src/java/org/apache/fop/util/DOMBuilderContentHandlerFactory.java b/src/java/org/apache/fop/util/DOMBuilderContentHandlerFactory.java new file mode 100644 index 000000000..5b5dd3b48 --- /dev/null +++ b/src/java/org/apache/fop/util/DOMBuilderContentHandlerFactory.java @@ -0,0 +1,138 @@ +/* + * 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()); + } + } + + } + +} diff --git a/src/java/org/apache/fop/util/DelegatingContentHandler.java b/src/java/org/apache/fop/util/DelegatingContentHandler.java new file mode 100644 index 000000000..86cfb1e41 --- /dev/null +++ b/src/java/org/apache/fop/util/DelegatingContentHandler.java @@ -0,0 +1,314 @@ +/* + * 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); + } + } + +} |