Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

FopFactory.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. /*
  2. * Copyright 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.apps;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.OutputStream;
  21. import java.net.MalformedURLException;
  22. import java.util.List;
  23. import javax.xml.transform.Source;
  24. import javax.xml.transform.TransformerException;
  25. import javax.xml.transform.URIResolver;
  26. import org.apache.avalon.framework.configuration.Configuration;
  27. import org.apache.avalon.framework.configuration.ConfigurationException;
  28. import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
  29. import org.apache.commons.logging.Log;
  30. import org.apache.commons.logging.LogFactory;
  31. import org.apache.fop.fo.ElementMapping;
  32. import org.apache.fop.fo.ElementMappingRegistry;
  33. import org.apache.fop.hyphenation.HyphenationTreeResolver;
  34. import org.apache.fop.image.ImageFactory;
  35. import org.apache.fop.layoutmgr.LayoutManagerMaker;
  36. import org.apache.fop.render.RendererFactory;
  37. import org.apache.fop.render.XMLHandlerRegistry;
  38. import org.apache.fop.util.ContentHandlerFactoryRegistry;
  39. import org.xml.sax.SAXException;
  40. /**
  41. * Factory class which instantiates new Fop and FOUserAgent instances. This class also holds
  42. * environmental information and configuration used by FOP. Information that may potentially be
  43. * different for each rendering run can be found and managed in the FOUserAgent.
  44. */
  45. public class FopFactory {
  46. /** Defines the default source resolution (72dpi) for FOP */
  47. private static final float DEFAULT_SOURCE_RESOLUTION = 72.0f; //dpi
  48. /** Defines the default page-height */
  49. private static final String DEFAULT_PAGE_HEIGHT = "11in";
  50. /** Defines the default page-width */
  51. private static final String DEFAULT_PAGE_WIDTH = "8.26in";
  52. /** logger instance */
  53. private static Log log = LogFactory.getLog(FopFactory.class);
  54. /** Factory for Renderers and FOEventHandlers */
  55. private RendererFactory rendererFactory = new RendererFactory();
  56. /** Registry for XML handlers */
  57. private XMLHandlerRegistry xmlHandlers = new XMLHandlerRegistry();
  58. /** The registry for ElementMapping instances */
  59. private ElementMappingRegistry elementMappingRegistry;
  60. /** The registry for ContentHandlerFactory instance */
  61. private ContentHandlerFactoryRegistry contentHandlerFactoryRegistry
  62. = new ContentHandlerFactoryRegistry();
  63. /** Our default resolver if none is set */
  64. private URIResolver foURIResolver = new FOURIResolver();
  65. /** A user settable URI Resolver */
  66. private URIResolver uriResolver = null;
  67. /** The resolver for user-supplied hyphenation patterns */
  68. private HyphenationTreeResolver hyphResolver;
  69. private ImageFactory imageFactory = new ImageFactory();
  70. /** user configuration */
  71. private Configuration userConfig = null;
  72. /** The base URL for all font URL resolutions */
  73. private String fontBaseURL;
  74. /**
  75. * FOP has the ability, for some FO's, to continue processing even if the
  76. * input XSL violates that FO's content model. This is the default
  77. * behavior for FOP. However, this flag, if set, provides the user the
  78. * ability for FOP to halt on all content model violations if desired.
  79. */
  80. private boolean strictValidation = true;
  81. /** Allows enabling kerning on the base 14 fonts, default is false */
  82. private boolean enableBase14Kerning = false;
  83. /** Source resolution in dpi */
  84. private float sourceResolution = DEFAULT_SOURCE_RESOLUTION;
  85. private String pageHeight = DEFAULT_PAGE_HEIGHT;
  86. private String pageWidth = DEFAULT_PAGE_WIDTH;
  87. /** @see #setBreakIndentInheritanceOnReferenceAreaBoundary(boolean) */
  88. private boolean breakIndentInheritanceOnReferenceAreaBoundary = false;
  89. /** Optional overriding LayoutManagerMaker */
  90. private LayoutManagerMaker lmMakerOverride = null;
  91. /**
  92. * Main constructor.
  93. */
  94. protected FopFactory() {
  95. this.elementMappingRegistry = new ElementMappingRegistry(this);
  96. }
  97. /**
  98. * Returns a new FopFactory instance.
  99. * @return the requested FopFactory instance.
  100. */
  101. public static FopFactory newInstance() {
  102. return new FopFactory();
  103. }
  104. /**
  105. * Returns a new FOUserAgent instance. Use the FOUserAgent to configure special values that
  106. * are particular to a rendering run. Don't reuse instances over multiple rendering runs but
  107. * instead create a new one each time and reuse the FopFactory.
  108. * @return the newly created FOUserAgent instance initialized with default values
  109. */
  110. public FOUserAgent newFOUserAgent() {
  111. FOUserAgent userAgent = new FOUserAgent(this);
  112. return userAgent;
  113. }
  114. /**
  115. * Returns a new {@link Fop} instance. FOP will be configured with a default user agent
  116. * instance.
  117. * <p>
  118. * MIME types are used to select the output format (ex. "application/pdf" for PDF). You can
  119. * use the constants defined in {@link MimeConstants}.
  120. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
  121. * @return the new Fop instance
  122. * @throws FOPException when the constructor fails
  123. */
  124. public Fop newFop(String outputFormat) throws FOPException {
  125. return new Fop(outputFormat, newFOUserAgent());
  126. }
  127. /**
  128. * Returns a new {@link Fop} instance. Use this factory method if you want to configure this
  129. * very rendering run, i.e. if you want to set some metadata like the title and author of the
  130. * document you want to render. In that case, create a new {@link FOUserAgent}
  131. * instance using {@link #newFOUserAgent()}.
  132. * <p>
  133. * MIME types are used to select the output format (ex. "application/pdf" for PDF). You can
  134. * use the constants defined in {@link MimeConstants}.
  135. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
  136. * @param userAgent the user agent that will be used to control the rendering run
  137. * @return the new Fop instance
  138. * @throws FOPException when the constructor fails
  139. */
  140. public Fop newFop(String outputFormat, FOUserAgent userAgent) throws FOPException {
  141. if (userAgent == null) {
  142. throw new NullPointerException("The userAgent parameter must not be null!");
  143. }
  144. return new Fop(outputFormat, userAgent);
  145. }
  146. /**
  147. * Returns a new {@link Fop} instance. FOP will be configured with a default user agent
  148. * instance. Use this factory method if your output type requires an output stream.
  149. * <p>
  150. * MIME types are used to select the output format (ex. "application/pdf" for PDF). You can
  151. * use the constants defined in {@link MimeConstants}.
  152. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
  153. * @param stream the output stream
  154. * @return the new Fop instance
  155. * @throws FOPException when the constructor fails
  156. */
  157. public Fop newFop(String outputFormat, OutputStream stream) throws FOPException {
  158. return new Fop(outputFormat, newFOUserAgent(), stream);
  159. }
  160. /**
  161. * Returns a new {@link Fop} instance. Use this factory method if your output type
  162. * requires an output stream and you want to configure this very rendering run,
  163. * i.e. if you want to set some metadata like the title and author of the document
  164. * you want to render. In that case, create a new {@link FOUserAgent} instance
  165. * using {@link #newFOUserAgent()}.
  166. * <p>
  167. * MIME types are used to select the output format (ex. "application/pdf" for PDF). You can
  168. * use the constants defined in {@link MimeConstants}.
  169. * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
  170. * @param userAgent the user agent that will be used to control the rendering run
  171. * @param stream the output stream
  172. * @return the new Fop instance
  173. * @throws FOPException when the constructor fails
  174. */
  175. public Fop newFop(String outputFormat, FOUserAgent userAgent, OutputStream stream)
  176. throws FOPException {
  177. if (userAgent == null) {
  178. throw new NullPointerException("The userAgent parameter must not be null!");
  179. }
  180. return new Fop(outputFormat, userAgent, stream);
  181. }
  182. /**
  183. * Returns a new {@link Fop} instance. Use this factory method if you want to supply your
  184. * own {@link org.apache.fop.render.Renderer Renderer} or
  185. * {@link org.apache.fop.fo.FOEventHandler FOEventHandler}
  186. * instance instead of the default ones created internally by FOP.
  187. * @param userAgent the user agent that will be used to control the rendering run
  188. * @return the new Fop instance
  189. * @throws FOPException when the constructor fails
  190. */
  191. public Fop newFop(FOUserAgent userAgent) throws FOPException {
  192. if (userAgent.getRendererOverride() == null
  193. && userAgent.getFOEventHandlerOverride() == null) {
  194. throw new IllegalStateException("Either the overriding renderer or the overriding"
  195. + " FOEventHandler must be set when this factory method is used!");
  196. }
  197. return newFop(null, userAgent);
  198. }
  199. /** @return the RendererFactory */
  200. public RendererFactory getRendererFactory() {
  201. return this.rendererFactory;
  202. }
  203. /** @return the XML handler registry */
  204. public XMLHandlerRegistry getXMLHandlerRegistry() {
  205. return this.xmlHandlers;
  206. }
  207. /** @return the element mapping registry */
  208. public ElementMappingRegistry getElementMappingRegistry() {
  209. return this.elementMappingRegistry;
  210. }
  211. /** @return the content handler factory registry */
  212. public ContentHandlerFactoryRegistry getContentHandlerFactoryRegistry() {
  213. return this.contentHandlerFactoryRegistry;
  214. }
  215. /** @return the image factory */
  216. public ImageFactory getImageFactory() {
  217. return this.imageFactory;
  218. }
  219. /**
  220. * Add the element mapping with the given class name.
  221. * @param elementMapping the class name representing the element mapping.
  222. */
  223. public void addElementMapping(ElementMapping elementMapping) {
  224. this.elementMappingRegistry.addElementMapping(elementMapping);
  225. }
  226. /**
  227. * Sets an explicit LayoutManagerMaker instance which overrides the one
  228. * defined by the AreaTreeHandler.
  229. * @param lmMaker the LayoutManagerMaker instance
  230. */
  231. public void setLayoutManagerMakerOverride(LayoutManagerMaker lmMaker) {
  232. this.lmMakerOverride = lmMaker;
  233. }
  234. /**
  235. * Returns the overriding LayoutManagerMaker instance, if any.
  236. * @return the overriding LayoutManagerMaker or null
  237. */
  238. public LayoutManagerMaker getLayoutManagerMakerOverride() {
  239. return this.lmMakerOverride;
  240. }
  241. /**
  242. * Sets the font base URL.
  243. * @param fontBaseURL font base URL
  244. */
  245. public void setFontBaseURL(String fontBaseURL) {
  246. this.fontBaseURL = fontBaseURL;
  247. }
  248. /** @return the font base URL */
  249. public String getFontBaseURL() {
  250. return this.fontBaseURL;
  251. }
  252. /**
  253. * Sets the URI Resolver. It is used for resolving factory-level URIs like hyphenation
  254. * patterns and as backup for URI resolution performed during a rendering run.
  255. * @param resolver the new URI resolver
  256. */
  257. public void setURIResolver(URIResolver resolver) {
  258. this.uriResolver = resolver;
  259. }
  260. /**
  261. * Returns the URI Resolver.
  262. * @return the URI Resolver
  263. */
  264. public URIResolver getURIResolver() {
  265. return this.uriResolver;
  266. }
  267. /** @return the HyphenationTreeResolver for resolving user-supplied hyphenation patterns. */
  268. public HyphenationTreeResolver getHyphenationTreeResolver() {
  269. return this.hyphResolver;
  270. }
  271. /**
  272. * Activates strict XSL content model validation for FOP
  273. * Default is false (FOP will continue processing where it can)
  274. * @param validateStrictly true to turn on strict validation
  275. */
  276. public void setStrictValidation(boolean validateStrictly) {
  277. this.strictValidation = validateStrictly;
  278. }
  279. /**
  280. * Returns whether FOP is strictly validating input XSL
  281. * @return true of strict validation turned on, false otherwise
  282. */
  283. public boolean validateStrictly() {
  284. return strictValidation;
  285. }
  286. /**
  287. * @return true if the indent inheritance should be broken when crossing reference area
  288. * boundaries (for more info, see the javadoc for the relative member variable)
  289. */
  290. public boolean isBreakIndentInheritanceOnReferenceAreaBoundary() {
  291. return breakIndentInheritanceOnReferenceAreaBoundary;
  292. }
  293. /**
  294. * Controls whether to enable a feature that breaks indent inheritance when crossing
  295. * reference area boundaries.
  296. * <p>
  297. * This flag controls whether FOP will enable special code that breaks property
  298. * inheritance for start-indent and end-indent when the evaluation of the inherited
  299. * value would cross a reference area. This is described under
  300. * http://wiki.apache.org/xmlgraphics-fop/IndentInheritance as is intended to
  301. * improve interoperability with commercial FO implementations and to produce
  302. * results that are more in line with the expectation of unexperienced FO users.
  303. * Note: Enabling this features violates the XSL specification!
  304. * @param value true to enable the feature
  305. */
  306. public void setBreakIndentInheritanceOnReferenceAreaBoundary(boolean value) {
  307. this.breakIndentInheritanceOnReferenceAreaBoundary = value;
  308. }
  309. /** @return true if kerning on base 14 fonts is enabled */
  310. public boolean isBase14KerningEnabled() {
  311. return this.enableBase14Kerning;
  312. }
  313. /**
  314. * Controls whether kerning is activated on base 14 fonts.
  315. * @param value true if kerning should be activated
  316. */
  317. public void setBase14KerningEnabled(boolean value) {
  318. this.enableBase14Kerning = value;
  319. }
  320. /** @return the resolution for resolution-dependant input */
  321. public float getSourceResolution() {
  322. return this.sourceResolution;
  323. }
  324. /**
  325. * Returns the conversion factor from pixel units to millimeters. This
  326. * depends on the desired source resolution.
  327. * @return float conversion factor
  328. * @see #getSourceResolution()
  329. */
  330. public float getSourcePixelUnitToMillimeter() {
  331. return 25.4f / getSourceResolution();
  332. }
  333. /**
  334. * Sets the source resolution in dpi. This value is used to interpret the pixel size
  335. * of source documents like SVG images and bitmap images without resolution information.
  336. * @param dpi resolution in dpi
  337. */
  338. public void setSourceResolution(int dpi) {
  339. this.sourceResolution = dpi;
  340. }
  341. /**
  342. * Gets the default page-height to use as fallback,
  343. * in case page-height="auto"
  344. *
  345. * @return the page-height, as a String
  346. */
  347. public String getPageHeight() {
  348. return this.pageHeight;
  349. }
  350. /**
  351. * Sets the page-height to use as fallback, in case
  352. * page-height="auto"
  353. *
  354. * @param pageHeight page-height as a String
  355. */
  356. public void setPageHeight(String pageHeight) {
  357. this.pageHeight = pageHeight;
  358. }
  359. /**
  360. * Gets the default page-width to use as fallback,
  361. * in case page-width="auto"
  362. *
  363. * @return the page-width, as a String
  364. */
  365. public String getPageWidth() {
  366. return this.pageWidth;
  367. }
  368. /**
  369. * Sets the page-width to use as fallback, in case
  370. * page-width="auto"
  371. *
  372. * @param pageWidth page-width as a String
  373. */
  374. public void setPageWidth(String pageWidth) {
  375. this.pageWidth = pageWidth;
  376. }
  377. //------------------------------------------- Configuration stuff
  378. /**
  379. * Set the user configuration.
  380. * @param userConfigFile the configuration file
  381. * @throws IOException if an I/O error occurs
  382. * @throws SAXException if a parsing error occurs
  383. */
  384. public void setUserConfig(File userConfigFile)
  385. throws SAXException, IOException {
  386. try {
  387. DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
  388. setUserConfig(cfgBuilder.buildFromFile(userConfigFile));
  389. } catch (ConfigurationException cfge) {
  390. log.error("Error loading configuration: "
  391. + cfge.getMessage());
  392. }
  393. }
  394. /**
  395. * Set the user configuration from an URI.
  396. * @param uri the URI to the configuration file
  397. * @throws IOException if an I/O error occurs
  398. * @throws SAXException if a parsing error occurs
  399. */
  400. public void setUserConfig(String uri)
  401. throws SAXException, IOException {
  402. try {
  403. DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
  404. setUserConfig(cfgBuilder.build(uri));
  405. } catch (ConfigurationException cfge) {
  406. log.error("Error loading configuration: "
  407. + cfge.getMessage());
  408. }
  409. }
  410. /**
  411. * Set the user configuration.
  412. * @param userConfig configuration
  413. */
  414. public void setUserConfig(Configuration userConfig) {
  415. this.userConfig = userConfig;
  416. try {
  417. initUserConfig();
  418. } catch (ConfigurationException cfge) {
  419. log.error("Error initializing factory configuration: "
  420. + cfge.getMessage());
  421. }
  422. }
  423. /**
  424. * Get the user configuration.
  425. * @return the user configuration
  426. */
  427. public Configuration getUserConfig() {
  428. return userConfig;
  429. }
  430. /**
  431. * Initializes user agent settings from the user configuration
  432. * file, if present: baseURL, resolution, default page size,...
  433. *
  434. * @throws ConfigurationException when there is an entry that
  435. * misses the required attribute
  436. */
  437. public void initUserConfig() throws ConfigurationException {
  438. log.debug("Initializing User Agent Configuration");
  439. setFontBaseURL(getBaseURLfromConfig(userConfig, "font-base"));
  440. final String hyphBase = getBaseURLfromConfig(userConfig, "hyphenation-base");
  441. if (hyphBase != null) {
  442. this.hyphResolver = new HyphenationTreeResolver() {
  443. public Source resolve(String href) {
  444. return resolveURI(href, hyphBase);
  445. }
  446. };
  447. }
  448. if (userConfig.getChild("source-resolution", false) != null) {
  449. this.sourceResolution
  450. = userConfig.getChild("source-resolution").getValueAsFloat(
  451. DEFAULT_SOURCE_RESOLUTION);
  452. log.info("Source resolution set to: " + sourceResolution
  453. + "dpi (px2mm=" + getSourcePixelUnitToMillimeter() + ")");
  454. }
  455. if (userConfig.getChild("strict-validation", false) != null) {
  456. this.strictValidation = userConfig.getChild("strict-validation").getValueAsBoolean();
  457. }
  458. if (userConfig.getChild("break-indent-inheritance", false) != null) {
  459. this.breakIndentInheritanceOnReferenceAreaBoundary
  460. = userConfig.getChild("break-indent-inheritance").getValueAsBoolean();
  461. }
  462. Configuration pageConfig = userConfig.getChild("default-page-settings");
  463. if (pageConfig.getAttribute("height", null) != null) {
  464. setPageHeight(pageConfig.getAttribute("height"));
  465. log.info("Default page-height set to: " + pageHeight);
  466. }
  467. if (pageConfig.getAttribute("width", null) != null) {
  468. setPageWidth(pageConfig.getAttribute("width"));
  469. log.info("Default page-width set to: " + pageWidth);
  470. }
  471. }
  472. /**
  473. * Retrieves and verifies a base URL.
  474. * @param cfg The Configuration object to retrieve the base URL from
  475. * @param name the element name for the base URL
  476. * @return the requested base URL or null if not available
  477. */
  478. public static String getBaseURLfromConfig(Configuration cfg, String name) {
  479. if (cfg.getChild(name, false) != null) {
  480. try {
  481. String cfgBaseDir = cfg.getChild(name).getValue(null);
  482. if (cfgBaseDir != null) {
  483. File dir = new File(cfgBaseDir);
  484. if (dir.isDirectory()) {
  485. cfgBaseDir = dir.toURL().toExternalForm();
  486. }
  487. }
  488. log.info(name + " set to: " + cfgBaseDir);
  489. return cfgBaseDir;
  490. } catch (MalformedURLException mue) {
  491. log.error("Base URL in user config is malformed!");
  492. }
  493. }
  494. return null;
  495. }
  496. //------------------------------------------- URI resolution
  497. /**
  498. * Attempts to resolve the given URI.
  499. * Will use the configured resolver and if not successful fall back
  500. * to the default resolver.
  501. * @param uri URI to access
  502. * @param base the base URI to resolve against
  503. * @return A {@link javax.xml.transform.Source} object, or null if the URI
  504. * cannot be resolved.
  505. * @see org.apache.fop.apps.FOURIResolver
  506. */
  507. public Source resolveURI(String uri, String base) {
  508. Source source = null;
  509. //RFC 2397 data URLs don't need to be resolved, just decode them.
  510. boolean bypassURIResolution = uri.startsWith("data:");
  511. if (!bypassURIResolution && uriResolver != null) {
  512. try {
  513. source = uriResolver.resolve(uri, base);
  514. } catch (TransformerException te) {
  515. log.error("Attempt to resolve URI '" + uri + "' failed: ", te);
  516. }
  517. }
  518. if (source == null) {
  519. // URI Resolver not configured or returned null, use default resolver
  520. try {
  521. source = foURIResolver.resolve(uri, base);
  522. } catch (TransformerException te) {
  523. log.error("Attempt to resolve URI '" + uri + "' failed: ", te);
  524. }
  525. }
  526. return source;
  527. }
  528. }