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.

FontInfoConfigurator.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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.fonts;
  19. import java.io.File;
  20. import java.io.IOException;
  21. import java.net.MalformedURLException;
  22. import java.net.URL;
  23. import java.util.List;
  24. import javax.xml.transform.Source;
  25. import javax.xml.transform.stream.StreamSource;
  26. import org.apache.avalon.framework.configuration.Configuration;
  27. import org.apache.avalon.framework.configuration.ConfigurationException;
  28. import org.apache.commons.io.IOUtils;
  29. import org.apache.commons.logging.Log;
  30. import org.apache.commons.logging.LogFactory;
  31. import org.apache.fop.apps.FOPException;
  32. import org.apache.fop.fonts.autodetect.FontFileFinder;
  33. import org.apache.fop.fonts.autodetect.FontInfoFinder;
  34. import org.apache.fop.util.LogUtil;
  35. /**
  36. * An abstract FontInfo configurator
  37. */
  38. public class FontInfoConfigurator {
  39. /** logger instance */
  40. protected static final Log log = LogFactory.getLog(FontInfoConfigurator.class);
  41. private final Configuration cfg;
  42. private final FontManager fontManager;
  43. private final FontResolver fontResolver;
  44. private final FontEventListener listener;
  45. private final boolean strict;
  46. /**
  47. * Main constructor
  48. * @param cfg the configuration object
  49. * @param fontManager the font manager
  50. * @param fontResolver the font resolver
  51. * @param listener the font event listener
  52. * @param strict true if an Exception should be thrown if an error is found.
  53. */
  54. public FontInfoConfigurator(Configuration cfg, FontManager fontManager,
  55. FontResolver fontResolver, FontEventListener listener, boolean strict) {
  56. this.cfg = cfg;
  57. this.fontManager = fontManager;
  58. this.fontResolver = fontResolver;
  59. this.listener = listener;
  60. this.strict = strict;
  61. }
  62. /**
  63. * Initializes font info settings from the user configuration
  64. * @param fontInfoList a font info list
  65. * @throws FOPException if an exception occurs while processing the configuration
  66. */
  67. public void configure(List<EmbedFontInfo> fontInfoList)
  68. throws FOPException {
  69. Configuration fontsCfg = cfg.getChild("fonts", false);
  70. if (fontsCfg != null) {
  71. long start = 0;
  72. if (log.isDebugEnabled()) {
  73. log.debug("Starting font configuration...");
  74. start = System.currentTimeMillis();
  75. }
  76. FontAdder fontAdder = new FontAdder(fontManager, fontResolver, listener);
  77. // native o/s search (autodetect) configuration
  78. boolean autodetectFonts = (fontsCfg.getChild("auto-detect", false) != null);
  79. if (autodetectFonts) {
  80. FontDetector fontDetector = new FontDetector(fontManager, fontAdder, strict,
  81. listener);
  82. fontDetector.detect(fontInfoList);
  83. }
  84. // Add configured directories to FontInfo list
  85. addDirectories(fontsCfg, fontAdder, fontInfoList);
  86. // Add fonts from configuration to FontInfo list
  87. addFonts(fontsCfg, fontManager.getFontCache(), fontInfoList);
  88. // Update referenced fonts (fonts which are not to be embedded)
  89. fontManager.updateReferencedFonts(fontInfoList);
  90. // Renderer-specific referenced fonts
  91. Configuration referencedFontsCfg = fontsCfg.getChild("referenced-fonts", false);
  92. if (referencedFontsCfg != null) {
  93. FontTriplet.Matcher matcher = FontManagerConfigurator.createFontsMatcher(
  94. referencedFontsCfg, strict);
  95. fontManager.updateReferencedFonts(fontInfoList, matcher);
  96. }
  97. // Update font cache if it has changed
  98. fontManager.saveCache();
  99. if (log.isDebugEnabled()) {
  100. log.debug("Finished font configuration in "
  101. + (System.currentTimeMillis() - start) + "ms");
  102. }
  103. }
  104. }
  105. private void addDirectories(Configuration fontsCfg,
  106. FontAdder fontAdder, List<EmbedFontInfo> fontInfoList) throws FOPException {
  107. // directory (multiple font) configuration
  108. Configuration[] directories = fontsCfg.getChildren("directory");
  109. for (int i = 0; i < directories.length; i++) {
  110. boolean recursive = directories[i].getAttributeAsBoolean("recursive", false);
  111. String directory = null;
  112. try {
  113. directory = directories[i].getValue();
  114. } catch (ConfigurationException e) {
  115. LogUtil.handleException(log, e, strict);
  116. continue;
  117. }
  118. if (directory == null) {
  119. LogUtil.handleException(log,
  120. new FOPException("directory defined without value"), strict);
  121. continue;
  122. }
  123. // add fonts found in directory
  124. FontFileFinder fontFileFinder = new FontFileFinder(recursive ? -1 : 1, listener);
  125. List<URL> fontURLList;
  126. try {
  127. fontURLList = fontFileFinder.find(directory);
  128. fontAdder.add(fontURLList, fontInfoList);
  129. } catch (IOException e) {
  130. LogUtil.handleException(log, e, strict);
  131. }
  132. }
  133. }
  134. /**
  135. * Populates the font info list from the fonts configuration
  136. * @param fontsCfg a fonts configuration
  137. * @param fontCache a font cache
  138. * @param fontInfoList a font info list
  139. * @throws FOPException if an exception occurs while processing the configuration
  140. */
  141. protected void addFonts(Configuration fontsCfg, FontCache fontCache,
  142. List<EmbedFontInfo> fontInfoList) throws FOPException {
  143. // font file (singular) configuration
  144. Configuration[] font = fontsCfg.getChildren("font");
  145. for (int i = 0; i < font.length; i++) {
  146. EmbedFontInfo embedFontInfo = getFontInfo(
  147. font[i], fontCache);
  148. if (embedFontInfo != null) {
  149. fontInfoList.add(embedFontInfo);
  150. }
  151. }
  152. }
  153. private static void closeSource(Source src) {
  154. if (src instanceof StreamSource) {
  155. StreamSource streamSource = (StreamSource)src;
  156. IOUtils.closeQuietly(streamSource.getInputStream());
  157. IOUtils.closeQuietly(streamSource.getReader());
  158. }
  159. }
  160. /**
  161. * Returns a font info from a font node Configuration definition
  162. *
  163. * @param fontCfg Configuration object (font node)
  164. * @param fontCache the font cache (or null if it is disabled)
  165. * @return the embedded font info
  166. * @throws FOPException if something's wrong with the config data
  167. */
  168. protected EmbedFontInfo getFontInfo(Configuration fontCfg, FontCache fontCache)
  169. throws FOPException {
  170. String metricsUrl = fontCfg.getAttribute("metrics-url", null);
  171. String embedUrl = fontCfg.getAttribute("embed-url", null);
  172. String subFont = fontCfg.getAttribute("sub-font", null);
  173. if (metricsUrl == null && embedUrl == null) {
  174. LogUtil.handleError(log,
  175. "Font configuration without metric-url or embed-url attribute",
  176. strict);
  177. return null;
  178. }
  179. if (strict) {
  180. //This section just checks early whether the URIs can be resolved
  181. //Stream are immediately closed again since they will never be used anyway
  182. if (embedUrl != null) {
  183. Source source = fontResolver.resolve(embedUrl);
  184. closeSource(source);
  185. if (source == null) {
  186. LogUtil.handleError(log,
  187. "Failed to resolve font with embed-url '" + embedUrl + "'", strict);
  188. return null;
  189. }
  190. }
  191. if (metricsUrl != null) {
  192. Source source = fontResolver.resolve(metricsUrl);
  193. closeSource(source);
  194. if (source == null) {
  195. LogUtil.handleError(log,
  196. "Failed to resolve font with metric-url '" + metricsUrl + "'", strict);
  197. return null;
  198. }
  199. }
  200. }
  201. Configuration[] tripletCfg = fontCfg.getChildren("font-triplet");
  202. // no font triplet info
  203. if (tripletCfg.length == 0) {
  204. LogUtil.handleError(log, "font without font-triplet", strict);
  205. File fontFile = FontCache.getFileFromUrls(new String[] {embedUrl, metricsUrl});
  206. URL fontURL = null;
  207. try {
  208. fontURL = fontFile.toURI().toURL();
  209. } catch (MalformedURLException e) {
  210. LogUtil.handleException(log, e, strict);
  211. }
  212. if (fontFile != null) {
  213. FontInfoFinder finder = new FontInfoFinder();
  214. finder.setEventListener(listener);
  215. EmbedFontInfo[] infos = finder.find(fontURL, fontResolver, fontCache);
  216. return infos[0]; //When subFont is set, only one font is returned
  217. } else {
  218. return null;
  219. }
  220. }
  221. List<FontTriplet> tripletList = new java.util.ArrayList<FontTriplet>();
  222. for (int j = 0; j < tripletCfg.length; j++) {
  223. FontTriplet fontTriplet = getFontTriplet(tripletCfg[j]);
  224. tripletList.add(fontTriplet);
  225. }
  226. boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true);
  227. boolean useAdvanced = fontCfg.getAttributeAsBoolean("advanced", true);
  228. EncodingMode encodingMode = EncodingMode.getValue(
  229. fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName()));
  230. EmbeddingMode embeddingMode = EmbeddingMode.getValue(
  231. fontCfg.getAttribute("embedding-mode", EmbeddingMode.AUTO.toString()));
  232. EmbedFontInfo embedFontInfo
  233. = new EmbedFontInfo(metricsUrl, useKerning, useAdvanced, tripletList, embedUrl,
  234. subFont);
  235. embedFontInfo.setEncodingMode(encodingMode);
  236. embedFontInfo.setEmbeddingMode(embeddingMode);
  237. boolean skipCachedFont = false;
  238. if (fontCache != null) {
  239. if (!fontCache.containsFont(embedFontInfo)) {
  240. fontCache.addFont(embedFontInfo);
  241. } else {
  242. skipCachedFont = true;
  243. }
  244. }
  245. if (log.isDebugEnabled()) {
  246. String embedFile = embedFontInfo.getEmbedFile();
  247. log.debug( ( skipCachedFont ? "Skipping (cached) font " : "Adding font " )
  248. + (embedFile != null ? embedFile + ", " : "")
  249. + "metric file " + embedFontInfo.getMetricsFile());
  250. for (int j = 0; j < tripletList.size(); ++j) {
  251. FontTriplet triplet = tripletList.get(j);
  252. log.debug(" Font triplet "
  253. + triplet.getName() + ", "
  254. + triplet.getStyle() + ", "
  255. + triplet.getWeight());
  256. }
  257. }
  258. return embedFontInfo;
  259. }
  260. /**
  261. * Creates a new FontTriplet given a triple Configuration
  262. *
  263. * @param tripletCfg a triplet configuration
  264. * @return a font triplet font key
  265. * @throws FOPException thrown if a FOP exception occurs
  266. */
  267. private FontTriplet getFontTriplet(Configuration tripletCfg) throws FOPException {
  268. try {
  269. String name = tripletCfg.getAttribute("name");
  270. if (name == null) {
  271. LogUtil.handleError(log, "font-triplet without name", strict);
  272. return null;
  273. }
  274. String weightStr = tripletCfg.getAttribute("weight");
  275. if (weightStr == null) {
  276. LogUtil.handleError(log, "font-triplet without weight", strict);
  277. return null;
  278. }
  279. int weight = FontUtil.parseCSS2FontWeight(FontUtil.stripWhiteSpace(weightStr));
  280. String style = tripletCfg.getAttribute("style");
  281. if (style == null) {
  282. LogUtil.handleError(log, "font-triplet without style", strict);
  283. return null;
  284. } else {
  285. style = FontUtil.stripWhiteSpace(style);
  286. }
  287. return FontInfo.createFontKey(name, style, weight);
  288. } catch (ConfigurationException e) {
  289. LogUtil.handleException(log, e, strict);
  290. }
  291. return null;
  292. }
  293. }