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.

Driver.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. /*
  2. * $Id$
  3. * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
  4. * For details on use and redistribution please refer to the
  5. * LICENSE file included with these sources.
  6. */
  7. package org.apache.fop.apps;
  8. // FOP
  9. import org.apache.fop.fo.FOUserAgent;
  10. import org.apache.fop.fo.FOTreeBuilder;
  11. import org.apache.fop.fo.ElementMapping;
  12. import org.apache.fop.render.Renderer;
  13. import org.apache.fop.configuration.ConfigurationReader;
  14. import org.apache.fop.configuration.Configuration;
  15. import org.apache.fop.tools.DocumentInputSource;
  16. import org.apache.fop.tools.DocumentReader;
  17. import org.apache.fop.render.pdf.PDFRenderer;
  18. // Avalon
  19. import org.apache.avalon.framework.logger.ConsoleLogger;
  20. import org.apache.avalon.framework.logger.Logger;
  21. import org.apache.avalon.framework.logger.LogEnabled;
  22. import org.apache.avalon.framework.logger.AbstractLogEnabled;
  23. // DOM
  24. import org.w3c.dom.Document;
  25. import org.w3c.dom.Node;
  26. import org.w3c.dom.NamedNodeMap;
  27. import org.w3c.dom.Attr;
  28. // SAX
  29. import org.xml.sax.ContentHandler;
  30. import org.xml.sax.InputSource;
  31. import org.xml.sax.XMLReader;
  32. import org.xml.sax.SAXException;
  33. import org.xml.sax.helpers.AttributesImpl;
  34. // Java
  35. import java.io.*;
  36. import java.util.*;
  37. /**
  38. * Primary class that drives overall FOP process.
  39. * <P>
  40. * The simplest way to use this is to instantiate it with the
  41. * InputSource and OutputStream, then set the renderer desired, and
  42. * calling run();
  43. * <P>
  44. * Here is an example use of Driver which outputs PDF:
  45. *
  46. * <PRE>
  47. * Driver driver = new Driver(new InputSource (args[0]),
  48. * new FileOutputStream(args[1]));
  49. * driver.enableLogging(myLogger); //optional
  50. * driver.setRenderer(RENDER_PDF);
  51. * driver.run();
  52. * </PRE>
  53. * If neccessary, calling classes can call into the lower level
  54. * methods to setup and
  55. * render. Methods can be called to set the
  56. * Renderer to use, the (possibly multiple) ElementMapping(s) to
  57. * use and the OutputStream to use to output the results of the
  58. * rendering (where applicable). In the case of the Renderer and
  59. * ElementMapping(s), the Driver may be supplied either with the
  60. * object itself, or the name of the class, in which case Driver will
  61. * instantiate the class itself. The advantage of the latter is it
  62. * enables runtime determination of Renderer and ElementMapping(s).
  63. * <P>
  64. * Once the Driver is set up, the render method
  65. * is called. Depending on whether DOM or SAX is being used, the
  66. * invocation of the method is either render(Document) or
  67. * buildFOTree(Parser, InputSource) respectively.
  68. * <P>
  69. * A third possibility may be used to build the FO Tree, namely
  70. * calling getContentHandler() and firing the SAX events yourself.
  71. * <P>
  72. * Once the FO Tree is built, the format() and render() methods may be
  73. * called in that order.
  74. * <P>
  75. * Here is an example use of Driver which outputs to AWT:
  76. *
  77. * <PRE>
  78. * Driver driver = new Driver();
  79. * driver.enableLogging(myLogger); //optional
  80. * driver.setRenderer(new org.apache.fop.render.awt.AWTRenderer(translator));
  81. * driver.render(parser, fileInputSource(args[0]));
  82. * </PRE>
  83. */
  84. public class Driver implements LogEnabled {
  85. /**
  86. * Render to PDF. OutputStream must be set
  87. */
  88. public static final int RENDER_PDF = 1;
  89. /**
  90. * Render to a GUI window. No OutputStream neccessary
  91. */
  92. public static final int RENDER_AWT = 2;
  93. /**
  94. * Render to MIF. OutputStream must be set
  95. */
  96. public static final int RENDER_MIF = 3;
  97. /**
  98. * Render to XML. OutputStream must be set
  99. */
  100. public static final int RENDER_XML = 4;
  101. /**
  102. * Render to PRINT. No OutputStream neccessary
  103. */
  104. public static final int RENDER_PRINT = 5;
  105. /**
  106. * Render to PCL. OutputStream must be set
  107. */
  108. public static final int RENDER_PCL = 6;
  109. /**
  110. * Render to Postscript. OutputStream must be set
  111. */
  112. public static final int RENDER_PS = 7;
  113. /**
  114. * Render to Text. OutputStream must be set
  115. */
  116. public static final int RENDER_TXT = 8;
  117. /**
  118. * Render to SVG. OutputStream must be set
  119. */
  120. public static final int RENDER_SVG = 9;
  121. /**
  122. * the FO tree builder
  123. */
  124. private FOTreeBuilder _treeBuilder;
  125. /**
  126. * the renderer to use to output the area tree
  127. */
  128. private Renderer _renderer;
  129. /**
  130. * the structure handler
  131. */
  132. private StructureHandler structHandler;
  133. /**
  134. * the source of the FO file
  135. */
  136. private InputSource _source;
  137. /**
  138. * the stream to use to output the results of the renderer
  139. */
  140. private OutputStream _stream;
  141. /**
  142. * The XML parser to use when building the FO tree
  143. */
  144. private XMLReader _reader;
  145. /**
  146. * If true, full error stacks are reported
  147. */
  148. private boolean _errorDump = false;
  149. /**
  150. * the system resources that FOP will use
  151. */
  152. private Logger log = null;
  153. private FOUserAgent userAgent = null;
  154. public static final String getParserClassName() {
  155. try {
  156. return javax.xml.parsers.SAXParserFactory.newInstance().newSAXParser().getXMLReader().getClass().getName();
  157. } catch (javax.xml.parsers.ParserConfigurationException e) {
  158. return null;
  159. } catch (org.xml.sax.SAXException e) {
  160. return null;
  161. }
  162. }
  163. /**
  164. * create a new Driver
  165. */
  166. public Driver() {
  167. _stream = null;
  168. }
  169. public Driver(InputSource source, OutputStream stream) {
  170. this();
  171. _source = source;
  172. _stream = stream;
  173. }
  174. public void initialize() {
  175. _stream = null;
  176. _treeBuilder = new FOTreeBuilder();
  177. _treeBuilder.setUserAgent(getUserAgent());
  178. setupDefaultMappings();
  179. }
  180. public void setUserAgent(FOUserAgent agent) {
  181. userAgent = agent;
  182. }
  183. private FOUserAgent getUserAgent() {
  184. if(userAgent == null) {
  185. userAgent = new FOUserAgent();
  186. userAgent.enableLogging(getLogger());
  187. String base = org.apache.fop.configuration.Configuration.getStringValue("baseDir");
  188. userAgent.setBaseURL(base);
  189. }
  190. return userAgent;
  191. }
  192. public void enableLogging(Logger log) {
  193. if (this.log == null) {
  194. this.log = log;
  195. } else {
  196. getLogger().warn("Logger is already set! Won't use the new logger.");
  197. }
  198. }
  199. protected Logger getLogger() {
  200. if(this.log == null) {
  201. this.log = new ConsoleLogger(ConsoleLogger.LEVEL_INFO);
  202. this.log.error("Logger not set. Using ConsoleLogger as default.");
  203. }
  204. return this.log;
  205. }
  206. /**
  207. * Resets the Driver so it can be reused. Property and element
  208. * mappings are reset to defaults.
  209. * The output stream is cleared. The renderer is cleared.
  210. */
  211. public synchronized void reset() {
  212. _source = null;
  213. _stream = null;
  214. _reader = null;
  215. _treeBuilder.reset();
  216. }
  217. public boolean hasData() {
  218. return (_treeBuilder.hasData());
  219. }
  220. /**
  221. * Set the error dump option
  222. * @param dump if true, full stacks will be reported to the error log
  223. */
  224. public void setErrorDump(boolean dump) {
  225. _errorDump = dump;
  226. }
  227. /**
  228. * Set the OutputStream to use to output the result of the Renderer
  229. * (if applicable)
  230. * @param stream the stream to output the result of rendering to
  231. *
  232. */
  233. public void setOutputStream(OutputStream stream) {
  234. _stream = stream;
  235. }
  236. /**
  237. * Set the source for the FO document. This can be a normal SAX
  238. * InputSource, or an DocumentInputSource containing a DOM document.
  239. * @see DocumentInputSource
  240. */
  241. public void setInputSource(InputSource source) {
  242. _source = source;
  243. }
  244. /**
  245. * Sets the reader used when reading in the source. If not set,
  246. * this defaults to a basic SAX parser.
  247. */
  248. public void setXMLReader(XMLReader reader) {
  249. _reader = reader;
  250. }
  251. /**
  252. * Sets all the element and property list mappings to their default values.
  253. *
  254. */
  255. public void setupDefaultMappings() {
  256. addElementMapping("org.apache.fop.fo.FOElementMapping");
  257. addElementMapping("org.apache.fop.svg.SVGElementMapping");
  258. addElementMapping("org.apache.fop.extensions.ExtensionElementMapping");
  259. // add mappings from available services
  260. Enumeration providers =
  261. Service.providers(org.apache.fop.fo.ElementMapping.class);
  262. if (providers != null) {
  263. while (providers.hasMoreElements()) {
  264. String str = (String)providers.nextElement();
  265. try {
  266. addElementMapping(str);
  267. } catch (IllegalArgumentException e) {}
  268. }
  269. }
  270. }
  271. /**
  272. * Set the rendering type to use. Must be one of
  273. * <ul>
  274. * <li>RENDER_PDF
  275. * <li>RENDER_AWT
  276. * <li>RENDER_MIF
  277. * <li>RENDER_XML
  278. * <li>RENDER_PCL
  279. * <li>RENDER_PS
  280. * <li>RENDER_TXT
  281. * <li>RENDER_SVG
  282. * </ul>
  283. * @param renderer the type of renderer to use
  284. */
  285. public void setRenderer(int renderer) throws IllegalArgumentException {
  286. switch (renderer) {
  287. case RENDER_PDF:
  288. setRenderer("org.apache.fop.render.pdf.PDFRenderer");
  289. break;
  290. case RENDER_AWT:
  291. throw new IllegalArgumentException("Use renderer form of setRenderer() for AWT");
  292. case RENDER_PRINT:
  293. throw new IllegalArgumentException("Use renderer form of setRenderer() for PRINT");
  294. case RENDER_PCL:
  295. setRenderer("org.apache.fop.render.pcl.PCLRenderer");
  296. break;
  297. case RENDER_PS:
  298. setRenderer("org.apache.fop.render.ps.PSRenderer");
  299. break;
  300. case RENDER_TXT:
  301. setRenderer("org.apache.fop.render.txt.TXTRenderer()");
  302. break;
  303. case RENDER_MIF:
  304. //structHandler = new org.apache.fop.mif.MIFHandler(_stream);
  305. break;
  306. case RENDER_XML:
  307. setRenderer("org.apache.fop.render.xml.XMLRenderer");
  308. break;
  309. case RENDER_SVG:
  310. setRenderer("org.apache.fop.render.svg.SVGRenderer");
  311. break;
  312. default:
  313. throw new IllegalArgumentException("Unknown renderer type");
  314. }
  315. }
  316. /**
  317. * Set the Renderer to use.
  318. * @param renderer the renderer instance to use (Note: Logger must be set at this point)
  319. */
  320. public void setRenderer(Renderer renderer) {
  321. renderer.setUserAgent(getUserAgent());
  322. _renderer = renderer;
  323. }
  324. public Renderer getRenderer() {
  325. return _renderer;
  326. }
  327. /**
  328. * @deprecated use renderer.setProducer(version) + setRenderer(renderer) or just setRenderer(renderer_type) which will use the default producer string.
  329. * @see #setRenderer(int)
  330. * @see #setRenderer(Renderer)
  331. */
  332. public void setRenderer(String rendererClassName, String version) {
  333. setRenderer(rendererClassName);
  334. }
  335. /**
  336. * Set the class name of the Renderer to use as well as the
  337. * producer string for those renderers that can make use of it.
  338. * @param rendererClassName classname of the renderer to use such as
  339. * "org.apache.fop.render.pdf.PDFRenderer"
  340. * @exception IllegalArgumentException if the classname was invalid.
  341. * @see #setRenderer(int)
  342. */
  343. public void setRenderer(String rendererClassName)
  344. throws IllegalArgumentException {
  345. try {
  346. _renderer =
  347. (Renderer)Class.forName(rendererClassName).newInstance();
  348. if (_renderer instanceof LogEnabled) {
  349. ((LogEnabled)_renderer).enableLogging(getLogger());
  350. }
  351. _renderer.setProducer(Version.getVersion());
  352. } catch (ClassNotFoundException e) {
  353. throw new IllegalArgumentException("Could not find "
  354. + rendererClassName);
  355. }
  356. catch (InstantiationException e) {
  357. throw new IllegalArgumentException("Could not instantiate "
  358. + rendererClassName);
  359. }
  360. catch (IllegalAccessException e) {
  361. throw new IllegalArgumentException("Could not access "
  362. + rendererClassName);
  363. }
  364. catch (ClassCastException e) {
  365. throw new IllegalArgumentException(rendererClassName
  366. + " is not a renderer");
  367. }
  368. }
  369. /**
  370. * Add the given element mapping.
  371. * An element mapping maps element names to Java classes.
  372. *
  373. * @param mapping the element mappingto add
  374. */
  375. public void addElementMapping(ElementMapping mapping) {
  376. mapping.addToBuilder(_treeBuilder);
  377. }
  378. /**
  379. * add the element mapping with the given class name
  380. */
  381. public void addElementMapping(String mappingClassName)
  382. throws IllegalArgumentException {
  383. try {
  384. ElementMapping mapping =
  385. (ElementMapping)Class.forName(mappingClassName).newInstance();
  386. addElementMapping(mapping);
  387. } catch (ClassNotFoundException e) {
  388. throw new IllegalArgumentException("Could not find "
  389. + mappingClassName);
  390. }
  391. catch (InstantiationException e) {
  392. throw new IllegalArgumentException("Could not instantiate "
  393. + mappingClassName);
  394. }
  395. catch (IllegalAccessException e) {
  396. throw new IllegalArgumentException("Could not access "
  397. + mappingClassName);
  398. }
  399. catch (ClassCastException e) {
  400. throw new IllegalArgumentException(mappingClassName
  401. + " is not an ElementMapping");
  402. }
  403. }
  404. /**
  405. * Returns the tree builder (a SAX ContentHandler).
  406. *
  407. * Used in situations where SAX is used but not via a FOP-invoked
  408. * SAX parser. A good example is an XSLT engine that fires SAX
  409. * events but isn't a SAX Parser itself.
  410. */
  411. public ContentHandler getContentHandler() {
  412. // TODO - do this stuff in a better way
  413. if(_renderer != null) {
  414. structHandler = new LayoutHandler(_stream, _renderer, true);
  415. } else {
  416. structHandler = new org.apache.fop.mif.MIFHandler(_stream);
  417. }
  418. structHandler.enableLogging(getLogger());
  419. _treeBuilder.setUserAgent(getUserAgent());
  420. _treeBuilder.setStructHandler(structHandler);
  421. return _treeBuilder;
  422. }
  423. /**
  424. * Build the formatting object tree using the given SAX Parser and
  425. * SAX InputSource
  426. */
  427. public synchronized void render(XMLReader parser, InputSource source)
  428. throws FOPException {
  429. parser.setContentHandler(getContentHandler());
  430. try {
  431. parser.parse(source);
  432. } catch (SAXException e) {
  433. if (e.getException() instanceof FOPException) {
  434. throw (FOPException)e.getException();
  435. } else {
  436. throw new FOPException(e);
  437. }
  438. }
  439. catch (IOException e) {
  440. throw new FOPException(e);
  441. }
  442. }
  443. /**
  444. * Build the formatting object tree using the given DOM Document
  445. */
  446. public synchronized void render(Document document)
  447. throws FOPException {
  448. try {
  449. DocumentInputSource source = new DocumentInputSource(document);
  450. DocumentReader reader = new DocumentReader();
  451. reader.setContentHandler(getContentHandler());
  452. reader.parse(source);
  453. } catch (SAXException e) {
  454. throw new FOPException(e);
  455. }
  456. catch (IOException e) {
  457. throw new FOPException(e);
  458. }
  459. }
  460. /**
  461. * Dumps an error
  462. */
  463. public void dumpError(Exception e) {
  464. if (_errorDump) {
  465. if (e instanceof SAXException) {
  466. getLogger().error("", e);
  467. if (((SAXException)e).getException() != null) {
  468. getLogger().error("", ((SAXException)e).getException());
  469. }
  470. } else if (e instanceof FOPException) {
  471. e.printStackTrace();
  472. if (((FOPException)e).getException() != null) {
  473. getLogger().error("", ((FOPException)e).getException());
  474. }
  475. } else {
  476. getLogger().error("", e);
  477. }
  478. }
  479. }
  480. /**
  481. * Runs the formatting and renderering process using the previously set
  482. * inputsource and outputstream
  483. */
  484. public synchronized void run() throws IOException, FOPException {
  485. if (_renderer == null) {
  486. setRenderer(RENDER_PDF);
  487. }
  488. if (_source == null) {
  489. throw new FOPException("InputSource is not set.");
  490. }
  491. if (_reader == null) {
  492. if (!(_source instanceof DocumentInputSource)) {
  493. _reader = ConfigurationReader.createParser();
  494. }
  495. }
  496. if (_source instanceof DocumentInputSource) {
  497. render(((DocumentInputSource)_source).getDocument());
  498. } else {
  499. render(_reader, _source);
  500. }
  501. }
  502. }
  503. // code stolen from org.apache.batik.util and modified slightly
  504. // does what sun.misc.Service probably does, but it cannot be relied on.
  505. // hopefully will be part of standard jdk sometime.
  506. /**
  507. * This class loads services present in the class path.
  508. */
  509. class Service {
  510. static Hashtable providerMap = new Hashtable();
  511. public static synchronized Enumeration providers(Class cls) {
  512. ClassLoader cl = cls.getClassLoader();
  513. // null if loaded by bootstrap class loader
  514. if(cl == null) {
  515. cl = ClassLoader.getSystemClassLoader();
  516. }
  517. String serviceFile = "META-INF/services/" + cls.getName();
  518. // getLogger().debug("File: " + serviceFile);
  519. Vector v = (Vector)providerMap.get(serviceFile);
  520. if (v != null)
  521. return v.elements();
  522. v = new Vector();
  523. providerMap.put(serviceFile, v);
  524. Enumeration e;
  525. try {
  526. e = cl.getResources(serviceFile);
  527. } catch (IOException ioe) {
  528. return v.elements();
  529. }
  530. while (e.hasMoreElements()) {
  531. try {
  532. java.net.URL u = (java.net.URL)e.nextElement();
  533. //getLogger().debug("URL: " + u);
  534. InputStream is = u.openStream();
  535. Reader r = new InputStreamReader(is, "UTF-8");
  536. BufferedReader br = new BufferedReader(r);
  537. String line = br.readLine();
  538. while (line != null) {
  539. try {
  540. // First strip any comment...
  541. int idx = line.indexOf('#');
  542. if (idx != -1)
  543. line = line.substring(0, idx);
  544. // Trim whitespace.
  545. line = line.trim();
  546. // If nothing left then loop around...
  547. if (line.length() == 0) {
  548. line = br.readLine();
  549. continue;
  550. }
  551. // getLogger().debug("Line: " + line);
  552. // Try and load the class
  553. // Object obj = cl.loadClass(line).newInstance();
  554. // stick it into our vector...
  555. v.add(line);
  556. } catch (Exception ex) {
  557. // Just try the next line
  558. }
  559. line = br.readLine();
  560. }
  561. } catch (Exception ex) {
  562. // Just try the next file...
  563. }
  564. }
  565. return v.elements();
  566. }
  567. }