123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* $Id$ */
-
- package org.apache.fop.fonts;
-
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- /**
- * The FontInfo holds font information for the layout and rendering of a fo document.
- * This stores the list of available fonts that are setup by
- * the renderer. The font name can be retrieved for the
- * family style and weight.
- * <br>
- * Currently font supported font-variant small-caps is not
- * implemented.
- */
- public class FontInfo {
-
- /** logging instance */
- protected static final Log log = LogFactory.getLog(FontInfo.class);
-
- /** Map containing fonts that have been used */
- private Map/*<String,FontMetrics>*/ usedFonts = null; //(String = font key)
-
- /** look up a font-triplet to find a font-name */
- private Map/*<FontTriplet,String>*/ triplets = null; //(String = font key)
-
- /** look up a font-triplet to find its priority
- * (only used inside addFontProperties()) */
- private Map/*<FontTriplet,Integer>*/ tripletPriorities = null; //Map<FontTriplet,Integer>
-
- /** look up a font-name to get a font (that implements FontMetrics at least) */
- private Map/*<String,FontMetrics>*/ fonts = null; //(String = font key)
-
- /**
- * a collection of missing fonts; used to make sure the user gets
- * a warning for a missing font only once (not every time the font is used)
- */
- private Set/*<FontTriplet>*/ loggedFontKeys = null;
-
- /** Cache for Font instances. */
- private Map/*<FontTriplet, Map>*/ fontInstanceCache = null;
-
- /** Event listener for font events */
- private FontEventListener eventListener = null;
-
- /**
- * Main constructor
- */
- public FontInfo() {
- this.triplets = new java.util.HashMap/*<FontTriplet, String>*/();
- this.tripletPriorities = new java.util.HashMap/*<FontTriplet, Integer>*/();
- this.fonts = new java.util.HashMap/*<String, FontMetrics>*/();
- this.usedFonts = new java.util.HashMap/*<String,FontMetrics>*/();
- }
-
- /**
- * Sets the font event listener that can be used to receive events about particular events
- * in this class.
- * @param listener the font event listener
- */
- public void setEventListener(FontEventListener listener) {
- this.eventListener = listener;
- }
-
- /**
- * Checks if the font setup is valid (At least the ultimate fallback font
- * must be registered.)
- * @return True if valid
- */
- public boolean isSetupValid() {
- //We're only called when font setup is done:
- tripletPriorities = null; // candidate for garbage collection
- return triplets.containsKey(Font.DEFAULT_FONT);
- }
-
- /**
- * Adds a new font triplet.
- * @param name internal key
- * @param family font family name
- * @param style font style (normal, italic, oblique...)
- * @param weight font weight
- */
- public void addFontProperties(String name, String family, String style, int weight) {
- addFontProperties(name, createFontKey(family, style, weight));
- }
-
- /**
- * Adds a series of new font triplets given an array of font family names.
- * @param name internal key
- * @param families an array of font family names
- * @param style font style (normal, italic, oblique...)
- * @param weight font weight
- */
- public void addFontProperties(String name, String[] families, String style, int weight) {
- for (int i = 0; i < families.length; i++) {
- addFontProperties(name, families[i], style, weight);
- }
- }
-
- /**
- * Adds a new font triplet.
- * @param internalFontKey internal font key
- * @param triplet the font triplet to associate with the internal key
- */
- public void addFontProperties(String internalFontKey, FontTriplet triplet) {
- /*
- * add the given family, style and weight as a lookup for the font
- * with the given name
- */
- if (log.isDebugEnabled()) {
- log.debug("Registering: " + triplet + " under " + internalFontKey);
- }
- String oldName = (String)triplets.get(triplet);
- int newPriority = triplet.getPriority();
- if (oldName != null) {
- int oldPriority = ((Integer)tripletPriorities.get(triplet)).intValue();
- if (oldPriority < newPriority) {
- logDuplicateFont(triplet, false, oldName, oldPriority,
- internalFontKey, newPriority);
- return;
- } else {
- logDuplicateFont(triplet, true, oldName, oldPriority,
- internalFontKey, newPriority);
- }
- }
- this.triplets.put(triplet, internalFontKey);
- this.tripletPriorities.put(triplet, new Integer(newPriority));
- }
-
- /**
- * Log warning about duplicate font triplets.
- *
- * @param triplet the duplicate font triplet
- * @param replacing true iff the new font will replace the old one
- * @param oldKey the old internal font name
- * @param oldPriority the priority of the existing font mapping
- * @param newKey the new internal font name
- * @param newPriority the priority of the duplicate font mapping
- */
- private void logDuplicateFont(FontTriplet triplet, boolean replacing,
- String oldKey, int oldPriority,
- String newKey, int newPriority) {
- if (log.isDebugEnabled()) {
- log.debug(triplet
- + (replacing ? ": Replacing " : ": Not replacing ")
- + ((FontMetrics)fonts.get(triplets.get(triplet))).getFullName()
- + " (priority=" + oldPriority + ") by "
- + ((FontMetrics)fonts.get(newKey)).getFullName()
- + " (priority=" + newPriority + ")");
- }
- }
-
- /**
- * Adds font metrics for a specific font.
- * @param internalFontKey internal key
- * @param metrics metrics to register
- */
- public void addMetrics(String internalFontKey, FontMetrics metrics) {
- // add the given metrics as a font with the given name
-
- if (metrics instanceof Typeface) {
- ((Typeface)metrics).setEventListener(this.eventListener);
- }
- this.fonts.put(internalFontKey, metrics);
- }
-
- /**
- * Lookup a font.
- * <br>
- * Locate the font name for a given family, style and weight.
- * The font name can then be used as a key as it is unique for
- * the associated document.
- * This also adds the font to the list of used fonts.
- * @param family font family
- * @param style font style
- * @param weight font weight
- * @param substitutable true if the font may be substituted with the
- * default font if not found
- * @return internal font triplet key
- */
- private FontTriplet fontLookup(String family, String style,
- int weight, boolean substitutable) {
- if (log.isTraceEnabled()) {
- log.trace("Font lookup: " + family + " " + style + " " + weight
- + (substitutable ? " substitutable" : ""));
- }
-
- FontTriplet startKey = createFontKey(family, style, weight);
- FontTriplet fontTriplet = startKey;
- // first try given parameters
- String internalFontKey = getInternalFontKey(fontTriplet);
- if (internalFontKey == null) {
- fontTriplet = fuzzyFontLookup(family, style, weight, startKey, substitutable);
- }
-
- if (fontTriplet != null) {
- if (fontTriplet != startKey) {
- notifyFontReplacement(startKey, fontTriplet);
- }
- return fontTriplet;
- } else {
- return null;
- }
- }
-
- private FontTriplet fuzzyFontLookup(String family, String style,
- int weight, FontTriplet startKey, boolean substitutable) {
- FontTriplet key;
- String internalFontKey = null;
- if (!family.equals(startKey.getName())) {
- key = createFontKey(family, style, weight);
- internalFontKey = getInternalFontKey(key);
- if (internalFontKey != null) {
- return key;
- }
- }
-
- // adjust weight, favouring normal or bold
- key = findAdjustWeight(family, style, weight);
- if (key != null) {
- internalFontKey = getInternalFontKey(key);
- }
-
- // return null if not found and not substitutable
- if (!substitutable && internalFontKey == null) {
- return null;
- }
-
- // only if the font may be substituted
- // fallback 1: try the same font-family and weight with default style
- if (internalFontKey == null && style != Font.STYLE_NORMAL) {
- key = createFontKey(family, Font.STYLE_NORMAL, weight);
- internalFontKey = getInternalFontKey(key);
- }
-
- // fallback 2: try the same font-family with default style and try to adjust weight
- if (internalFontKey == null && style != Font.STYLE_NORMAL) {
- key = findAdjustWeight(family, Font.STYLE_NORMAL, weight);
- if (key != null) {
- internalFontKey = getInternalFontKey(key);
- }
- }
-
- // fallback 3: try any family with original style/weight
- if (internalFontKey == null) {
- return fuzzyFontLookup("any", style, weight, startKey, false);
- }
-
- // last resort: use default
- if (key == null && internalFontKey == null) {
- key = Font.DEFAULT_FONT;
- internalFontKey = getInternalFontKey(key);
- }
-
- if (internalFontKey != null) {
- return key;
- } else {
- return null;
- }
- }
-
- /**
- * Tells this class that the font with the given internal name has been used.
- * @param internalName the internal font name (F1, F2 etc.)
- */
- public void useFont(String internalName) {
- usedFonts.put(internalName, fonts.get(internalName));
- }
-
- private Map/*<FontTriplet,Map<Integer,Font>>*/ getFontInstanceCache() {
- if (fontInstanceCache == null) {
- fontInstanceCache = new java.util.HashMap/*<FontTriplet, Map<Integer,Font>>*/();
- }
- return fontInstanceCache;
- }
-
- /**
- * Retrieves a (possibly cached) Font instance based on a FontTriplet and a font size.
- *
- * @param triplet the font triplet designating the requested font
- * @param fontSize the font size
- * @return the requested Font instance
- */
- public Font getFontInstance(FontTriplet triplet, int fontSize) {
- Map/*<Integer,Font>*/ sizes
- = (Map/*<Integer,Font>*/)getFontInstanceCache().get(triplet);
- if (sizes == null) {
- sizes = new java.util.HashMap/*<Integer,Font>*/();
- getFontInstanceCache().put(triplet, sizes);
- }
- Integer size = new Integer(fontSize);
- Font font = (Font)sizes.get(size);
- if (font == null) {
- String fontKey = getInternalFontKey(triplet);
- useFont(fontKey);
- FontMetrics metrics = getMetricsFor(fontKey);
- font = new Font(fontKey, triplet, metrics, fontSize);
- sizes.put(size, font);
- }
- return font;
- }
-
- private List/*<FontTriplet>*/ getTripletsForName(String fontName) {
- List/*<FontTriplet>*/ matchedTriplets = new java.util.ArrayList/*<FontTriplet>*/();
- Iterator it = triplets.keySet().iterator();
- while (it.hasNext()) {
- FontTriplet triplet = (FontTriplet)it.next();
- String tripletName = triplet.getName();
- if (tripletName.toLowerCase().equals(fontName.toLowerCase())) {
- matchedTriplets.add(triplet);
- }
- }
- return matchedTriplets;
- }
-
- /**
- * Returns a suitable internal font given an AWT Font instance.
- *
- * @param awtFont the AWT font
- * @return a best matching internal Font
- */
- public Font getFontInstanceForAWTFont(java.awt.Font awtFont) {
- String awtFontName = awtFont.getName();
- String awtFontFamily = awtFont.getFamily();
- String awtFontStyle = awtFont.isItalic() ? Font.STYLE_ITALIC : Font.STYLE_NORMAL;
- int awtFontWeight = awtFont.isBold() ? Font.WEIGHT_BOLD : Font.WEIGHT_NORMAL;
-
- FontTriplet matchedTriplet = null;
- List/*<FontTriplet>*/ triplets = getTripletsForName(awtFontName);
- if (!triplets.isEmpty()) {
- Iterator it = triplets.iterator();
- while (it.hasNext()) {
- FontTriplet triplet = (FontTriplet)it.next();
- boolean styleMatched = triplet.getStyle().equals(awtFontStyle);
- boolean weightMatched = triplet.getWeight() == awtFontWeight;
- if (styleMatched && weightMatched) {
- matchedTriplet = triplet;
- break;
- }
- }
- }
-
- // not matched on font name so do a lookup using family
- if (matchedTriplet == null) {
- if (awtFontFamily.equals("sanserif")) {
- awtFontFamily = "sans-serif";
- }
- matchedTriplet = fontLookup(awtFontFamily, awtFontStyle, awtFontWeight);
- }
- int fontSize = Math.round(awtFont.getSize2D() * 1000);
- return getFontInstance(matchedTriplet, fontSize);
- }
-
- /**
- * Lookup a font.
- * <br>
- * Locate the font name for a given family, style and weight.
- * The font name can then be used as a key as it is unique for
- * the associated document.
- * This also adds the font to the list of used fonts.
- * @param family font family
- * @param style font style
- * @param weight font weight
- * @return the font triplet of the font chosen
- */
- public FontTriplet fontLookup(String family, String style,
- int weight) {
- return fontLookup(family, style, weight, true);
- }
-
- private List/*<FontTriplet>*/ fontLookup(String[] families, String style,
- int weight, boolean substitutable) {
- List/*<FontTriplet>*/ matchingTriplets = new java.util.ArrayList/*<FontTriplet>*/();
- FontTriplet triplet = null;
- for (int i = 0; i < families.length; i++) {
- triplet = fontLookup(families[i], style, weight, substitutable);
- if (triplet != null) {
- matchingTriplets.add(triplet);
- }
- }
- return matchingTriplets;
- }
-
- /**
- * Looks up a set of fonts.
- * <br>
- * Locate the font name(s) for the given families, style and weight.
- * The font name(s) can then be used as a key as they are unique for
- * the associated document.
- * This also adds the fonts to the list of used fonts.
- * @param families font families (priority list)
- * @param style font style
- * @param weight font weight
- * @return the set of font triplets of all supported and chosen font-families
- * in the specified style and weight.
- */
- public FontTriplet[] fontLookup(String[] families, String style,
- int weight) {
- if (families.length == 0) {
- throw new IllegalArgumentException("Specify at least one font family");
- }
-
- // try matching without substitutions
- List/*<FontTriplet>*/ matchedTriplets = fontLookup(families, style, weight, false);
-
- // if there are no matching font triplets found try with substitutions
- if (matchedTriplets.size() == 0) {
- matchedTriplets = fontLookup(families, style, weight, true);
- }
-
- // no matching font triplets found!
- if (matchedTriplets.size() == 0) {
- StringBuffer sb = new StringBuffer();
- for (int i = 0, c = families.length; i < c; i++) {
- if (i > 0) {
- sb.append(", ");
- }
- sb.append(families[i]);
- }
- throw new IllegalStateException(
- "fontLookup must return an array with at least one "
- + "FontTriplet on the last call. Lookup: " + sb.toString());
-
- }
- FontTriplet[] fontTriplets = new FontTriplet[matchedTriplets.size()];
- matchedTriplets.toArray(fontTriplets);
-
- // found some matching fonts so return them
- return fontTriplets;
- }
-
- private Set/*<FontTriplet>*/ getLoggedFontKeys() {
- if (loggedFontKeys == null) {
- loggedFontKeys = new java.util.HashSet/*<FontTriplet>*/();
- }
- return loggedFontKeys;
- }
-
- private void notifyFontReplacement(FontTriplet replacedKey, FontTriplet newKey) {
- if (!getLoggedFontKeys().contains(replacedKey)) {
- getLoggedFontKeys().add(replacedKey);
- if (this.eventListener != null) {
- this.eventListener.fontSubstituted(this, replacedKey, newKey);
- } else {
- log.warn("Font '" + replacedKey + "' not found. "
- + "Substituting with '" + newKey + "'.");
- }
- }
- }
-
- /**
- * Find a font with a given family and style by trying
- * different font weights according to the spec.
- * @param family font family
- * @param style font style
- * @param weight font weight
- * @return internal key
- */
- public FontTriplet findAdjustWeight(String family, String style,
- int weight) {
- FontTriplet key = null;
- String f = null;
- int newWeight = weight;
- if (newWeight <= 400) {
- while (f == null && newWeight > 100) {
- newWeight -= 100;
- key = createFontKey(family, style, newWeight);
- f = getInternalFontKey(key);
- }
- newWeight = weight;
- while (f == null && newWeight < 400) {
- newWeight += 100;
- key = createFontKey(family, style, newWeight);
- f = getInternalFontKey(key);
- }
- } else if (newWeight == 500) {
- key = createFontKey(family, style, 400);
- f = getInternalFontKey(key);
- } else if (newWeight > 500) {
- while (f == null && newWeight < 1000) {
- newWeight += 100;
- key = createFontKey(family, style, newWeight);
- f = getInternalFontKey(key);
- }
- newWeight = weight;
- while (f == null && newWeight > 400) {
- newWeight -= 100;
- key = createFontKey(family, style, newWeight);
- f = getInternalFontKey(key);
- }
- }
- if (f == null && weight != 400) {
- key = createFontKey(family, style, 400);
- f = getInternalFontKey(key);
- }
-
- if (f != null) {
- return key;
- } else {
- return null;
- }
- }
-
- /**
- * Determines if a particular font is available.
- * @param family font family
- * @param style font style
- * @param weight font weight
- * @return True if available
- */
- public boolean hasFont(String family, String style, int weight) {
- FontTriplet key = createFontKey(family, style, weight);
- return this.triplets.containsKey(key);
- }
-
- /**
- * Returns the internal font key (F1, F2, F3 etc.) for a given triplet.
- * @param triplet the font triplet
- * @return the associated internal key or null, if not found
- */
- public String getInternalFontKey(FontTriplet triplet) {
- return (String)triplets.get(triplet);
- }
-
- /**
- * Creates a key from the given strings.
- * @param family font family
- * @param style font style
- * @param weight font weight
- * @return internal key
- */
- public static FontTriplet createFontKey(String family, String style,
- int weight) {
- return new FontTriplet(family, style, weight);
- }
-
- /**
- * Gets a Map of all registered fonts.
- * @return a read-only Map with font key/FontMetrics pairs
- */
- public Map/*<String,FontMetrics>*/ getFonts() {
- return java.util.Collections.unmodifiableMap(this.fonts);
- }
-
- /**
- * Gets a Map of all registered font triplets.
- * @return a Map with FontTriplet/font key pairs
- */
- public Map/*<FontTriplet,String>*/ getFontTriplets() {
- return this.triplets;
- }
-
- /**
- * This is used by the renderers to retrieve all the
- * fonts used in the document.
- * This is for embedded font or creating a list of used fonts.
- * @return a read-only Map with font key/FontMetrics pairs
- */
- public Map/*<String,FontMetrics>*/ getUsedFonts() {
- return this.usedFonts;
- }
-
- /**
- * Returns the FontMetrics for a particular font
- * @param fontName internal key
- * @return font metrics
- */
- public FontMetrics getMetricsFor(String fontName) {
- FontMetrics metrics = (FontMetrics)fonts.get(fontName);
- usedFonts.put(fontName, metrics);
- return metrics;
- }
-
- /**
- * Returns all font triplet matching the given font name.
- * @param fontName The font name we are looking for
- * @return A list of matching font triplets
- */
- public List/*<FontTriplet>*/ getTripletsFor(String fontName) {
- List/*<FontTriplet>*/ foundTriplets = new java.util.ArrayList();
- for (Iterator iter = triplets.entrySet().iterator(); iter.hasNext();) {
- Map.Entry tripletEntry = (Map.Entry) iter.next();
- if (fontName.equals((tripletEntry.getValue()))) {
- foundTriplets.add(tripletEntry.getKey());
- }
- }
- return foundTriplets;
- }
-
- /**
- * Returns the first triplet matching the given font name.
- * As there may be multiple triplets matching the font name
- * the result set is sorted first to guarantee consistent results.
- * @param fontName The font name we are looking for
- * @return The first triplet for the given font name
- */
- public FontTriplet getTripletFor(String fontName) {
- List/*<FontTriplet>*/ foundTriplets = getTripletsFor(fontName);
- if (foundTriplets.size() > 0) {
- Collections.sort(foundTriplets);
- return (FontTriplet)foundTriplets.get(0);
- }
- return null;
- }
-
- /**
- * Returns the font style for a particular font.
- * There may be multiple font styles matching this font. Only the first
- * found is returned. Searching is done on a sorted list to guarantee consistent
- * results.
- * @param fontName internal key
- * @return font style
- */
- public String getFontStyleFor(String fontName) {
- FontTriplet triplet = getTripletFor(fontName);
- if (triplet != null) {
- return triplet.getStyle();
- } else {
- return "";
- }
- }
-
- /**
- * Returns the font weight for a particular font.
- * There may be multiple font weights matching this font. Only the first
- * found is returned. Searching is done on a sorted list to guarantee consistent
- * results.
- * @param fontName internal key
- * @return font weight
- */
- public int getFontWeightFor(String fontName) {
- FontTriplet triplet = getTripletFor(fontName);
- if (triplet != null) {
- return triplet.getWeight();
- } else {
- return 0;
- }
- }
-
- /**
- * Diagnostic method for logging all registered fonts to System.out.
- */
- public void dumpAllTripletsToSystemOut() {
- System.out.print(toString());
- }
-
- /**
- * {@inheritDoc}
- */
- public String toString() {
- Collection entries = new java.util.TreeSet();
- Iterator iter = this.triplets.keySet().iterator();
- while (iter.hasNext()) {
- FontTriplet triplet = (FontTriplet)iter.next();
- String key = getInternalFontKey(triplet);
- FontMetrics metrics = getMetricsFor(key);
- entries.add(triplet.toString() + " -> " + key + " -> " + metrics.getFontName() + "\n");
- }
- StringBuffer stringBuffer = new StringBuffer();
- for (iter = entries.iterator(); iter.hasNext();) {
- stringBuffer.append(iter.next());
- }
- return stringBuffer.toString();
- }
- }
|