選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

FOTreeBuilder.java 15KB

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