< prev index next >

src/java.desktop/share/classes/sun/font/SunFontManager.java

Print this page


   1 /*
   2  * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  33 import java.io.FilenameFilter;
  34 import java.io.IOException;
  35 import java.io.InputStreamReader;
  36 import java.security.AccessController;
  37 import java.security.PrivilegedAction;
  38 import java.util.ArrayList;
  39 import java.util.HashMap;
  40 import java.util.HashSet;
  41 import java.util.Hashtable;
  42 import java.util.Iterator;
  43 import java.util.List;
  44 import java.util.Locale;
  45 import java.util.Map;
  46 import java.util.NoSuchElementException;
  47 import java.util.StringTokenizer;
  48 import java.util.TreeMap;
  49 import java.util.Vector;
  50 import java.util.concurrent.ConcurrentHashMap;
  51 
  52 import javax.swing.plaf.FontUIResource;
  53 import sun.awt.AppContext;
  54 import sun.awt.FontConfiguration;
  55 import sun.awt.SunToolkit;
  56 import sun.awt.util.ThreadGroupUtils;
  57 import sun.java2d.FontSupport;
  58 import sun.util.logging.PlatformLogger;
  59 
  60 /**
  61  * The base implementation of the {@link FontManager} interface. It implements
  62  * the platform independent, shared parts of OpenJDK's FontManager
  63  * implementations. The platform specific parts are declared as abstract
  64  * methods that have to be implemented by specific implementations.
  65  */
  66 public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
  67 
  68     private static class TTFilter implements FilenameFilter {
  69         public boolean accept(File dir,String name) {
  70             /* all conveniently have the same suffix length */
  71             int offset = name.length()-4;
  72             if (offset <= 0) { /* must be at least A.ttf */
  73                 return false;


 239      * Returns the global SunFontManager instance. This is similar to
 240      * {@link FontManagerFactory#getInstance()} but it returns a
 241      * SunFontManager instance instead. This is only used in internal classes
 242      * where we can safely assume that a SunFontManager is to be used.
 243      *
 244      * @return the global SunFontManager instance
 245      */
 246     public static SunFontManager getInstance() {
 247         FontManager fm = FontManagerFactory.getInstance();
 248         return (SunFontManager) fm;
 249     }
 250 
 251     public FilenameFilter getTrueTypeFilter() {
 252         return ttFilter;
 253     }
 254 
 255     public FilenameFilter getType1Filter() {
 256         return t1Filter;
 257     }
 258 
 259     @Override
 260     public boolean usingPerAppContextComposites() {
 261         return _usingPerAppContextComposites;
 262     }
 263 
 264     static {
 265 
 266         java.security.AccessController.doPrivileged(
 267                                     new java.security.PrivilegedAction<Object>() {
 268 
 269            public Object run() {
 270                FontManagerNativeLibrary.load();
 271 
 272                // JNI throws an exception if a class/method/field is not found,
 273                // so there's no need to do anything explicit here.
 274                initIDs();
 275 
 276                switch (StrikeCache.nativeAddressSize) {
 277                case 8: longAddresses = true; break;
 278                case 4: longAddresses = false; break;
 279                default: throw new RuntimeException("Unexpected address size");
 280                }
 281 
 282                noType1Font =
 283                    "true".equals(System.getProperty("sun.java2d.noType1Font"));


1919             if (font == null) {
1920                 font = fontFamily.getClosestStyle(style);
1921             }
1922         }
1923         return font;
1924     }
1925 
1926     private ConcurrentHashMap<String, Font2D> fontNameCache =
1927         new ConcurrentHashMap<String, Font2D>();
1928 
1929     /*
1930      * The client supplies a name and a style.
1931      * The name could be a family name, or a full name.
1932      * A font may exist with the specified style, or it may
1933      * exist only in some other style. For non-native fonts the scaler
1934      * may be able to emulate the required style.
1935      */
1936     public Font2D findFont2D(String name, int style, int fallback) {
1937         String lowerCaseName = name.toLowerCase(Locale.ENGLISH);
1938         String mapName = lowerCaseName + dotStyleStr(style);
1939         Font2D font;
1940 
1941         /* If preferLocaleFonts() or preferProportionalFonts() has been
1942          * called we may be using an alternate set of composite fonts in this
1943          * app context. The presence of a pre-built name map indicates whether
1944          * this is so, and gives access to the alternate composite for the
1945          * name.
1946          */
1947         if (_usingPerAppContextComposites) {
1948             @SuppressWarnings("unchecked")
1949             ConcurrentHashMap<String, Font2D> altNameCache =
1950                 (ConcurrentHashMap<String, Font2D>)
1951                 AppContext.getAppContext().get(CompositeFont.class);
1952             if (altNameCache != null) {
1953                 font = altNameCache.get(mapName);
1954             } else {
1955                 font = null;
1956             }
1957         } else {
1958             font = fontNameCache.get(mapName);
1959         }
1960         if (font != null) {
1961             return font;
1962         }
1963 
1964         if (FontUtilities.isLogging()) {
1965             FontUtilities.getLogger().info("Search for font: " + name);
1966         }
1967 
1968         // The check below is just so that the bitmap fonts being set by
1969         // AWT and Swing thru the desktop properties do not trigger the
1970         // the load fonts case. The two bitmap fonts are now mapped to
1971         // appropriate equivalents for serif and sansserif.
1972         // Note that the cost of this comparison is only for the first
1973         // call until the map is filled.
1974         if (FontUtilities.isWindows) {
1975             if (lowerCaseName.equals("ms sans serif")) {
1976                 name = "sansserif";
1977             } else if (lowerCaseName.equals("ms serif")) {
1978                 name = "serif";
1979             }


2144             if (lowerCaseName.equals("timesroman")) {
2145                 font = findFont2D("serif", style, fallback);
2146                 fontNameCache.put(mapName, font);
2147             }
2148             register1dot0Fonts();
2149             loaded1dot0Fonts = true;
2150             Font2D ff = findFont2D(name, style, fallback);
2151             return ff;
2152         }
2153 
2154         /* We check for application registered fonts before
2155          * explicitly loading all fonts as if necessary the registration
2156          * code will have done so anyway. And we don't want to needlessly
2157          * load the actual files for all fonts.
2158          * Just as for installed fonts we check for family before fullname.
2159          * We do not add these fonts to fontNameCache for the
2160          * app context case which eliminates the overhead of a per context
2161          * cache for these.
2162          */
2163 
2164         if (fontsAreRegistered || fontsAreRegisteredPerAppContext) {
2165             Hashtable<String, FontFamily> familyTable = null;
2166             Hashtable<String, Font2D> nameTable;
2167 
2168             if (fontsAreRegistered) {
2169                 familyTable = createdByFamilyName;
2170                 nameTable = createdByFullName;
2171             } else {
2172                 AppContext appContext = AppContext.getAppContext();
2173                 @SuppressWarnings("unchecked")
2174                 Hashtable<String,FontFamily> tmp1 =
2175                     (Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
2176                 familyTable = tmp1;
2177 
2178                 @SuppressWarnings("unchecked")
2179                 Hashtable<String, Font2D> tmp2 =
2180                     (Hashtable<String,Font2D>)appContext.get(regFullNameKey);
2181                 nameTable = tmp2;
2182             }
2183 
2184             family = familyTable.get(lowerCaseName);
2185             if (family != null) {
2186                 font = family.getFontWithExactStyleMatch(style);
2187                 if (font == null) {
2188                     font = family.getFont(style);
2189                 }
2190                 if (font == null) {
2191                     font = family.getClosestStyle(style);
2192                 }
2193                 if (font != null) {
2194                     if (fontsAreRegistered) {
2195                         fontNameCache.put(mapName, font);
2196                     }
2197                     return font;
2198                 }
2199             }
2200             font = nameTable.get(lowerCaseName);
2201             if (font != null) {
2202                 if (fontsAreRegistered) {


2667      * are not placed in the "default" maps but into an AppContext instance.
2668      * The font lookup mechanism in java.awt.Font.getFont2D() is also updated
2669      * so that look-up for composite fonts will in that case always
2670      * do a lookup rather than returning a cached result.
2671      * This is inefficient but necessary else singleton java.awt.Font
2672      * instances would not retrieve the correct Font2D for the appcontext.
2673      * sun.font.FontManager.findFont2D is also updated to that it uses
2674      * a name map cache specific to that appcontext.
2675      *
2676      * Getting an AppContext is expensive, so there is a global variable
2677      * that records whether these methods have ever been called and can
2678      * avoid the expense for almost all applications. Once the correct
2679      * CompositeFont is associated with the Font, everything should work
2680      * through existing mechanisms.
2681      * A special case is that GraphicsEnvironment.getAllFonts() must
2682      * return an AppContext specific list.
2683      *
2684      * Calling the methods below is "heavyweight" but it is expected that
2685      * these methods will be called very rarely.
2686      *
2687      * If _usingPerAppContextComposites is true, we are in "applet"
2688      * (eg browser) environment and at least one context has selected
2689      * an alternate composite font behaviour.
2690      * If _usingAlternateComposites is true, we are not in an "applet"
2691      * environment and the (single) application has selected
2692      * an alternate composite font behaviour.
2693      *
2694      * - Printing: The implementation delegates logical fonts to an AWT
2695      * mechanism which cannot use these alternate configurations.
2696      * We can detect that alternate fonts are in use and back-off to 2D, but
2697      * that uses outlines. Much of this can be fixed with additional work
2698      * but that may have to wait. The results should be correct, just not
2699      * optimal.
2700      */
2701     private static final Object altJAFontKey       = new Object();
2702     private static final Object localeFontKey       = new Object();
2703     private static final Object proportionalFontKey = new Object();
2704     private boolean _usingPerAppContextComposites = false;
2705     private boolean _usingAlternateComposites = false;
2706 
2707     /* These values are used only if we are running as a standalone
2708      * application, as determined by maybeMultiAppContext();
2709      */
2710     private static boolean gAltJAFont = false;
2711     private boolean gLocalePref = false;
2712     private boolean gPropPref = false;
2713 
2714     /* This method doesn't check if alternates are selected in this app
2715      * context. Its used by the FontMetrics caching code which in such
2716      * a case cannot retrieve a cached metrics solely on the basis of
2717      * the Font.equals() method since it needs to also check if the Font2D
2718      * is the same.
2719      * We also use non-standard composites for Swing native L&F fonts on
2720      * Windows. In that case the policy is that the metrics reported are
2721      * based solely on the physical font in the first slot which is the
2722      * visible java.awt.Font. So in that case the metrics cache which tests
2723      * the Font does what we want. In the near future when we expand the GTK
2724      * logical font definitions we may need to revisit this if GTK reports
2725      * combined metrics instead. For now though this test can be simple.
2726      */
2727     public boolean maybeUsingAlternateCompositeFonts() {
2728        return _usingAlternateComposites || _usingPerAppContextComposites;
2729     }
2730 
2731     public boolean usingAlternateCompositeFonts() {
2732         return (_usingAlternateComposites ||
2733                 (_usingPerAppContextComposites &&
2734                 AppContext.getAppContext().get(CompositeFont.class) != null));
2735     }
2736 
2737     private static boolean maybeMultiAppContext() {
2738         Boolean appletSM = (Boolean)
2739             java.security.AccessController.doPrivileged(
2740                 new java.security.PrivilegedAction<Object>() {
2741                         public Object run() {
2742                             SecurityManager sm = System.getSecurityManager();
2743                             return sm instanceof sun.awt.AWTSecurityManager;
2744                         }
2745                     });
2746         return appletSM.booleanValue();
2747     }
2748 
2749     /* Modifies the behaviour of a subsequent call to preferLocaleFonts()
2750      * to use Mincho instead of Gothic for dialoginput in JA locales
2751      * on windows. Not needed on other platforms.
2752      */
2753     public synchronized void useAlternateFontforJALocales() {
2754         if (FontUtilities.isLogging()) {
2755             FontUtilities.getLogger()
2756                 .info("Entered useAlternateFontforJALocales().");
2757         }
2758         if (!FontUtilities.isWindows) {
2759             return;
2760         }
2761 
2762         if (!maybeMultiAppContext()) {
2763             gAltJAFont = true;
2764         } else {
2765             AppContext appContext = AppContext.getAppContext();
2766             appContext.put(altJAFontKey, altJAFontKey);
2767         }
2768     }
2769 
2770     public boolean usingAlternateFontforJALocales() {
2771         if (!maybeMultiAppContext()) {
2772             return gAltJAFont;
2773         } else {
2774             AppContext appContext = AppContext.getAppContext();
2775             return appContext.get(altJAFontKey) == altJAFontKey;
2776         }
2777     }
2778 
2779     public synchronized void preferLocaleFonts() {
2780         if (FontUtilities.isLogging()) {
2781             FontUtilities.getLogger().info("Entered preferLocaleFonts().");
2782         }
2783         /* Test if re-ordering will have any effect */
2784         if (!FontConfiguration.willReorderForStartupLocale()) {
2785             return;
2786         }
2787 
2788         if (!maybeMultiAppContext()) {
2789             if (gLocalePref == true) {
2790                 return;
2791             }
2792             gLocalePref = true;
2793             createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
2794             _usingAlternateComposites = true;
2795         } else {
2796             AppContext appContext = AppContext.getAppContext();
2797             if (appContext.get(localeFontKey) == localeFontKey) {
2798                 return;
2799             }
2800             appContext.put(localeFontKey, localeFontKey);
2801             boolean acPropPref =
2802                 appContext.get(proportionalFontKey) == proportionalFontKey;
2803             ConcurrentHashMap<String, Font2D>
2804                 altNameCache = new ConcurrentHashMap<String, Font2D> ();
2805             /* If there is an existing hashtable, we can drop it. */
2806             appContext.put(CompositeFont.class, altNameCache);
2807             _usingPerAppContextComposites = true;
2808             createCompositeFonts(altNameCache, true, acPropPref);
2809         }
2810     }
2811 
2812     public synchronized void preferProportionalFonts() {
2813         if (FontUtilities.isLogging()) {
2814             FontUtilities.getLogger()
2815                 .info("Entered preferProportionalFonts().");
2816         }
2817         /* If no proportional fonts are configured, there's no need
2818          * to take any action.
2819          */
2820         if (!FontConfiguration.hasMonoToPropMap()) {
2821             return;
2822         }
2823 
2824         if (!maybeMultiAppContext()) {
2825             if (gPropPref == true) {
2826                 return;
2827             }
2828             gPropPref = true;
2829             createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
2830             _usingAlternateComposites = true;
2831         } else {
2832             AppContext appContext = AppContext.getAppContext();
2833             if (appContext.get(proportionalFontKey) == proportionalFontKey) {
2834                 return;
2835             }
2836             appContext.put(proportionalFontKey, proportionalFontKey);
2837             boolean acLocalePref =
2838                 appContext.get(localeFontKey) == localeFontKey;
2839             ConcurrentHashMap<String, Font2D>
2840                 altNameCache = new ConcurrentHashMap<String, Font2D> ();
2841             /* If there is an existing hashtable, we can drop it. */
2842             appContext.put(CompositeFont.class, altNameCache);
2843             _usingPerAppContextComposites = true;
2844             createCompositeFonts(altNameCache, acLocalePref, true);
2845         }
2846     }
2847 
2848     private static HashSet<String> installedNames = null;
2849     private static HashSet<String> getInstalledNames() {
2850         if (installedNames == null) {
2851            Locale l = getSystemStartupLocale();
2852            SunFontManager fontManager = SunFontManager.getInstance();
2853            String[] installedFamilies =
2854                fontManager.getInstalledFontFamilyNames(l);
2855            Font[] installedFonts = fontManager.getAllInstalledFonts();
2856            HashSet<String> names = new HashSet<String>();
2857            for (int i=0; i<installedFamilies.length; i++) {
2858                names.add(installedFamilies[i].toLowerCase(l));
2859            }
2860            for (int i=0; i<installedFonts.length; i++) {
2861                names.add(installedFonts[i].getFontName(l).toLowerCase(l));
2862            }
2863            installedNames = names;
2864         }
2865         return installedNames;
2866     }
2867 
2868     /* Keys are used to lookup per-AppContext Hashtables */
2869     private static final Object regFamilyKey  = new Object();
2870     private static final Object regFullNameKey = new Object();
2871     private Hashtable<String,FontFamily> createdByFamilyName;
2872     private Hashtable<String,Font2D>     createdByFullName;
2873     private boolean fontsAreRegistered = false;
2874     private boolean fontsAreRegisteredPerAppContext = false;
2875 
2876     public boolean registerFont(Font font) {
2877         /* This method should not be called with "null".
2878          * It is the caller's responsibility to ensure that.
2879          */
2880         if (font == null) {
2881             return false;
2882         }
2883 
2884         /* Initialise these objects only once we start to use this API */
2885         synchronized (regFamilyKey) {
2886             if (createdByFamilyName == null) {
2887                 createdByFamilyName = new Hashtable<String,FontFamily>();
2888                 createdByFullName = new Hashtable<String,Font2D>();
2889             }
2890         }
2891 
2892         if (! FontAccess.getFontAccess().isCreatedFont(font)) {
2893             return false;
2894         }
2895         /* We want to ensure that this font cannot override existing
2896          * installed fonts. Check these conditions :
2897          * - family name is not that of an installed font
2898          * - full name is not that of an installed font
2899          * - family name is not the same as the full name of an installed font
2900          * - full name is not the same as the family name of an installed font
2901          * The last two of these may initially look odd but the reason is
2902          * that (unfortunately) Font constructors do not distinuguish these.
2903          * An extreme example of such a problem would be a font which has
2904          * family name "Dialog.Plain" and full name of "Dialog".
2905          * The one arguably overly stringent restriction here is that if an
2906          * application wants to supply a new member of an existing family
2907          * It will get rejected. But since the JRE can perform synthetic
2908          * styling in many cases its not necessary.
2909          * We don't apply the same logic to registered fonts. If apps want
2910          * to do this lets assume they have a reason. It won't cause problems
2911          * except for themselves.
2912          */
2913         HashSet<String> names = getInstalledNames();
2914         Locale l = getSystemStartupLocale();
2915         String familyName = font.getFamily(l).toLowerCase();
2916         String fullName = font.getFontName(l).toLowerCase();
2917         if (names.contains(familyName) || names.contains(fullName)) {
2918             return false;
2919         }
2920 
2921         /* Checks passed, now register the font */
2922         Hashtable<String,FontFamily> familyTable;
2923         Hashtable<String,Font2D> fullNameTable;
2924         if (!maybeMultiAppContext()) {
2925             familyTable = createdByFamilyName;
2926             fullNameTable = createdByFullName;
2927             fontsAreRegistered = true;
2928         } else {
2929             AppContext appContext = AppContext.getAppContext();
2930             @SuppressWarnings("unchecked")
2931             Hashtable<String,FontFamily> tmp1 =
2932                 (Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
2933             familyTable = tmp1;
2934             @SuppressWarnings("unchecked")
2935             Hashtable<String,Font2D> tmp2 =
2936                 (Hashtable<String,Font2D>)appContext.get(regFullNameKey);
2937             fullNameTable = tmp2;
2938 
2939             if (familyTable == null) {
2940                 familyTable = new Hashtable<String,FontFamily>();
2941                 fullNameTable = new Hashtable<String,Font2D>();
2942                 appContext.put(regFamilyKey, familyTable);
2943                 appContext.put(regFullNameKey, fullNameTable);
2944             }
2945             fontsAreRegisteredPerAppContext = true;
2946         }
2947         /* Create the FontFamily and add font to the tables */
2948         Font2D font2D = FontUtilities.getFont2D(font);
2949         int style = font2D.getStyle();
2950         FontFamily family = familyTable.get(familyName);
2951         if (family == null) {
2952             family = new FontFamily(font.getFamily(l));
2953             familyTable.put(familyName, family);
2954         }
2955         /* Remove name cache entries if not using app contexts.
2956          * To accommodate a case where code may have registered first a plain
2957          * family member and then used it and is now registering a bold family
2958          * member, we need to remove all members of the family, so that the
2959          * new style can get picked up rather than continuing to synthesise.
2960          */
2961         if (fontsAreRegistered) {
2962             removeFromCache(family.getFont(Font.PLAIN));
2963             removeFromCache(family.getFont(Font.BOLD));
2964             removeFromCache(family.getFont(Font.ITALIC));
2965             removeFromCache(family.getFont(Font.BOLD|Font.ITALIC));
2966             removeFromCache(fullNameTable.get(fullName));


2972 
2973     /* Remove from the name cache all references to the Font2D */
2974     private void removeFromCache(Font2D font) {
2975         if (font == null) {
2976             return;
2977         }
2978         String[] keys = fontNameCache.keySet().toArray(STR_ARRAY);
2979         for (int k=0; k<keys.length;k++) {
2980             if (fontNameCache.get(keys[k]) == font) {
2981                 fontNameCache.remove(keys[k]);
2982             }
2983         }
2984     }
2985 
2986     // It may look odd to use TreeMap but its more convenient to the caller.
2987     public TreeMap<String, String> getCreatedFontFamilyNames() {
2988 
2989         Hashtable<String,FontFamily> familyTable;
2990         if (fontsAreRegistered) {
2991             familyTable = createdByFamilyName;
2992         } else if (fontsAreRegisteredPerAppContext) {
2993             AppContext appContext = AppContext.getAppContext();
2994             @SuppressWarnings("unchecked")
2995             Hashtable<String,FontFamily> tmp =
2996                 (Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
2997             familyTable = tmp;
2998         } else {
2999             return null;
3000         }
3001 
3002         Locale l = getSystemStartupLocale();
3003         synchronized (familyTable) {
3004             TreeMap<String, String> map = new TreeMap<String, String>();
3005             for (FontFamily f : familyTable.values()) {
3006                 Font2D font2D = f.getFont(Font.PLAIN);
3007                 if (font2D == null) {
3008                     font2D = f.getClosestStyle(Font.PLAIN);
3009                 }
3010                 String name = font2D.getFamilyName(l);
3011                 map.put(name.toLowerCase(l), name);
3012             }
3013             return map;
3014         }
3015     }
3016 
3017     public Font[] getCreatedFonts() {
3018 
3019         Hashtable<String,Font2D> nameTable;
3020         if (fontsAreRegistered) {
3021             nameTable = createdByFullName;
3022         } else if (fontsAreRegisteredPerAppContext) {
3023             AppContext appContext = AppContext.getAppContext();
3024             @SuppressWarnings("unchecked")
3025             Hashtable<String,Font2D> tmp =
3026                 (Hashtable<String,Font2D>)appContext.get(regFullNameKey);
3027             nameTable = tmp;
3028         } else {
3029             return null;
3030         }
3031 
3032         Locale l = getSystemStartupLocale();
3033         synchronized (nameTable) {
3034             Font[] fonts = new Font[nameTable.size()];
3035             int i=0;
3036             for (Font2D font2D : nameTable.values()) {
3037                 fonts[i++] = new Font(font2D.getFontName(l), Font.PLAIN, 1);
3038             }
3039             return fonts;
3040         }
3041     }
3042 
3043 
3044     protected String[] getPlatformFontDirs(boolean noType1Fonts) {
3045 
3046         /* First check if we already initialised path dirs */
3047         if (pathDirs != null) {


   1 /*
   2  * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  33 import java.io.FilenameFilter;
  34 import java.io.IOException;
  35 import java.io.InputStreamReader;
  36 import java.security.AccessController;
  37 import java.security.PrivilegedAction;
  38 import java.util.ArrayList;
  39 import java.util.HashMap;
  40 import java.util.HashSet;
  41 import java.util.Hashtable;
  42 import java.util.Iterator;
  43 import java.util.List;
  44 import java.util.Locale;
  45 import java.util.Map;
  46 import java.util.NoSuchElementException;
  47 import java.util.StringTokenizer;
  48 import java.util.TreeMap;
  49 import java.util.Vector;
  50 import java.util.concurrent.ConcurrentHashMap;
  51 
  52 import javax.swing.plaf.FontUIResource;
  53 
  54 import sun.awt.FontConfiguration;
  55 import sun.awt.SunToolkit;
  56 import sun.awt.util.ThreadGroupUtils;
  57 import sun.java2d.FontSupport;
  58 import sun.util.logging.PlatformLogger;
  59 
  60 /**
  61  * The base implementation of the {@link FontManager} interface. It implements
  62  * the platform independent, shared parts of OpenJDK's FontManager
  63  * implementations. The platform specific parts are declared as abstract
  64  * methods that have to be implemented by specific implementations.
  65  */
  66 public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
  67 
  68     private static class TTFilter implements FilenameFilter {
  69         public boolean accept(File dir,String name) {
  70             /* all conveniently have the same suffix length */
  71             int offset = name.length()-4;
  72             if (offset <= 0) { /* must be at least A.ttf */
  73                 return false;


 239      * Returns the global SunFontManager instance. This is similar to
 240      * {@link FontManagerFactory#getInstance()} but it returns a
 241      * SunFontManager instance instead. This is only used in internal classes
 242      * where we can safely assume that a SunFontManager is to be used.
 243      *
 244      * @return the global SunFontManager instance
 245      */
 246     public static SunFontManager getInstance() {
 247         FontManager fm = FontManagerFactory.getInstance();
 248         return (SunFontManager) fm;
 249     }
 250 
 251     public FilenameFilter getTrueTypeFilter() {
 252         return ttFilter;
 253     }
 254 
 255     public FilenameFilter getType1Filter() {
 256         return t1Filter;
 257     }
 258 





 259     static {
 260 
 261         java.security.AccessController.doPrivileged(
 262                                     new java.security.PrivilegedAction<Object>() {
 263 
 264            public Object run() {
 265                FontManagerNativeLibrary.load();
 266 
 267                // JNI throws an exception if a class/method/field is not found,
 268                // so there's no need to do anything explicit here.
 269                initIDs();
 270 
 271                switch (StrikeCache.nativeAddressSize) {
 272                case 8: longAddresses = true; break;
 273                case 4: longAddresses = false; break;
 274                default: throw new RuntimeException("Unexpected address size");
 275                }
 276 
 277                noType1Font =
 278                    "true".equals(System.getProperty("sun.java2d.noType1Font"));


1914             if (font == null) {
1915                 font = fontFamily.getClosestStyle(style);
1916             }
1917         }
1918         return font;
1919     }
1920 
1921     private ConcurrentHashMap<String, Font2D> fontNameCache =
1922         new ConcurrentHashMap<String, Font2D>();
1923 
1924     /*
1925      * The client supplies a name and a style.
1926      * The name could be a family name, or a full name.
1927      * A font may exist with the specified style, or it may
1928      * exist only in some other style. For non-native fonts the scaler
1929      * may be able to emulate the required style.
1930      */
1931     public Font2D findFont2D(String name, int style, int fallback) {
1932         String lowerCaseName = name.toLowerCase(Locale.ENGLISH);
1933         String mapName = lowerCaseName + dotStyleStr(style);

1934 
1935         /* If preferLocaleFonts() or preferProportionalFonts() has been
1936          * called we may be using an alternate set of composite fonts in this
1937          * app context. The presence of a pre-built name map indicates whether
1938          * this is so, and gives access to the alternate composite for the
1939          * name.
1940          */
1941         Font2D font = fontNameCache.get(mapName);












1942         if (font != null) {
1943             return font;
1944         }
1945 
1946         if (FontUtilities.isLogging()) {
1947             FontUtilities.getLogger().info("Search for font: " + name);
1948         }
1949 
1950         // The check below is just so that the bitmap fonts being set by
1951         // AWT and Swing thru the desktop properties do not trigger the
1952         // the load fonts case. The two bitmap fonts are now mapped to
1953         // appropriate equivalents for serif and sansserif.
1954         // Note that the cost of this comparison is only for the first
1955         // call until the map is filled.
1956         if (FontUtilities.isWindows) {
1957             if (lowerCaseName.equals("ms sans serif")) {
1958                 name = "sansserif";
1959             } else if (lowerCaseName.equals("ms serif")) {
1960                 name = "serif";
1961             }


2126             if (lowerCaseName.equals("timesroman")) {
2127                 font = findFont2D("serif", style, fallback);
2128                 fontNameCache.put(mapName, font);
2129             }
2130             register1dot0Fonts();
2131             loaded1dot0Fonts = true;
2132             Font2D ff = findFont2D(name, style, fallback);
2133             return ff;
2134         }
2135 
2136         /* We check for application registered fonts before
2137          * explicitly loading all fonts as if necessary the registration
2138          * code will have done so anyway. And we don't want to needlessly
2139          * load the actual files for all fonts.
2140          * Just as for installed fonts we check for family before fullname.
2141          * We do not add these fonts to fontNameCache for the
2142          * app context case which eliminates the overhead of a per context
2143          * cache for these.
2144          */
2145 




2146         if (fontsAreRegistered) {
2147             Hashtable<String, FontFamily> familyTable = createdByFamilyName;
2148             Hashtable<String, Font2D> nameTable = createdByFullName;












2149 
2150             family = familyTable.get(lowerCaseName);
2151             if (family != null) {
2152                 font = family.getFontWithExactStyleMatch(style);
2153                 if (font == null) {
2154                     font = family.getFont(style);
2155                 }
2156                 if (font == null) {
2157                     font = family.getClosestStyle(style);
2158                 }
2159                 if (font != null) {
2160                     if (fontsAreRegistered) {
2161                         fontNameCache.put(mapName, font);
2162                     }
2163                     return font;
2164                 }
2165             }
2166             font = nameTable.get(lowerCaseName);
2167             if (font != null) {
2168                 if (fontsAreRegistered) {


2633      * are not placed in the "default" maps but into an AppContext instance.
2634      * The font lookup mechanism in java.awt.Font.getFont2D() is also updated
2635      * so that look-up for composite fonts will in that case always
2636      * do a lookup rather than returning a cached result.
2637      * This is inefficient but necessary else singleton java.awt.Font
2638      * instances would not retrieve the correct Font2D for the appcontext.
2639      * sun.font.FontManager.findFont2D is also updated to that it uses
2640      * a name map cache specific to that appcontext.
2641      *
2642      * Getting an AppContext is expensive, so there is a global variable
2643      * that records whether these methods have ever been called and can
2644      * avoid the expense for almost all applications. Once the correct
2645      * CompositeFont is associated with the Font, everything should work
2646      * through existing mechanisms.
2647      * A special case is that GraphicsEnvironment.getAllFonts() must
2648      * return an AppContext specific list.
2649      *
2650      * Calling the methods below is "heavyweight" but it is expected that
2651      * these methods will be called very rarely.
2652      *



2653      * If _usingAlternateComposites is true, we are not in an "applet"
2654      * environment and the (single) application has selected
2655      * an alternate composite font behaviour.
2656      *
2657      * - Printing: The implementation delegates logical fonts to an AWT
2658      * mechanism which cannot use these alternate configurations.
2659      * We can detect that alternate fonts are in use and back-off to 2D, but
2660      * that uses outlines. Much of this can be fixed with additional work
2661      * but that may have to wait. The results should be correct, just not
2662      * optimal.
2663      */




2664     private boolean _usingAlternateComposites = false;
2665 



2666     private static boolean gAltJAFont = false;
2667     private boolean gLocalePref = false;
2668     private boolean gPropPref = false;
2669 
2670     /* Its used by the FontMetrics caching code which in such

2671      * a case cannot retrieve a cached metrics solely on the basis of
2672      * the Font.equals() method since it needs to also check if the Font2D
2673      * is the same.
2674      * We also use non-standard composites for Swing native L&F fonts on
2675      * Windows. In that case the policy is that the metrics reported are
2676      * based solely on the physical font in the first slot which is the
2677      * visible java.awt.Font. So in that case the metrics cache which tests
2678      * the Font does what we want. In the near future when we expand the GTK
2679      * logical font definitions we may need to revisit this if GTK reports
2680      * combined metrics instead. For now though this test can be simple.
2681      */




2682     public boolean usingAlternateCompositeFonts() {
2683         return _usingAlternateComposites;














2684     }
2685 
2686     /* Modifies the behaviour of a subsequent call to preferLocaleFonts()
2687      * to use Mincho instead of Gothic for dialoginput in JA locales
2688      * on windows. Not needed on other platforms.
2689      */
2690     public synchronized void useAlternateFontforJALocales() {
2691         if (FontUtilities.isLogging()) {
2692             FontUtilities.getLogger()
2693                 .info("Entered useAlternateFontforJALocales().");
2694         }
2695         if (!FontUtilities.isWindows) {
2696             return;
2697         }


2698         gAltJAFont = true;




2699     }
2700 
2701     public boolean usingAlternateFontforJALocales() {

2702         return gAltJAFont;




2703     }
2704 
2705     public synchronized void preferLocaleFonts() {
2706         if (FontUtilities.isLogging()) {
2707             FontUtilities.getLogger().info("Entered preferLocaleFonts().");
2708         }
2709         /* Test if re-ordering will have any effect */
2710         if (!FontConfiguration.willReorderForStartupLocale()) {
2711             return;
2712         }


2713         if (gLocalePref == true) {
2714             return;
2715         }
2716         gLocalePref = true;
2717         createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
2718         _usingAlternateComposites = true;















2719     }
2720 
2721     public synchronized void preferProportionalFonts() {
2722         if (FontUtilities.isLogging()) {
2723             FontUtilities.getLogger()
2724                 .info("Entered preferProportionalFonts().");
2725         }
2726         /* If no proportional fonts are configured, there's no need
2727          * to take any action.
2728          */
2729         if (!FontConfiguration.hasMonoToPropMap()) {
2730             return;
2731         }


2732         if (gPropPref == true) {
2733             return;
2734         }
2735         gPropPref = true;
2736         createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
2737         _usingAlternateComposites = true;















2738     }
2739 
2740     private static HashSet<String> installedNames = null;
2741     private static HashSet<String> getInstalledNames() {
2742         if (installedNames == null) {
2743            Locale l = getSystemStartupLocale();
2744            SunFontManager fontManager = SunFontManager.getInstance();
2745            String[] installedFamilies =
2746                fontManager.getInstalledFontFamilyNames(l);
2747            Font[] installedFonts = fontManager.getAllInstalledFonts();
2748            HashSet<String> names = new HashSet<String>();
2749            for (int i=0; i<installedFamilies.length; i++) {
2750                names.add(installedFamilies[i].toLowerCase(l));
2751            }
2752            for (int i=0; i<installedFonts.length; i++) {
2753                names.add(installedFonts[i].getFontName(l).toLowerCase(l));
2754            }
2755            installedNames = names;
2756         }
2757         return installedNames;
2758     }
2759 
2760     private static final Object regFamilyLock  = new Object();


2761     private Hashtable<String,FontFamily> createdByFamilyName;
2762     private Hashtable<String,Font2D>     createdByFullName;
2763     private boolean fontsAreRegistered = false;

2764 
2765     public boolean registerFont(Font font) {
2766         /* This method should not be called with "null".
2767          * It is the caller's responsibility to ensure that.
2768          */
2769         if (font == null) {
2770             return false;
2771         }
2772 
2773         /* Initialise these objects only once we start to use this API */
2774         synchronized (regFamilyLock) {
2775             if (createdByFamilyName == null) {
2776                 createdByFamilyName = new Hashtable<String,FontFamily>();
2777                 createdByFullName = new Hashtable<String,Font2D>();
2778             }
2779         }
2780 
2781         if (! FontAccess.getFontAccess().isCreatedFont(font)) {
2782             return false;
2783         }
2784         /* We want to ensure that this font cannot override existing
2785          * installed fonts. Check these conditions :
2786          * - family name is not that of an installed font
2787          * - full name is not that of an installed font
2788          * - family name is not the same as the full name of an installed font
2789          * - full name is not the same as the family name of an installed font
2790          * The last two of these may initially look odd but the reason is
2791          * that (unfortunately) Font constructors do not distinuguish these.
2792          * An extreme example of such a problem would be a font which has
2793          * family name "Dialog.Plain" and full name of "Dialog".
2794          * The one arguably overly stringent restriction here is that if an
2795          * application wants to supply a new member of an existing family
2796          * It will get rejected. But since the JRE can perform synthetic
2797          * styling in many cases its not necessary.
2798          * We don't apply the same logic to registered fonts. If apps want
2799          * to do this lets assume they have a reason. It won't cause problems
2800          * except for themselves.
2801          */
2802         HashSet<String> names = getInstalledNames();
2803         Locale l = getSystemStartupLocale();
2804         String familyName = font.getFamily(l).toLowerCase();
2805         String fullName = font.getFontName(l).toLowerCase();
2806         if (names.contains(familyName) || names.contains(fullName)) {
2807             return false;
2808         }
2809 
2810         /* Checks passed, now register the font */
2811         Hashtable<String, FontFamily> familyTable = createdByFamilyName;
2812         Hashtable<String, Font2D> fullNameTable = createdByFullName;



2813         fontsAreRegistered = true;
2814 


















2815         /* Create the FontFamily and add font to the tables */
2816         Font2D font2D = FontUtilities.getFont2D(font);
2817         int style = font2D.getStyle();
2818         FontFamily family = familyTable.get(familyName);
2819         if (family == null) {
2820             family = new FontFamily(font.getFamily(l));
2821             familyTable.put(familyName, family);
2822         }
2823         /* Remove name cache entries if not using app contexts.
2824          * To accommodate a case where code may have registered first a plain
2825          * family member and then used it and is now registering a bold family
2826          * member, we need to remove all members of the family, so that the
2827          * new style can get picked up rather than continuing to synthesise.
2828          */
2829         if (fontsAreRegistered) {
2830             removeFromCache(family.getFont(Font.PLAIN));
2831             removeFromCache(family.getFont(Font.BOLD));
2832             removeFromCache(family.getFont(Font.ITALIC));
2833             removeFromCache(family.getFont(Font.BOLD|Font.ITALIC));
2834             removeFromCache(fullNameTable.get(fullName));


2840 
2841     /* Remove from the name cache all references to the Font2D */
2842     private void removeFromCache(Font2D font) {
2843         if (font == null) {
2844             return;
2845         }
2846         String[] keys = fontNameCache.keySet().toArray(STR_ARRAY);
2847         for (int k=0; k<keys.length;k++) {
2848             if (fontNameCache.get(keys[k]) == font) {
2849                 fontNameCache.remove(keys[k]);
2850             }
2851         }
2852     }
2853 
2854     // It may look odd to use TreeMap but its more convenient to the caller.
2855     public TreeMap<String, String> getCreatedFontFamilyNames() {
2856 
2857         Hashtable<String,FontFamily> familyTable;
2858         if (fontsAreRegistered) {
2859             familyTable = createdByFamilyName;






2860         } else {
2861             return null;
2862         }
2863 
2864         Locale l = getSystemStartupLocale();
2865         synchronized (familyTable) {
2866             TreeMap<String, String> map = new TreeMap<String, String>();
2867             for (FontFamily f : familyTable.values()) {
2868                 Font2D font2D = f.getFont(Font.PLAIN);
2869                 if (font2D == null) {
2870                     font2D = f.getClosestStyle(Font.PLAIN);
2871                 }
2872                 String name = font2D.getFamilyName(l);
2873                 map.put(name.toLowerCase(l), name);
2874             }
2875             return map;
2876         }
2877     }
2878 
2879     public Font[] getCreatedFonts() {
2880 
2881         Hashtable<String,Font2D> nameTable;
2882         if (fontsAreRegistered) {
2883             nameTable = createdByFullName;






2884         } else {
2885             return null;
2886         }
2887 
2888         Locale l = getSystemStartupLocale();
2889         synchronized (nameTable) {
2890             Font[] fonts = new Font[nameTable.size()];
2891             int i=0;
2892             for (Font2D font2D : nameTable.values()) {
2893                 fonts[i++] = new Font(font2D.getFontName(l), Font.PLAIN, 1);
2894             }
2895             return fonts;
2896         }
2897     }
2898 
2899 
2900     protected String[] getPlatformFontDirs(boolean noType1Fonts) {
2901 
2902         /* First check if we already initialised path dirs */
2903         if (pathDirs != null) {


< prev index next >