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) {
|