package org.geogebra.desktop.main; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Locale; import javax.swing.UIManager; import org.geogebra.common.awt.GFont; import org.geogebra.common.main.FontManager; import org.geogebra.common.util.lang.Language; import org.geogebra.common.util.lang.Unicode; import org.geogebra.desktop.awt.GFontD; /** * Manages fonts for different languages. Use setLanguage() and setFontSize() to * initialize the default fonts. */ public class FontManagerD extends FontManager { private Font boldFont, italicFont, plainFont, smallFont, serifFont, serifFontBold, javaSans, javaSerif; private int fontSize; private String sansName, serifName; private HashMap fontMap = new HashMap(); private StringBuilder key = new StringBuilder(); private static final String[] FONT_NAMES_SANSSERIF = { "SansSerif", // Java "Arial Unicode MS", // Windows "Helvetica", // Mac OS X "LucidaGrande", // Mac OS X "ArialUnicodeMS" // Mac OS X }; private static final String[] FONT_NAMES_SERIF = { "Serif", // Java "Times New Roman", // Windows "Times" // Mac OS X }; public FontManagerD() { setFontSize(12); } /** * Sets default font that works with the given language. */ public void setLanguage(final Locale locale) throws Exception { final String lang = locale.getLanguage(); // new font names for language String fontNameSansSerif = null; String fontNameSerif = null; // certain languages need special fonts to display its characters final StringBuilder testCharacters = new StringBuilder(); final LinkedList<String> tryFontsSansSerif = new LinkedList<String>( Arrays.asList(FONT_NAMES_SANSSERIF)); final LinkedList<String> tryFontsSerif = new LinkedList<String>( Arrays.asList(FONT_NAMES_SERIF)); final String testChar = Language.getTestChar(lang); if (testChar != null) { testCharacters.append(testChar); // Application.debug("Using test // char:"+Util.toHexString(testChar.charValue())); } // else Application.debug("No language specific test char"); // CHINESE if ("zh".equals(lang)) { // last CJK unified ideograph in unicode alphabet // testCharacters.append('\u984F'); tryFontsSansSerif.addFirst("\u00cb\u00ce\u00cc\u00e5"); tryFontsSerif.addFirst("\u00cb\u00ce\u00cc\u00e5"); } // GEORGIAN else if ("ka".equals(lang)) { // some Georgian letter // testCharacters.append('\u10d8'); tryFontsSerif.addFirst("Sylfaen"); } // HEBREW // Guy Hed, 26.4.2009 - added Yiddish, which also use Hebrew letters. else if ("iw".equals(lang) || "ji".equals(lang)) { // Hebrew letter "tav" // testCharacters.append('\u05ea'); // move Java fonts to end of list tryFontsSansSerif.remove("SansSerif"); tryFontsSansSerif.addLast("SansSerif"); tryFontsSerif.remove("Serif"); tryFontsSerif.addLast("Serif"); } /* * replaced by Unicode.getTestChar() * * // JAPANESE else if ("ja".equals(lang)) { // Katakana letter N * testCharacters.append('\uff9d'); } * * // TAMIL else if ("ta".equals(lang)) { // Tamil digit 1 * testCharacters.append('\u0be7'); } * * // Punjabi else if ("pa".equals(lang)) { * testCharacters.append('\u0be7'); } // Hindi else if * ("hi".equals(lang)) { testCharacters.append('\u0be7'); } // Urdu else * if ("ur".equals(lang)) { testCharacters.append('\u0be7'); } // * Gujarati else if ("gu".equals(lang)) { * testCharacters.append('\u0be7'); } else if ("si".equals(lang)) { * testCharacters.append('\u0d9a'); // letter a } */ // we need roman (English) characters if possible // eg the language menu :) testCharacters.append('a'); // make sure we use a font that can display the Euler character // add at end -> lowest priority testCharacters.append(Unicode.eulerChar); // get fonts that can display all test characters fontNameSansSerif = getFontCanDisplay(tryFontsSansSerif, testCharacters.toString()); fontNameSerif = getFontCanDisplay(tryFontsSerif, testCharacters.toString()); // make sure we have sans serif and serif fonts if (fontNameSansSerif == null) { fontNameSansSerif = "SansSerif"; } if (fontNameSerif == null) { fontNameSerif = "Serif"; } // update application fonts if changed updateDefaultFonts(fontSize, fontNameSansSerif, fontNameSerif); } /** * Set default font size. */ @Override public void setFontSize(final int size) { // current sans and sansserif font names final String sans = plainFont == null ? "SansSerif" : plainFont.getFontName(); final String serif = serifFont == null ? "Serif" : serifFont.getFontName(); // update size updateDefaultFonts(size, sans, serif); } public void updateDefaultFonts(final int size, final String sans, final String serif) { if ((size == fontSize) && sans.equals(sansName) && serif.equals(serifName)) { return; } fontSize = size; sansName = sans; serifName = serif; // Java fonts javaSans = getFont("SansSerif", Font.PLAIN, size); javaSerif = getFont("Serif", Font.PLAIN, size); // create similar font with the specified size plainFont = getFont(sans, Font.PLAIN, size); boldFont = getFont(sans, Font.BOLD, size); italicFont = getFont(sans, Font.ITALIC, size); smallFont = getFont(sans, Font.PLAIN, size - 2); // serif serifFont = getFont(serif, Font.PLAIN, size); serifFontBold = getFont(serif, Font.BOLD, size); // TODO: causes problems with multiple windows (File -> New Window) setLAFFont(plainFont); // System.out.println("Fonts updated: sans: " + sans + ", serif: " + // serif); } /** * Returns a font with the specified attributes. * * @param serif * @param style * @param size */ public Font getFont(final boolean serif, final int style, final int size) { final String name = serif ? getSerifFont().getFontName() : getPlainFont().getFontName(); return getFont(name, style, size); } /** * Gets a font from a HashMap to avoid multiple creations of the same font. */ private Font getFont(final String name, final int style, final int size) { // build font's key name for HashMap key.setLength(0); key.append(name); key.append('_'); key.append(style); key.append('_'); key.append(size); // look if we have this font already in the HashMap Font f = (Font) fontMap.get(key.toString()); if (f == null) { // new font: create it and keep it in the HashMap f = new Font(name, style, size); fontMap.put(key.toString(), f); // System.out.println("NEW font: " + f); } return f; } /** * Returns a font that can display testString. */ public Font getFontCanDisplayAwt(final String testString, final boolean serif, final int fontStyle, final int fontSize) { final Font appFont = serif ? serifFont : plainFont; if (appFont == null) { return plainFont; } // check if default font is ok if ((testString == null) || (appFont.canDisplayUpTo(testString) == -1)) { if (appFont.getSize() == fontSize) { if (appFont.getStyle() == fontStyle) { return appFont; } else if (fontStyle == Font.BOLD) { return serif ? serifFontBold : boldFont; } } // need to compute new font return getFont(appFont.getFontName(), fontStyle, fontSize); } // check if standard Java fonts can be used final Font javaFont = serif ? javaSerif : javaSans; if (javaFont.canDisplayUpTo(testString) == -1) { return getFont(javaFont.getName(), fontStyle, fontSize); } // no standard fonts worked: try harder and go through all // fonts to find one that can display the testString try { final LinkedList<String> tryFonts = serif ? new LinkedList<String>(Arrays.asList(FONT_NAMES_SERIF)) : new LinkedList<String>( Arrays.asList(FONT_NAMES_SANSSERIF)); final String fontName = getFontCanDisplay(tryFonts, testString); return getFont(fontName, fontStyle, fontSize); } catch (final Exception e) { return appFont; } } /** * Tries to find a font that can display all given unicode characters. * Starts with tryFontNames first. */ public String getFontCanDisplay(final LinkedList<String> tryFontNames, final String testCharacters) throws Exception { // System.out.println("expensive test getFontCanDisplay, " + // testCharacters); // if (true) { // return "Comic Sans MS"; // } // try given fonts if (tryFontNames != null) { final Iterator<String> it = tryFontNames.iterator(); while (it.hasNext()) { // create font for name final String fontName = it.next(); final Font font = getFont(fontName, Font.PLAIN, 12); // check if creating font worked if (font.getFamily().startsWith(fontName)) { // test if this font can display all test characters if (font.canDisplayUpTo(testCharacters) == -1) { return font.getFontName(); } } } } int maxDisplayedChars = 0; int bestFont = -1; // Determine which fonts best support the characters in testCharacters final Font[] allfonts = GraphicsEnvironment .getLocalGraphicsEnvironment().getAllFonts(); for (int j = 0; j < allfonts.length; j++) { // Log.debug(allfonts[j].toString()); final int charsDisplayed = allfonts[j] .canDisplayUpTo(testCharacters); if (charsDisplayed == -1) { // avoid "Monospace" font here if (!allfonts[j].getFamily().equals("Monospaced")) { return allfonts[j].getFontName(); } } // no exact match, but store how much matches if (charsDisplayed > maxDisplayedChars) { bestFont = j; maxDisplayedChars = charsDisplayed; // Application.debug(allfonts[bestFont].getFontName()+" matched // "+charsDisplayed); } } // no exact match, return the font that matches the most characters if (bestFont > -1) { return allfonts[bestFont].getFontName(); } throw new Exception( "Sorry, there is no font for this language available on your computer."); } final public Font getBoldFont() { return boldFont; } final public Font getItalicFont() { return italicFont; } final public Font getPlainFont() { return plainFont; } final public Font getSmallFont() { return smallFont; } final public Font getSerifFont() { return serifFont; } private static void setLAFFont(final Font plain) { UIManager.put("ColorChooser.font", plain); UIManager.put("FileChooser.font", plain); // Panel, Pane, Bars UIManager.put("Panel.font", plain); UIManager.put("TextPane.font", plain); UIManager.put("OptionPane.font", plain); UIManager.put("OptionPane.messageFont", plain); UIManager.put("OptionPane.buttonFont", plain); UIManager.put("EditorPane.font", plain); UIManager.put("ScrollPane.font", plain); UIManager.put("TabbedPane.font", plain); UIManager.put("ToolBar.font", plain); UIManager.put("ProgressBar.font", plain); UIManager.put("Viewport.font", plain); UIManager.put("TitledBorder.font", plain); // Buttons UIManager.put("Button.font", plain); UIManager.put("RadioButton.font", plain); UIManager.put("ToggleButton.font", plain); UIManager.put("ComboBox.font", plain); UIManager.put("CheckBox.font", plain); // Menus UIManager.put("Menu.font", plain); UIManager.put("Menu.acceleratorFont", plain); UIManager.put("PopupMenu.font", plain); UIManager.put("MenuBar.font", plain); UIManager.put("MenuItem.font", plain); UIManager.put("MenuItem.acceleratorFont", plain); UIManager.put("CheckBoxMenuItem.font", plain); UIManager.put("RadioButtonMenuItem.font", plain); // Fields, Labels UIManager.put("Label.font", plain); UIManager.put("Table.font", plain); UIManager.put("TableHeader.font", plain); UIManager.put("Tree.font", plain); UIManager.put("Tree.rowHeight", Integer.valueOf(plain.getSize() + 5)); UIManager.put("List.font", plain); UIManager.put("TextField.font", plain); UIManager.put("PasswordField.font", plain); UIManager.put("TextArea.font", plain); UIManager.put("ToolTip.font", plain); } @Override public GFont getFontCanDisplay(final String testString, final boolean serif, final int fontStyle, final int fontSize) { return new GFontD( getFontCanDisplayAwt(testString, serif, fontStyle, fontSize)); } /** * @return font size */ public int getFontSize() { return fontSize; } }