/* * GNU LESSER GENERAL PUBLIC LICENSE Copyright (C) 2006 The Lobo Project This * library is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. This library is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. You should have received a copy of * the GNU Lesser General Public License along with this library; if not, write * to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA Contact info: lobochief@users.sourceforge.net */ /* * Created on Apr 17, 2005 */ package com.nvarghese.beowulf.common.cobra.util.gui; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.font.TextAttribute; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import com.nvarghese.beowulf.common.cobra.util.Objects; /** * @author J. H. S. */ public class FontFactory { private static final Logger logger = Logger.getLogger(FontFactory.class.getName()); private static final boolean loggableFine = logger.isLoggable(Level.FINE); private static final FontFactory instance = new FontFactory(); private final Set fontFamilies = new HashSet(40); private final Map fontMap = new HashMap(50); /** * */ private FontFactory() { boolean liflag = loggableFine; String[] ffns = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); Set fontFamilies = this.fontFamilies; synchronized (this) { for (int i = 0; i < ffns.length; i++) { String ffn = ffns[i]; if (liflag) { logger.fine("FontFactory(): family=" + ffn); } fontFamilies.add(ffn.toLowerCase()); } } } public static final FontFactory getInstance() { return instance; } private final Map registeredFonts = new HashMap(0); /** * Registers a font family. It does not close the stream provided. Fonts * should be registered before the renderer has a chance to cache document * font specifications. * * @param fontName * The name of a font as it would appear in a font-family * specification. * @param fontFormat * Should be {@link Font#TRUETYPE_FONT}. */ public void registerFont(String fontName, int fontFormat, java.io.InputStream fontStream) throws java.awt.FontFormatException, java.io.IOException { Font f = Font.createFont(fontFormat, fontStream); synchronized (this) { this.registeredFonts.put(fontName.toLowerCase(), f); } } /** * Unregisters a font previously registered with * {@link #registerFont(String, int, java.io.InputStream)}. * * @param fontName * The font name to be removed. */ public void unregisterFont(String fontName) { synchronized (this) { this.registeredFonts.remove(fontName.toLowerCase()); } } public Font getFont(String fontFamily, String fontStyle, String fontVariant, String fontWeight, float fontSize, Set locales, Integer superscript) { FontKey key = new FontKey(fontFamily, fontStyle, fontVariant, fontWeight, fontSize, locales, superscript); synchronized (this) { Font font = (Font) this.fontMap.get(key); if (font == null) { font = this.createFont(key); this.fontMap.put(key, font); } return font; } } private String defaultFontName = "SansSerif"; public String getDefaultFontName() { return defaultFontName; } /** * Sets the default font name to be used when a name is unrecognized or when * a font is determined not to be capable of diplaying characters from a * given language. This should be the name of a font that can display * unicode text across all or most languages. * * @param defaultFontName * The name of a font. */ public void setDefaultFontName(String defaultFontName) { if (defaultFontName == null) { throw new IllegalArgumentException("defaultFontName cannot be null"); } this.defaultFontName = defaultFontName; } private final Font createFont(FontKey key) { Font font = createFont_Impl(key); return superscriptFont(font, key.superscript); } public static Font superscriptFont(Font baseFont, Integer newSuperscript) { if (newSuperscript == null) { return baseFont; } Integer fontSuperScript = (Integer) baseFont.getAttributes().get(TextAttribute.SUPERSCRIPT); if (fontSuperScript == null) { fontSuperScript = new Integer(0); } if (fontSuperScript.equals(newSuperscript)) { return baseFont; } else { Map additionalAttributes = new HashMap(); additionalAttributes.put(TextAttribute.SUPERSCRIPT, newSuperscript); return baseFont.deriveFont(additionalAttributes); } } private final Font createFont_Impl(FontKey key) { String fontNames = key.fontFamily; String matchingFace = null; Set fontFamilies = this.fontFamilies; Map registeredFonts = this.registeredFonts; Font baseFont = null; if (fontNames != null) { StringTokenizer tok = new StringTokenizer(fontNames, ","); while (tok.hasMoreTokens()) { String face = tok.nextToken().trim(); String faceTL = face.toLowerCase(); if (registeredFonts.containsKey(faceTL)) { baseFont = (Font) registeredFonts.get(faceTL); break; } else if (fontFamilies.contains(faceTL)) { matchingFace = faceTL; break; } } } int fontStyle = Font.PLAIN; if ("italic".equalsIgnoreCase(key.fontStyle)) { fontStyle |= Font.ITALIC; } if ("bold".equalsIgnoreCase(key.fontWeight) || "bolder".equalsIgnoreCase(key.fontWeight)) { fontStyle |= Font.BOLD; } if (baseFont != null) { return baseFont.deriveFont(fontStyle, key.fontSize); } else if (matchingFace != null) { Font font = createFont(matchingFace, fontStyle, (int) Math.round(key.fontSize)); Set locales = key.locales; if (locales == null) { Locale locale = Locale.getDefault(); if (font.canDisplayUpTo(locale.getDisplayLanguage(locale)) == -1) { return font; } } else { Iterator i = locales.iterator(); boolean allMatch = true; while (i.hasNext()) { Locale locale = (Locale) i.next(); if (font.canDisplayUpTo(locale.getDisplayLanguage(locale)) != -1) { allMatch = false; break; } } if (allMatch) { return font; } } // Otherwise, fall through. } // Last resort: return createFont(this.defaultFontName, fontStyle, (int) Math.round(key.fontSize)); } private Font createFont(String name, int style, int size) { // Proprietary Sun API. Maybe shouldn't use it. Works well for Chinese. // return sun.font.FontManager.getCompositeFontUIResource(new Font(name, // style, size)); return null; } private static class FontKey { public final String fontFamily; public final String fontStyle; public final String fontVariant; public final String fontWeight; public final float fontSize; public final Set locales; public final Integer superscript; /** * @param fontFamily * @param fontStyle * @param fontVariant * @param fontWeight * @param fontSize */ public FontKey(final String fontFamily, final String fontStyle, final String fontVariant, final String fontWeight, final float fontSize, final Set locales, final Integer superscript) { this.fontFamily = fontFamily == null ? null : fontFamily.intern(); this.fontStyle = fontStyle == null ? null : fontStyle.intern(); this.fontVariant = fontVariant == null ? null : fontVariant.intern(); this.fontWeight = fontWeight == null ? null : fontWeight.intern(); this.fontSize = fontSize; this.locales = locales; this.superscript = superscript; } @Override public boolean equals(Object other) { boolean check = false; if (other != null) { if (other == this) { // Quick check. return true; } FontKey ors; try { ors = (FontKey) other; } catch (ClassCastException cce) { // Not expected return check; } // Note that we use String.intern() for all string fields, // so we can do instance comparisons. if (this.fontSize == ors.fontSize && this.fontFamily == ors.fontFamily && this.fontStyle == ors.fontStyle && this.fontWeight == ors.fontWeight && this.fontVariant == ors.fontVariant && this.superscript == ors.superscript && Objects.equals(this.locales, ors.locales)) { check = true; } } return check; } private int cachedHash = -1; public int hashCode() { int ch = this.cachedHash; if (ch != -1) { // Object is immutable - caching is ok. return ch; } String ff = this.fontFamily; if (ff == null) { ff = ""; } String fw = this.fontWeight; if (fw == null) { fw = ""; } String fs = this.fontStyle; if (fs == null) { fs = ""; } Integer ss = this.superscript; ch = ff.hashCode() ^ fw.hashCode() ^ fs.hashCode() ^ (int) this.fontSize ^ (ss == null ? 0 : ss.intValue()); this.cachedHash = ch; return ch; } public String toString() { return "FontKey[family=" + this.fontFamily + ",size=" + this.fontSize + ",style=" + this.fontStyle + ",weight=" + this.fontWeight + ",variant=" + this.fontVariant + ",superscript=" + this.superscript + "]"; } } }