/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.awt.font.spi;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.jnode.awt.font.FontProvider;
import org.jnode.awt.font.TextRenderer;
import org.jnode.awt.font.renderer.RenderCache;
import org.jnode.awt.font.renderer.RenderContext;
/**
* @author epr
* @author Fabien DUMINY (fduminy@jnode.org)
*/
public abstract class AbstractFontProvider<F extends Font, FD> implements FontProvider<F> {
/**
* My logger
*/
private static final Logger log = Logger.getLogger(AbstractFontProvider.class);
static {
log.setLevel(Level.DEBUG);
}
/**
* Cache font renderers
*/
private final HashMap<Font, TextRenderer> renderers = new HashMap<Font, TextRenderer>();
/**
* Cache font metrics
*/
private final HashMap<Font, FontMetrics> metrics = new HashMap<Font, FontMetrics>();
/**
* All loaded fonts (name, Font)
*/
private final HashMap<String, F> fontsByName = new HashMap<String, F>();
/**
* Have the system fonts been loaded yet
*/
private boolean fontsLoaded = false;
private final RenderContext context = new RenderContext();
/**
* The render cache
*/
private final RenderCache renderCache = new RenderCache(context);
private final String name;
private final Class<F> fontClass;
private final List<FD> userFontDatas = new Vector<FD>();
private Map<FD, Size> maxCharBounds = new HashMap<FD, Size>();
protected AbstractFontProvider(Class<F> fontClass, String name) {
this.name = name;
this.fontClass = fontClass;
}
/**
* Give the name of the font (used for setting the first provider to use
* among all available ones)
*/
public final String getName() {
return name;
}
/**
* Does this provides provide the given font?
*
* @param font
* @return {@code true} if this provider provides the given font, {@code false} otherwise
*/
public final boolean provides(Font font) {
if (font == null) return false; // don't provide default (null) fonts
loadFonts();
return (getCompatibleFont(font) != null);
}
/**
* Returns a set containing a one-point size instance of all fonts available
* in this provider. Typical usage would be to allow a user to select a particular
* font. Then, the application can size the font and set various font attributes
* by calling the deriveFont method on the choosen instance.
* This method provides for the application the most precise control over which
* Font instance is used to render text. If a font in this provider has multiple
* programmable variations, only one instance of that Font is returned in the set,
* and other variations must be derived by the application. If a font in this provider
* has multiple programmable variations, such as Multiple-Master fonts, only
* one instance of that font is returned in the Font set. The other variations
* must be derived by the application.
*
* @return The set containing all fonts provides by this provider.
*/
public final Set<F> getAllFonts() {
loadFonts();
return new HashSet<F>(fontsByName.values());
}
/**
* Gets a text renderer for the given font.
*
* @param font
* @return The renderer
*/
@Override
public final TextRenderer getTextRenderer(Font font) {
TextRenderer r = renderers.get(font);
if (r == null) {
r = createTextRenderer(renderCache, font);
renderers.put(font, r);
}
return r;
}
protected abstract TextRenderer createTextRenderer(RenderCache renderCache, Font font);
/**
* Gets the font metrics for the given font.
*
* @param font
* @return The metrics
*/
public final FontMetrics getFontMetrics(Font font) {
FontMetrics fm = metrics.get(font);
if (fm == null) {
try {
fm = createFontMetrics(font);
metrics.put(font, fm);
} catch (IOException ex) {
log.error("Cannot create font metrics for " + font, ex);
}
}
return fm;
}
/*
* Load all default fonts.
*/
private final void loadFonts() {
if (!fontsLoaded) {
loadFontsImpl();
fontsLoaded = true;
}
}
protected abstract FontMetrics createFontMetrics(Font font) throws IOException;
/**
* Load all default fonts.
*/
protected abstract void loadFontsImpl();
/**
* Translates the font into a font that is provided by this provider.
*
* @param font
* @return the compatible font
*/
@Override
public final F getCompatibleFont(Font font) {
F f = null;
if (fontClass.isInstance(font)) {
f = fontClass.cast(font);
}
if (f == null) {
f = fontsByName.get(font.getFamily());
}
if (f == null) {
f = fontsByName.get(font.getName());
}
if (f == null) {
f = fontsByName.get(font.getFontName());
}
if ((f == null) && (fontsByName.size() > 0)) {
f = fontsByName.values().iterator().next();
}
return f;
}
protected void addUserFontData(FD data) {
userFontDatas.add(data);
}
protected List<FD> getUserFontDatas() {
return userFontDatas;
}
protected void addFont(F font) {
//fontsByName.put(font.getName(), font);
fontsByName.put(font.getFamily(), font);
//fontsByName.put(font.getFontName(), font);
}
// /**
// * Load all default fonts.
// */
// private final void loadFonts() {
// for (String font : getSystemFonts()) {
// loadFont(font);
// }
// fontsLoaded = true;
// }
//
// private final void loadFont(String resName) {
// try {
// final ClassLoader cl = Thread.currentThread().getContextClassLoader();
// final URL url = cl.getResource(resName);
// if (url != null) {
// final F font = loadFont(url);
// //fontsByName.put(font.getName(), font);
// fontsByName.put(font.getFamily(), font);
// //fontsByName.put(font.getFontName(), font);
// } else {
// log.error("Cannot find font resource " + resName);
// }
// } catch (IOException ex) {
// log.error("Cannot find font " + resName + ": " + ex.getMessage());
// } catch (Throwable ex) {
// log.error("Cannot find font " + resName, ex);
// }
// }
public Rectangle2D getMaxCharBounds(FD container) {
Size size = maxCharBounds.get(container);
if (size == null) {
size = getMaxCharSize(container);
maxCharBounds.put(container, size);
}
return new Rectangle2D.Double(0, 0, size.maxCharWidth, size.maxCharHeight);
}
protected abstract Size getMaxCharSize(FD fontData);
public static class Size {
public int maxCharWidth = 0;
public int maxCharHeight = 0;
}
}