Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

FontInfoFinder.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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.autodetect;
  19. import java.io.InputStream;
  20. import java.net.URI;
  21. import java.util.Collection;
  22. import java.util.List;
  23. import java.util.Set;
  24. import java.util.regex.Pattern;
  25. import org.apache.commons.io.IOUtils;
  26. import org.apache.commons.logging.Log;
  27. import org.apache.commons.logging.LogFactory;
  28. import org.apache.fop.apps.io.InternalResourceResolver;
  29. import org.apache.fop.fonts.CustomFont;
  30. import org.apache.fop.fonts.EmbedFontInfo;
  31. import org.apache.fop.fonts.EncodingMode;
  32. import org.apache.fop.fonts.Font;
  33. import org.apache.fop.fonts.FontCache;
  34. import org.apache.fop.fonts.FontEventListener;
  35. import org.apache.fop.fonts.FontLoader;
  36. import org.apache.fop.fonts.FontTriplet;
  37. import org.apache.fop.fonts.FontUtil;
  38. import org.apache.fop.fonts.MultiByteFont;
  39. import org.apache.fop.fonts.truetype.FontFileReader;
  40. import org.apache.fop.fonts.truetype.TTFFile;
  41. import org.apache.fop.fonts.truetype.TTFFontLoader;
  42. /**
  43. * Attempts to determine correct FontInfo
  44. */
  45. public class FontInfoFinder {
  46. /** logging instance */
  47. private final Log log = LogFactory.getLog(FontInfoFinder.class);
  48. private FontEventListener eventListener;
  49. /**
  50. * Sets the font event listener that can be used to receive events about particular events
  51. * in this class.
  52. * @param listener the font event listener
  53. */
  54. public void setEventListener(FontEventListener listener) {
  55. this.eventListener = listener;
  56. }
  57. /**
  58. * Attempts to determine FontTriplets from a given CustomFont.
  59. * It seems to be fairly accurate but will probably require some tweaking over time
  60. *
  61. * @param customFont CustomFont
  62. * @param triplets Collection that will take the generated triplets
  63. */
  64. private void generateTripletsFromFont(CustomFont customFont, Collection<FontTriplet> triplets) {
  65. if (log.isTraceEnabled()) {
  66. log.trace("Font: " + customFont.getFullName()
  67. + ", family: " + customFont.getFamilyNames()
  68. + ", PS: " + customFont.getFontName()
  69. + ", EmbedName: " + customFont.getEmbedFontName());
  70. }
  71. // default style and weight triplet vales (fallback)
  72. String strippedName = stripQuotes(customFont.getStrippedFontName());
  73. //String subName = customFont.getFontSubName();
  74. String fullName = stripQuotes(customFont.getFullName());
  75. String searchName = fullName.toLowerCase();
  76. String style = guessStyle(customFont, searchName);
  77. int weight; //= customFont.getWeight();
  78. int guessedWeight = FontUtil.guessWeight(searchName);
  79. //We always take the guessed weight for now since it yield much better results.
  80. //OpenType's OS/2 usWeightClass value proves to be unreliable.
  81. weight = guessedWeight;
  82. //Full Name usually includes style/weight info so don't use these traits
  83. //If we still want to use these traits, we have to make FontInfo.fontLookup() smarter
  84. triplets.add(new FontTriplet(fullName, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL));
  85. if (!fullName.equals(strippedName)) {
  86. triplets.add(new FontTriplet(strippedName, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL));
  87. }
  88. Set<String> familyNames = customFont.getFamilyNames();
  89. for (String familyName : familyNames) {
  90. familyName = stripQuotes(familyName);
  91. if (!fullName.equals(familyName)) {
  92. /* Heuristic:
  93. * The more similar the family name to the full font name,
  94. * the higher the priority of its triplet.
  95. * (Lower values indicate higher priorities.) */
  96. int priority = fullName.startsWith(familyName)
  97. ? fullName.length() - familyName.length()
  98. : fullName.length();
  99. triplets.add(new FontTriplet(familyName, style, weight, priority));
  100. }
  101. }
  102. }
  103. private final Pattern quotePattern = Pattern.compile("'");
  104. private String stripQuotes(String name) {
  105. return quotePattern.matcher(name).replaceAll("");
  106. }
  107. private String guessStyle(CustomFont customFont, String fontName) {
  108. // style
  109. String style = Font.STYLE_NORMAL;
  110. if (customFont.getItalicAngle() > 0) {
  111. style = Font.STYLE_ITALIC;
  112. } else {
  113. style = FontUtil.guessStyle(fontName);
  114. }
  115. return style;
  116. }
  117. private EmbedFontInfo getFontInfoFromCustomFont(URI fontUri, CustomFont customFont,
  118. FontCache fontCache, InternalResourceResolver resourceResolver) {
  119. List<FontTriplet> fontTripletList = new java.util.ArrayList<FontTriplet>();
  120. generateTripletsFromFont(customFont, fontTripletList);
  121. String subFontName = null;
  122. if (customFont instanceof MultiByteFont) {
  123. subFontName = ((MultiByteFont) customFont).getTTCName();
  124. }
  125. EmbedFontInfo fontInfo = new EmbedFontInfo(null, customFont.isKerningEnabled(),
  126. customFont.isAdvancedEnabled(), fontTripletList, fontUri, subFontName);
  127. fontInfo.setPostScriptName(customFont.getFontName());
  128. if (fontCache != null) {
  129. fontCache.addFont(fontInfo, resourceResolver);
  130. }
  131. return fontInfo;
  132. }
  133. /**
  134. * Attempts to determine EmbedFontInfo from a given font file.
  135. *
  136. * @param fontURI the URI of the font resource
  137. * @param resourceResolver font resolver used to resolve font
  138. * @param fontCache font cache (may be null)
  139. * @return an array of newly created embed font info. Generally, this array
  140. * will have only one entry, unless the fontUrl is a TrueType Collection
  141. */
  142. public EmbedFontInfo[] find(URI fontURI, InternalResourceResolver resourceResolver, FontCache fontCache) {
  143. URI embedUri = resourceResolver.resolveFromBase(fontURI);
  144. String embedStr = embedUri.toASCIIString();
  145. boolean useKerning = true;
  146. boolean useAdvanced = true;
  147. long fileLastModified = -1;
  148. if (fontCache != null) {
  149. fileLastModified = FontCache.getLastModified(fontURI);
  150. // firstly try and fetch it from cache before loading/parsing the font file
  151. if (fontCache.containsFont(embedStr)) {
  152. EmbedFontInfo[] fontInfos = fontCache.getFontInfos(embedStr, fileLastModified);
  153. if (fontInfos != null) {
  154. return fontInfos;
  155. }
  156. // is this a previously failed parsed font?
  157. } else if (fontCache.isFailedFont(embedStr, fileLastModified)) {
  158. if (log.isDebugEnabled()) {
  159. log.debug("Skipping font file that failed to load previously: " + embedUri);
  160. }
  161. return null;
  162. }
  163. }
  164. // try to determine triplet information from font file
  165. CustomFont customFont = null;
  166. if (fontURI.toASCIIString().toLowerCase().endsWith(".ttc")) {
  167. // Get a list of the TTC Font names
  168. List<String> ttcNames = null;
  169. InputStream in = null;
  170. try {
  171. in = resourceResolver.getResource(fontURI);
  172. TTFFile ttf = new TTFFile(false, false);
  173. FontFileReader reader = new FontFileReader(in);
  174. ttcNames = ttf.getTTCnames(reader);
  175. } catch (Exception e) {
  176. if (this.eventListener != null) {
  177. this.eventListener.fontLoadingErrorAtAutoDetection(this,
  178. fontURI.toASCIIString(), e);
  179. }
  180. return null;
  181. } finally {
  182. IOUtils.closeQuietly(in);
  183. }
  184. List<EmbedFontInfo> embedFontInfoList = new java.util.ArrayList<EmbedFontInfo>();
  185. // For each font name ...
  186. for (String fontName : ttcNames) {
  187. if (log.isDebugEnabled()) {
  188. log.debug("Loading " + fontName);
  189. }
  190. try {
  191. TTFFontLoader ttfLoader = new TTFFontLoader(fontURI, fontName, true,
  192. EncodingMode.AUTO, useKerning, useAdvanced, resourceResolver);
  193. customFont = ttfLoader.getFont();
  194. if (this.eventListener != null) {
  195. customFont.setEventListener(this.eventListener);
  196. }
  197. } catch (Exception e) {
  198. if (fontCache != null) {
  199. fontCache.registerFailedFont(embedUri.toASCIIString(), fileLastModified);
  200. }
  201. if (this.eventListener != null) {
  202. this.eventListener.fontLoadingErrorAtAutoDetection(this,
  203. embedUri.toASCIIString(), e);
  204. }
  205. continue;
  206. }
  207. EmbedFontInfo fi = getFontInfoFromCustomFont(fontURI, customFont, fontCache,
  208. resourceResolver);
  209. if (fi != null) {
  210. embedFontInfoList.add(fi);
  211. }
  212. }
  213. return embedFontInfoList.toArray(
  214. new EmbedFontInfo[embedFontInfoList.size()]);
  215. } else {
  216. // The normal case
  217. try {
  218. customFont = FontLoader.loadFont(fontURI, null, true, EncodingMode.AUTO,
  219. useKerning, useAdvanced, resourceResolver);
  220. if (this.eventListener != null) {
  221. customFont.setEventListener(this.eventListener);
  222. }
  223. } catch (Exception e) {
  224. if (fontCache != null) {
  225. fontCache.registerFailedFont(embedUri.toASCIIString(), fileLastModified);
  226. }
  227. if (this.eventListener != null) {
  228. this.eventListener.fontLoadingErrorAtAutoDetection(this,
  229. embedUri.toASCIIString(), e);
  230. }
  231. return null;
  232. }
  233. EmbedFontInfo fi = getFontInfoFromCustomFont(fontURI, customFont, fontCache, resourceResolver);
  234. if (fi != null) {
  235. return new EmbedFontInfo[] {fi};
  236. } else {
  237. return null;
  238. }
  239. }
  240. }
  241. }