123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* $Id$ */
-
- package org.apache.fop.fo;
-
- import java.io.OutputStream;
-
- 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;
- import org.xml.sax.helpers.DefaultHandler;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.xmlgraphics.util.QName;
-
- import org.apache.fop.accessibility.fo.FO2StructureTreeConverter;
- import org.apache.fop.apps.FOPException;
- import org.apache.fop.apps.FOUserAgent;
- import org.apache.fop.apps.FormattingResults;
- import org.apache.fop.fo.ElementMapping.Maker;
- import org.apache.fop.fo.extensions.ExtensionElementMapping;
- import org.apache.fop.fo.pagination.Root;
- import org.apache.fop.util.ContentHandlerFactory;
- import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
- import org.apache.fop.util.ContentHandlerFactory.ObjectSource;
-
- /**
- * SAX Handler that passes parsed data to the various
- * FO objects, where they can be used either to build
- * an FO Tree, or used by Structure Renderers to build
- * other data structures.
- */
- public class FOTreeBuilder extends DefaultHandler {
-
- /** logging instance */
- private static final Log LOG = LogFactory.getLog(FOTreeBuilder.class);
-
- /** The registry for ElementMapping instances */
- protected ElementMappingRegistry elementMappingRegistry;
-
- /** The root of the formatting object tree */
- protected Root rootFObj = null;
-
- /** Main DefaultHandler that handles the FO namespace. */
- protected MainFOHandler mainFOHandler;
-
- /** Current delegate ContentHandler to receive the SAX events */
- protected ContentHandler delegate;
-
- /** Provides information used during tree building stage. */
- private FOTreeBuilderContext builderContext;
-
- /** The object that handles formatting and rendering to a stream */
- private FOEventHandler foEventHandler;
-
- /** The SAX locator object managing the line and column counters */
- private Locator locator;
-
- /** The user agent for this processing run. */
- private FOUserAgent userAgent;
-
- private boolean used = false;
- private boolean empty = true;
-
- private int depth;
-
- /**
- * <code>FOTreeBuilder</code> constructor
- *
- * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
- * @param foUserAgent the {@link FOUserAgent} in effect for this process
- * @param stream the <code>OutputStream</code> to direct the results to
- * @throws FOPException if the <code>FOTreeBuilder</code> cannot be properly created
- */
- public FOTreeBuilder(
- String outputFormat,
- FOUserAgent foUserAgent,
- OutputStream stream)
- throws FOPException {
-
- this.userAgent = foUserAgent;
- this.elementMappingRegistry = userAgent.getElementMappingRegistry();
- //This creates either an AreaTreeHandler and ultimately a Renderer, or
- //one of the RTF-, MIF- etc. Handlers.
- foEventHandler = foUserAgent.getRendererFactory().createFOEventHandler(
- foUserAgent, outputFormat, stream);
- if (userAgent.isAccessibilityEnabled()) {
- foEventHandler = new FO2StructureTreeConverter(
- foUserAgent.getStructureTreeEventHandler(), foEventHandler);
- }
- builderContext = new FOTreeBuilderContext();
- builderContext.setPropertyListMaker(new PropertyListMaker() {
- public PropertyList make(FObj fobj, PropertyList parentPropertyList) {
- return new StaticPropertyList(fobj, parentPropertyList);
- }
- });
- }
-
- /** {@inheritDoc} */
- public void setDocumentLocator(Locator locator) {
- this.locator = locator;
- }
-
- /**
- * @return a {@link Locator} instance if it is available and not disabled
- */
- protected Locator getEffectiveLocator() {
- return (userAgent.isLocatorEnabled() ? this.locator : null);
- }
-
- /** {@inheritDoc} */
- public void characters(char[] data, int start, int length)
- throws SAXException {
- delegate.characters(data, start, length);
- }
-
- /** {@inheritDoc} */
- public void startDocument() throws SAXException {
- if (used) {
- throw new IllegalStateException("FOTreeBuilder (and the Fop class) cannot be reused."
- + " Please instantiate a new instance.");
- }
-
- used = true;
- empty = true;
- rootFObj = null; // allows FOTreeBuilder to be reused
- if (LOG.isDebugEnabled()) {
- LOG.debug("Building formatting object tree");
- }
- foEventHandler.startDocument();
- this.mainFOHandler = new MainFOHandler();
- this.mainFOHandler.startDocument();
- this.delegate = this.mainFOHandler;
- }
-
- /** {@inheritDoc} */
- public void endDocument() throws SAXException {
- this.delegate.endDocument();
- if (this.rootFObj == null && empty) {
- FOValidationEventProducer eventProducer
- = FOValidationEventProducer.Provider.get(userAgent.getEventBroadcaster());
- eventProducer.emptyDocument(this);
- }
- rootFObj = null;
- if (LOG.isDebugEnabled()) {
- LOG.debug("Parsing of document complete");
- }
- foEventHandler.endDocument();
- }
-
- /** {@inheritDoc} */
- public void startElement(String namespaceURI, String localName, String rawName,
- Attributes attlist) throws SAXException {
- this.depth++;
- delegate.startElement(namespaceURI, localName, rawName, attlist);
- }
-
- /** {@inheritDoc} */
- public void endElement(String uri, String localName, String rawName)
- 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);
- }
- }
- }
-
- /** {@inheritDoc} */
- public void warning(SAXParseException e) {
- LOG.warn(e.getLocalizedMessage());
- }
-
- /** {@inheritDoc} */
- public void error(SAXParseException e) {
- LOG.error(e.toString());
- }
-
- /** {@inheritDoc} */
- public void fatalError(SAXParseException e) throws SAXException {
- LOG.error(e.toString());
- throw e;
- }
-
- /**
- * Provides access to the underlying {@link FOEventHandler} object.
- *
- * @return the FOEventHandler object
- */
- public FOEventHandler getEventHandler() {
- return foEventHandler;
- }
-
- /**
- * Returns the results of the rendering process. Information includes
- * the total number of pages generated and the number of pages per
- * page-sequence.
- *
- * @return the results of the rendering process.
- */
- public FormattingResults getResults() {
- return getEventHandler().getResults();
- }
-
- /**
- * Main <code>DefaultHandler</code> 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;
-
- /** Current marker nesting-depth */
- private int nestedMarkerDepth = 0;
-
- /** {@inheritDoc} */
- public void startElement(String namespaceURI, String localName, String rawName,
- Attributes attlist) throws SAXException {
-
- /* the node found in the FO document */
- FONode foNode;
- PropertyList propertyList = null;
-
- // Check to ensure first node encountered is an fo:root
- if (rootFObj == null) {
- empty = false;
- if (!namespaceURI.equals(FOElementMapping.URI)
- || !localName.equals("root")) {
- FOValidationEventProducer eventProducer
- = FOValidationEventProducer.Provider.get(
- userAgent.getEventBroadcaster());
- eventProducer.invalidFORoot(this, FONode.getNodeString(namespaceURI, localName),
- getEffectiveLocator());
- }
- } else { // check that incoming node is valid for currentFObj
- if (currentFObj.getNamespaceURI().equals(FOElementMapping.URI)
- || currentFObj.getNamespaceURI().equals(ExtensionElementMapping.URI)) {
- currentFObj.validateChildNode(locator, namespaceURI, localName);
- }
- }
-
- ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName);
-
- try {
- foNode = fobjMaker.make(currentFObj);
- if (rootFObj == null) {
- rootFObj = (Root) foNode;
- rootFObj.setBuilderContext(builderContext);
- rootFObj.setFOEventHandler(foEventHandler);
- }
- propertyList = foNode.createPropertyList(
- currentPropertyList, foEventHandler);
- foNode.processNode(localName, getEffectiveLocator(),
- attlist, propertyList);
- if (foNode.getNameId() == Constants.FO_MARKER) {
- if (builderContext.inMarker()) {
- nestedMarkerDepth++;
- } else {
- builderContext.switchMarkerContext(true);
- }
- }
- if (foNode.getNameId() == Constants.FO_PAGE_SEQUENCE) {
- builderContext.getXMLWhiteSpaceHandler().reset();
- }
- } 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 (currentFObj != null) {
- currentFObj.addChildNode(foNode);
- }
-
- currentFObj = foNode;
- if (propertyList != null && !builderContext.inMarker()) {
- currentPropertyList = propertyList;
- }
-
- // fo:characters can potentially be removed during
- // white-space handling.
- // Do not notify the FOEventHandler.
- if (currentFObj.getNameId() != Constants.FO_CHARACTER) {
- currentFObj.startOfNode();
- }
- }
-
- /** {@inheritDoc} */
- public void endElement(String uri, String localName, String rawName)
- throws SAXException {
- if (currentFObj == null) {
- throw new SAXException(
- "endElement() called for " + rawName
- + " where there is no current element.");
- } else if (!currentFObj.getLocalName().equals(localName)
- || !currentFObj.getNamespaceURI().equals(uri)) {
- throw new SAXException("Mismatch: " + currentFObj.getLocalName()
- + " (" + currentFObj.getNamespaceURI()
- + ") vs. " + localName + " (" + uri + ")");
- }
-
- // fo:characters can potentially be removed during
- // white-space handling.
- // Do not notify the FOEventHandler.
- if (currentFObj.getNameId() != Constants.FO_CHARACTER) {
- currentFObj.endOfNode();
- }
-
- if (currentPropertyList != null
- && currentPropertyList.getFObj() == currentFObj
- && !builderContext.inMarker()) {
- currentPropertyList = currentPropertyList.getParentPropertyList();
- }
-
- if (currentFObj.getNameId() == Constants.FO_MARKER) {
- if (nestedMarkerDepth == 0) {
- builderContext.switchMarkerContext(false);
- } else {
- nestedMarkerDepth--;
- }
- }
-
- if (currentFObj.getParent() == null) {
- LOG.debug("endElement for top-level " + currentFObj.getName());
- }
-
- currentFObj = currentFObj.getParent();
- }
-
- /** {@inheritDoc} */
- public void characters(char[] data, int start, int length)
- throws FOPException {
- if (currentFObj != null) {
- currentFObj.characters(data, start, length,
- currentPropertyList, getEffectiveLocator());
- }
- }
-
- /** {@inheritDoc} */
- public void endDocument() throws SAXException {
- currentFObj = null;
- }
-
- /**
- * Finds the {@link Maker} used to create {@link FONode} objects of a particular type
- *
- * @param namespaceURI URI for the namespace of the element
- * @param localName name of the Element
- * @return the ElementMapping.Maker that can create an FO object for this element
- * @throws FOPException if a Maker could not be found for a bound namespace.
- */
- private Maker findFOMaker(String namespaceURI, String localName) throws FOPException {
- Maker maker = elementMappingRegistry.findFOMaker(namespaceURI, localName, locator);
- if (maker instanceof UnknownXMLObj.Maker) {
- FOValidationEventProducer eventProducer
- = FOValidationEventProducer.Provider.get(
- userAgent.getEventBroadcaster());
- String name = (currentFObj != null ? currentFObj.getName()
- : "{" + namespaceURI + "}" + localName);
- eventProducer.unknownFormattingObject(this, name,
- new QName(namespaceURI, localName),
- getEffectiveLocator());
- }
- return maker;
- }
-
- }
- }
|