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.

FopConfParser.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  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.apps;
  19. import java.io.File;
  20. import java.io.FileInputStream;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.net.URI;
  24. import java.net.URISyntaxException;
  25. import java.util.HashMap;
  26. import java.util.Locale;
  27. import java.util.Map;
  28. import org.xml.sax.SAXException;
  29. import org.apache.commons.logging.Log;
  30. import org.apache.commons.logging.LogFactory;
  31. import org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry;
  32. import org.apache.xmlgraphics.image.loader.util.Penalty;
  33. import org.apache.xmlgraphics.io.ResourceResolver;
  34. import org.apache.fop.accessibility.Accessibility;
  35. import org.apache.fop.apps.io.InternalResourceResolver;
  36. import org.apache.fop.apps.io.ResourceResolverFactory;
  37. import org.apache.fop.configuration.Configuration;
  38. import org.apache.fop.configuration.ConfigurationException;
  39. import org.apache.fop.configuration.DefaultConfigurationBuilder;
  40. import org.apache.fop.fonts.FontManagerConfigurator;
  41. import org.apache.fop.hyphenation.HyphenationTreeCache;
  42. import org.apache.fop.hyphenation.Hyphenator;
  43. import org.apache.fop.util.LogUtil;
  44. /**
  45. * Parses the FOP configuration file and returns a {@link FopFactoryBuilder} which builds a
  46. * {@link FopFactory}.
  47. */
  48. public class FopConfParser {
  49. private static final String PREFER_RENDERER = "prefer-renderer";
  50. private final Log log = LogFactory.getLog(FopConfParser.class);
  51. private final FopFactoryBuilder fopFactoryBuilder;
  52. /**
  53. * Constructor that takes the FOP conf in the form of an {@link InputStream}. A default base URI
  54. * must be given as a fall-back mechanism for URI resolution.
  55. *
  56. * @param fopConfStream the fop conf input stream
  57. * @param enviro the profile of the FOP deployment environment
  58. * @throws SAXException if a SAX error was thrown parsing the FOP conf
  59. * @throws IOException if an I/O error is thrown while parsing the FOP conf
  60. */
  61. public FopConfParser(InputStream fopConfStream, EnvironmentProfile enviro)
  62. throws SAXException, IOException {
  63. this(fopConfStream, enviro.getDefaultBaseURI(), enviro);
  64. }
  65. /**
  66. * Constructor that takes the FOP conf in the form of an {@link InputStream}. A default base URI
  67. * must be given as a fall-back mechanism for URI resolution.
  68. *
  69. * @param fopConfStream the fop conf input stream
  70. * @param defaultBaseURI the default base URI
  71. * @param resourceResolver the URI resolver
  72. * @throws SAXException if a SAX error was thrown parsing the FOP conf
  73. * @throws IOException if an I/O error is thrown while parsing the FOP conf
  74. */
  75. public FopConfParser(InputStream fopConfStream, URI defaultBaseURI,
  76. ResourceResolver resourceResolver) throws SAXException, IOException {
  77. this(fopConfStream, defaultBaseURI,
  78. EnvironmentalProfileFactory.createDefault(defaultBaseURI, resourceResolver));
  79. }
  80. /**
  81. * Constructor that takes the FOP conf in the form of an {@link InputStream}. A default base URI
  82. * must be given as a fall-back mechanism for URI resolution. The default URI resolvers is used.
  83. *
  84. * @param fopConfStream the fop conf input stream
  85. * @param defaultBaseURI the default base URI
  86. * @throws SAXException if a SAX error was thrown parsing the FOP conf
  87. * @throws IOException if an I/O error is thrown while parsing the FOP conf
  88. */
  89. public FopConfParser(InputStream fopConfStream, URI defaultBaseURI) throws SAXException,
  90. IOException {
  91. this(fopConfStream, defaultBaseURI, ResourceResolverFactory.createDefaultResourceResolver());
  92. }
  93. /**
  94. * Constructor that takes the FOP conf and uses the default URI resolver.
  95. *
  96. * @param fopConfFile the FOP conf file
  97. * @throws SAXException if a SAX error was thrown parsing the FOP conf
  98. * @throws IOException if an I/O error is thrown while parsing the FOP conf
  99. */
  100. public FopConfParser(File fopConfFile) throws SAXException, IOException {
  101. this(fopConfFile, ResourceResolverFactory.createDefaultResourceResolver());
  102. }
  103. /**
  104. * Constructor that takes the FOP conf and a default base URI and uses the default URI resolver.
  105. *
  106. * @param fopConfFile the FOP conf file
  107. * @param defaultBaseURI the default base URI
  108. * @throws SAXException if a SAX error was thrown parsing the FOP conf
  109. * @throws IOException if an I/O error is thrown while parsing the FOP conf
  110. */
  111. public FopConfParser(File fopConfFile, URI defaultBaseURI) throws SAXException, IOException {
  112. this(new FileInputStream(fopConfFile), fopConfFile.toURI(),
  113. EnvironmentalProfileFactory.createDefault(defaultBaseURI,
  114. ResourceResolverFactory.createDefaultResourceResolver()));
  115. }
  116. /**
  117. * Constructor that parses the FOP conf and uses the URI resolver given.
  118. *
  119. * @param fopConfFile the FOP conf file
  120. * @param resourceResolver the URI resolver
  121. * @throws SAXException if a SAX error was thrown parsing the FOP conf
  122. * @throws IOException if an I/O error is thrown while parsing the FOP conf
  123. */
  124. public FopConfParser(File fopConfFile, ResourceResolver resourceResolver)
  125. throws SAXException, IOException {
  126. this(new FileInputStream(fopConfFile), fopConfFile.toURI(), resourceResolver);
  127. }
  128. public FopConfParser(InputStream fopConfStream, URI baseURI, EnvironmentProfile enviro)
  129. throws SAXException, IOException {
  130. DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
  131. Configuration cfg;
  132. try {
  133. cfg = cfgBuilder.build(fopConfStream);
  134. } catch (ConfigurationException e) {
  135. throw new FOPException(e);
  136. }
  137. // The default base URI is taken from the directory in which the fopConf resides
  138. fopFactoryBuilder = new FopFactoryBuilder(enviro).setConfiguration(cfg);
  139. configure(baseURI, enviro.getResourceResolver(), cfg);
  140. }
  141. private void configure(final URI baseURI, final ResourceResolver resourceResolver,
  142. Configuration cfg) throws FOPException {
  143. if (log.isDebugEnabled()) {
  144. log.debug("Initializing FopFactory Configuration");
  145. }
  146. // strict fo validation
  147. if (cfg.getChild("strict-validation", false) != null) {
  148. try {
  149. boolean strict = cfg.getChild("strict-validation").getValueAsBoolean();
  150. fopFactoryBuilder.setStrictFOValidation(strict);
  151. } catch (ConfigurationException e) {
  152. LogUtil.handleException(log, e, false);
  153. }
  154. }
  155. boolean strict = false;
  156. if (cfg.getChild("strict-configuration", false) != null) {
  157. try {
  158. strict = cfg.getChild("strict-configuration").getValueAsBoolean();
  159. fopFactoryBuilder.setStrictUserConfigValidation(strict);
  160. } catch (ConfigurationException e) {
  161. LogUtil.handleException(log, e, false);
  162. }
  163. }
  164. if (cfg.getChild("accessibility", false) != null) {
  165. try {
  166. fopFactoryBuilder.setAccessibility(cfg.getChild("accessibility").getValueAsBoolean());
  167. fopFactoryBuilder.setKeepEmptyTags(
  168. cfg.getChild("accessibility").getAttributeAsBoolean(Accessibility.KEEP_EMPTY_TAGS, true));
  169. } catch (ConfigurationException e) {
  170. LogUtil.handleException(log, e, false);
  171. }
  172. }
  173. // base definitions for relative path resolution
  174. if (cfg.getChild("base", false) != null) {
  175. try {
  176. URI confUri = InternalResourceResolver.getBaseURI(cfg.getChild("base").getValue(null));
  177. fopFactoryBuilder.setBaseURI(baseURI.resolve(confUri));
  178. } catch (URISyntaxException use) {
  179. LogUtil.handleException(log, use, strict);
  180. }
  181. }
  182. // renderer options
  183. if (cfg.getChild("source-resolution", false) != null) {
  184. float srcRes = cfg.getChild("source-resolution").getValueAsFloat(
  185. FopFactoryConfig.DEFAULT_SOURCE_RESOLUTION);
  186. fopFactoryBuilder.setSourceResolution(srcRes);
  187. if (log.isDebugEnabled()) {
  188. log.debug("source-resolution set to: " + srcRes + "dpi");
  189. }
  190. }
  191. if (cfg.getChild("target-resolution", false) != null) {
  192. float targetRes = cfg.getChild("target-resolution").getValueAsFloat(
  193. FopFactoryConfig.DEFAULT_TARGET_RESOLUTION);
  194. fopFactoryBuilder.setTargetResolution(targetRes);
  195. if (log.isDebugEnabled()) {
  196. log.debug("target-resolution set to: " + targetRes + "dpi");
  197. }
  198. }
  199. if (cfg.getChild("break-indent-inheritance", false) != null) {
  200. try {
  201. fopFactoryBuilder.setBreakIndentInheritanceOnReferenceAreaBoundary(
  202. cfg.getChild("break-indent-inheritance").getValueAsBoolean());
  203. } catch (ConfigurationException e) {
  204. LogUtil.handleException(log, e, strict);
  205. }
  206. }
  207. Configuration pageConfig = cfg.getChild("default-page-settings");
  208. if (pageConfig.getAttribute("height", null) != null) {
  209. String pageHeight = pageConfig.getAttribute("height",
  210. FopFactoryConfig.DEFAULT_PAGE_HEIGHT);
  211. fopFactoryBuilder.setPageHeight(pageHeight);
  212. if (log.isInfoEnabled()) {
  213. log.info("Default page-height set to: " + pageHeight);
  214. }
  215. }
  216. if (pageConfig.getAttribute("width", null) != null) {
  217. String pageWidth = pageConfig.getAttribute("width",
  218. FopFactoryConfig.DEFAULT_PAGE_WIDTH);
  219. fopFactoryBuilder.setPageWidth(pageWidth);
  220. if (log.isInfoEnabled()) {
  221. log.info("Default page-width set to: " + pageWidth);
  222. }
  223. }
  224. if (cfg.getChild("complex-scripts") != null) {
  225. Configuration csConfig = cfg.getChild("complex-scripts");
  226. fopFactoryBuilder.setComplexScriptFeatures(!csConfig.getAttributeAsBoolean("disabled",
  227. false));
  228. }
  229. setHyphenationBase(cfg, resourceResolver, baseURI, fopFactoryBuilder);
  230. setHyphPatNames(cfg, fopFactoryBuilder, strict);
  231. // prefer Renderer over IFDocumentHandler
  232. if (cfg.getChild(PREFER_RENDERER, false) != null) {
  233. try {
  234. fopFactoryBuilder.setPreferRenderer(
  235. cfg.getChild(PREFER_RENDERER).getValueAsBoolean());
  236. } catch (ConfigurationException e) {
  237. LogUtil.handleException(log, e, strict);
  238. }
  239. }
  240. // configure font manager
  241. new FontManagerConfigurator(cfg, baseURI, fopFactoryBuilder.getBaseURI(), resourceResolver)
  242. .configure(fopFactoryBuilder.getFontManager(), strict);
  243. // configure image loader framework
  244. configureImageLoading(cfg.getChild("image-loading", false), strict);
  245. }
  246. private void setHyphenationBase(Configuration cfg, ResourceResolver resourceResolver, URI baseURI,
  247. FopFactoryBuilder fopFactoryBuilder) throws FOPException {
  248. if (cfg.getChild("hyphenation-base", false) != null) {
  249. try {
  250. URI fontBase = InternalResourceResolver.getBaseURI(cfg.getChild("hyphenation-base").getValue(null));
  251. fopFactoryBuilder.setHyphenBaseResourceResolver(
  252. ResourceResolverFactory.createInternalResourceResolver(
  253. baseURI.resolve(fontBase), resourceResolver));
  254. } catch (URISyntaxException use) {
  255. LogUtil.handleException(log, use, true);
  256. }
  257. } else {
  258. fopFactoryBuilder.setHyphenBaseResourceResolver(
  259. ResourceResolverFactory.createInternalResourceResolver(
  260. fopFactoryBuilder.getBaseURI(), resourceResolver));
  261. }
  262. }
  263. private void setHyphPatNames(Configuration cfg, FopFactoryBuilder builder, boolean strict)
  264. throws FOPException {
  265. Configuration[] hyphPatConfig = cfg.getChildren("hyphenation-pattern");
  266. if (hyphPatConfig.length != 0) {
  267. Map<String, String> hyphPatNames = new HashMap<String, String>();
  268. for (Configuration aHyphPatConfig : hyphPatConfig) {
  269. String lang;
  270. String country;
  271. String filename;
  272. StringBuffer error = new StringBuffer();
  273. String location = aHyphPatConfig.getLocation();
  274. lang = aHyphPatConfig.getAttribute("lang", null);
  275. if (lang == null) {
  276. addError("The lang attribute of a hyphenation-pattern configuration"
  277. + " element must exist (" + location + ")", error);
  278. } else if (!lang.matches("[a-zA-Z]{2}")) {
  279. addError("The lang attribute of a hyphenation-pattern configuration"
  280. + " element must consist of exactly two letters ("
  281. + location + ")", error);
  282. }
  283. lang = lang.toLowerCase(Locale.getDefault());
  284. country = aHyphPatConfig.getAttribute("country", null);
  285. if ("".equals(country)) {
  286. country = null;
  287. }
  288. if (country != null) {
  289. if (!country.matches("[a-zA-Z]{2}")) {
  290. addError("The country attribute of a hyphenation-pattern configuration"
  291. + " element must consist of exactly two letters ("
  292. + location + ")", error);
  293. }
  294. country = country.toUpperCase(Locale.getDefault());
  295. }
  296. filename = aHyphPatConfig.getValue(null);
  297. if (filename == null) {
  298. addError("The value of a hyphenation-pattern configuration"
  299. + " element may not be empty (" + location + ")", error);
  300. }
  301. if (error.length() != 0) {
  302. LogUtil.handleError(log, error.toString(), strict);
  303. continue;
  304. }
  305. String llccKey = HyphenationTreeCache.constructLlccKey(lang, country);
  306. String extension = aHyphPatConfig.getAttribute("extension", null);
  307. if ("xml".equals(extension)) {
  308. hyphPatNames.put(llccKey, filename + Hyphenator.XMLTYPE);
  309. } else if ("hyp".equals(extension)) {
  310. hyphPatNames.put(llccKey, filename + Hyphenator.HYPTYPE);
  311. } else {
  312. hyphPatNames.put(llccKey, filename);
  313. }
  314. if (log.isDebugEnabled()) {
  315. log.debug("Using hyphenation pattern filename " + filename
  316. + " for lang=\"" + lang + "\""
  317. + (country != null ? ", country=\"" + country + "\"" : ""));
  318. }
  319. }
  320. builder.setHyphPatNames(hyphPatNames);
  321. }
  322. }
  323. private static void addError(String message, StringBuffer error) {
  324. if (error.length() != 0) {
  325. error.append(". ");
  326. }
  327. error.append(message);
  328. }
  329. private void configureImageLoading(Configuration parent, boolean strict) throws FOPException {
  330. if (parent == null) {
  331. return;
  332. }
  333. ImageImplRegistry registry = fopFactoryBuilder.getImageManager().getRegistry();
  334. Configuration[] penalties = parent.getChildren("penalty");
  335. try {
  336. for (Configuration penaltyCfg : penalties) {
  337. String className = penaltyCfg.getAttribute("class");
  338. String value = penaltyCfg.getAttribute("value");
  339. Penalty p = null;
  340. if (value.toUpperCase(Locale.getDefault()).startsWith("INF")) {
  341. p = Penalty.INFINITE_PENALTY;
  342. } else {
  343. try {
  344. p = Penalty.toPenalty(Integer.parseInt(value));
  345. } catch (NumberFormatException nfe) {
  346. LogUtil.handleException(log, nfe, strict);
  347. }
  348. }
  349. if (p != null) {
  350. registry.setAdditionalPenalty(className, p);
  351. }
  352. }
  353. } catch (ConfigurationException e) {
  354. LogUtil.handleException(log, e, strict);
  355. }
  356. }
  357. /**
  358. * Returns the {@link FopFactoryBuilder}.
  359. *
  360. * @return the object for configuring the {@link FopFactory}
  361. */
  362. public FopFactoryBuilder getFopFactoryBuilder() {
  363. return fopFactoryBuilder;
  364. }
  365. }