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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /*
  2. * Copyright 1999-2005 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 java.util.Iterator;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.Set;
  23. import org.apache.commons.logging.Log;
  24. import org.apache.commons.logging.LogFactory;
  25. import org.apache.fop.apps.FOPException;
  26. import org.apache.fop.apps.FOUserAgent;
  27. import org.apache.fop.apps.FormattingResults;
  28. import org.apache.fop.area.AreaTreeHandler;
  29. import org.apache.fop.util.Service;
  30. import org.apache.fop.fo.ElementMapping.Maker;
  31. import org.apache.fop.fo.pagination.Root;
  32. import org.apache.fop.image.ImageFactory;
  33. import org.xml.sax.Attributes;
  34. import org.xml.sax.Locator;
  35. import org.xml.sax.SAXException;
  36. import org.xml.sax.SAXParseException;
  37. import org.xml.sax.helpers.DefaultHandler;
  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. /**
  46. * Table mapping element names to the makers of objects
  47. * representing formatting objects.
  48. */
  49. protected Map fobjTable = new java.util.HashMap();
  50. /**
  51. * logging instance
  52. */
  53. protected Log log = LogFactory.getLog(FOTreeBuilder.class);
  54. /**
  55. * Set of mapped namespaces.
  56. */
  57. protected Set namespaces = new java.util.HashSet();
  58. /**
  59. * The root of the formatting object tree
  60. */
  61. protected Root rootFObj = null;
  62. /**
  63. * Current formatting object being handled
  64. */
  65. protected FONode currentFObj = null;
  66. /**
  67. * Current propertyList for the node being handled.
  68. */
  69. protected PropertyList currentPropertyList;
  70. /**
  71. * The class that handles formatting and rendering to a stream
  72. * (mark-fop@inomial.com)
  73. */
  74. private FOEventHandler foEventHandler;
  75. /** The SAX locator object managing the line and column counters */
  76. private Locator locator;
  77. /** The user agent for this processing run. */
  78. private FOUserAgent userAgent;
  79. /**
  80. * FOTreeBuilder constructor
  81. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
  82. * @param foUserAgent in effect for this process
  83. * @param stream OutputStream to direct results
  84. * @throws FOPException if the FOTreeBuilder cannot be properly created
  85. */
  86. public FOTreeBuilder(String outputFormat, FOUserAgent foUserAgent,
  87. OutputStream stream) throws FOPException {
  88. this.userAgent = foUserAgent;
  89. //This creates either an AreaTreeHandler and ultimately a Renderer, or
  90. //one of the RTF-, MIF- etc. Handlers.
  91. foEventHandler = foUserAgent.getRendererFactory().createFOEventHandler(
  92. foUserAgent, outputFormat, stream);
  93. foEventHandler.setPropertyListMaker(new PropertyListMaker() {
  94. public PropertyList make(FObj fobj, PropertyList parentPropertyList) {
  95. return new StaticPropertyList(fobj, parentPropertyList);
  96. }
  97. });
  98. // Add standard element mappings
  99. setupDefaultMappings();
  100. // add additional ElementMappings defined within FOUserAgent
  101. List addlEMs = foUserAgent.getAdditionalElementMappings();
  102. if (addlEMs != null) {
  103. for (int i = 0; i < addlEMs.size(); i++) {
  104. addElementMapping((ElementMapping) addlEMs.get(i));
  105. }
  106. }
  107. }
  108. /**
  109. * Sets all the element and property list mappings to their default values.
  110. *
  111. */
  112. private void setupDefaultMappings() {
  113. addElementMapping("org.apache.fop.fo.FOElementMapping");
  114. addElementMapping("org.apache.fop.fo.extensions.svg.SVGElementMapping");
  115. addElementMapping("org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping");
  116. addElementMapping("org.apache.fop.fo.extensions.ExtensionElementMapping");
  117. addElementMapping("org.apache.fop.render.ps.extensions.PSExtensionElementMapping");
  118. // add mappings from available services
  119. Iterator providers = Service.providers(ElementMapping.class);
  120. if (providers != null) {
  121. while (providers.hasNext()) {
  122. String str = (String)providers.next();
  123. try {
  124. addElementMapping(str);
  125. } catch (IllegalArgumentException e) {
  126. log.warn("Error while adding element mapping", e);
  127. }
  128. }
  129. }
  130. }
  131. /**
  132. * Add the element mapping with the given class name.
  133. * @param mappingClassName the class name representing the element mapping.
  134. * @throws IllegalArgumentException if there was not such element mapping.
  135. */
  136. public void addElementMapping(String mappingClassName)
  137. throws IllegalArgumentException {
  138. try {
  139. ElementMapping mapping
  140. = (ElementMapping)Class.forName(mappingClassName).newInstance();
  141. addElementMapping(mapping);
  142. } catch (ClassNotFoundException e) {
  143. throw new IllegalArgumentException("Could not find "
  144. + mappingClassName);
  145. } catch (InstantiationException e) {
  146. throw new IllegalArgumentException("Could not instantiate "
  147. + mappingClassName);
  148. } catch (IllegalAccessException e) {
  149. throw new IllegalArgumentException("Could not access "
  150. + mappingClassName);
  151. } catch (ClassCastException e) {
  152. throw new IllegalArgumentException(mappingClassName
  153. + " is not an ElementMapping");
  154. }
  155. }
  156. private void addElementMapping(ElementMapping mapping) {
  157. this.fobjTable.put(mapping.getNamespaceURI(), mapping.getTable());
  158. this.namespaces.add(mapping.getNamespaceURI().intern());
  159. }
  160. /**
  161. * SAX Handler for locator
  162. * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
  163. */
  164. public void setDocumentLocator(Locator locator) {
  165. this.locator = locator;
  166. }
  167. /**
  168. * SAX Handler for characters
  169. * @see org.xml.sax.ContentHandler#characters(char[], int, int)
  170. */
  171. public void characters(char[] data, int start, int length)
  172. throws FOPException {
  173. if (currentFObj != null) {
  174. currentFObj.addCharacters(data, start, start + length,
  175. currentPropertyList, locator);
  176. }
  177. }
  178. /**
  179. * SAX Handler for the start of the document
  180. * @see org.xml.sax.ContentHandler#startDocument()
  181. */
  182. public void startDocument() throws SAXException {
  183. rootFObj = null; // allows FOTreeBuilder to be reused
  184. if (log.isDebugEnabled()) {
  185. log.debug("Building formatting object tree");
  186. }
  187. foEventHandler.startDocument();
  188. }
  189. /**
  190. * SAX Handler for the end of the document
  191. * @see org.xml.sax.ContentHandler#endDocument()
  192. */
  193. public void endDocument() throws SAXException {
  194. rootFObj = null;
  195. currentFObj = null;
  196. if (log.isDebugEnabled()) {
  197. log.debug("Parsing of document complete");
  198. }
  199. foEventHandler.endDocument();
  200. //Notify the image factory that this user agent has expired.
  201. ImageFactory.getInstance().removeContext(this.userAgent);
  202. }
  203. /**
  204. * SAX Handler for the start of an element
  205. * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
  206. */
  207. public void startElement(String namespaceURI, String localName, String rawName,
  208. Attributes attlist) throws SAXException {
  209. /* the node found in the FO document */
  210. FONode foNode;
  211. PropertyList propertyList;
  212. // Check to ensure first node encountered is an fo:root
  213. if (rootFObj == null) {
  214. if (!namespaceURI.equals(FOElementMapping.URI)
  215. || !localName.equals("root")) {
  216. throw new SAXException(new ValidationException(
  217. "Error: First element must be the fo:root formatting object. Found "
  218. + FONode.getNodeString(namespaceURI, localName) + " instead."
  219. + " Please make sure you're producing a valid XSL-FO document."));
  220. }
  221. } else { // check that incoming node is valid for currentFObj
  222. if (namespaceURI.equals(FOElementMapping.URI)) {
  223. // currently no fox: elements to validate
  224. // || namespaceURI.equals(ExtensionElementMapping.URI) */) {
  225. try {
  226. currentFObj.validateChildNode(locator, namespaceURI, localName);
  227. } catch (ValidationException e) {
  228. throw e;
  229. }
  230. }
  231. }
  232. ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName);
  233. // System.out.println("found a " + fobjMaker.toString());
  234. try {
  235. foNode = fobjMaker.make(currentFObj);
  236. propertyList = foNode.createPropertyList(currentPropertyList, foEventHandler);
  237. foNode.processNode(localName, locator, attlist, propertyList);
  238. foNode.startOfNode();
  239. } catch (IllegalArgumentException e) {
  240. throw new SAXException(e);
  241. }
  242. if (rootFObj == null) {
  243. rootFObj = (Root) foNode;
  244. rootFObj.setFOEventHandler(foEventHandler);
  245. } else {
  246. currentFObj.addChildNode(foNode);
  247. }
  248. currentFObj = foNode;
  249. if (propertyList != null) {
  250. currentPropertyList = propertyList;
  251. }
  252. }
  253. /**
  254. * SAX Handler for the end of an element
  255. * @see org.xml.sax.ContentHandler#endElement(String, String, String)
  256. */
  257. public void endElement(String uri, String localName, String rawName)
  258. throws FOPException {
  259. if (currentFObj == null) {
  260. throw new IllegalStateException(
  261. "endElement() called for " + rawName + " where there is no current element.");
  262. } else if (!currentFObj.getLocalName().equals(localName)
  263. || !currentFObj.getNamespaceURI().equals(uri)) {
  264. log.warn("Mismatch: " + currentFObj.getLocalName()
  265. + " (" + currentFObj.getNamespaceURI()
  266. + ") vs. " + localName + " (" + uri + ")");
  267. }
  268. currentFObj.endOfNode();
  269. if (currentPropertyList.getFObj() == currentFObj) {
  270. currentPropertyList = currentPropertyList.getParentPropertyList();
  271. }
  272. if (currentFObj.getParent() == null) {
  273. log.debug("endElement for top-level " + currentFObj.getName());
  274. }
  275. currentFObj = currentFObj.getParent();
  276. }
  277. /**
  278. * Finds the Maker used to create node objects of a particular type
  279. * @param namespaceURI URI for the namespace of the element
  280. * @param localName name of the Element
  281. * @return the ElementMapping.Maker that can create an FO object for this element
  282. * @throws FOPException if a Maker could not be found for a bound namespace.
  283. */
  284. private Maker findFOMaker(String namespaceURI, String localName) throws FOPException {
  285. Map table = (Map)fobjTable.get(namespaceURI);
  286. Maker fobjMaker = null;
  287. if (table != null) {
  288. fobjMaker = (ElementMapping.Maker)table.get(localName);
  289. // try default
  290. if (fobjMaker == null) {
  291. fobjMaker = (ElementMapping.Maker)table.get(ElementMapping.DEFAULT);
  292. }
  293. }
  294. if (fobjMaker == null) {
  295. if (namespaces.contains(namespaceURI.intern())) {
  296. throw new FOPException(FONode.errorText(locator)
  297. + "No element mapping definition found for "
  298. + FONode.getNodeString(namespaceURI, localName), locator);
  299. } else {
  300. log.warn("Unknown formatting object " + namespaceURI + "^" + localName);
  301. fobjMaker = new UnknownXMLObj.Maker(namespaceURI);
  302. }
  303. }
  304. return fobjMaker;
  305. }
  306. /** @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) */
  307. public void warning(SAXParseException e) {
  308. log.warn(e.toString());
  309. }
  310. /** @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) */
  311. public void error(SAXParseException e) {
  312. log.error(e.toString());
  313. }
  314. /** @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) */
  315. public void fatalError(SAXParseException e) throws SAXException {
  316. log.error(e.toString());
  317. throw e;
  318. }
  319. /**
  320. * Provides access to the underlying FOEventHandler object.
  321. * @return the FOEventHandler object
  322. */
  323. public FOEventHandler getEventHandler() {
  324. return foEventHandler;
  325. }
  326. /**
  327. * Returns the results of the rendering process. Information includes
  328. * the total number of pages generated and the number of pages per
  329. * page-sequence.
  330. * @return the results of the rendering process.
  331. */
  332. public FormattingResults getResults() {
  333. if (getEventHandler() instanceof AreaTreeHandler) {
  334. return ((AreaTreeHandler)getEventHandler()).getResults();
  335. } else {
  336. //No formatting results available for output formats no
  337. //involving the layout engine.
  338. return null;
  339. }
  340. }
  341. /**
  342. * Resets this object for another run.
  343. */
  344. public void reset() {
  345. currentFObj = null;
  346. rootFObj = null;
  347. foEventHandler = null;
  348. }
  349. }