org.cobraparser.util.gui.FontFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Cobra Show documentation
Show all versions of Cobra Show documentation
Cobra is the rendering engine designed for LoboBrowser
/*
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: [email protected]
*/
/*
* Created on Apr 17, 2005
*/
package org.cobraparser.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 javax.swing.text.StyleContext;
import org.cobraparser.util.Strings;
/** Note: Undocumented class? */
//import sun.font.FontManager;
/**
* @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() {
final boolean liflag = loggableFine;
final String[] ffns = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
final Set fontFamilies = this.fontFamilies;
synchronized (this) {
for (final String ffn : ffns) {
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(final String fontName, final int fontFormat, final java.io.InputStream fontStream)
throws java.awt.FontFormatException,
java.io.IOException {
final 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(final String fontName) {
synchronized (this) {
this.registeredFonts.remove(fontName.toLowerCase());
}
}
public Font getFont(final String fontFamily, final String fontStyle, final String fontVariant, final String fontWeight,
final float fontSize, final Set locales,
final Integer superscript) {
final FontKey key = new FontKey(fontFamily, fontStyle, fontVariant, fontWeight, fontSize, locales, superscript);
synchronized (this) {
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(final String defaultFontName) {
if (defaultFontName == null) {
throw new IllegalArgumentException("defaultFontName cannot be null");
}
this.defaultFontName = defaultFontName;
}
private final Font createFont(final FontKey key) {
final Font font = createFont_Impl(key);
return superscriptFont(font, key.superscript);
}
public static Font superscriptFont(final Font baseFont, final 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 {
final Map additionalAttributes = new HashMap<>();
additionalAttributes.put(TextAttribute.SUPERSCRIPT, newSuperscript);
return baseFont.deriveFont(additionalAttributes);
}
}
private final Font createFont_Impl(final FontKey key) {
final String fontNames = key.fontFamily;
String matchingFace = null;
final Set fontFamilies = this.fontFamilies;
final Map registeredFonts = this.registeredFonts;
Font baseFont = null;
if (fontNames != null) {
final StringTokenizer tok = new StringTokenizer(fontNames, ",");
while (tok.hasMoreTokens()) {
final String face = Strings.unquoteSingle(tok.nextToken().trim());
final String faceTL = face.toLowerCase();
if (registeredFonts.containsKey(faceTL)) {
baseFont = registeredFonts.get(faceTL);
break;
} else if (fontFamilies.contains(faceTL)) {
matchingFace = faceTL;
break;
} else if ("monospace".equals(faceTL)){
baseFont = Font.decode("monospaced");
}
}
}
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) {
final Font font = createFont(matchingFace, fontStyle, Math.round(key.fontSize));
final Set locales = key.locales;
if (locales == null) {
final Locale locale = Locale.getDefault();
if (font.canDisplayUpTo(locale.getDisplayLanguage(locale)) == -1) {
return font;
}
} else {
final Iterator i = locales.iterator();
boolean allMatch = true;
while (i.hasNext()) {
final 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, Math.round(key.fontSize));
}
private static Font createFont(final String name, final int style, final int size) {
return StyleContext.getDefaultStyleContext().getFont(name, style, size);
// Proprietary Sun API. Maybe shouldn't use it. Works well for Chinese.
// return FontManager.getCompositeFontUIResource(new Font(name, style,
// size));
}
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(final Object other) {
if (other == this) {
// Quick check.
return true;
}
FontKey ors;
try {
ors = (FontKey) other;
} catch (final ClassCastException cce) {
// Not expected
return false;
}
// Note that we use String.intern() for all string fields,
// so we can do instance comparisons.
return (this.fontSize == ors.fontSize) && (this.fontFamily == ors.fontFamily) && (this.fontStyle == ors.fontStyle)
&& (this.fontWeight == ors.fontWeight) && (this.fontVariant == ors.fontVariant) && (this.superscript == ors.superscript)
&& java.util.Objects.equals(this.locales, ors.locales);
}
private int cachedHash = -1;
@Override
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 = "";
}
final Integer ss = this.superscript;
ch = ff.hashCode() ^ fw.hashCode() ^ fs.hashCode() ^ (int) this.fontSize ^ (ss == null ? 0 : ss.intValue());
this.cachedHash = ch;
return ch;
}
@Override
public String toString() {
return "FontKey[family=" + this.fontFamily + ",size=" + this.fontSize + ",style=" + this.fontStyle + ",weight=" + this.fontWeight
+ ",variant=" + this.fontVariant + ",superscript=" + this.superscript + "]";
}
}
}