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.

PrintRendererConfigurator.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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.render;
  19. import java.io.File;
  20. import java.io.IOException;
  21. import java.net.MalformedURLException;
  22. import java.net.URL;
  23. import java.util.Iterator;
  24. import java.util.List;
  25. import javax.xml.transform.Source;
  26. import javax.xml.transform.stream.StreamSource;
  27. import org.apache.avalon.framework.configuration.Configuration;
  28. import org.apache.avalon.framework.configuration.ConfigurationException;
  29. import org.apache.commons.io.FileUtils;
  30. import org.apache.commons.io.IOUtils;
  31. import org.apache.commons.logging.Log;
  32. import org.apache.commons.logging.LogFactory;
  33. import org.apache.fop.apps.FOPException;
  34. import org.apache.fop.apps.FOUserAgent;
  35. import org.apache.fop.apps.FopFactory;
  36. import org.apache.fop.fonts.CachedFontInfo;
  37. import org.apache.fop.fonts.EmbedFontInfo;
  38. import org.apache.fop.fonts.FontCache;
  39. import org.apache.fop.fonts.FontInfo;
  40. import org.apache.fop.fonts.FontResolver;
  41. import org.apache.fop.fonts.FontSetup;
  42. import org.apache.fop.fonts.FontTriplet;
  43. import org.apache.fop.fonts.FontUtil;
  44. import org.apache.fop.fonts.autodetect.FontFileFinder;
  45. import org.apache.fop.fonts.autodetect.FontInfoFinder;
  46. import org.apache.fop.util.LogUtil;
  47. import org.apache.xmlgraphics.util.ClasspathResource;
  48. /**
  49. * Base Print renderer configurator (mostly handles font configuration)
  50. */
  51. public class PrintRendererConfigurator extends AbstractRendererConfigurator
  52. implements RendererConfigurator {
  53. /** logger instance */
  54. protected static Log log = LogFactory.getLog(PrintRendererConfigurator.class);
  55. /**
  56. * Default constructor
  57. * @param userAgent user agent
  58. */
  59. public PrintRendererConfigurator(FOUserAgent userAgent) {
  60. super(userAgent);
  61. }
  62. /**
  63. * Builds a list of EmbedFontInfo objects for use with the setup() method.
  64. *
  65. * @param renderer print renderer
  66. * @throws FOPException if something's wrong with the config data
  67. */
  68. public void configure(Renderer renderer) throws FOPException {
  69. Configuration cfg = getRendererConfig(renderer);
  70. if (cfg == null) {
  71. return;
  72. }
  73. PrintRenderer printRenderer = (PrintRenderer)renderer;
  74. FontResolver fontResolver = printRenderer.getFontResolver();
  75. if (fontResolver == null) {
  76. //Ensure that we have minimal font resolution capabilities
  77. fontResolver = FontSetup.createMinimalFontResolver();
  78. }
  79. FopFactory factory = userAgent.getFactory();
  80. boolean strict = factory.validateUserConfigStrictly();
  81. FontCache fontCache = factory.getFontCache();
  82. List fontInfoList = buildFontListFromConfiguration(cfg,
  83. userAgent.getFontBaseURL(), fontResolver, strict,
  84. fontCache);
  85. if (fontCache != null && fontCache.hasChanged()) {
  86. fontCache.save();
  87. }
  88. printRenderer.addFontList(fontInfoList);
  89. }
  90. /**
  91. * Builds a list of EmbedFontInfo objects for use with the setup() method.
  92. *
  93. * @param cfg Configuration object
  94. * @param fontBaseURL the base URL to resolve relative font URLs with
  95. * @param fontResolver the FontResolver to use
  96. * @param strict true if an Exception should be thrown if an error is found.
  97. * @param fontCache the font cache (or null if it is disabled)
  98. * @return a List of EmbedFontInfo objects.
  99. * @throws FOPException If an error occurs while processing the configuration
  100. */
  101. public static List buildFontListFromConfiguration(Configuration cfg,
  102. String fontBaseURL, FontResolver fontResolver,
  103. boolean strict, FontCache fontCache) throws FOPException {
  104. List fontInfoList = new java.util.ArrayList();
  105. Configuration fonts = cfg.getChild("fonts", false);
  106. if (fonts != null) {
  107. long start = 0;
  108. if (log.isDebugEnabled()) {
  109. log.debug("Starting font configuration...");
  110. start = System.currentTimeMillis();
  111. }
  112. // native o/s search (autodetect) configuration
  113. boolean autodetectFonts = (fonts.getChild("auto-detect", false) != null);
  114. if (autodetectFonts) {
  115. // search in font base if it is defined and
  116. // is a directory but don't recurse
  117. FontFileFinder fontFileFinder = new FontFileFinder();
  118. if (fontBaseURL != null) {
  119. try {
  120. File fontBase = FileUtils.toFile(new URL(fontBaseURL));
  121. if (fontBase != null) {
  122. //Can only use the font base URL if it's a file URL
  123. addFontInfoListFromFileList(
  124. fontFileFinder.find(fontBase.getAbsolutePath()),
  125. fontInfoList,
  126. fontResolver,
  127. fontCache
  128. );
  129. }
  130. } catch (IOException e) {
  131. LogUtil.handleException(log, e, strict);
  132. }
  133. }
  134. // native o/s font directory finder
  135. try {
  136. addFontInfoListFromFileList(
  137. fontFileFinder.find(),
  138. fontInfoList,
  139. fontResolver,
  140. fontCache
  141. );
  142. } catch (IOException e) {
  143. LogUtil.handleException(log, e, strict);
  144. }
  145. // load fonts from classpath
  146. addFontInfoListFromFileList(ClasspathResource.getInstance()
  147. .listResourcesOfMimeType("application/x-font"),
  148. fontInfoList, fontResolver, fontCache);
  149. addFontInfoListFromFileList(
  150. ClasspathResource.getInstance()
  151. .listResourcesOfMimeType(
  152. "application/x-font-truetype"),
  153. fontInfoList, fontResolver, fontCache);
  154. }
  155. // directory (multiple font) configuration
  156. Configuration[] directories = fonts.getChildren("directory");
  157. for (int i = 0; i < directories.length; i++) {
  158. boolean recursive = directories[i].getAttributeAsBoolean("recursive", false);
  159. String directory = null;
  160. try {
  161. directory = directories[i].getValue();
  162. } catch (ConfigurationException e) {
  163. LogUtil.handleException(log, e, strict);
  164. continue;
  165. }
  166. if (directory == null) {
  167. LogUtil.handleException(log,
  168. new FOPException("directory defined without value"), strict);
  169. continue;
  170. }
  171. FontFileFinder fontFileFinder = new FontFileFinder(recursive ? -1 : 1);
  172. try {
  173. addFontInfoListFromFileList(
  174. fontFileFinder.find(directory),
  175. fontInfoList,
  176. fontResolver,
  177. fontCache
  178. );
  179. } catch (IOException e) {
  180. LogUtil.handleException(log, e, strict);
  181. }
  182. }
  183. // font file (singular) configuration
  184. Configuration[] font = fonts.getChildren("font");
  185. for (int i = 0; i < font.length; i++) {
  186. EmbedFontInfo fontInfo = getFontInfoFromConfiguration(
  187. font[i], fontResolver, strict, fontCache);
  188. if (fontInfo != null) {
  189. fontInfoList.add(fontInfo);
  190. }
  191. }
  192. if (log.isDebugEnabled()) {
  193. log.debug("Finished font configuration in "
  194. + (System.currentTimeMillis() - start) + "ms");
  195. }
  196. }
  197. return fontInfoList;
  198. }
  199. /**
  200. * Iterates over font file list adding font info to list
  201. * @param fontFileList font file list
  202. * @param fontInfoList font info list
  203. * @param resolver font resolver
  204. */
  205. private static void addFontInfoListFromFileList(
  206. List fontFileList, List fontInfoList, FontResolver resolver, FontCache fontCache) {
  207. for (Iterator iter = fontFileList.iterator(); iter.hasNext();) {
  208. URL fontUrl = (URL)iter.next();
  209. // parse font to ascertain font info
  210. FontInfoFinder finder = new FontInfoFinder();
  211. EmbedFontInfo fontInfo = finder.find(fontUrl, resolver, fontCache);
  212. if (fontInfo != null) {
  213. fontInfoList.add(fontInfo);
  214. }
  215. }
  216. }
  217. private static void closeSource(Source src) {
  218. if (src instanceof StreamSource) {
  219. StreamSource streamSource = (StreamSource)src;
  220. IOUtils.closeQuietly(streamSource.getInputStream());
  221. IOUtils.closeQuietly(streamSource.getReader());
  222. }
  223. }
  224. /**
  225. * Returns a font info from a font node Configuration definition
  226. *
  227. * @param fontCfg Configuration object (font node)
  228. * @param fontResolver font resolver used to resolve font
  229. * @param strict validate configuration strictly
  230. * @param fontCache the font cache (or null if it is disabled)
  231. * @return font info
  232. * @throws FOPException if something's wrong with the config data
  233. */
  234. public static EmbedFontInfo getFontInfoFromConfiguration(
  235. Configuration fontCfg, FontResolver fontResolver, boolean strict, FontCache fontCache)
  236. throws FOPException {
  237. String metricsUrl = fontCfg.getAttribute("metrics-url", null);
  238. String embedUrl = fontCfg.getAttribute("embed-url", null);
  239. if (metricsUrl == null && embedUrl == null) {
  240. LogUtil.handleError(log, "Font configuration without metric-url or embed-url", strict);
  241. return null;
  242. }
  243. if (strict) {
  244. //This section just checks early whether the URIs can be resolved
  245. //Stream are immediately closed again since they will never be used anyway
  246. if (embedUrl != null) {
  247. Source source = fontResolver.resolve(embedUrl);
  248. closeSource(source);
  249. if (source == null) {
  250. LogUtil.handleError(log,
  251. "Failed to resolve font with embed-url '" + embedUrl + "'", strict);
  252. return null;
  253. }
  254. }
  255. if (metricsUrl != null) {
  256. Source source = fontResolver.resolve(metricsUrl);
  257. closeSource(source);
  258. if (source == null) {
  259. LogUtil.handleError(log,
  260. "Failed to resolve font with metric-url '" + metricsUrl + "'", strict);
  261. return null;
  262. }
  263. }
  264. }
  265. boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true);
  266. EmbedFontInfo fontInfo = null;
  267. Configuration[] tripletCfg = fontCfg.getChildren("font-triplet");
  268. // no font triplet info
  269. if (tripletCfg.length == 0) {
  270. LogUtil.handleError(log, "font without font-triplet", strict);
  271. // if not strict try to determine font info from the embed/metrics url
  272. File fontFile = CachedFontInfo.getFileFromUrls(new String[] {embedUrl, metricsUrl});
  273. URL fontUrl;
  274. try {
  275. fontUrl = fontFile.toURI().toURL();
  276. } catch (MalformedURLException e) {
  277. // Should never happen
  278. log.debug("Malformed Url: " + e.getMessage());
  279. return null;
  280. }
  281. if (fontFile != null) {
  282. FontInfoFinder finder = new FontInfoFinder();
  283. return finder.find(fontUrl, fontResolver, fontCache);
  284. } else {
  285. return null;
  286. }
  287. } else {
  288. List tripleList = new java.util.ArrayList();
  289. for (int j = 0; j < tripletCfg.length; j++) {
  290. try {
  291. String name = tripletCfg[j].getAttribute("name");
  292. if (name == null) {
  293. LogUtil.handleError(log, "font-triplet without name", strict);
  294. continue;
  295. }
  296. String weightStr = tripletCfg[j].getAttribute("weight");
  297. if (weightStr == null) {
  298. LogUtil.handleError(log, "font-triplet without weight", strict);
  299. continue;
  300. }
  301. int weight = FontUtil.parseCSS2FontWeight(weightStr);
  302. String style = tripletCfg[j].getAttribute("style");
  303. if (style == null) {
  304. LogUtil.handleError(log, "font-triplet without style", strict);
  305. continue;
  306. }
  307. tripleList.add(FontInfo.createFontKey(name, style, weight));
  308. } catch (ConfigurationException e) {
  309. LogUtil.handleException(log, e, strict);
  310. }
  311. }
  312. fontInfo = new EmbedFontInfo(metricsUrl, useKerning, tripleList, embedUrl);
  313. if (fontCache != null) {
  314. if (!fontCache.containsFont(fontInfo)) {
  315. fontCache.addFont(fontInfo);
  316. }
  317. }
  318. if (log.isDebugEnabled()) {
  319. log.debug("Adding font " + fontInfo.getEmbedFile()
  320. + ", metric file " + fontInfo.getMetricsFile());
  321. for (int j = 0; j < tripleList.size(); ++j) {
  322. FontTriplet triplet = (FontTriplet) tripleList.get(j);
  323. log.debug(" Font triplet "
  324. + triplet.getName() + ", "
  325. + triplet.getStyle() + ", "
  326. + triplet.getWeight());
  327. }
  328. }
  329. }
  330. return fontInfo;
  331. }
  332. }