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

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