/* * $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.def; import gnu.java.security.action.GetPropertyAction; import java.awt.Color; import java.awt.Font; import java.awt.FontFormatException; import java.awt.FontMetrics; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.io.IOException; import java.io.InputStream; import java.security.AccessController; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import javax.naming.NamingException; import org.apache.log4j.Logger; import org.jnode.awt.font.FontManager; import org.jnode.awt.font.FontProvider; import org.jnode.awt.font.JNodeFontPeer; import org.jnode.awt.font.TextRenderer; import org.jnode.driver.video.Surface; import org.jnode.naming.InitialNaming; import org.jnode.plugin.ConfigurationElement; import org.jnode.plugin.Extension; import org.jnode.plugin.ExtensionPoint; import org.jnode.plugin.ExtensionPointListener; import org.jnode.plugin.PluginException; /** * @author Ewout Prangsma (epr@users.sourceforge.net) */ public class DefaultFontManager implements FontManager, ExtensionPointListener { private static final Logger log = Logger.getLogger(DefaultFontManager.class); private final ExtensionPoint providersEP; /** * Note : For now, we have only 2 providers (bdf, ttf) and we will probably * never have more than 5 ones. So, a {@link List} is enough for our usage. */ private final List<FontProvider<?>> providers = new ArrayList<FontProvider<?>>(2); public final Map<Integer, String> fontTypeToProviderName = (Map<Integer, String>) Collections.singletonMap(Font.TRUETYPE_FONT, "ttf"); /** * Create a new instance * * @param providersEP */ public DefaultFontManager(ExtensionPoint providersEP) { this.providersEP = providersEP; } /** * Start this manager * * @throws PluginException */ public void start() throws PluginException { providersEP.addListener(this); try { InitialNaming.bind(NAME, this); } catch (NamingException ex) { throw new PluginException(ex); } updateFontProviders(); } /** * Start this manager */ public void stop() { InitialNaming.unbind(NAME); providersEP.removeListener(this); } /** * Returns an array 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 array. * The other variations must be derived by the application. * * @return All fonts */ public synchronized Font[] getAllFonts() { final HashSet<Font> all = new HashSet<Font>(); for (FontProvider<?> prv : providers) { all.addAll(prv.getAllFonts()); } return (Font[]) all.toArray(new Font[all.size()]); } /** * Gets the font metrics for the given font. * * @param font * @return The font metrics for the given font */ public FontMetrics getFontMetrics(Font font) { Font txFont = getTranslatedFont(font); return getProvider(txFont).getFontMetrics(txFont); } /** * Draw the given text to the given graphics at the given location, * using the given font. * * @param g * @param text * @param font * @param x * @param y */ public void drawText(Surface g, Shape clip, AffineTransform tx, CharSequence text, Font font, int x, int y, Color color) { Font txFont = getTranslatedFont(font); final TextRenderer renderer = getProvider(txFont).getTextRenderer(txFont); renderer.render(g, clip, tx, text, x, y, color); } /** * @param point * @param extension * @see org.jnode.plugin.ExtensionPointListener#extensionAdded(org.jnode.plugin.ExtensionPoint, * org.jnode.plugin.Extension) */ public void extensionAdded(ExtensionPoint point, Extension extension) { updateFontProviders(); } /** * @param point * @param extension * @see org.jnode.plugin.ExtensionPointListener#extensionRemoved(org.jnode.plugin.ExtensionPoint, * org.jnode.plugin.Extension) */ public void extensionRemoved(ExtensionPoint point, Extension extension) { final ConfigurationElement[] elements = extension.getConfigurationElements(); for (int j = 0; j < elements.length; j++) { final String className = elements[j].getAttribute("class"); if (log.isDebugEnabled()) { log.debug("Removed provider: class=" + className); } if (className != null) { int idx = getProviderIndexByClass(className); if (idx >= 0) { providers.remove(idx); } } } } public Font createFont(int format, InputStream stream) throws FontFormatException, IOException { String name = fontTypeToProviderName.get(format); if (name == null) { throw new IllegalArgumentException("unknown format " + format); } for (FontProvider<?> prv : getProviders()) { if (prv.getName().equals(name)) { return prv.createFont(stream); } } throw new FontFormatException("can't create font with format " + name); } @Override public JNodeFontPeer<?, ?> createFontPeer(String name, Map<?, ?> attrs) { for (FontProvider<?> prv : getProviders()) { JNodeFontPeer<?, ?> peer = prv.createFontPeer(name, attrs); if (peer != null) { return peer; } } throw new IllegalArgumentException("can't create font peer from name " + name); } /** * Gets the provider for a given font * * @param font * @return The provider */ private FontProvider<?> getProvider(Font font) { for (FontProvider<?> prv : getProviders()) { if (prv.provides(font)) { return prv; } } if (log.isDebugEnabled()) { log.debug("font=" + font + " NO PROVIDER"); } return null; } /** * Get the list of providers by taking care that the preferred provider * (specified by the jnode.font.renderer system property) is always at the * first position. * * @return */ private synchronized List<FontProvider<?>> getProviders() { // TODO fix true type font // final String firstProviderName = (String)AccessController. // doPrivileged(new GetPropertyAction("jnode.font.renderer", "ttf")); final String firstProviderName = (String) AccessController.doPrivileged(new GetPropertyAction("jnode.font.renderer", "bdf")); if ((providers.size() > 1) && !firstProviderName.equals(providers.get(0).getName())) { for (int i = 1; i < providers.size(); i++) { if (firstProviderName.equals(providers.get(i).getName())) { // exchange the providers so that firstProvider is always at index 0 FontProvider<?> firstProvider = providers.get(i); providers.set(i, providers.get(0)); providers.set(0, firstProvider); break; } } } return providers; } /** * Translates the font into a font that is provided by a provider. * * @param font * @return */ private Font getTranslatedFont(Font font) { Font txFont = font; if (getProvider(font) == null) { txFont = getCompatibleFont(font); } return txFont; } /** * Translates the font into a font that is provided by a provider. * * @param font * @return */ private Font getCompatibleFont(Font font) { return getProviders().get(0).getCompatibleFont(font); } private synchronized void updateFontProviders() { final Extension[] extensions = providersEP.getExtensions(); if (log.isDebugEnabled()) { log.debug("Found " + extensions.length + " font providers"); } for (int i = 0; i < extensions.length; i++) { final ConfigurationElement[] elements = extensions[i].getConfigurationElements(); for (int j = 0; j < elements.length; j++) { configureProvider(providers, elements[j]); } } } private void configureProvider(List<FontProvider<?>> providers, ConfigurationElement element) { final String className = element.getAttribute("class"); if (log.isDebugEnabled()) { log.debug("Configure provider: class=" + className); } if ((className != null) && (getProviderIndexByClass(className) < 0)) { try { final Class<?> cls = Thread.currentThread().getContextClassLoader().loadClass(className); final FontProvider<?> provider = (FontProvider<?>) cls.newInstance(); providers.add(provider); } catch (ClassNotFoundException ex) { log.error("Cannot find provider class " + className); } catch (IllegalAccessException ex) { log.error("Cannot access provider class " + className); } catch (InstantiationException ex) { log.error("Cannot instantiate provider class " + className); } catch (ClassCastException ex) { log.error("Provider class " + className + " does not implement the FontProvider interface"); } } } private int getProviderIndexByClass(String className) { int idx = -1; for (int i = 0; i < providers.size(); i++) { if (providers.get(i).getClass().getName().equals(className)) { idx = i; break; } } return idx; } }