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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  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.util.ArrayList;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.Map;
  25. import org.apache.commons.logging.Log;
  26. import org.apache.commons.logging.LogFactory;
  27. /**
  28. * The FontInfo for the layout and rendering of a fo document.
  29. * This stores the list of available fonts that are setup by
  30. * the renderer. The font name can be retrieved for the
  31. * family style and weight.
  32. * <br>
  33. * Currently font supported font-variant small-caps is not
  34. * implemented.
  35. */
  36. public class FontInfo {
  37. /** logging instance */
  38. protected static Log log = LogFactory.getLog(FontInfo.class);
  39. /** Map containing fonts that have been used */
  40. private Map usedFonts;
  41. /** look up a font-triplet to find a font-name */
  42. private Map triplets;
  43. /** look up a font-name to get a font (that implements FontMetrics at least) */
  44. private Map fonts;
  45. /** collection of missing fonts; used to make sure the user gets
  46. * a warning for a missing font only once (not every time the font is used)
  47. */
  48. private Collection loggedFontKeys;
  49. /** Cache for Font instances. */
  50. private Map fontInstanceCache = new java.util.HashMap();
  51. /**
  52. * Main constructor
  53. */
  54. public FontInfo() {
  55. this.triplets = new java.util.HashMap();
  56. this.fonts = new java.util.HashMap();
  57. this.usedFonts = new java.util.HashMap();
  58. }
  59. /**
  60. * Checks if the font setup is valid (At least the ultimate fallback font
  61. * must be registered.)
  62. * @return True if valid
  63. */
  64. public boolean isSetupValid() {
  65. return triplets.containsKey(Font.DEFAULT_FONT);
  66. }
  67. /**
  68. * Adds a new font triplet.
  69. * @param name internal key
  70. * @param family font family name
  71. * @param style font style (normal, italic, oblique...)
  72. * @param weight font weight
  73. */
  74. public void addFontProperties(String name, String family, String style, int weight) {
  75. addFontProperties(name, createFontKey(family, style, weight));
  76. }
  77. /**
  78. * Adds a new font triplet.
  79. * @param name internal key
  80. * @param triplet the font triplet to associate with the internal key
  81. */
  82. public void addFontProperties(String name, FontTriplet triplet) {
  83. /*
  84. * add the given family, style and weight as a lookup for the font
  85. * with the given name
  86. */
  87. this.triplets.put(triplet, name);
  88. }
  89. /**
  90. * Adds font metrics for a specific font.
  91. * @param name internal key
  92. * @param metrics metrics to register
  93. */
  94. public void addMetrics(String name, FontMetrics metrics) {
  95. // add the given metrics as a font with the given name
  96. this.fonts.put(name, metrics);
  97. }
  98. /**
  99. * Lookup a font.
  100. * <br>
  101. * Locate the font name for a given family, style and weight.
  102. * The font name can then be used as a key as it is unique for
  103. * the associated document.
  104. * This also adds the font to the list of used fonts.
  105. * @param family font family
  106. * @param style font style
  107. * @param weight font weight
  108. * @param substFont true if the font may be substituted with the
  109. * default font if not found
  110. * @return internal key
  111. */
  112. private FontTriplet fontLookup(String family, String style,
  113. int weight, boolean substFont) {
  114. if (log.isTraceEnabled()) {
  115. log.trace("Font lookup: " + family + " " + style + " " + weight);
  116. }
  117. FontTriplet startKey = createFontKey(family, style, weight);
  118. FontTriplet key = startKey;
  119. // first try given parameters
  120. String f = getInternalFontKey(key);
  121. if (f == null) {
  122. // then adjust weight, favouring normal or bold
  123. key = findAdjustWeight(family, style, weight);
  124. f = getInternalFontKey(key);
  125. if (!substFont && f == null) {
  126. return null;
  127. }
  128. // only if the font may be substituted
  129. // fallback 1: try the same font-family and weight with default style
  130. if (f == null) {
  131. key = createFontKey(family, Font.STYLE_NORMAL, weight);
  132. f = getInternalFontKey(key);
  133. }
  134. // fallback 2: try the same font-family with default style and weight
  135. if (f == null) {
  136. key = createFontKey(family, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL);
  137. f = getInternalFontKey(key);
  138. }
  139. // fallback 3: try any family with orig style/weight
  140. if (f == null) {
  141. key = createFontKey("any", style, weight);
  142. f = getInternalFontKey(key);
  143. }
  144. // last resort: use default
  145. if (f == null) {
  146. key = Font.DEFAULT_FONT;
  147. f = getInternalFontKey(key);
  148. }
  149. }
  150. if (f != null) {
  151. if (key != startKey) {
  152. notifyFontReplacement(startKey, key);
  153. }
  154. return key;
  155. } else {
  156. return null;
  157. }
  158. }
  159. /**
  160. * Tells this class that the font with the given internal name has been used.
  161. * @param internalName the internal font name (F1, F2 etc.)
  162. */
  163. public void useFont(String internalName) {
  164. usedFonts.put(internalName, fonts.get(internalName));
  165. }
  166. /**
  167. * Retrieves a (possibly cached) Font instance based on a FontTriplet and a font size.
  168. * @param triplet the font triplet designating the requested font
  169. * @param fontSize the font size
  170. * @return the requested Font instance
  171. */
  172. public Font getFontInstance(FontTriplet triplet, int fontSize) {
  173. Map sizes = (Map)fontInstanceCache.get(triplet);
  174. if (sizes == null) {
  175. sizes = new java.util.HashMap();
  176. fontInstanceCache.put(triplet, sizes);
  177. }
  178. Integer size = new Integer(fontSize);
  179. Font font = (Font)sizes.get(size);
  180. if (font == null) {
  181. String fname = getInternalFontKey(triplet);
  182. useFont(fname);
  183. FontMetrics metrics = getMetricsFor(fname);
  184. font = new Font(fname, triplet, metrics, fontSize);
  185. sizes.put(size, font);
  186. }
  187. return font;
  188. }
  189. /**
  190. * Lookup a font.
  191. * <br>
  192. * Locate the font name for a given family, style and weight.
  193. * The font name can then be used as a key as it is unique for
  194. * the associated document.
  195. * This also adds the font to the list of used fonts.
  196. * @param family font family
  197. * @param style font style
  198. * @param weight font weight
  199. * @return the font triplet of the font chosen
  200. */
  201. public FontTriplet fontLookup(String family, String style,
  202. int weight) {
  203. return fontLookup(family, style, weight, true);
  204. }
  205. /**
  206. * Lookup a font.
  207. * <br>
  208. * Locate the font name for a given family, style and weight.
  209. * The font name can then be used as a key as it is unique for
  210. * the associated document.
  211. * This also adds the font to the list of used fonts.
  212. * @param family font family (priority list)
  213. * @param style font style
  214. * @param weight font weight
  215. * @return font triplet of the font chosen
  216. */
  217. public FontTriplet fontLookup(String[] family, String style,
  218. int weight) {
  219. for (int i = 0; i < family.length; i++) {
  220. FontTriplet triplet = fontLookup(family[i], style, weight, (i >= family.length - 1));
  221. if (triplet != null) {
  222. return triplet;
  223. }
  224. }
  225. throw new IllegalStateException("fontLookup must return a key on the last call");
  226. }
  227. private void notifyFontReplacement(FontTriplet replacedKey, FontTriplet newKey) {
  228. if (loggedFontKeys == null) {
  229. loggedFontKeys = new java.util.HashSet();
  230. }
  231. if (!loggedFontKeys.contains(replacedKey)) {
  232. loggedFontKeys.add(replacedKey);
  233. log.warn("Font '" + replacedKey + "' not found. "
  234. + "Substituting with '" + newKey + "'.");
  235. }
  236. }
  237. /**
  238. * Find a font with a given family and style by trying
  239. * different font weights according to the spec.
  240. * @param family font family
  241. * @param style font style
  242. * @param weight font weight
  243. * @return internal key
  244. */
  245. public FontTriplet findAdjustWeight(String family, String style,
  246. int weight) {
  247. FontTriplet key = null;
  248. String f = null;
  249. int newWeight = weight;
  250. if (newWeight < 400) {
  251. while (f == null && newWeight > 0) {
  252. newWeight -= 100;
  253. key = createFontKey(family, style, newWeight);
  254. f = getInternalFontKey(key);
  255. }
  256. } else if (newWeight == 500) {
  257. key = createFontKey(family, style, 400);
  258. f = getInternalFontKey(key);
  259. } else if (newWeight > 500) {
  260. while (f == null && newWeight < 1000) {
  261. newWeight += 100;
  262. key = createFontKey(family, style, newWeight);
  263. f = getInternalFontKey(key);
  264. }
  265. newWeight = weight;
  266. while (f == null && newWeight > 400) {
  267. newWeight -= 100;
  268. key = createFontKey(family, style, newWeight);
  269. f = getInternalFontKey(key);
  270. }
  271. }
  272. if (f == null && weight != 400) {
  273. key = createFontKey(family, style, 400);
  274. f = getInternalFontKey(key);
  275. }
  276. if (f != null) {
  277. return key;
  278. } else {
  279. return null;
  280. }
  281. }
  282. /**
  283. * Determines if a particular font is available.
  284. * @param family font family
  285. * @param style font style
  286. * @param weight font weight
  287. * @return True if available
  288. */
  289. public boolean hasFont(String family, String style, int weight) {
  290. FontTriplet key = createFontKey(family, style, weight);
  291. return this.triplets.containsKey(key);
  292. }
  293. /**
  294. * Returns the internal font key (F1, F2, F3 etc.) for a given triplet.
  295. * @param triplet the font triplet
  296. * @return the associated internal key or null, if not found
  297. */
  298. public String getInternalFontKey(FontTriplet triplet) {
  299. return (String)triplets.get(triplet);
  300. }
  301. /**
  302. * Creates a key from the given strings.
  303. * @param family font family
  304. * @param style font style
  305. * @param weight font weight
  306. * @return internal key
  307. */
  308. public static FontTriplet createFontKey(String family, String style,
  309. int weight) {
  310. return new FontTriplet(family, style, weight);
  311. }
  312. /**
  313. * Gets a Map of all registred fonts.
  314. * @return a read-only Map with font key/FontMetrics pairs
  315. */
  316. public Map getFonts() {
  317. return java.util.Collections.unmodifiableMap(this.fonts);
  318. }
  319. /**
  320. * This is used by the renderers to retrieve all the
  321. * fonts used in the document.
  322. * This is for embedded font or creating a list of used fonts.
  323. * @return a read-only Map with font key/FontMetrics pairs
  324. */
  325. public Map getUsedFonts() {
  326. return this.usedFonts;
  327. }
  328. /**
  329. * Returns the FontMetrics for a particular font
  330. * @param fontName internal key
  331. * @return font metrics
  332. */
  333. public FontMetrics getMetricsFor(String fontName) {
  334. FontMetrics metrics = (FontMetrics)fonts.get(fontName);
  335. usedFonts.put(fontName, metrics);
  336. return metrics;
  337. }
  338. /**
  339. * Returns the first triplet matching the given font name.
  340. * As there may be multiple triplets matching the font name
  341. * the result set is sorted first to guarantee consistent results.
  342. * @param fontName The font name we are looking for
  343. * @return The first triplet for the given font name
  344. */
  345. public FontTriplet getTripletFor(String fontName) {
  346. List foundTriplets = new ArrayList();
  347. for (Iterator iter = triplets.entrySet().iterator(); iter.hasNext();) {
  348. Map.Entry tripletEntry = (Map.Entry) iter.next();
  349. if (fontName.equals(((String)tripletEntry.getValue()))) {
  350. foundTriplets.add(tripletEntry.getKey());
  351. }
  352. }
  353. if (foundTriplets.size() > 0) {
  354. Collections.sort(foundTriplets);
  355. return (FontTriplet)foundTriplets.get(0);
  356. }
  357. return null;
  358. }
  359. /**
  360. * Returns the font style for a particular font.
  361. * There may be multiple font styles matching this font. Only the first
  362. * found is returned. Searching is done on a sorted list to guarantee consistent
  363. * results.
  364. * @param fontName internal key
  365. * @return font style
  366. */
  367. public String getFontStyleFor(String fontName) {
  368. FontTriplet triplet = getTripletFor(fontName);
  369. if (triplet != null) {
  370. return triplet.getStyle();
  371. } else {
  372. return "";
  373. }
  374. }
  375. /**
  376. * Returns the font weight for a particular font.
  377. * There may be multiple font weights matching this font. Only the first
  378. * found is returned. Searching is done on a sorted list to guarantee consistent
  379. * results.
  380. * @param fontName internal key
  381. * @return font weight
  382. */
  383. public int getFontWeightFor(String fontName) {
  384. FontTriplet triplet = getTripletFor(fontName);
  385. if (triplet != null) {
  386. return triplet.getWeight();
  387. } else {
  388. return 0;
  389. }
  390. }
  391. }