Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

FOTreeBuilder.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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.apache.fop.util.ContentHandlerFactory;
  29. import org.apache.fop.util.ContentHandlerFactory.ObjectSource;
  30. import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
  31. import org.xml.sax.Attributes;
  32. import org.xml.sax.ContentHandler;
  33. import org.xml.sax.Locator;
  34. import org.xml.sax.SAXException;
  35. import org.xml.sax.SAXParseException;
  36. import org.xml.sax.helpers.DefaultHandler;
  37. /**
  38. * SAX Handler that passes parsed data to the various
  39. * FO objects, where they can be used either to build
  40. * an FO Tree, or used by Structure Renderers to build
  41. * other data structures.
  42. */
  43. public class FOTreeBuilder extends DefaultHandler {
  44. /** logging instance */
  45. protected Log log = LogFactory.getLog(FOTreeBuilder.class);
  46. /** The registry for ElementMapping instances */
  47. protected ElementMappingRegistry elementMappingRegistry;
  48. /**
  49. * The root of the formatting object tree
  50. */
  51. protected Root rootFObj = null;
  52. /** Main DefaultHandler that handles the FO namespace. */
  53. protected MainFOHandler mainFOHandler;
  54. /** Current delegate ContentHandler to receive the SAX events */
  55. protected ContentHandler delegate;
  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. private boolean used = false;
  66. private int depth;
  67. /**
  68. * FOTreeBuilder constructor
  69. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
  70. * @param foUserAgent in effect for this process
  71. * @param stream OutputStream to direct results
  72. * @throws FOPException if the FOTreeBuilder cannot be properly created
  73. */
  74. public FOTreeBuilder(String outputFormat, FOUserAgent foUserAgent,
  75. OutputStream stream) throws FOPException {
  76. this.userAgent = foUserAgent;
  77. this.elementMappingRegistry = userAgent.getFactory().getElementMappingRegistry();
  78. //This creates either an AreaTreeHandler and ultimately a Renderer, or
  79. //one of the RTF-, MIF- etc. Handlers.
  80. foEventHandler = foUserAgent.getRendererFactory().createFOEventHandler(
  81. foUserAgent, outputFormat, stream);
  82. foEventHandler.setPropertyListMaker(new PropertyListMaker() {
  83. public PropertyList make(FObj fobj, PropertyList parentPropertyList) {
  84. return new StaticPropertyList(fobj, parentPropertyList);
  85. }
  86. });
  87. }
  88. /**
  89. * This method enables to reduce memory consumption of the FO tree slightly. When it returns
  90. * true no Locator is passed to the FO tree nodes which would copy the information into
  91. * a SAX LocatorImpl instance.
  92. * @return true if no context information should be stored on each node in the FO tree.
  93. */
  94. protected boolean isLocatorDisabled() {
  95. //TODO make this configurable through the FOUserAgent so people can optimize memory
  96. //consumption.
  97. return false;
  98. }
  99. /**
  100. * SAX Handler for locator
  101. * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
  102. */
  103. public void setDocumentLocator(Locator locator) {
  104. this.locator = locator;
  105. }
  106. /** @return a Locator instance if it is available and not disabled */
  107. protected Locator getEffectiveLocator() {
  108. return (isLocatorDisabled() ? null : this.locator);
  109. }
  110. /**
  111. * SAX Handler for characters
  112. * @see org.xml.sax.ContentHandler#characters(char[], int, int)
  113. */
  114. public void characters(char[] data, int start, int length)
  115. throws SAXException {
  116. delegate.characters(data, start, length);
  117. }
  118. /**
  119. * SAX Handler for the start of the document
  120. * @see org.xml.sax.ContentHandler#startDocument()
  121. */
  122. public void startDocument() throws SAXException {
  123. if (used) {
  124. throw new IllegalStateException("FOTreeBuilder (and the Fop class) cannot be reused."
  125. + " Please instantiate a new instance.");
  126. }
  127. used = true;
  128. rootFObj = null; // allows FOTreeBuilder to be reused
  129. if (log.isDebugEnabled()) {
  130. log.debug("Building formatting object tree");
  131. }
  132. foEventHandler.startDocument();
  133. this.mainFOHandler = new MainFOHandler();
  134. this.mainFOHandler.startDocument();
  135. this.delegate = this.mainFOHandler;
  136. }
  137. /**
  138. * SAX Handler for the end of the document
  139. * @see org.xml.sax.ContentHandler#endDocument()
  140. */
  141. public void endDocument() throws SAXException {
  142. this.delegate.endDocument();
  143. rootFObj = null;
  144. if (log.isDebugEnabled()) {
  145. log.debug("Parsing of document complete");
  146. }
  147. foEventHandler.endDocument();
  148. //Notify the image factory that this user agent has expired.
  149. ImageFactory imageFactory = userAgent.getFactory().getImageFactory();
  150. imageFactory.removeContext(this.userAgent);
  151. }
  152. /**
  153. * SAX Handler for the start of an element
  154. * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
  155. */
  156. public void startElement(String namespaceURI, String localName, String rawName,
  157. Attributes attlist) throws SAXException {
  158. this.depth++;
  159. delegate.startElement(namespaceURI, localName, rawName, attlist);
  160. }
  161. /**
  162. * SAX Handler for the end of an element
  163. * @see org.xml.sax.ContentHandler#endElement(String, String, String)
  164. */
  165. public void endElement(String uri, String localName, String rawName)
  166. throws SAXException {
  167. this.delegate.endElement(uri, localName, rawName);
  168. this.depth--;
  169. if (depth == 0) {
  170. if (delegate != mainFOHandler) {
  171. //Return from sub-handler back to main handler
  172. delegate.endDocument();
  173. delegate = mainFOHandler;
  174. delegate.endElement(uri, localName, rawName);
  175. }
  176. }
  177. }
  178. /**
  179. * Finds the Maker used to create node objects of a particular type
  180. * @param namespaceURI URI for the namespace of the element
  181. * @param localName name of the Element
  182. * @return the ElementMapping.Maker that can create an FO object for this element
  183. * @throws FOPException if a Maker could not be found for a bound namespace.
  184. */
  185. private Maker findFOMaker(String namespaceURI, String localName) throws FOPException {
  186. return elementMappingRegistry.findFOMaker(namespaceURI, localName, locator);
  187. }
  188. /** @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) */
  189. public void warning(SAXParseException e) {
  190. log.warn(e.toString());
  191. }
  192. /** @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) */
  193. public void error(SAXParseException e) {
  194. log.error(e.toString());
  195. }
  196. /** @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) */
  197. public void fatalError(SAXParseException e) throws SAXException {
  198. log.error(e.toString());
  199. throw e;
  200. }
  201. /**
  202. * Provides access to the underlying FOEventHandler object.
  203. * @return the FOEventHandler object
  204. */
  205. public FOEventHandler getEventHandler() {
  206. return foEventHandler;
  207. }
  208. /**
  209. * Returns the results of the rendering process. Information includes
  210. * the total number of pages generated and the number of pages per
  211. * page-sequence.
  212. * @return the results of the rendering process.
  213. */
  214. public FormattingResults getResults() {
  215. if (getEventHandler() instanceof AreaTreeHandler) {
  216. return ((AreaTreeHandler)getEventHandler()).getResults();
  217. } else {
  218. //No formatting results available for output formats no
  219. //involving the layout engine.
  220. return null;
  221. }
  222. }
  223. /**
  224. * Main DefaultHandler implementation which builds the FO tree.
  225. */
  226. private class MainFOHandler extends DefaultHandler {
  227. /**
  228. * Current formatting object being handled
  229. */
  230. protected FONode currentFObj = null;
  231. /**
  232. * Current propertyList for the node being handled.
  233. */
  234. protected PropertyList currentPropertyList;
  235. /**
  236. * SAX Handler for the start of an element
  237. * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
  238. */
  239. public void startElement(String namespaceURI, String localName, String rawName,
  240. Attributes attlist) throws SAXException {
  241. /* the node found in the FO document */
  242. FONode foNode;
  243. PropertyList propertyList;
  244. // Check to ensure first node encountered is an fo:root
  245. if (rootFObj == null) {
  246. if (!namespaceURI.equals(FOElementMapping.URI)
  247. || !localName.equals("root")) {
  248. throw new SAXException(new ValidationException(
  249. "Error: First element must be the fo:root formatting object. Found "
  250. + FONode.getNodeString(namespaceURI, localName) + " instead."
  251. + " Please make sure you're producing a valid XSL-FO document."));
  252. }
  253. } else { // check that incoming node is valid for currentFObj
  254. if (namespaceURI.equals(FOElementMapping.URI)) {
  255. // currently no fox: elements to validate
  256. // || namespaceURI.equals(ExtensionElementMapping.URI) */) {
  257. try {
  258. currentFObj.validateChildNode(locator, namespaceURI, localName);
  259. } catch (ValidationException e) {
  260. throw e;
  261. }
  262. }
  263. }
  264. ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName);
  265. try {
  266. foNode = fobjMaker.make(currentFObj);
  267. propertyList = foNode.createPropertyList(currentPropertyList, foEventHandler);
  268. foNode.processNode(localName, getEffectiveLocator(), attlist, propertyList);
  269. foNode.startOfNode();
  270. } catch (IllegalArgumentException e) {
  271. throw new SAXException(e);
  272. }
  273. ContentHandlerFactory chFactory = foNode.getContentHandlerFactory();
  274. if (chFactory != null) {
  275. ContentHandler subHandler = chFactory.createContentHandler();
  276. if (subHandler instanceof ObjectSource
  277. && foNode instanceof ObjectBuiltListener) {
  278. ((ObjectSource)subHandler).setObjectBuiltListener((ObjectBuiltListener)foNode);
  279. }
  280. subHandler.startDocument();
  281. subHandler.startElement(namespaceURI, localName, rawName, attlist);
  282. depth = 1;
  283. delegate = subHandler;
  284. }
  285. if (rootFObj == null) {
  286. rootFObj = (Root) foNode;
  287. rootFObj.setFOEventHandler(foEventHandler);
  288. } else {
  289. currentFObj.addChildNode(foNode);
  290. }
  291. currentFObj = foNode;
  292. if (propertyList != null) {
  293. currentPropertyList = propertyList;
  294. }
  295. }
  296. /**
  297. * SAX Handler for the end of an element
  298. * @see org.xml.sax.ContentHandler#endElement(String, String, String)
  299. */
  300. public void endElement(String uri, String localName, String rawName)
  301. throws SAXException {
  302. if (currentFObj == null) {
  303. throw new IllegalStateException(
  304. "endElement() called for " + rawName
  305. + " where there is no current element.");
  306. } else if (!currentFObj.getLocalName().equals(localName)
  307. || !currentFObj.getNamespaceURI().equals(uri)) {
  308. log.warn("Mismatch: " + currentFObj.getLocalName()
  309. + " (" + currentFObj.getNamespaceURI()
  310. + ") vs. " + localName + " (" + uri + ")");
  311. }
  312. currentFObj.endOfNode();
  313. if (currentPropertyList.getFObj() == currentFObj) {
  314. currentPropertyList = currentPropertyList.getParentPropertyList();
  315. }
  316. if (currentFObj.getParent() == null) {
  317. log.debug("endElement for top-level " + currentFObj.getName());
  318. }
  319. currentFObj = currentFObj.getParent();
  320. }
  321. /**
  322. * SAX Handler for characters
  323. * @see org.xml.sax.ContentHandler#characters(char[], int, int)
  324. */
  325. public void characters(char[] data, int start, int length)
  326. throws FOPException {
  327. if (currentFObj != null) {
  328. currentFObj.addCharacters(data, start, start + length,
  329. currentPropertyList, getEffectiveLocator());
  330. }
  331. }
  332. public void endDocument() throws SAXException {
  333. currentFObj = null;
  334. }
  335. }
  336. }