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 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. * Copyright 1999-2006 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.fo;
  18. import java.io.OutputStream;
  19. import org.apache.commons.logging.Log;
  20. import org.apache.commons.logging.LogFactory;
  21. import org.apache.fop.apps.FOPException;
  22. import org.apache.fop.apps.FOUserAgent;
  23. import org.apache.fop.apps.FormattingResults;
  24. import org.apache.fop.area.AreaTreeHandler;
  25. import org.apache.fop.fo.ElementMapping.Maker;
  26. import org.apache.fop.fo.pagination.Root;
  27. import org.apache.fop.image.ImageFactory;
  28. import org.xml.sax.Attributes;
  29. import org.xml.sax.Locator;
  30. import org.xml.sax.SAXException;
  31. import org.xml.sax.SAXParseException;
  32. import org.xml.sax.helpers.DefaultHandler;
  33. /**
  34. * SAX Handler that passes parsed data to the various
  35. * FO objects, where they can be used either to build
  36. * an FO Tree, or used by Structure Renderers to build
  37. * other data structures.
  38. */
  39. public class FOTreeBuilder extends DefaultHandler {
  40. /** logging instance */
  41. protected Log log = LogFactory.getLog(FOTreeBuilder.class);
  42. /** The registry for ElementMapping instances */
  43. protected ElementMappingRegistry elementMappingRegistry;
  44. /**
  45. * The root of the formatting object tree
  46. */
  47. protected Root rootFObj = null;
  48. /**
  49. * Current formatting object being handled
  50. */
  51. protected FONode currentFObj = null;
  52. /**
  53. * Current propertyList for the node being handled.
  54. */
  55. protected PropertyList currentPropertyList;
  56. /**
  57. * The class that handles formatting and rendering to a stream
  58. * (mark-fop@inomial.com)
  59. */
  60. private FOEventHandler foEventHandler;
  61. /** The SAX locator object managing the line and column counters */
  62. private Locator locator;
  63. /** The user agent for this processing run. */
  64. private FOUserAgent userAgent;
  65. /**
  66. * FOTreeBuilder constructor
  67. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
  68. * @param foUserAgent in effect for this process
  69. * @param stream OutputStream to direct results
  70. * @throws FOPException if the FOTreeBuilder cannot be properly created
  71. */
  72. public FOTreeBuilder(String outputFormat, FOUserAgent foUserAgent,
  73. OutputStream stream) throws FOPException {
  74. this.userAgent = foUserAgent;
  75. //This creates either an AreaTreeHandler and ultimately a Renderer, or
  76. //one of the RTF-, MIF- etc. Handlers.
  77. foEventHandler = foUserAgent.getRendererFactory().createFOEventHandler(
  78. foUserAgent, outputFormat, stream);
  79. foEventHandler.setPropertyListMaker(new PropertyListMaker() {
  80. public PropertyList make(FObj fobj, PropertyList parentPropertyList) {
  81. return new StaticPropertyList(fobj, parentPropertyList);
  82. }
  83. });
  84. this.elementMappingRegistry = new ElementMappingRegistry(foUserAgent);
  85. }
  86. /**
  87. * This method enables to reduce memory consumption of the FO tree slightly. When it returns
  88. * true no Locator is passed to the FO tree nodes which would copy the information into
  89. * a SAX LocatorImpl instance.
  90. * @return true if no context information should be stored on each node in the FO tree.
  91. */
  92. protected boolean isLocatorDisabled() {
  93. //TODO make this configurable through the FOUserAgent so people can optimize memory
  94. //consumption.
  95. return false;
  96. }
  97. /**
  98. * SAX Handler for locator
  99. * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
  100. */
  101. public void setDocumentLocator(Locator locator) {
  102. this.locator = locator;
  103. }
  104. /** @return a Locator instance if it is available and not disabled */
  105. protected Locator getEffectiveLocator() {
  106. return (isLocatorDisabled() ? null : this.locator);
  107. }
  108. /**
  109. * SAX Handler for characters
  110. * @see org.xml.sax.ContentHandler#characters(char[], int, int)
  111. */
  112. public void characters(char[] data, int start, int length)
  113. throws FOPException {
  114. if (currentFObj != null) {
  115. currentFObj.addCharacters(data, start, start + length,
  116. currentPropertyList, getEffectiveLocator());
  117. }
  118. }
  119. /**
  120. * SAX Handler for the start of the document
  121. * @see org.xml.sax.ContentHandler#startDocument()
  122. */
  123. public void startDocument() throws SAXException {
  124. rootFObj = null; // allows FOTreeBuilder to be reused
  125. if (log.isDebugEnabled()) {
  126. log.debug("Building formatting object tree");
  127. }
  128. foEventHandler.startDocument();
  129. }
  130. /**
  131. * SAX Handler for the end of the document
  132. * @see org.xml.sax.ContentHandler#endDocument()
  133. */
  134. public void endDocument() throws SAXException {
  135. rootFObj = null;
  136. currentFObj = null;
  137. if (log.isDebugEnabled()) {
  138. log.debug("Parsing of document complete");
  139. }
  140. foEventHandler.endDocument();
  141. //Notify the image factory that this user agent has expired.
  142. ImageFactory.getInstance().removeContext(this.userAgent);
  143. }
  144. /**
  145. * SAX Handler for the start of an element
  146. * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
  147. */
  148. public void startElement(String namespaceURI, String localName, String rawName,
  149. Attributes attlist) throws SAXException {
  150. /* the node found in the FO document */
  151. FONode foNode;
  152. PropertyList propertyList;
  153. // Check to ensure first node encountered is an fo:root
  154. if (rootFObj == null) {
  155. if (!namespaceURI.equals(FOElementMapping.URI)
  156. || !localName.equals("root")) {
  157. throw new SAXException(new ValidationException(
  158. "Error: First element must be the fo:root formatting object. Found "
  159. + FONode.getNodeString(namespaceURI, localName) + " instead."
  160. + " Please make sure you're producing a valid XSL-FO document."));
  161. }
  162. } else { // check that incoming node is valid for currentFObj
  163. if (namespaceURI.equals(FOElementMapping.URI)) {
  164. // currently no fox: elements to validate
  165. // || namespaceURI.equals(ExtensionElementMapping.URI) */) {
  166. try {
  167. currentFObj.validateChildNode(locator, namespaceURI, localName);
  168. } catch (ValidationException e) {
  169. throw e;
  170. }
  171. }
  172. }
  173. ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName);
  174. // log.debug("found a " + fobjMaker.toString());
  175. try {
  176. foNode = fobjMaker.make(currentFObj);
  177. propertyList = foNode.createPropertyList(currentPropertyList, foEventHandler);
  178. foNode.processNode(localName, getEffectiveLocator(), attlist, propertyList);
  179. foNode.startOfNode();
  180. } catch (IllegalArgumentException e) {
  181. throw new SAXException(e);
  182. }
  183. if (rootFObj == null) {
  184. rootFObj = (Root) foNode;
  185. rootFObj.setFOEventHandler(foEventHandler);
  186. } else {
  187. currentFObj.addChildNode(foNode);
  188. }
  189. currentFObj = foNode;
  190. if (propertyList != null) {
  191. currentPropertyList = propertyList;
  192. }
  193. }
  194. /**
  195. * SAX Handler for the end of an element
  196. * @see org.xml.sax.ContentHandler#endElement(String, String, String)
  197. */
  198. public void endElement(String uri, String localName, String rawName)
  199. throws FOPException {
  200. if (currentFObj == null) {
  201. throw new IllegalStateException(
  202. "endElement() called for " + rawName + " where there is no current element.");
  203. } else if (!currentFObj.getLocalName().equals(localName)
  204. || !currentFObj.getNamespaceURI().equals(uri)) {
  205. log.warn("Mismatch: " + currentFObj.getLocalName()
  206. + " (" + currentFObj.getNamespaceURI()
  207. + ") vs. " + localName + " (" + uri + ")");
  208. }
  209. currentFObj.endOfNode();
  210. if (currentPropertyList.getFObj() == currentFObj) {
  211. currentPropertyList = currentPropertyList.getParentPropertyList();
  212. }
  213. if (currentFObj.getParent() == null) {
  214. log.debug("endElement for top-level " + currentFObj.getName());
  215. }
  216. currentFObj = currentFObj.getParent();
  217. }
  218. /**
  219. * Finds the Maker used to create node objects of a particular type
  220. * @param namespaceURI URI for the namespace of the element
  221. * @param localName name of the Element
  222. * @return the ElementMapping.Maker that can create an FO object for this element
  223. * @throws FOPException if a Maker could not be found for a bound namespace.
  224. */
  225. private Maker findFOMaker(String namespaceURI, String localName) throws FOPException {
  226. return elementMappingRegistry.findFOMaker(namespaceURI, localName, locator);
  227. }
  228. /** @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) */
  229. public void warning(SAXParseException e) {
  230. log.warn(e.toString());
  231. }
  232. /** @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) */
  233. public void error(SAXParseException e) {
  234. log.error(e.toString());
  235. }
  236. /** @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) */
  237. public void fatalError(SAXParseException e) throws SAXException {
  238. log.error(e.toString());
  239. throw e;
  240. }
  241. /**
  242. * Provides access to the underlying FOEventHandler object.
  243. * @return the FOEventHandler object
  244. */
  245. public FOEventHandler getEventHandler() {
  246. return foEventHandler;
  247. }
  248. /**
  249. * Returns the results of the rendering process. Information includes
  250. * the total number of pages generated and the number of pages per
  251. * page-sequence.
  252. * @return the results of the rendering process.
  253. */
  254. public FormattingResults getResults() {
  255. if (getEventHandler() instanceof AreaTreeHandler) {
  256. return ((AreaTreeHandler)getEventHandler()).getResults();
  257. } else {
  258. //No formatting results available for output formats no
  259. //involving the layout engine.
  260. return null;
  261. }
  262. }
  263. /**
  264. * Resets this object for another run.
  265. */
  266. public void reset() {
  267. currentFObj = null;
  268. rootFObj = null;
  269. foEventHandler = null;
  270. }
  271. }