You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FOTreeBuilder.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.fo;
  19. import java.io.OutputStream;
  20. import org.xml.sax.Attributes;
  21. import org.xml.sax.ContentHandler;
  22. import org.xml.sax.Locator;
  23. import org.xml.sax.SAXException;
  24. import org.xml.sax.SAXParseException;
  25. import org.xml.sax.helpers.DefaultHandler;
  26. import org.apache.commons.logging.Log;
  27. import org.apache.commons.logging.LogFactory;
  28. import org.apache.fop.apps.FOPException;
  29. import org.apache.fop.apps.FOUserAgent;
  30. import org.apache.fop.apps.FormattingResults;
  31. import org.apache.fop.area.AreaTreeHandler;
  32. import org.apache.fop.fo.ElementMapping.Maker;
  33. import org.apache.fop.fo.extensions.ExtensionElementMapping;
  34. import org.apache.fop.fo.pagination.Root;
  35. import org.apache.fop.util.ContentHandlerFactory;
  36. import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
  37. import org.apache.fop.util.ContentHandlerFactory.ObjectSource;
  38. /**
  39. * SAX Handler that passes parsed data to the various
  40. * FO objects, where they can be used either to build
  41. * an FO Tree, or used by Structure Renderers to build
  42. * other data structures.
  43. */
  44. public class FOTreeBuilder extends DefaultHandler {
  45. /** logging instance */
  46. protected Log log = LogFactory.getLog(FOTreeBuilder.class);
  47. /** The registry for ElementMapping instances */
  48. protected ElementMappingRegistry elementMappingRegistry;
  49. /** The root of the formatting object tree */
  50. protected Root rootFObj = null;
  51. /** Main DefaultHandler that handles the FO namespace. */
  52. protected MainFOHandler mainFOHandler;
  53. /** Current delegate ContentHandler to receive the SAX events */
  54. protected ContentHandler delegate;
  55. /** The object that handles formatting and rendering to a stream */
  56. private FOEventHandler foEventHandler;
  57. /** The SAX locator object managing the line and column counters */
  58. private Locator locator;
  59. /** The user agent for this processing run. */
  60. private FOUserAgent userAgent;
  61. private boolean used = false;
  62. private boolean empty = true;
  63. private int depth;
  64. /**
  65. * <code>FOTreeBuilder</code> constructor
  66. *
  67. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
  68. * @param foUserAgent the {@link FOUserAgent} in effect for this process
  69. * @param stream the <code>OutputStream</code> to direct the results to
  70. * @throws FOPException if the <code>FOTreeBuilder</code> cannot be properly created
  71. */
  72. public FOTreeBuilder(
  73. String outputFormat,
  74. FOUserAgent foUserAgent,
  75. OutputStream stream)
  76. throws FOPException {
  77. this.userAgent = foUserAgent;
  78. this.elementMappingRegistry = userAgent.getFactory().getElementMappingRegistry();
  79. //This creates either an AreaTreeHandler and ultimately a Renderer, or
  80. //one of the RTF-, MIF- etc. Handlers.
  81. foEventHandler = foUserAgent.getRendererFactory().createFOEventHandler(
  82. foUserAgent, outputFormat, stream);
  83. foEventHandler.setPropertyListMaker(new PropertyListMaker() {
  84. public PropertyList make(FObj fobj, PropertyList parentPropertyList) {
  85. return new StaticPropertyList(fobj, parentPropertyList);
  86. }
  87. });
  88. }
  89. /** {@inheritDoc} */
  90. public void setDocumentLocator(Locator locator) {
  91. this.locator = locator;
  92. }
  93. /**
  94. * @return a {@link Locator} instance if it is available and not disabled
  95. */
  96. protected Locator getEffectiveLocator() {
  97. return (userAgent.isLocatorEnabled() ? this.locator : null);
  98. }
  99. /** {@inheritDoc} */
  100. public void characters(char[] data, int start, int length)
  101. throws SAXException {
  102. delegate.characters(data, start, length);
  103. }
  104. /** {@inheritDoc} */
  105. public void startDocument() throws SAXException {
  106. if (used) {
  107. throw new IllegalStateException("FOTreeBuilder (and the Fop class) cannot be reused."
  108. + " Please instantiate a new instance.");
  109. }
  110. used = true;
  111. empty = true;
  112. rootFObj = null; // allows FOTreeBuilder to be reused
  113. if (log.isDebugEnabled()) {
  114. log.debug("Building formatting object tree");
  115. }
  116. foEventHandler.startDocument();
  117. this.mainFOHandler = new MainFOHandler();
  118. this.mainFOHandler.startDocument();
  119. this.delegate = this.mainFOHandler;
  120. }
  121. /** {@inheritDoc} */
  122. public void endDocument() throws SAXException {
  123. this.delegate.endDocument();
  124. if (this.rootFObj == null && empty) {
  125. throw new ValidationException(
  126. "Document is empty (something might be wrong with your XSLT stylesheet).");
  127. }
  128. rootFObj = null;
  129. if (log.isDebugEnabled()) {
  130. log.debug("Parsing of document complete");
  131. }
  132. foEventHandler.endDocument();
  133. }
  134. /** {@inheritDoc} */
  135. public void startElement(String namespaceURI, String localName, String rawName,
  136. Attributes attlist) throws SAXException {
  137. this.depth++;
  138. delegate.startElement(namespaceURI, localName, rawName, attlist);
  139. }
  140. /** {@inheritDoc} */
  141. public void endElement(String uri, String localName, String rawName)
  142. throws SAXException {
  143. this.delegate.endElement(uri, localName, rawName);
  144. this.depth--;
  145. if (depth == 0) {
  146. if (delegate != mainFOHandler) {
  147. //Return from sub-handler back to main handler
  148. delegate.endDocument();
  149. delegate = mainFOHandler;
  150. delegate.endElement(uri, localName, rawName);
  151. }
  152. }
  153. }
  154. /**
  155. * Finds the {@link Maker} used to create {@link FONode} objects of a particular type
  156. *
  157. * @param namespaceURI URI for the namespace of the element
  158. * @param localName name of the Element
  159. * @return the ElementMapping.Maker that can create an FO object for this element
  160. * @throws FOPException if a Maker could not be found for a bound namespace.
  161. */
  162. private Maker findFOMaker(String namespaceURI, String localName) throws FOPException {
  163. return elementMappingRegistry.findFOMaker(namespaceURI, localName, locator);
  164. }
  165. /** {@inheritDoc} */
  166. public void warning(SAXParseException e) {
  167. log.warn(e.getLocalizedMessage());
  168. }
  169. /** {@inheritDoc} */
  170. public void error(SAXParseException e) {
  171. log.error(e.toString());
  172. }
  173. /** {@inheritDoc} */
  174. public void fatalError(SAXParseException e) throws SAXException {
  175. log.error(e.toString());
  176. throw e;
  177. }
  178. /**
  179. * Provides access to the underlying {@link FOEventHandler} object.
  180. *
  181. * @return the FOEventHandler object
  182. */
  183. public FOEventHandler getEventHandler() {
  184. return foEventHandler;
  185. }
  186. /**
  187. * Returns the results of the rendering process. Information includes
  188. * the total number of pages generated and the number of pages per
  189. * page-sequence.
  190. *
  191. * @return the results of the rendering process.
  192. */
  193. public FormattingResults getResults() {
  194. if (getEventHandler() instanceof AreaTreeHandler) {
  195. return ((AreaTreeHandler) getEventHandler()).getResults();
  196. } else {
  197. //No formatting results available for output formats no
  198. //involving the layout engine.
  199. return null;
  200. }
  201. }
  202. /**
  203. * Main <code>DefaultHandler</code> implementation which builds the FO tree.
  204. */
  205. private class MainFOHandler extends DefaultHandler {
  206. /** Current formatting object being handled */
  207. protected FONode currentFObj = null;
  208. /** Current propertyList for the node being handled */
  209. protected PropertyList currentPropertyList;
  210. /** Current marker nesting-depth */
  211. private int nestedMarkerDepth = 0;
  212. /** {@inheritDoc} */
  213. public void startElement(String namespaceURI, String localName, String rawName,
  214. Attributes attlist) throws SAXException {
  215. /* the node found in the FO document */
  216. FONode foNode;
  217. PropertyList propertyList = null;
  218. // Check to ensure first node encountered is an fo:root
  219. if (rootFObj == null) {
  220. empty = false;
  221. if (!namespaceURI.equals(FOElementMapping.URI)
  222. || !localName.equals("root")) {
  223. throw new ValidationException(
  224. "Error: First element must be the fo:root formatting object. "
  225. + "Found " + FONode.getNodeString(namespaceURI, localName)
  226. + " instead."
  227. + " Please make sure you're producing a valid XSL-FO document.");
  228. }
  229. } else { // check that incoming node is valid for currentFObj
  230. if (namespaceURI.equals(FOElementMapping.URI)
  231. || namespaceURI.equals(ExtensionElementMapping.URI)) {
  232. currentFObj.validateChildNode(locator, namespaceURI, localName);
  233. }
  234. }
  235. ElementMapping.Maker fobjMaker =
  236. findFOMaker(namespaceURI, localName);
  237. try {
  238. foNode = fobjMaker.make(currentFObj);
  239. if (rootFObj == null) {
  240. rootFObj = (Root) foNode;
  241. rootFObj.setFOEventHandler(foEventHandler);
  242. }
  243. propertyList = foNode.createPropertyList(
  244. currentPropertyList, foEventHandler);
  245. foNode.processNode(localName, getEffectiveLocator(),
  246. attlist, propertyList);
  247. if (foNode.getNameId() == Constants.FO_MARKER) {
  248. if (foEventHandler.inMarker()) {
  249. nestedMarkerDepth++;
  250. } else {
  251. foEventHandler.switchMarkerContext(true);
  252. }
  253. }
  254. foNode.startOfNode();
  255. } catch (IllegalArgumentException e) {
  256. throw new SAXException(e);
  257. }
  258. ContentHandlerFactory chFactory = foNode.getContentHandlerFactory();
  259. if (chFactory != null) {
  260. ContentHandler subHandler = chFactory.createContentHandler();
  261. if (subHandler instanceof ObjectSource
  262. && foNode instanceof ObjectBuiltListener) {
  263. ((ObjectSource) subHandler).setObjectBuiltListener(
  264. (ObjectBuiltListener) foNode);
  265. }
  266. subHandler.startDocument();
  267. subHandler.startElement(namespaceURI, localName,
  268. rawName, attlist);
  269. depth = 1;
  270. delegate = subHandler;
  271. }
  272. if (currentFObj != null) {
  273. currentFObj.addChildNode(foNode);
  274. }
  275. currentFObj = foNode;
  276. if (propertyList != null && !foEventHandler.inMarker()) {
  277. currentPropertyList = propertyList;
  278. }
  279. }
  280. /** {@inheritDoc} */
  281. public void endElement(String uri, String localName, String rawName)
  282. throws SAXException {
  283. if (currentFObj == null) {
  284. throw new SAXException(
  285. "endElement() called for " + rawName
  286. + " where there is no current element.");
  287. } else if (!currentFObj.getLocalName().equals(localName)
  288. || !currentFObj.getNamespaceURI().equals(uri)) {
  289. throw new SAXException("Mismatch: " + currentFObj.getLocalName()
  290. + " (" + currentFObj.getNamespaceURI()
  291. + ") vs. " + localName + " (" + uri + ")");
  292. }
  293. currentFObj.endOfNode();
  294. if (currentPropertyList != null
  295. && currentPropertyList.getFObj() == currentFObj
  296. && !foEventHandler.inMarker()) {
  297. currentPropertyList =
  298. currentPropertyList.getParentPropertyList();
  299. }
  300. if (currentFObj.getNameId() == Constants.FO_MARKER) {
  301. if (nestedMarkerDepth == 0) {
  302. foEventHandler.switchMarkerContext(false);
  303. } else {
  304. nestedMarkerDepth--;
  305. }
  306. }
  307. if (currentFObj.getParent() == null) {
  308. log.debug("endElement for top-level " + currentFObj.getName());
  309. }
  310. currentFObj = currentFObj.getParent();
  311. }
  312. /** {@inheritDoc} */
  313. public void characters(char[] data, int start, int length)
  314. throws FOPException {
  315. if (currentFObj != null) {
  316. currentFObj.addCharacters(data, start, start + length,
  317. currentPropertyList, getEffectiveLocator());
  318. }
  319. }
  320. /** {@inheritDoc} */
  321. public void endDocument() throws SAXException {
  322. currentFObj = null;
  323. }
  324. }
  325. }