Showing the changes to java/pkg/sun/font/SunFontManager.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,3912 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2008, 2011, 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 | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.font; | |
| 27 | + | |
| 28 | +import java.awt.Font; | |
| 29 | +import java.awt.FontFormatException; | |
| 30 | +import java.io.BufferedReader; | |
| 31 | +import java.io.File; | |
| 32 | +import java.io.FileInputStream; | |
| 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.Locale; | |
| 44 | +import java.util.Map; | |
| 45 | +import java.util.NoSuchElementException; | |
| 46 | +import java.util.StringTokenizer; | |
| 47 | +import java.util.TreeMap; | |
| 48 | +import java.util.Vector; | |
| 49 | +import java.util.concurrent.ConcurrentHashMap; | |
| 50 | + | |
| 51 | +import javax.swing.plaf.FontUIResource; | |
| 52 | +import sun.awt.AppContext; | |
| 53 | +import sun.awt.FontConfiguration; | |
| 54 | +import sun.awt.SunToolkit; | |
| 55 | +import sun.java2d.FontSupport; | |
| 56 | +import sun.util.logging.PlatformLogger; | |
| 57 | + | |
| 58 | +/** | |
| 59 | + * The base implementation of the {@link FontManager} interface. It implements | |
| 60 | + * the platform independent, shared parts of OpenJDK's FontManager | |
| 61 | + * implementations. The platform specific parts are declared as abstract | |
| 62 | + * methods that have to be implemented by specific implementations. | |
| 63 | + */ | |
| 64 | +public abstract class SunFontManager implements FontSupport, FontManagerForSGE { | |
| 65 | + | |
| 66 | + private static class TTFilter implements FilenameFilter { | |
| 67 | + public boolean accept(File dir,String name) { | |
| 68 | + /* all conveniently have the same suffix length */ | |
| 69 | + int offset = name.length()-4; | |
| 70 | + if (offset <= 0) { /* must be at least A.ttf */ | |
| 71 | + return false; | |
| 72 | + } else { | |
| 73 | + return(name.startsWith(".ttf", offset) || | |
| 74 | + name.startsWith(".TTF", offset) || | |
| 75 | + name.startsWith(".ttc", offset) || | |
| 76 | + name.startsWith(".TTC", offset) || | |
| 77 | + name.startsWith(".otf", offset) || | |
| 78 | + name.startsWith(".OTF", offset)); | |
| 79 | + } | |
| 80 | + } | |
| 81 | + } | |
| 82 | + | |
| 83 | + private static class T1Filter implements FilenameFilter { | |
| 84 | + public boolean accept(File dir,String name) { | |
| 85 | + if (noType1Font) { | |
| 86 | + return false; | |
| 87 | + } | |
| 88 | + /* all conveniently have the same suffix length */ | |
| 89 | + int offset = name.length()-4; | |
| 90 | + if (offset <= 0) { /* must be at least A.pfa */ | |
| 91 | + return false; | |
| 92 | + } else { | |
| 93 | + return(name.startsWith(".pfa", offset) || | |
| 94 | + name.startsWith(".pfb", offset) || | |
| 95 | + name.startsWith(".PFA", offset) || | |
| 96 | + name.startsWith(".PFB", offset)); | |
| 97 | + } | |
| 98 | + } | |
| 99 | + } | |
| 100 | + | |
| 101 | + private static class TTorT1Filter implements FilenameFilter { | |
| 102 | + public boolean accept(File dir, String name) { | |
| 103 | + | |
| 104 | + /* all conveniently have the same suffix length */ | |
| 105 | + int offset = name.length()-4; | |
| 106 | + if (offset <= 0) { /* must be at least A.ttf or A.pfa */ | |
| 107 | + return false; | |
| 108 | + } else { | |
| 109 | + boolean isTT = | |
| 110 | + name.startsWith(".ttf", offset) || | |
| 111 | + name.startsWith(".TTF", offset) || | |
| 112 | + name.startsWith(".ttc", offset) || | |
| 113 | + name.startsWith(".TTC", offset) || | |
| 114 | + name.startsWith(".otf", offset) || | |
| 115 | + name.startsWith(".OTF", offset); | |
| 116 | + if (isTT) { | |
| 117 | + return true; | |
| 118 | + } else if (noType1Font) { | |
| 119 | + return false; | |
| 120 | + } else { | |
| 121 | + return(name.startsWith(".pfa", offset) || | |
| 122 | + name.startsWith(".pfb", offset) || | |
| 123 | + name.startsWith(".PFA", offset) || | |
| 124 | + name.startsWith(".PFB", offset)); | |
| 125 | + } | |
| 126 | + } | |
| 127 | + } | |
| 128 | + } | |
| 129 | + | |
| 130 | + public static final int FONTFORMAT_NONE = -1; | |
| 131 | + public static final int FONTFORMAT_TRUETYPE = 0; | |
| 132 | + public static final int FONTFORMAT_TYPE1 = 1; | |
| 133 | + public static final int FONTFORMAT_T2K = 2; | |
| 134 | + public static final int FONTFORMAT_TTC = 3; | |
| 135 | + public static final int FONTFORMAT_COMPOSITE = 4; | |
| 136 | + public static final int FONTFORMAT_NATIVE = 5; | |
| 137 | + | |
| 138 | + /* Pool of 20 font file channels chosen because some UTF-8 locale | |
| 139 | + * composite fonts can use up to 16 platform fonts (including the | |
| 140 | + * Lucida fall back). This should prevent channel thrashing when | |
| 141 | + * dealing with one of these fonts. | |
| 142 | + * The pool array stores the fonts, rather than directly referencing | |
| 143 | + * the channels, as the font needs to do the open/close work. | |
| 144 | + */ | |
| 145 | + private static final int CHANNELPOOLSIZE = 20; | |
| 146 | + private int lastPoolIndex = 0; | |
| 147 | + private FileFont fontFileCache[] = new FileFont[CHANNELPOOLSIZE]; | |
| 148 | + | |
| 149 | + /* Need to implement a simple linked list scheme for fast | |
| 150 | + * traversal and lookup. | |
| 151 | + * Also want to "fast path" dialog so there's minimal overhead. | |
| 152 | + */ | |
| 153 | + /* There are at exactly 20 composite fonts: 5 faces (but some are not | |
| 154 | + * usually different), in 4 styles. The array may be auto-expanded | |
| 155 | + * later if more are needed, eg for user-defined composites or locale | |
| 156 | + * variants. | |
| 157 | + */ | |
| 158 | + private int maxCompFont = 0; | |
| 159 | + private CompositeFont [] compFonts = new CompositeFont[20]; | |
| 160 | + private ConcurrentHashMap<String, CompositeFont> | |
| 161 | + compositeFonts = new ConcurrentHashMap<String, CompositeFont>(); | |
| 162 | + private ConcurrentHashMap<String, PhysicalFont> | |
| 163 | + physicalFonts = new ConcurrentHashMap<String, PhysicalFont>(); | |
| 164 | + private ConcurrentHashMap<String, PhysicalFont> | |
| 165 | + registeredFonts = new ConcurrentHashMap<String, PhysicalFont>(); | |
| 166 | + | |
| 167 | + /* given a full name find the Font. Remind: there's duplication | |
| 168 | + * here in that this contains the content of compositeFonts + | |
| 169 | + * physicalFonts. | |
| 170 | + */ | |
| 171 | + private ConcurrentHashMap<String, Font2D> | |
| 172 | + fullNameToFont = new ConcurrentHashMap<String, Font2D>(); | |
| 173 | + | |
| 174 | + /* TrueType fonts have localised names. Support searching all | |
| 175 | + * of these before giving up on a name. | |
| 176 | + */ | |
| 177 | + private HashMap<String, TrueTypeFont> localeFullNamesToFont; | |
| 178 | + | |
| 179 | + private PhysicalFont defaultPhysicalFont; | |
| 180 | + | |
| 181 | + static boolean longAddresses; | |
| 182 | + private boolean loaded1dot0Fonts = false; | |
| 183 | + boolean loadedAllFonts = false; | |
| 184 | + boolean loadedAllFontFiles = false; | |
| 185 | + HashMap<String,String> jreFontMap; | |
| 186 | + HashSet<String> jreLucidaFontFiles; | |
| 187 | + String[] jreOtherFontFiles; | |
| 188 | + boolean noOtherJREFontFiles = false; // initial assumption. | |
| 189 | + | |
| 190 | + public static final String lucidaFontName = "Lucida Sans Regular"; | |
| 191 | + public static String jreLibDirName; | |
| 192 | + public static String jreFontDirName; | |
| 193 | + private static HashSet<String> missingFontFiles = null; | |
| 194 | + private String defaultFontName; | |
| 195 | + private String defaultFontFileName; | |
| 196 | + protected HashSet registeredFontFiles = new HashSet(); | |
| 197 | + | |
| 198 | + private ArrayList badFonts; | |
| 199 | + /* fontPath is the location of all fonts on the system, excluding the | |
| 200 | + * JRE's own font directory but including any path specified using the | |
| 201 | + * sun.java2d.fontpath property. Together with that property, it is | |
| 202 | + * initialised by the getPlatformFontPath() method | |
| 203 | + * This call must be followed by a call to registerFontDirs(fontPath) | |
| 204 | + * once any extra debugging path has been appended. | |
| 205 | + */ | |
| 206 | + protected String fontPath; | |
| 207 | + private FontConfiguration fontConfig; | |
| 208 | + /* discoveredAllFonts is set to true when all fonts on the font path are | |
| 209 | + * discovered. This usually also implies opening, validating and | |
| 210 | + * registering, but an implementation may be optimized to avold this. | |
| 211 | + * So see also "loadedAllFontFiles" | |
| 212 | + */ | |
| 213 | + private boolean discoveredAllFonts = false; | |
| 214 | + | |
| 215 | + /* No need to keep consing up new instances - reuse a singleton. | |
| 216 | + * The trade-off is that these objects don't get GC'd. | |
| 217 | + */ | |
| 218 | + private static final FilenameFilter ttFilter = new TTFilter(); | |
| 219 | + private static final FilenameFilter t1Filter = new T1Filter(); | |
| 220 | + | |
| 221 | + private Font[] allFonts; | |
| 222 | + private String[] allFamilies; // cache for default locale only | |
| 223 | + private Locale lastDefaultLocale; | |
| 224 | + | |
| 225 | + public static boolean noType1Font; | |
| 226 | + | |
| 227 | + /* Used to indicate required return type from toArray(..); */ | |
| 228 | + private static String[] STR_ARRAY = new String[0]; | |
| 229 | + | |
| 230 | + /** | |
| 231 | + * Deprecated, unsupported hack - actually invokes a bug! | |
| 232 | + * Left in for a customer, don't remove. | |
| 233 | + */ | |
| 234 | + private boolean usePlatformFontMetrics = false; | |
| 235 | + | |
| 236 | + /** | |
| 237 | + * Returns the global SunFontManager instance. This is similar to | |
| 238 | + * {@link FontManagerFactory#getInstance()} but it returns a | |
| 239 | + * SunFontManager instance instead. This is only used in internal classes | |
| 240 | + * where we can safely assume that a SunFontManager is to be used. | |
| 241 | + * | |
| 242 | + * @return the global SunFontManager instance | |
| 243 | + */ | |
| 244 | + public static SunFontManager getInstance() { | |
| 245 | + FontManager fm = FontManagerFactory.getInstance(); | |
| 246 | + return (SunFontManager) fm; | |
| 247 | + } | |
| 248 | + | |
| 249 | + public FilenameFilter getTrueTypeFilter() { | |
| 250 | + return ttFilter; | |
| 251 | + } | |
| 252 | + | |
| 253 | + public FilenameFilter getType1Filter() { | |
| 254 | + return t1Filter; | |
| 255 | + } | |
| 256 | + | |
| 257 | + @Override | |
| 258 | + public boolean usingPerAppContextComposites() { | |
| 259 | + return _usingPerAppContextComposites; | |
| 260 | + } | |
| 261 | + | |
| 262 | + private void initJREFontMap() { | |
| 263 | + | |
| 264 | + /* Key is familyname+style value as an int. | |
| 265 | + * Value is filename containing the font. | |
| 266 | + * If no mapping exists, it means there is no font file for the style | |
| 267 | + * If the mapping exists but the file doesn't exist in the deferred | |
| 268 | + * list then it means its not installed. | |
| 269 | + * This looks like a lot of code and strings but if it saves even | |
| 270 | + * a single file being opened at JRE start-up there's a big payoff. | |
| 271 | + * Lucida Sans is probably the only important case as the others | |
| 272 | + * are rarely used. Consider removing the other mappings if there's | |
| 273 | + * no evidence they are useful in practice. | |
| 274 | + */ | |
| 275 | + jreFontMap = new HashMap<String,String>(); | |
| 276 | + jreLucidaFontFiles = new HashSet<String>(); | |
| 277 | + if (isOpenJDK()) { | |
| 278 | + return; | |
| 279 | + } | |
| 280 | + /* Lucida Sans Family */ | |
| 281 | + jreFontMap.put("lucida sans0", "LucidaSansRegular.ttf"); | |
| 282 | + jreFontMap.put("lucida sans1", "LucidaSansDemiBold.ttf"); | |
| 283 | + /* Lucida Sans full names (map Bold and DemiBold to same file) */ | |
| 284 | + jreFontMap.put("lucida sans regular0", "LucidaSansRegular.ttf"); | |
| 285 | + jreFontMap.put("lucida sans regular1", "LucidaSansDemiBold.ttf"); | |
| 286 | + jreFontMap.put("lucida sans bold1", "LucidaSansDemiBold.ttf"); | |
| 287 | + jreFontMap.put("lucida sans demibold1", "LucidaSansDemiBold.ttf"); | |
| 288 | + | |
| 289 | + /* Lucida Sans Typewriter Family */ | |
| 290 | + jreFontMap.put("lucida sans typewriter0", | |
| 291 | + "LucidaTypewriterRegular.ttf"); | |
| 292 | + jreFontMap.put("lucida sans typewriter1", "LucidaTypewriterBold.ttf"); | |
| 293 | + /* Typewriter full names (map Bold and DemiBold to same file) */ | |
| 294 | + jreFontMap.put("lucida sans typewriter regular0", | |
| 295 | + "LucidaTypewriter.ttf"); | |
| 296 | + jreFontMap.put("lucida sans typewriter regular1", | |
| 297 | + "LucidaTypewriterBold.ttf"); | |
| 298 | + jreFontMap.put("lucida sans typewriter bold1", | |
| 299 | + "LucidaTypewriterBold.ttf"); | |
| 300 | + jreFontMap.put("lucida sans typewriter demibold1", | |
| 301 | + "LucidaTypewriterBold.ttf"); | |
| 302 | + | |
| 303 | + /* Lucida Bright Family */ | |
| 304 | + jreFontMap.put("lucida bright0", "LucidaBrightRegular.ttf"); | |
| 305 | + jreFontMap.put("lucida bright1", "LucidaBrightDemiBold.ttf"); | |
| 306 | + jreFontMap.put("lucida bright2", "LucidaBrightItalic.ttf"); | |
| 307 | + jreFontMap.put("lucida bright3", "LucidaBrightDemiItalic.ttf"); | |
| 308 | + /* Lucida Bright full names (map Bold and DemiBold to same file) */ | |
| 309 | + jreFontMap.put("lucida bright regular0", "LucidaBrightRegular.ttf"); | |
| 310 | + jreFontMap.put("lucida bright regular1", "LucidaBrightDemiBold.ttf"); | |
| 311 | + jreFontMap.put("lucida bright regular2", "LucidaBrightItalic.ttf"); | |
| 312 | + jreFontMap.put("lucida bright regular3", "LucidaBrightDemiItalic.ttf"); | |
| 313 | + jreFontMap.put("lucida bright bold1", "LucidaBrightDemiBold.ttf"); | |
| 314 | + jreFontMap.put("lucida bright bold3", "LucidaBrightDemiItalic.ttf"); | |
| 315 | + jreFontMap.put("lucida bright demibold1", "LucidaBrightDemiBold.ttf"); | |
| 316 | + jreFontMap.put("lucida bright demibold3","LucidaBrightDemiItalic.ttf"); | |
| 317 | + jreFontMap.put("lucida bright italic2", "LucidaBrightItalic.ttf"); | |
| 318 | + jreFontMap.put("lucida bright italic3", "LucidaBrightDemiItalic.ttf"); | |
| 319 | + jreFontMap.put("lucida bright bold italic3", | |
| 320 | + "LucidaBrightDemiItalic.ttf"); | |
| 321 | + jreFontMap.put("lucida bright demibold italic3", | |
| 322 | + "LucidaBrightDemiItalic.ttf"); | |
| 323 | + for (String ffile : jreFontMap.values()) { | |
| 324 | + jreLucidaFontFiles.add(ffile); | |
| 325 | + } | |
| 326 | + } | |
| 327 | + | |
| 328 | + static { | |
| 329 | + | |
| 330 | + java.security.AccessController.doPrivileged( | |
| 331 | + new java.security.PrivilegedAction() { | |
| 332 | + | |
| 333 | + public Object run() { | |
| 334 | + FontManagerNativeLibrary.load(); | |
| 335 | + | |
| 336 | + // JNI throws an exception if a class/method/field is not found, | |
| 337 | + // so there's no need to do anything explicit here. | |
| 338 | + initIDs(); | |
| 339 | + | |
| 340 | + switch (StrikeCache.nativeAddressSize) { | |
| 341 | + case 8: longAddresses = true; break; | |
| 342 | + case 4: longAddresses = false; break; | |
| 343 | + default: throw new RuntimeException("Unexpected address size"); | |
| 344 | + } | |
| 345 | + | |
| 346 | + noType1Font = | |
| 347 | + "true".equals(System.getProperty("sun.java2d.noType1Font")); | |
| 348 | + jreLibDirName = | |
| 349 | + System.getProperty("java.home","") + File.separator + "lib"; | |
| 350 | + jreFontDirName = jreLibDirName + File.separator + "fonts"; | |
| 351 | + File lucidaFile = | |
| 352 | + new File(jreFontDirName + File.separator + FontUtilities.LUCIDA_FILE_NAME); | |
| 353 | + | |
| 354 | + return null; | |
| 355 | + } | |
| 356 | + }); | |
| 357 | + } | |
| 358 | + | |
| 359 | + public TrueTypeFont getEUDCFont() { | |
| 360 | + // Overridden in Windows. | |
| 361 | + return null; | |
| 362 | + } | |
| 363 | + | |
| 364 | + /* Initialise ptrs used by JNI methods */ | |
| 365 | + private static native void initIDs(); | |
| 366 | + | |
| 367 | + @SuppressWarnings("unchecked") | |
| 368 | + protected SunFontManager() { | |
| 369 | + | |
| 370 | + initJREFontMap(); | |
| 371 | + java.security.AccessController.doPrivileged( | |
| 372 | + new java.security.PrivilegedAction() { | |
| 373 | + public Object run() { | |
| 374 | + File badFontFile = | |
| 375 | + new File(jreFontDirName + File.separator + | |
| 376 | + "badfonts.txt"); | |
| 377 | + if (badFontFile.exists()) { | |
| 378 | + FileInputStream fis = null; | |
| 379 | + try { | |
| 380 | + badFonts = new ArrayList(); | |
| 381 | + fis = new FileInputStream(badFontFile); | |
| 382 | + InputStreamReader isr = new InputStreamReader(fis); | |
| 383 | + BufferedReader br = new BufferedReader(isr); | |
| 384 | + while (true) { | |
| 385 | + String name = br.readLine(); | |
| 386 | + if (name == null) { | |
| 387 | + break; | |
| 388 | + } else { | |
| 389 | + if (FontUtilities.debugFonts()) { | |
| 390 | + FontUtilities.getLogger().warning("read bad font: " + | |
| 391 | + name); | |
| 392 | + } | |
| 393 | + badFonts.add(name); | |
| 394 | + } | |
| 395 | + } | |
| 396 | + } catch (IOException e) { | |
| 397 | + try { | |
| 398 | + if (fis != null) { | |
| 399 | + fis.close(); | |
| 400 | + } | |
| 401 | + } catch (IOException ioe) { | |
| 402 | + } | |
| 403 | + } | |
| 404 | + } | |
| 405 | + | |
| 406 | + /* Here we get the fonts in jre/lib/fonts and register | |
| 407 | + * them so they are always available and preferred over | |
| 408 | + * other fonts. This needs to be registered before the | |
| 409 | + * composite fonts as otherwise some native font that | |
| 410 | + * corresponds may be found as we don't have a way to | |
| 411 | + * handle two fonts of the same name, so the JRE one | |
| 412 | + * must be the first one registered. Pass "true" to | |
| 413 | + * registerFonts method as on-screen these JRE fonts | |
| 414 | + * always go through the T2K rasteriser. | |
| 415 | + */ | |
| 416 | + if (FontUtilities.isLinux) { | |
| 417 | + /* Linux font configuration uses these fonts */ | |
| 418 | + registerFontDir(jreFontDirName); | |
| 419 | + } | |
| 420 | + registerFontsInDir(jreFontDirName, true, Font2D.JRE_RANK, | |
| 421 | + true, false); | |
| 422 | + | |
| 423 | + /* Create the font configuration and get any font path | |
| 424 | + * that might be specified. | |
| 425 | + */ | |
| 426 | + fontConfig = createFontConfiguration(); | |
| 427 | + if (isOpenJDK()) { | |
| 428 | + String[] fontInfo = getDefaultPlatformFont(); | |
| 429 | + defaultFontName = fontInfo[0]; | |
| 430 | + defaultFontFileName = fontInfo[1]; | |
| 431 | + } | |
| 432 | + | |
| 433 | + String extraFontPath = fontConfig.getExtraFontPath(); | |
| 434 | + | |
| 435 | + /* In prior releases the debugging font path replaced | |
| 436 | + * all normally located font directories except for the | |
| 437 | + * JRE fonts dir. This directory is still always located | |
| 438 | + * and placed at the head of the path but as an | |
| 439 | + * augmentation to the previous behaviour the | |
| 440 | + * changes below allow you to additionally append to | |
| 441 | + * the font path by starting with append: or prepend by | |
| 442 | + * starting with a prepend: sign. Eg: to append | |
| 443 | + * -Dsun.java2d.fontpath=append:/usr/local/myfonts | |
| 444 | + * and to prepend | |
| 445 | + * -Dsun.java2d.fontpath=prepend:/usr/local/myfonts Disp | |
| 446 | + * | |
| 447 | + * If there is an appendedfontpath it in the font | |
| 448 | + * configuration it is used instead of searching the | |
| 449 | + * system for dirs. | |
| 450 | + * The behaviour of append and prepend is then similar | |
| 451 | + * to the normal case. ie it goes after what | |
| 452 | + * you prepend and * before what you append. If the | |
| 453 | + * sun.java2d.fontpath property is used, but it | |
| 454 | + * neither the append or prepend syntaxes is used then | |
| 455 | + * as except for the JRE dir the path is replaced and it | |
| 456 | + * is up to you to make sure that all the right | |
| 457 | + * directories are located. This is platform and | |
| 458 | + * locale-specific so its almost impossible to get | |
| 459 | + * right, so it should be used with caution. | |
| 460 | + */ | |
| 461 | + boolean prependToPath = false; | |
| 462 | + boolean appendToPath = false; | |
| 463 | + String dbgFontPath = | |
| 464 | + System.getProperty("sun.java2d.fontpath"); | |
| 465 | + | |
| 466 | + if (dbgFontPath != null) { | |
| 467 | + if (dbgFontPath.startsWith("prepend:")) { | |
| 468 | + prependToPath = true; | |
| 469 | + dbgFontPath = | |
| 470 | + dbgFontPath.substring("prepend:".length()); | |
| 471 | + } else if (dbgFontPath.startsWith("append:")) { | |
| 472 | + appendToPath = true; | |
| 473 | + dbgFontPath = | |
| 474 | + dbgFontPath.substring("append:".length()); | |
| 475 | + } | |
| 476 | + } | |
| 477 | + | |
| 478 | + if (FontUtilities.debugFonts()) { | |
| 479 | + PlatformLogger logger = FontUtilities.getLogger(); | |
| 480 | + logger.info("JRE font directory: " + jreFontDirName); | |
| 481 | + logger.info("Extra font path: " + extraFontPath); | |
| 482 | + logger.info("Debug font path: " + dbgFontPath); | |
| 483 | + } | |
| 484 | + | |
| 485 | + if (dbgFontPath != null) { | |
| 486 | + /* In debugging mode we register all the paths | |
| 487 | + * Caution: this is a very expensive call on Solaris:- | |
| 488 | + */ | |
| 489 | + fontPath = getPlatformFontPath(noType1Font); | |
| 490 | + | |
| 491 | + if (extraFontPath != null) { | |
| 492 | + fontPath = | |
| 493 | + extraFontPath + File.pathSeparator + fontPath; | |
| 494 | + } | |
| 495 | + if (appendToPath) { | |
| 496 | + fontPath = | |
| 497 | + fontPath + File.pathSeparator + dbgFontPath; | |
| 498 | + } else if (prependToPath) { | |
| 499 | + fontPath = | |
| 500 | + dbgFontPath + File.pathSeparator + fontPath; | |
| 501 | + } else { | |
| 502 | + fontPath = dbgFontPath; | |
| 503 | + } | |
| 504 | + registerFontDirs(fontPath); | |
| 505 | + } else if (extraFontPath != null) { | |
| 506 | + /* If the font configuration contains an | |
| 507 | + * "appendedfontpath" entry, it is interpreted as a | |
| 508 | + * set of locations that should always be registered. | |
| 509 | + * It may be additional to locations normally found | |
| 510 | + * for that place, or it may be locations that need | |
| 511 | + * to have all their paths registered to locate all | |
| 512 | + * the needed platform names. | |
| 513 | + * This is typically when the same .TTF file is | |
| 514 | + * referenced from multiple font.dir files and all | |
| 515 | + * of these must be read to find all the native | |
| 516 | + * (XLFD) names for the font, so that X11 font APIs | |
| 517 | + * can be used for as many code points as possible. | |
| 518 | + */ | |
| 519 | + registerFontDirs(extraFontPath); | |
| 520 | + } | |
| 521 | + | |
| 522 | + /* On Solaris, we need to register the Japanese TrueType | |
| 523 | + * directory so that we can find the corresponding | |
| 524 | + * bitmap fonts. This could be done by listing the | |
| 525 | + * directory in the font configuration file, but we | |
| 526 | + * don't want to confuse users with this quirk. There | |
| 527 | + * are no bitmap fonts for other writing systems that | |
| 528 | + * correspond to TrueType fonts and have matching XLFDs. | |
| 529 | + * We need to register the bitmap fonts only in | |
| 530 | + * environments where they're on the X font path, i.e., | |
| 531 | + * in the Japanese locale. Note that if the X Toolkit | |
| 532 | + * is in use the font path isn't set up by JDK, but | |
| 533 | + * users of a JA locale should have it | |
| 534 | + * set up already by their login environment. | |
| 535 | + */ | |
| 536 | + if (FontUtilities.isSolaris && Locale.JAPAN.equals(Locale.getDefault())) { | |
| 537 | + registerFontDir("/usr/openwin/lib/locale/ja/X11/fonts/TT"); | |
| 538 | + } | |
| 539 | + | |
| 540 | + initCompositeFonts(fontConfig, null); | |
| 541 | + | |
| 542 | + return null; | |
| 543 | + } | |
| 544 | + }); | |
| 545 | + | |
| 546 | + boolean platformFont = AccessController.doPrivileged( | |
| 547 | + new PrivilegedAction<Boolean>() { | |
| 548 | + public Boolean run() { | |
| 549 | + String prop = | |
| 550 | + System.getProperty("java2d.font.usePlatformFont"); | |
| 551 | + String env = System.getenv("JAVA2D_USEPLATFORMFONT"); | |
| 552 | + return "true".equals(prop) || env != null; | |
| 553 | + } | |
| 554 | + }); | |
| 555 | + | |
| 556 | + if (platformFont) { | |
| 557 | + usePlatformFontMetrics = true; | |
| 558 | + System.out.println("Enabling platform font metrics for win32. This is an unsupported option."); | |
| 559 | + System.out.println("This yields incorrect composite font metrics as reported by 1.1.x releases."); | |
| 560 | + System.out.println("It is appropriate only for use by applications which do not use any Java 2"); | |
| 561 | + System.out.println("functionality. This property will be removed in a later release."); | |
| 562 | + } | |
| 563 | + } | |
| 564 | + | |
| 565 | + /** | |
| 566 | + * This method is provided for internal and exclusive use by Swing. | |
| 567 | + * | |
| 568 | + * @param font representing a physical font. | |
| 569 | + * @return true if the underlying font is a TrueType or OpenType font | |
| 570 | + * that claims to support the Microsoft Windows encoding corresponding to | |
| 571 | + * the default file.encoding property of this JRE instance. | |
| 572 | + * This narrow value is useful for Swing to decide if the font is useful | |
| 573 | + * for the the Windows Look and Feel, or, if a composite font should be | |
| 574 | + * used instead. | |
| 575 | + * The information used to make the decision is obtained from | |
| 576 | + * the ulCodePageRange fields in the font. | |
| 577 | + * A caller can use isLogicalFont(Font) in this class before calling | |
| 578 | + * this method and would not need to call this method if that | |
| 579 | + * returns true. | |
| 580 | + */ | |
| 581 | +// static boolean fontSupportsDefaultEncoding(Font font) { | |
| 582 | +// String encoding = | |
| 583 | +// (String) java.security.AccessController.doPrivileged( | |
| 584 | +// new sun.security.action.GetPropertyAction("file.encoding")); | |
| 585 | + | |
| 586 | +// if (encoding == null || font == null) { | |
| 587 | +// return false; | |
| 588 | +// } | |
| 589 | + | |
| 590 | +// encoding = encoding.toLowerCase(Locale.ENGLISH); | |
| 591 | + | |
| 592 | +// return FontManager.fontSupportsEncoding(font, encoding); | |
| 593 | +// } | |
| 594 | + | |
| 595 | + public Font2DHandle getNewComposite(String family, int style, | |
| 596 | + Font2DHandle handle) { | |
| 597 | + | |
| 598 | + if (!(handle.font2D instanceof CompositeFont)) { | |
| 599 | + return handle; | |
| 600 | + } | |
| 601 | + | |
| 602 | + CompositeFont oldComp = (CompositeFont)handle.font2D; | |
| 603 | + PhysicalFont oldFont = oldComp.getSlotFont(0); | |
| 604 | + | |
| 605 | + if (family == null) { | |
| 606 | + family = oldFont.getFamilyName(null); | |
| 607 | + } | |
| 608 | + if (style == -1) { | |
| 609 | + style = oldComp.getStyle(); | |
| 610 | + } | |
| 611 | + | |
| 612 | + Font2D newFont = findFont2D(family, style, NO_FALLBACK); | |
| 613 | + if (!(newFont instanceof PhysicalFont)) { | |
| 614 | + newFont = oldFont; | |
| 615 | + } | |
| 616 | + PhysicalFont physicalFont = (PhysicalFont)newFont; | |
| 617 | + CompositeFont dialog2D = | |
| 618 | + (CompositeFont)findFont2D("dialog", style, NO_FALLBACK); | |
| 619 | + if (dialog2D == null) { /* shouldn't happen */ | |
| 620 | + return handle; | |
| 621 | + } | |
| 622 | + CompositeFont compFont = new CompositeFont(physicalFont, dialog2D); | |
| 623 | + Font2DHandle newHandle = new Font2DHandle(compFont); | |
| 624 | + return newHandle; | |
| 625 | + } | |
| 626 | + | |
| 627 | + protected void registerCompositeFont(String compositeName, | |
| 628 | + String[] componentFileNames, | |
| 629 | + String[] componentNames, | |
| 630 | + int numMetricsSlots, | |
| 631 | + int[] exclusionRanges, | |
| 632 | + int[] exclusionMaxIndex, | |
| 633 | + boolean defer) { | |
| 634 | + | |
| 635 | + CompositeFont cf = new CompositeFont(compositeName, | |
| 636 | + componentFileNames, | |
| 637 | + componentNames, | |
| 638 | + numMetricsSlots, | |
| 639 | + exclusionRanges, | |
| 640 | + exclusionMaxIndex, defer, this); | |
| 641 | + addCompositeToFontList(cf, Font2D.FONT_CONFIG_RANK); | |
| 642 | + synchronized (compFonts) { | |
| 643 | + compFonts[maxCompFont++] = cf; | |
| 644 | + } | |
| 645 | + } | |
| 646 | + | |
| 647 | + /* This variant is used only when the application specifies | |
| 648 | + * a variant of composite fonts which prefers locale specific or | |
| 649 | + * proportional fonts. | |
| 650 | + */ | |
| 651 | + protected static void registerCompositeFont(String compositeName, | |
| 652 | + String[] componentFileNames, | |
| 653 | + String[] componentNames, | |
| 654 | + int numMetricsSlots, | |
| 655 | + int[] exclusionRanges, | |
| 656 | + int[] exclusionMaxIndex, | |
| 657 | + boolean defer, | |
| 658 | + ConcurrentHashMap<String, Font2D> | |
| 659 | + altNameCache) { | |
| 660 | + | |
| 661 | + CompositeFont cf = new CompositeFont(compositeName, | |
| 662 | + componentFileNames, | |
| 663 | + componentNames, | |
| 664 | + numMetricsSlots, | |
| 665 | + exclusionRanges, | |
| 666 | + exclusionMaxIndex, defer, | |
| 667 | + SunFontManager.getInstance()); | |
| 668 | + | |
| 669 | + /* if the cache has an existing composite for this case, make | |
| 670 | + * its handle point to this new font. | |
| 671 | + * This ensures that when the altNameCache that is passed in | |
| 672 | + * is the global mapNameCache - ie we are running as an application - | |
| 673 | + * that any statically created java.awt.Font instances which already | |
| 674 | + * have a Font2D instance will have that re-directed to the new Font | |
| 675 | + * on subsequent uses. This is particularly important for "the" | |
| 676 | + * default font instance, or similar cases where a UI toolkit (eg | |
| 677 | + * Swing) has cached a java.awt.Font. Note that if Swing is using | |
| 678 | + * a custom composite APIs which update the standard composites have | |
| 679 | + * no effect - this is typically the case only when using the Windows | |
| 680 | + * L&F where these APIs would conflict with that L&F anyway. | |
| 681 | + */ | |
| 682 | + Font2D oldFont = (Font2D) | |
| 683 | + altNameCache.get(compositeName.toLowerCase(Locale.ENGLISH)); | |
| 684 | + if (oldFont instanceof CompositeFont) { | |
| 685 | + oldFont.handle.font2D = cf; | |
| 686 | + } | |
| 687 | + altNameCache.put(compositeName.toLowerCase(Locale.ENGLISH), cf); | |
| 688 | + } | |
| 689 | + | |
| 690 | + private void addCompositeToFontList(CompositeFont f, int rank) { | |
| 691 | + | |
| 692 | + if (FontUtilities.isLogging()) { | |
| 693 | + FontUtilities.getLogger().info("Add to Family "+ f.familyName + | |
| 694 | + ", Font " + f.fullName + " rank="+rank); | |
| 695 | + } | |
| 696 | + f.setRank(rank); | |
| 697 | + compositeFonts.put(f.fullName, f); | |
| 698 | + fullNameToFont.put(f.fullName.toLowerCase(Locale.ENGLISH), f); | |
| 699 | + | |
| 700 | + FontFamily family = FontFamily.getFamily(f.familyName); | |
| 701 | + if (family == null) { | |
| 702 | + family = new FontFamily(f.familyName, true, rank); | |
| 703 | + } | |
| 704 | + family.setFont(f, f.style); | |
| 705 | + } | |
| 706 | + | |
| 707 | + /* | |
| 708 | + * Systems may have fonts with the same name. | |
| 709 | + * We want to register only one of such fonts (at least until | |
| 710 | + * such time as there might be APIs which can accommodate > 1). | |
| 711 | + * Rank is 1) font configuration fonts, 2) JRE fonts, 3) OT/TT fonts, | |
| 712 | + * 4) Type1 fonts, 5) native fonts. | |
| 713 | + * | |
| 714 | + * If the new font has the same name as the old font, the higher | |
| 715 | + * ranked font gets added, replacing the lower ranked one. | |
| 716 | + * If the fonts are of equal rank, then make a special case of | |
| 717 | + * font configuration rank fonts, which are on closer inspection, | |
| 718 | + * OT/TT fonts such that the larger font is registered. This is | |
| 719 | + * a heuristic since a font may be "larger" in the sense of more | |
| 720 | + * code points, or be a larger "file" because it has more bitmaps. | |
| 721 | + * So it is possible that using filesize may lead to less glyphs, and | |
| 722 | + * using glyphs may lead to lower quality display. Probably number | |
| 723 | + * of glyphs is the ideal, but filesize is information we already | |
| 724 | + * have and is good enough for the known cases. | |
| 725 | + * Also don't want to register fonts that match JRE font families | |
| 726 | + * but are coming from a source other than the JRE. | |
| 727 | + * This will ensure that we will algorithmically style the JRE | |
| 728 | + * plain font and get the same set of glyphs for all styles. | |
| 729 | + * | |
| 730 | + * Note that this method returns a value | |
| 731 | + * if it returns the same object as its argument that means this | |
| 732 | + * font was newly registered. | |
| 733 | + * If it returns a different object it means this font already exists, | |
| 734 | + * and you should use that one. | |
| 735 | + * If it returns null means this font was not registered and none | |
| 736 | + * in that name is registered. The caller must find a substitute | |
| 737 | + */ | |
| 738 | + private PhysicalFont addToFontList(PhysicalFont f, int rank) { | |
| 739 | + | |
| 740 | + String fontName = f.fullName; | |
| 741 | + String familyName = f.familyName; | |
| 742 | + if (fontName == null || "".equals(fontName)) { | |
| 743 | + return null; | |
| 744 | + } | |
| 745 | + if (compositeFonts.containsKey(fontName)) { | |
| 746 | + /* Don't register any font that has the same name as a composite */ | |
| 747 | + return null; | |
| 748 | + } | |
| 749 | + f.setRank(rank); | |
| 750 | + if (!physicalFonts.containsKey(fontName)) { | |
| 751 | + if (FontUtilities.isLogging()) { | |
| 752 | + FontUtilities.getLogger().info("Add to Family "+familyName + | |
| 753 | + ", Font " + fontName + " rank="+rank); | |
| 754 | + } | |
| 755 | + physicalFonts.put(fontName, f); | |
| 756 | + FontFamily family = FontFamily.getFamily(familyName); | |
| 757 | + if (family == null) { | |
| 758 | + family = new FontFamily(familyName, false, rank); | |
| 759 | + family.setFont(f, f.style); | |
| 760 | + } else if (family.getRank() >= rank) { | |
| 761 | + family.setFont(f, f.style); | |
| 762 | + } | |
| 763 | + fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH), f); | |
| 764 | + return f; | |
| 765 | + } else { | |
| 766 | + PhysicalFont newFont = f; | |
| 767 | + PhysicalFont oldFont = physicalFonts.get(fontName); | |
| 768 | + if (oldFont == null) { | |
| 769 | + return null; | |
| 770 | + } | |
| 771 | + /* If the new font is of an equal or higher rank, it is a | |
| 772 | + * candidate to replace the current one, subject to further tests. | |
| 773 | + */ | |
| 774 | + if (oldFont.getRank() >= rank) { | |
| 775 | + | |
| 776 | + /* All fonts initialise their mapper when first | |
| 777 | + * used. If the mapper is non-null then this font | |
| 778 | + * has been accessed at least once. In that case | |
| 779 | + * do not replace it. This may be overly stringent, | |
| 780 | + * but its probably better not to replace a font that | |
| 781 | + * someone is already using without a compelling reason. | |
| 782 | + * Additionally the primary case where it is known | |
| 783 | + * this behaviour is important is in certain composite | |
| 784 | + * fonts, and since all the components of a given | |
| 785 | + * composite are usually initialised together this | |
| 786 | + * is unlikely. For this to be a problem, there would | |
| 787 | + * have to be a case where two different composites used | |
| 788 | + * different versions of the same-named font, and they | |
| 789 | + * were initialised and used at separate times. | |
| 790 | + * In that case we continue on and allow the new font to | |
| 791 | + * be installed, but replaceFont will continue to allow | |
| 792 | + * the original font to be used in Composite fonts. | |
| 793 | + */ | |
| 794 | + if (oldFont.mapper != null && rank > Font2D.FONT_CONFIG_RANK) { | |
| 795 | + return oldFont; | |
| 796 | + } | |
| 797 | + | |
| 798 | + /* Normally we require a higher rank to replace a font, | |
| 799 | + * but as a special case, if the two fonts are the same rank, | |
| 800 | + * and are instances of TrueTypeFont we want the | |
| 801 | + * more complete (larger) one. | |
| 802 | + */ | |
| 803 | + if (oldFont.getRank() == rank) { | |
| 804 | + if (oldFont instanceof TrueTypeFont && | |
| 805 | + newFont instanceof TrueTypeFont) { | |
| 806 | + TrueTypeFont oldTTFont = (TrueTypeFont)oldFont; | |
| 807 | + TrueTypeFont newTTFont = (TrueTypeFont)newFont; | |
| 808 | + if (oldTTFont.fileSize >= newTTFont.fileSize) { | |
| 809 | + return oldFont; | |
| 810 | + } | |
| 811 | + } else { | |
| 812 | + return oldFont; | |
| 813 | + } | |
| 814 | + } | |
| 815 | + /* Don't replace ever JRE fonts. | |
| 816 | + * This test is in case a font configuration references | |
| 817 | + * a Lucida font, which has been mapped to a Lucida | |
| 818 | + * from the host O/S. The assumption here is that any | |
| 819 | + * such font configuration file is probably incorrect, or | |
| 820 | + * the host O/S version is for the use of AWT. | |
| 821 | + * In other words if we reach here, there's a possible | |
| 822 | + * problem with our choice of font configuration fonts. | |
| 823 | + */ | |
| 824 | + if (oldFont.platName.startsWith(jreFontDirName)) { | |
| 825 | + if (FontUtilities.isLogging()) { | |
| 826 | + FontUtilities.getLogger() | |
| 827 | + .warning("Unexpected attempt to replace a JRE " + | |
| 828 | + " font " + fontName + " from " + | |
| 829 | + oldFont.platName + | |
| 830 | + " with " + newFont.platName); | |
| 831 | + } | |
| 832 | + return oldFont; | |
| 833 | + } | |
| 834 | + | |
| 835 | + if (FontUtilities.isLogging()) { | |
| 836 | + FontUtilities.getLogger() | |
| 837 | + .info("Replace in Family " + familyName + | |
| 838 | + ",Font " + fontName + " new rank="+rank + | |
| 839 | + " from " + oldFont.platName + | |
| 840 | + " with " + newFont.platName); | |
| 841 | + } | |
| 842 | + replaceFont(oldFont, newFont); | |
| 843 | + physicalFonts.put(fontName, newFont); | |
| 844 | + fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH), | |
| 845 | + newFont); | |
| 846 | + | |
| 847 | + FontFamily family = FontFamily.getFamily(familyName); | |
| 848 | + if (family == null) { | |
| 849 | + family = new FontFamily(familyName, false, rank); | |
| 850 | + family.setFont(newFont, newFont.style); | |
| 851 | + } else if (family.getRank() >= rank) { | |
| 852 | + family.setFont(newFont, newFont.style); | |
| 853 | + } | |
| 854 | + return newFont; | |
| 855 | + } else { | |
| 856 | + return oldFont; | |
| 857 | + } | |
| 858 | + } | |
| 859 | + } | |
| 860 | + | |
| 861 | + public Font2D[] getRegisteredFonts() { | |
| 862 | + PhysicalFont[] physFonts = getPhysicalFonts(); | |
| 863 | + int mcf = maxCompFont; /* for MT-safety */ | |
| 864 | + Font2D[] regFonts = new Font2D[physFonts.length+mcf]; | |
| 865 | + System.arraycopy(compFonts, 0, regFonts, 0, mcf); | |
| 866 | + System.arraycopy(physFonts, 0, regFonts, mcf, physFonts.length); | |
| 867 | + return regFonts; | |
| 868 | + } | |
| 869 | + | |
| 870 | + protected PhysicalFont[] getPhysicalFonts() { | |
| 871 | + return physicalFonts.values().toArray(new PhysicalFont[0]); | |
| 872 | + } | |
| 873 | + | |
| 874 | + | |
| 875 | + /* The class FontRegistrationInfo is used when a client says not | |
| 876 | + * to register a font immediately. This mechanism is used to defer | |
| 877 | + * initialisation of all the components of composite fonts at JRE | |
| 878 | + * start-up. The CompositeFont class is "aware" of this and when it | |
| 879 | + * is first used it asks for the registration of its components. | |
| 880 | + * Also in the event that any physical font is requested the | |
| 881 | + * deferred fonts are initialised before triggering a search of the | |
| 882 | + * system. | |
| 883 | + * Two maps are used. One to track the deferred fonts. The | |
| 884 | + * other to track the fonts that have been initialised through this | |
| 885 | + * mechanism. | |
| 886 | + */ | |
| 887 | + | |
| 888 | + private static final class FontRegistrationInfo { | |
| 889 | + | |
| 890 | + String fontFilePath; | |
| 891 | + String[] nativeNames; | |
| 892 | + int fontFormat; | |
| 893 | + boolean javaRasterizer; | |
| 894 | + int fontRank; | |
| 895 | + | |
| 896 | + FontRegistrationInfo(String fontPath, String[] names, int format, | |
| 897 | + boolean useJavaRasterizer, int rank) { | |
| 898 | + this.fontFilePath = fontPath; | |
| 899 | + this.nativeNames = names; | |
| 900 | + this.fontFormat = format; | |
| 901 | + this.javaRasterizer = useJavaRasterizer; | |
| 902 | + this.fontRank = rank; | |
| 903 | + } | |
| 904 | + } | |
| 905 | + | |
| 906 | + private final ConcurrentHashMap<String, FontRegistrationInfo> | |
| 907 | + deferredFontFiles = | |
| 908 | + new ConcurrentHashMap<String, FontRegistrationInfo>(); | |
| 909 | + private final ConcurrentHashMap<String, Font2DHandle> | |
| 910 | + initialisedFonts = new ConcurrentHashMap<String, Font2DHandle>(); | |
| 911 | + | |
| 912 | + /* Remind: possibly enhance initialiseDeferredFonts() to be | |
| 913 | + * optionally given a name and a style and it could stop when it | |
| 914 | + * finds that font - but this would be a problem if two of the | |
| 915 | + * fonts reference the same font face name (cf the Solaris | |
| 916 | + * euro fonts). | |
| 917 | + */ | |
| 918 | + protected synchronized void initialiseDeferredFonts() { | |
| 919 | + for (String fileName : deferredFontFiles.keySet()) { | |
| 920 | + initialiseDeferredFont(fileName); | |
| 921 | + } | |
| 922 | + } | |
| 923 | + | |
| 924 | + protected synchronized void registerDeferredJREFonts(String jreDir) { | |
| 925 | + for (FontRegistrationInfo info : deferredFontFiles.values()) { | |
| 926 | + if (info.fontFilePath != null && | |
| 927 | + info.fontFilePath.startsWith(jreDir)) { | |
| 928 | + initialiseDeferredFont(info.fontFilePath); | |
| 929 | + } | |
| 930 | + } | |
| 931 | + } | |
| 932 | + | |
| 933 | + public boolean isDeferredFont(String fileName) { | |
| 934 | + return deferredFontFiles.containsKey(fileName); | |
| 935 | + } | |
| 936 | + | |
| 937 | + /* We keep a map of the files which contain the Lucida fonts so we | |
| 938 | + * don't need to search for them. | |
| 939 | + * But since we know what fonts these files contain, we can also avoid | |
| 940 | + * opening them to look for a font name we don't recognise - see | |
| 941 | + * findDeferredFont(). | |
| 942 | + * For typical cases where the font isn't a JRE one the overhead is | |
| 943 | + * this method call, HashMap.get() and null reference test, then | |
| 944 | + * a boolean test of noOtherJREFontFiles. | |
| 945 | + */ | |
| 946 | + public | |
| 947 | + /*private*/ PhysicalFont findJREDeferredFont(String name, int style) { | |
| 948 | + | |
| 949 | + PhysicalFont physicalFont; | |
| 950 | + String nameAndStyle = name.toLowerCase(Locale.ENGLISH) + style; | |
| 951 | + String fileName = jreFontMap.get(nameAndStyle); | |
| 952 | + if (fileName != null) { | |
| 953 | + fileName = jreFontDirName + File.separator + fileName; | |
| 954 | + if (deferredFontFiles.get(fileName) != null) { | |
| 955 | + physicalFont = initialiseDeferredFont(fileName); | |
| 956 | + if (physicalFont != null && | |
| 957 | + (physicalFont.getFontName(null).equalsIgnoreCase(name) || | |
| 958 | + physicalFont.getFamilyName(null).equalsIgnoreCase(name)) | |
| 959 | + && physicalFont.style == style) { | |
| 960 | + return physicalFont; | |
| 961 | + } | |
| 962 | + } | |
| 963 | + } | |
| 964 | + | |
| 965 | + /* Iterate over the deferred font files looking for any in the | |
| 966 | + * jre directory that we didn't recognise, open each of these. | |
| 967 | + * In almost all installations this will quickly fall through | |
| 968 | + * because only the Lucidas will be present and jreOtherFontFiles | |
| 969 | + * will be empty. | |
| 970 | + * noOtherJREFontFiles is used so we can skip this block as soon | |
| 971 | + * as its determined that its not needed - almost always after the | |
| 972 | + * very first time through. | |
| 973 | + */ | |
| 974 | + if (noOtherJREFontFiles) { | |
| 975 | + return null; | |
| 976 | + } | |
| 977 | + synchronized (jreLucidaFontFiles) { | |
| 978 | + if (jreOtherFontFiles == null) { | |
| 979 | + HashSet<String> otherFontFiles = new HashSet<String>(); | |
| 980 | + for (String deferredFile : deferredFontFiles.keySet()) { | |
| 981 | + File file = new File(deferredFile); | |
| 982 | + String dir = file.getParent(); | |
| 983 | + String fname = file.getName(); | |
| 984 | + /* skip names which aren't absolute, aren't in the JRE | |
| 985 | + * directory, or are known Lucida fonts. | |
| 986 | + */ | |
| 987 | + if (dir == null || | |
| 988 | + !dir.equals(jreFontDirName) || | |
| 989 | + jreLucidaFontFiles.contains(fname)) { | |
| 990 | + continue; | |
| 991 | + } | |
| 992 | + otherFontFiles.add(deferredFile); | |
| 993 | + } | |
| 994 | + jreOtherFontFiles = otherFontFiles.toArray(STR_ARRAY); | |
| 995 | + if (jreOtherFontFiles.length == 0) { | |
| 996 | + noOtherJREFontFiles = true; | |
| 997 | + } | |
| 998 | + } | |
| 999 | + | |
| 1000 | + for (int i=0; i<jreOtherFontFiles.length;i++) { | |
| 1001 | + fileName = jreOtherFontFiles[i]; | |
| 1002 | + if (fileName == null) { | |
| 1003 | + continue; | |
| 1004 | + } | |
| 1005 | + jreOtherFontFiles[i] = null; | |
| 1006 | + physicalFont = initialiseDeferredFont(fileName); | |
| 1007 | + if (physicalFont != null && | |
| 1008 | + (physicalFont.getFontName(null).equalsIgnoreCase(name) || | |
| 1009 | + physicalFont.getFamilyName(null).equalsIgnoreCase(name)) | |
| 1010 | + && physicalFont.style == style) { | |
| 1011 | + return physicalFont; | |
| 1012 | + } | |
| 1013 | + } | |
| 1014 | + } | |
| 1015 | + | |
| 1016 | + return null; | |
| 1017 | + } | |
| 1018 | + | |
| 1019 | + /* This skips JRE installed fonts. */ | |
| 1020 | + private PhysicalFont findOtherDeferredFont(String name, int style) { | |
| 1021 | + for (String fileName : deferredFontFiles.keySet()) { | |
| 1022 | + File file = new File(fileName); | |
| 1023 | + String dir = file.getParent(); | |
| 1024 | + String fname = file.getName(); | |
| 1025 | + if (dir != null && | |
| 1026 | + dir.equals(jreFontDirName) && | |
| 1027 | + jreLucidaFontFiles.contains(fname)) { | |
| 1028 | + continue; | |
| 1029 | + } | |
| 1030 | + PhysicalFont physicalFont = initialiseDeferredFont(fileName); | |
| 1031 | + if (physicalFont != null && | |
| 1032 | + (physicalFont.getFontName(null).equalsIgnoreCase(name) || | |
| 1033 | + physicalFont.getFamilyName(null).equalsIgnoreCase(name)) && | |
| 1034 | + physicalFont.style == style) { | |
| 1035 | + return physicalFont; | |
| 1036 | + } | |
| 1037 | + } | |
| 1038 | + return null; | |
| 1039 | + } | |
| 1040 | + | |
| 1041 | + private PhysicalFont findDeferredFont(String name, int style) { | |
| 1042 | + | |
| 1043 | + PhysicalFont physicalFont = findJREDeferredFont(name, style); | |
| 1044 | + if (physicalFont != null) { | |
| 1045 | + return physicalFont; | |
| 1046 | + } else { | |
| 1047 | + return findOtherDeferredFont(name, style); | |
| 1048 | + } | |
| 1049 | + } | |
| 1050 | + | |
| 1051 | + public void registerDeferredFont(String fileNameKey, | |
| 1052 | + String fullPathName, | |
| 1053 | + String[] nativeNames, | |
| 1054 | + int fontFormat, | |
| 1055 | + boolean useJavaRasterizer, | |
| 1056 | + int fontRank) { | |
| 1057 | + FontRegistrationInfo regInfo = | |
| 1058 | + new FontRegistrationInfo(fullPathName, nativeNames, fontFormat, | |
| 1059 | + useJavaRasterizer, fontRank); | |
| 1060 | + deferredFontFiles.put(fileNameKey, regInfo); | |
| 1061 | + } | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + public synchronized | |
| 1065 | + PhysicalFont initialiseDeferredFont(String fileNameKey) { | |
| 1066 | + | |
| 1067 | + if (fileNameKey == null) { | |
| 1068 | + return null; | |
| 1069 | + } | |
| 1070 | + if (FontUtilities.isLogging()) { | |
| 1071 | + FontUtilities.getLogger() | |
| 1072 | + .info("Opening deferred font file " + fileNameKey); | |
| 1073 | + } | |
| 1074 | + | |
| 1075 | + PhysicalFont physicalFont; | |
| 1076 | + FontRegistrationInfo regInfo = deferredFontFiles.get(fileNameKey); | |
| 1077 | + if (regInfo != null) { | |
| 1078 | + deferredFontFiles.remove(fileNameKey); | |
| 1079 | + physicalFont = registerFontFile(regInfo.fontFilePath, | |
| 1080 | + regInfo.nativeNames, | |
| 1081 | + regInfo.fontFormat, | |
| 1082 | + regInfo.javaRasterizer, | |
| 1083 | + regInfo.fontRank); | |
| 1084 | + | |
| 1085 | + | |
| 1086 | + if (physicalFont != null) { | |
| 1087 | + /* Store the handle, so that if a font is bad, we | |
| 1088 | + * retrieve the substituted font. | |
| 1089 | + */ | |
| 1090 | + initialisedFonts.put(fileNameKey, physicalFont.handle); | |
| 1091 | + } else { | |
| 1092 | + initialisedFonts.put(fileNameKey, | |
| 1093 | + getDefaultPhysicalFont().handle); | |
| 1094 | + } | |
| 1095 | + } else { | |
| 1096 | + Font2DHandle handle = initialisedFonts.get(fileNameKey); | |
| 1097 | + if (handle == null) { | |
| 1098 | + /* Probably shouldn't happen, but just in case */ | |
| 1099 | + physicalFont = getDefaultPhysicalFont(); | |
| 1100 | + } else { | |
| 1101 | + physicalFont = (PhysicalFont)(handle.font2D); | |
| 1102 | + } | |
| 1103 | + } | |
| 1104 | + return physicalFont; | |
| 1105 | + } | |
| 1106 | + | |
| 1107 | + public boolean isRegisteredFontFile(String name) { | |
| 1108 | + return registeredFonts.containsKey(name); | |
| 1109 | + } | |
| 1110 | + | |
| 1111 | + public PhysicalFont getRegisteredFontFile(String name) { | |
| 1112 | + return registeredFonts.get(name); | |
| 1113 | + } | |
| 1114 | + | |
| 1115 | + /* Note that the return value from this method is not always | |
| 1116 | + * derived from this file, and may be null. See addToFontList for | |
| 1117 | + * some explanation of this. | |
| 1118 | + */ | |
| 1119 | + public PhysicalFont registerFontFile(String fileName, | |
| 1120 | + String[] nativeNames, | |
| 1121 | + int fontFormat, | |
| 1122 | + boolean useJavaRasterizer, | |
| 1123 | + int fontRank) { | |
| 1124 | + | |
| 1125 | + PhysicalFont regFont = registeredFonts.get(fileName); | |
| 1126 | + if (regFont != null) { | |
| 1127 | + return regFont; | |
| 1128 | + } | |
| 1129 | + | |
| 1130 | + PhysicalFont physicalFont = null; | |
| 1131 | + try { | |
| 1132 | + String name; | |
| 1133 | + | |
| 1134 | + switch (fontFormat) { | |
| 1135 | + | |
| 1136 | + case FONTFORMAT_TRUETYPE: | |
| 1137 | + int fn = 0; | |
| 1138 | + TrueTypeFont ttf; | |
| 1139 | + do { | |
| 1140 | + ttf = new TrueTypeFont(fileName, nativeNames, fn++, | |
| 1141 | + useJavaRasterizer); | |
| 1142 | + PhysicalFont pf = addToFontList(ttf, fontRank); | |
| 1143 | + if (physicalFont == null) { | |
| 1144 | + physicalFont = pf; | |
| 1145 | + } | |
| 1146 | + } | |
| 1147 | + while (fn < ttf.getFontCount()); | |
| 1148 | + break; | |
| 1149 | + | |
| 1150 | + case FONTFORMAT_TYPE1: | |
| 1151 | + Type1Font t1f = new Type1Font(fileName, nativeNames); | |
| 1152 | + physicalFont = addToFontList(t1f, fontRank); | |
| 1153 | + break; | |
| 1154 | + | |
| 1155 | + case FONTFORMAT_NATIVE: | |
| 1156 | + NativeFont nf = new NativeFont(fileName, false); | |
| 1157 | + physicalFont = addToFontList(nf, fontRank); | |
| 1158 | + default: | |
| 1159 | + | |
| 1160 | + } | |
| 1161 | + if (FontUtilities.isLogging()) { | |
| 1162 | + FontUtilities.getLogger() | |
| 1163 | + .info("Registered file " + fileName + " as font " + | |
| 1164 | + physicalFont + " rank=" + fontRank); | |
| 1165 | + } | |
| 1166 | + } catch (FontFormatException ffe) { | |
| 1167 | + if (FontUtilities.isLogging()) { | |
| 1168 | + FontUtilities.getLogger().warning("Unusable font: " + | |
| 1169 | + fileName + " " + ffe.toString()); | |
| 1170 | + } | |
| 1171 | + } | |
| 1172 | + if (physicalFont != null && | |
| 1173 | + fontFormat != FONTFORMAT_NATIVE) { | |
| 1174 | + registeredFonts.put(fileName, physicalFont); | |
| 1175 | + } | |
| 1176 | + return physicalFont; | |
| 1177 | + } | |
| 1178 | + | |
| 1179 | + public void registerFonts(String[] fileNames, | |
| 1180 | + String[][] nativeNames, | |
| 1181 | + int fontCount, | |
| 1182 | + int fontFormat, | |
| 1183 | + boolean useJavaRasterizer, | |
| 1184 | + int fontRank, boolean defer) { | |
| 1185 | + | |
| 1186 | + for (int i=0; i < fontCount; i++) { | |
| 1187 | + if (defer) { | |
| 1188 | + registerDeferredFont(fileNames[i],fileNames[i], nativeNames[i], | |
| 1189 | + fontFormat, useJavaRasterizer, fontRank); | |
| 1190 | + } else { | |
| 1191 | + registerFontFile(fileNames[i], nativeNames[i], | |
| 1192 | + fontFormat, useJavaRasterizer, fontRank); | |
| 1193 | + } | |
| 1194 | + } | |
| 1195 | + } | |
| 1196 | + | |
| 1197 | + /* | |
| 1198 | + * This is the Physical font used when some other font on the system | |
| 1199 | + * can't be located. There has to be at least one font or the font | |
| 1200 | + * system is not useful and the graphics environment cannot sustain | |
| 1201 | + * the Java platform. | |
| 1202 | + */ | |
| 1203 | + public PhysicalFont getDefaultPhysicalFont() { | |
| 1204 | + if (defaultPhysicalFont == null) { | |
| 1205 | + /* findFont2D will load all fonts before giving up the search. | |
| 1206 | + * If the JRE Lucida isn't found (eg because the JRE fonts | |
| 1207 | + * directory is missing), it could find another version of Lucida | |
| 1208 | + * from the host system. This is OK because at that point we are | |
| 1209 | + * trying to gracefully handle/recover from a system | |
| 1210 | + * misconfiguration and this is probably a reasonable substitution. | |
| 1211 | + */ | |
| 1212 | + defaultPhysicalFont = (PhysicalFont) | |
| 1213 | + findFont2D("Lucida Sans Regular", Font.PLAIN, NO_FALLBACK); | |
| 1214 | + if (defaultPhysicalFont == null) { | |
| 1215 | + defaultPhysicalFont = (PhysicalFont) | |
| 1216 | + findFont2D("Arial", Font.PLAIN, NO_FALLBACK); | |
| 1217 | + } | |
| 1218 | + if (defaultPhysicalFont == null) { | |
| 1219 | + /* Because of the findFont2D call above, if we reach here, we | |
| 1220 | + * know all fonts have already been loaded, just accept any | |
| 1221 | + * match at this point. If this fails we are in real trouble | |
| 1222 | + * and I don't know how to recover from there being absolutely | |
| 1223 | + * no fonts anywhere on the system. | |
| 1224 | + */ | |
| 1225 | + Iterator i = physicalFonts.values().iterator(); | |
| 1226 | + if (i.hasNext()) { | |
| 1227 | + defaultPhysicalFont = (PhysicalFont)i.next(); | |
| 1228 | + } else { | |
| 1229 | + throw new Error("Probable fatal error:No fonts found."); | |
| 1230 | + } | |
| 1231 | + } | |
| 1232 | + } | |
| 1233 | + return defaultPhysicalFont; | |
| 1234 | + } | |
| 1235 | + | |
| 1236 | + public CompositeFont getDefaultLogicalFont(int style) { | |
| 1237 | + return (CompositeFont)findFont2D("dialog", style, NO_FALLBACK); | |
| 1238 | + } | |
| 1239 | + | |
| 1240 | + /* | |
| 1241 | + * return String representation of style prepended with "." | |
| 1242 | + * This is useful for performance to avoid unnecessary string operations. | |
| 1243 | + */ | |
| 1244 | + private static String dotStyleStr(int num) { | |
| 1245 | + switch(num){ | |
| 1246 | + case Font.BOLD: | |
| 1247 | + return ".bold"; | |
| 1248 | + case Font.ITALIC: | |
| 1249 | + return ".italic"; | |
| 1250 | + case Font.ITALIC | Font.BOLD: | |
| 1251 | + return ".bolditalic"; | |
| 1252 | + default: | |
| 1253 | + return ".plain"; | |
| 1254 | + } | |
| 1255 | + } | |
| 1256 | + | |
| 1257 | + /* This is implemented only on windows and is called from code that | |
| 1258 | + * executes only on windows. This isn't pretty but its not a precedent | |
| 1259 | + * in this file. This very probably should be cleaned up at some point. | |
| 1260 | + */ | |
| 1261 | + protected void | |
| 1262 | + populateFontFileNameMap(HashMap<String,String> fontToFileMap, | |
| 1263 | + HashMap<String,String> fontToFamilyNameMap, | |
| 1264 | + HashMap<String,ArrayList<String>> | |
| 1265 | + familyToFontListMap, | |
| 1266 | + Locale locale) { | |
| 1267 | + } | |
| 1268 | + | |
| 1269 | + /* Obtained from Platform APIs (windows only) | |
| 1270 | + * Map from lower-case font full name to basename of font file. | |
| 1271 | + * Eg "arial bold" -> ARIALBD.TTF. | |
| 1272 | + * For TTC files, there is a mapping for each font in the file. | |
| 1273 | + */ | |
| 1274 | + private HashMap<String,String> fontToFileMap = null; | |
| 1275 | + | |
| 1276 | + /* Obtained from Platform APIs (windows only) | |
| 1277 | + * Map from lower-case font full name to the name of its font family | |
| 1278 | + * Eg "arial bold" -> "Arial" | |
| 1279 | + */ | |
| 1280 | + private HashMap<String,String> fontToFamilyNameMap = null; | |
| 1281 | + | |
| 1282 | + /* Obtained from Platform APIs (windows only) | |
| 1283 | + * Map from a lower-case family name to a list of full names of | |
| 1284 | + * the member fonts, eg: | |
| 1285 | + * "arial" -> ["Arial", "Arial Bold", "Arial Italic","Arial Bold Italic"] | |
| 1286 | + */ | |
| 1287 | + private HashMap<String,ArrayList<String>> familyToFontListMap= null; | |
| 1288 | + | |
| 1289 | + /* The directories which contain platform fonts */ | |
| 1290 | + private String[] pathDirs = null; | |
| 1291 | + | |
| 1292 | + private boolean haveCheckedUnreferencedFontFiles; | |
| 1293 | + | |
| 1294 | + private String[] getFontFilesFromPath(boolean noType1) { | |
| 1295 | + final FilenameFilter filter; | |
| 1296 | + if (noType1) { | |
| 1297 | + filter = ttFilter; | |
| 1298 | + } else { | |
| 1299 | + filter = new TTorT1Filter(); | |
| 1300 | + } | |
| 1301 | + return (String[])AccessController.doPrivileged(new PrivilegedAction() { | |
| 1302 | + public Object run() { | |
| 1303 | + if (pathDirs.length == 1) { | |
| 1304 | + File dir = new File(pathDirs[0]); | |
| 1305 | + String[] files = dir.list(filter); | |
| 1306 | + if (files == null) { | |
| 1307 | + return new String[0]; | |
| 1308 | + } | |
| 1309 | + for (int f=0; f<files.length; f++) { | |
| 1310 | + files[f] = files[f].toLowerCase(); | |
| 1311 | + } | |
| 1312 | + return files; | |
| 1313 | + } else { | |
| 1314 | + ArrayList<String> fileList = new ArrayList<String>(); | |
| 1315 | + for (int i = 0; i< pathDirs.length; i++) { | |
| 1316 | + File dir = new File(pathDirs[i]); | |
| 1317 | + String[] files = dir.list(filter); | |
| 1318 | + if (files == null) { | |
| 1319 | + continue; | |
| 1320 | + } | |
| 1321 | + for (int f=0; f<files.length ; f++) { | |
| 1322 | + fileList.add(files[f].toLowerCase()); | |
| 1323 | + } | |
| 1324 | + } | |
| 1325 | + return fileList.toArray(STR_ARRAY); | |
| 1326 | + } | |
| 1327 | + } | |
| 1328 | + }); | |
| 1329 | + } | |
| 1330 | + | |
| 1331 | + /* This is needed since some windows registry names don't match | |
| 1332 | + * the font names. | |
| 1333 | + * - UPC styled font names have a double space, but the | |
| 1334 | + * registry entry mapping to a file doesn't. | |
| 1335 | + * - Marlett is in a hidden file not listed in the registry | |
| 1336 | + * - The registry advertises that the file david.ttf contains a | |
| 1337 | + * font with the full name "David Regular" when in fact its | |
| 1338 | + * just "David". | |
| 1339 | + * Directly fix up these known cases as this is faster. | |
| 1340 | + * If a font which doesn't match these known cases has no file, | |
| 1341 | + * it may be a font that has been temporarily added to the known set | |
| 1342 | + * or it may be an installed font with a missing registry entry. | |
| 1343 | + * Installed fonts are those in the windows font directories. | |
| 1344 | + * Make a best effort attempt to locate these. | |
| 1345 | + * We obtain the list of TrueType fonts in these directories and | |
| 1346 | + * filter out all the font files we already know about from the registry. | |
| 1347 | + * What remains may be "bad" fonts, duplicate fonts, or perhaps the | |
| 1348 | + * missing font(s) we are looking for. | |
| 1349 | + * Open each of these files to find out. | |
| 1350 | + */ | |
| 1351 | + private void resolveWindowsFonts() { | |
| 1352 | + | |
| 1353 | + ArrayList<String> unmappedFontNames = null; | |
| 1354 | + for (String font : fontToFamilyNameMap.keySet()) { | |
| 1355 | + String file = fontToFileMap.get(font); | |
| 1356 | + if (file == null) { | |
| 1357 | + if (font.indexOf(" ") > 0) { | |
| 1358 | + String newName = font.replaceFirst(" ", " "); | |
| 1359 | + file = fontToFileMap.get(newName); | |
| 1360 | + /* If this name exists and isn't for a valid name | |
| 1361 | + * replace the mapping to the file with this font | |
| 1362 | + */ | |
| 1363 | + if (file != null && | |
| 1364 | + !fontToFamilyNameMap.containsKey(newName)) { | |
| 1365 | + fontToFileMap.remove(newName); | |
| 1366 | + fontToFileMap.put(font, file); | |
| 1367 | + } | |
| 1368 | + } else if (font.equals("marlett")) { | |
| 1369 | + fontToFileMap.put(font, "marlett.ttf"); | |
| 1370 | + } else if (font.equals("david")) { | |
| 1371 | + file = fontToFileMap.get("david regular"); | |
| 1372 | + if (file != null) { | |
| 1373 | + fontToFileMap.remove("david regular"); | |
| 1374 | + fontToFileMap.put("david", file); | |
| 1375 | + } | |
| 1376 | + } else { | |
| 1377 | + if (unmappedFontNames == null) { | |
| 1378 | + unmappedFontNames = new ArrayList<String>(); | |
| 1379 | + } | |
| 1380 | + unmappedFontNames.add(font); | |
| 1381 | + } | |
| 1382 | + } | |
| 1383 | + } | |
| 1384 | + | |
| 1385 | + if (unmappedFontNames != null) { | |
| 1386 | + HashSet<String> unmappedFontFiles = new HashSet<String>(); | |
| 1387 | + | |
| 1388 | + /* Every font key in fontToFileMap ought to correspond to a | |
| 1389 | + * font key in fontToFamilyNameMap. Entries that don't seem | |
| 1390 | + * to correspond are likely fonts that were named differently | |
| 1391 | + * by GDI than in the registry. One known cause of this is when | |
| 1392 | + * Windows has had its regional settings changed so that from | |
| 1393 | + * GDI we get a localised (eg Chinese or Japanese) name for the | |
| 1394 | + * font, but the registry retains the English version of the name | |
| 1395 | + * that corresponded to the "install" locale for windows. | |
| 1396 | + * Since we are in this code block because there are unmapped | |
| 1397 | + * font names, we can look to find unused font->file mappings | |
| 1398 | + * and then open the files to read the names. We don't generally | |
| 1399 | + * want to open font files, as its a performance hit, but this | |
| 1400 | + * occurs only for a small number of fonts on specific system | |
| 1401 | + * configs - ie is believed that a "true" Japanese windows would | |
| 1402 | + * have JA names in the registry too. | |
| 1403 | + * Clone fontToFileMap and remove from the clone all keys which | |
| 1404 | + * match a fontToFamilyNameMap key. What remains maps to the | |
| 1405 | + * files we want to open to find the fonts GDI returned. | |
| 1406 | + * A font in such a file is added to the fontToFileMap after | |
| 1407 | + * checking its one of the unmappedFontNames we are looking for. | |
| 1408 | + * The original name that didn't map is removed from fontToFileMap | |
| 1409 | + * so essentially this "fixes up" fontToFileMap to use the same | |
| 1410 | + * name as GDI. | |
| 1411 | + * Also note that typically the fonts for which this occurs in | |
| 1412 | + * CJK locales are TTC fonts and not all fonts in a TTC may have | |
| 1413 | + * localised names. Eg MSGOTHIC.TTC contains 3 fonts and one of | |
| 1414 | + * them "MS UI Gothic" has no JA name whereas the other two do. | |
| 1415 | + * So not every font in these files is unmapped or new. | |
| 1416 | + */ | |
| 1417 | + HashMap<String,String> ffmapCopy = | |
| 1418 | + (HashMap<String,String>)(fontToFileMap.clone()); | |
| 1419 | + for (String key : fontToFamilyNameMap.keySet()) { | |
| 1420 | + ffmapCopy.remove(key); | |
| 1421 | + } | |
| 1422 | + for (String key : ffmapCopy.keySet()) { | |
| 1423 | + unmappedFontFiles.add(ffmapCopy.get(key)); | |
| 1424 | + fontToFileMap.remove(key); | |
| 1425 | + } | |
| 1426 | + | |
| 1427 | + resolveFontFiles(unmappedFontFiles, unmappedFontNames); | |
| 1428 | + | |
| 1429 | + /* If there are still unmapped font names, this means there's | |
| 1430 | + * something that wasn't in the registry. We need to get all | |
| 1431 | + * the font files directly and look at the ones that weren't | |
| 1432 | + * found in the registry. | |
| 1433 | + */ | |
| 1434 | + if (unmappedFontNames.size() > 0) { | |
| 1435 | + | |
| 1436 | + /* getFontFilesFromPath() returns all lower case names. | |
| 1437 | + * To compare we also need lower case | |
| 1438 | + * versions of the names from the registry. | |
| 1439 | + */ | |
| 1440 | + ArrayList<String> registryFiles = new ArrayList<String>(); | |
| 1441 | + | |
| 1442 | + for (String regFile : fontToFileMap.values()) { | |
| 1443 | + registryFiles.add(regFile.toLowerCase()); | |
| 1444 | + } | |
| 1445 | + /* We don't look for Type1 files here as windows will | |
| 1446 | + * not enumerate these, so aren't useful in reconciling | |
| 1447 | + * GDI's unmapped files. We do find these later when | |
| 1448 | + * we enumerate all fonts. | |
| 1449 | + */ | |
| 1450 | + for (String pathFile : getFontFilesFromPath(true)) { | |
| 1451 | + if (!registryFiles.contains(pathFile)) { | |
| 1452 | + unmappedFontFiles.add(pathFile); | |
| 1453 | + } | |
| 1454 | + } | |
| 1455 | + | |
| 1456 | + resolveFontFiles(unmappedFontFiles, unmappedFontNames); | |
| 1457 | + } | |
| 1458 | + | |
| 1459 | + /* remove from the set of names that will be returned to the | |
| 1460 | + * user any fonts that can't be mapped to files. | |
| 1461 | + */ | |
| 1462 | + if (unmappedFontNames.size() > 0) { | |
| 1463 | + int sz = unmappedFontNames.size(); | |
| 1464 | + for (int i=0; i<sz; i++) { | |
| 1465 | + String name = unmappedFontNames.get(i); | |
| 1466 | + String familyName = fontToFamilyNameMap.get(name); | |
| 1467 | + if (familyName != null) { | |
| 1468 | + ArrayList family = familyToFontListMap.get(familyName); | |
| 1469 | + if (family != null) { | |
| 1470 | + if (family.size() <= 1) { | |
| 1471 | + familyToFontListMap.remove(familyName); | |
| 1472 | + } | |
| 1473 | + } | |
| 1474 | + } | |
| 1475 | + fontToFamilyNameMap.remove(name); | |
| 1476 | + if (FontUtilities.isLogging()) { | |
| 1477 | + FontUtilities.getLogger() | |
| 1478 | + .info("No file for font:" + name); | |
| 1479 | + } | |
| 1480 | + } | |
| 1481 | + } | |
| 1482 | + } | |
| 1483 | + } | |
| 1484 | + | |
| 1485 | + /** | |
| 1486 | + * In some cases windows may have fonts in the fonts folder that | |
| 1487 | + * don't show up in the registry or in the GDI calls to enumerate fonts. | |
| 1488 | + * The only way to find these is to list the directory. We invoke this | |
| 1489 | + * only in getAllFonts/Families, so most searches for a specific | |
| 1490 | + * font that is satisfied by the GDI/registry calls don't take the | |
| 1491 | + * additional hit of listing the directory. This hit is small enough | |
| 1492 | + * that its not significant in these 'enumerate all the fonts' cases. | |
| 1493 | + * The basic approach is to cross-reference the files windows found | |
| 1494 | + * with the ones in the directory listing approach, and for each | |
| 1495 | + * in the latter list that is missing from the former list, register it. | |
| 1496 | + */ | |
| 1497 | + private synchronized void checkForUnreferencedFontFiles() { | |
| 1498 | + if (haveCheckedUnreferencedFontFiles) { | |
| 1499 | + return; | |
| 1500 | + } | |
| 1501 | + haveCheckedUnreferencedFontFiles = true; | |
| 1502 | + if (!FontUtilities.isWindows) { | |
| 1503 | + return; | |
| 1504 | + } | |
| 1505 | + /* getFontFilesFromPath() returns all lower case names. | |
| 1506 | + * To compare we also need lower case | |
| 1507 | + * versions of the names from the registry. | |
| 1508 | + */ | |
| 1509 | + ArrayList<String> registryFiles = new ArrayList<String>(); | |
| 1510 | + for (String regFile : fontToFileMap.values()) { | |
| 1511 | + registryFiles.add(regFile.toLowerCase()); | |
| 1512 | + } | |
| 1513 | + | |
| 1514 | + /* To avoid any issues with concurrent modification, create | |
| 1515 | + * copies of the existing maps, add the new fonts into these | |
| 1516 | + * and then replace the references to the old ones with the | |
| 1517 | + * new maps. ConcurrentHashmap is another option but its a lot | |
| 1518 | + * more changes and with this exception, these maps are intended | |
| 1519 | + * to be static. | |
| 1520 | + */ | |
| 1521 | + HashMap<String,String> fontToFileMap2 = null; | |
| 1522 | + HashMap<String,String> fontToFamilyNameMap2 = null; | |
| 1523 | + HashMap<String,ArrayList<String>> familyToFontListMap2 = null;; | |
| 1524 | + | |
| 1525 | + for (String pathFile : getFontFilesFromPath(false)) { | |
| 1526 | + if (!registryFiles.contains(pathFile)) { | |
| 1527 | + if (FontUtilities.isLogging()) { | |
| 1528 | + FontUtilities.getLogger() | |
| 1529 | + .info("Found non-registry file : " + pathFile); | |
| 1530 | + } | |
| 1531 | + PhysicalFont f = registerFontFile(getPathName(pathFile)); | |
| 1532 | + if (f == null) { | |
| 1533 | + continue; | |
| 1534 | + } | |
| 1535 | + if (fontToFileMap2 == null) { | |
| 1536 | + fontToFileMap2 = new HashMap<String,String>(fontToFileMap); | |
| 1537 | + fontToFamilyNameMap2 = | |
| 1538 | + new HashMap<String,String>(fontToFamilyNameMap); | |
| 1539 | + familyToFontListMap2 = new | |
| 1540 | + HashMap<String,ArrayList<String>>(familyToFontListMap); | |
| 1541 | + } | |
| 1542 | + String fontName = f.getFontName(null); | |
| 1543 | + String family = f.getFamilyName(null); | |
| 1544 | + String familyLC = family.toLowerCase(); | |
| 1545 | + fontToFamilyNameMap2.put(fontName, family); | |
| 1546 | + fontToFileMap2.put(fontName, pathFile); | |
| 1547 | + ArrayList<String> fonts = familyToFontListMap2.get(familyLC); | |
| 1548 | + if (fonts == null) { | |
| 1549 | + fonts = new ArrayList<String>(); | |
| 1550 | + } else { | |
| 1551 | + fonts = new ArrayList<String>(fonts); | |
| 1552 | + } | |
| 1553 | + fonts.add(fontName); | |
| 1554 | + familyToFontListMap2.put(familyLC, fonts); | |
| 1555 | + } | |
| 1556 | + } | |
| 1557 | + if (fontToFileMap2 != null) { | |
| 1558 | + fontToFileMap = fontToFileMap2; | |
| 1559 | + familyToFontListMap = familyToFontListMap2; | |
| 1560 | + fontToFamilyNameMap = fontToFamilyNameMap2; | |
| 1561 | + } | |
| 1562 | + } | |
| 1563 | + | |
| 1564 | + private void resolveFontFiles(HashSet<String> unmappedFiles, | |
| 1565 | + ArrayList<String> unmappedFonts) { | |
| 1566 | + | |
| 1567 | + Locale l = SunToolkit.getStartupLocale(); | |
| 1568 | + | |
| 1569 | + for (String file : unmappedFiles) { | |
| 1570 | + try { | |
| 1571 | + int fn = 0; | |
| 1572 | + TrueTypeFont ttf; | |
| 1573 | + String fullPath = getPathName(file); | |
| 1574 | + if (FontUtilities.isLogging()) { | |
| 1575 | + FontUtilities.getLogger() | |
| 1576 | + .info("Trying to resolve file " + fullPath); | |
| 1577 | + } | |
| 1578 | + do { | |
| 1579 | + ttf = new TrueTypeFont(fullPath, null, fn++, false); | |
| 1580 | + // prefer the font's locale name. | |
| 1581 | + String fontName = ttf.getFontName(l).toLowerCase(); | |
| 1582 | + if (unmappedFonts.contains(fontName)) { | |
| 1583 | + fontToFileMap.put(fontName, file); | |
| 1584 | + unmappedFonts.remove(fontName); | |
| 1585 | + if (FontUtilities.isLogging()) { | |
| 1586 | + FontUtilities.getLogger() | |
| 1587 | + .info("Resolved absent registry entry for " + | |
| 1588 | + fontName + " located in " + fullPath); | |
| 1589 | + } | |
| 1590 | + } | |
| 1591 | + } | |
| 1592 | + while (fn < ttf.getFontCount()); | |
| 1593 | + } catch (Exception e) { | |
| 1594 | + } | |
| 1595 | + } | |
| 1596 | + } | |
| 1597 | + | |
| 1598 | + /* Hardwire the English names and expected file names of fonts | |
| 1599 | + * commonly used at start up. Avoiding until later even the small | |
| 1600 | + * cost of calling platform APIs to locate these can help. | |
| 1601 | + * The code that registers these fonts needs to "bail" if any | |
| 1602 | + * of the files do not exist, so it will verify the existence of | |
| 1603 | + * all non-null file names first. | |
| 1604 | + * They are added in to a map with nominally the first | |
| 1605 | + * word in the name of the family as the key. In all the cases | |
| 1606 | + * we are using the the family name is a single word, and as is | |
| 1607 | + * more or less required the family name is the initial sequence | |
| 1608 | + * in a full name. So lookup first finds the matching description, | |
| 1609 | + * then registers the whole family, returning the right font. | |
| 1610 | + */ | |
| 1611 | + public static class FamilyDescription { | |
| 1612 | + public String familyName; | |
| 1613 | + public String plainFullName; | |
| 1614 | + public String boldFullName; | |
| 1615 | + public String italicFullName; | |
| 1616 | + public String boldItalicFullName; | |
| 1617 | + public String plainFileName; | |
| 1618 | + public String boldFileName; | |
| 1619 | + public String italicFileName; | |
| 1620 | + public String boldItalicFileName; | |
| 1621 | + } | |
| 1622 | + | |
| 1623 | + static HashMap<String, FamilyDescription> platformFontMap; | |
| 1624 | + | |
| 1625 | + /** | |
| 1626 | + * default implementation does nothing. | |
| 1627 | + */ | |
| 1628 | + public HashMap<String, FamilyDescription> populateHardcodedFileNameMap() { | |
| 1629 | + return new HashMap<String, FamilyDescription>(0); | |
| 1630 | + } | |
| 1631 | + | |
| 1632 | + Font2D findFontFromPlatformMap(String lcName, int style) { | |
| 1633 | + if (platformFontMap == null) { | |
| 1634 | + platformFontMap = populateHardcodedFileNameMap(); | |
| 1635 | + } | |
| 1636 | + | |
| 1637 | + if (platformFontMap == null || platformFontMap.size() == 0) { | |
| 1638 | + return null; | |
| 1639 | + } | |
| 1640 | + | |
| 1641 | + int spaceIndex = lcName.indexOf(' '); | |
| 1642 | + String firstWord = lcName; | |
| 1643 | + if (spaceIndex > 0) { | |
| 1644 | + firstWord = lcName.substring(0, spaceIndex); | |
| 1645 | + } | |
| 1646 | + | |
| 1647 | + FamilyDescription fd = platformFontMap.get(firstWord); | |
| 1648 | + if (fd == null) { | |
| 1649 | + return null; | |
| 1650 | + } | |
| 1651 | + /* Once we've established that its at least the first word, | |
| 1652 | + * we need to dig deeper to make sure its a match for either | |
| 1653 | + * a full name, or the family name, to make sure its not | |
| 1654 | + * a request for some other font that just happens to start | |
| 1655 | + * with the same first word. | |
| 1656 | + */ | |
| 1657 | + int styleIndex = -1; | |
| 1658 | + if (lcName.equalsIgnoreCase(fd.plainFullName)) { | |
| 1659 | + styleIndex = 0; | |
| 1660 | + } else if (lcName.equalsIgnoreCase(fd.boldFullName)) { | |
| 1661 | + styleIndex = 1; | |
| 1662 | + } else if (lcName.equalsIgnoreCase(fd.italicFullName)) { | |
| 1663 | + styleIndex = 2; | |
| 1664 | + } else if (lcName.equalsIgnoreCase(fd.boldItalicFullName)) { | |
| 1665 | + styleIndex = 3; | |
| 1666 | + } | |
| 1667 | + if (styleIndex == -1 && !lcName.equalsIgnoreCase(fd.familyName)) { | |
| 1668 | + return null; | |
| 1669 | + } | |
| 1670 | + | |
| 1671 | + String plainFile = null, boldFile = null, | |
| 1672 | + italicFile = null, boldItalicFile = null; | |
| 1673 | + | |
| 1674 | + boolean failure = false; | |
| 1675 | + /* In a terminal server config, its possible that getPathName() | |
| 1676 | + * will return null, if the file doesn't exist, hence the null | |
| 1677 | + * checks on return. But in the normal client config we need to | |
| 1678 | + * follow this up with a check to see if all the files really | |
| 1679 | + * exist for the non-null paths. | |
| 1680 | + */ | |
| 1681 | + getPlatformFontDirs(noType1Font); | |
| 1682 | + | |
| 1683 | + if (fd.plainFileName != null) { | |
| 1684 | + plainFile = getPathName(fd.plainFileName); | |
| 1685 | + if (plainFile == null) { | |
| 1686 | + failure = true; | |
| 1687 | + } | |
| 1688 | + } | |
| 1689 | + | |
| 1690 | + if (fd.boldFileName != null) { | |
| 1691 | + boldFile = getPathName(fd.boldFileName); | |
| 1692 | + if (boldFile == null) { | |
| 1693 | + failure = true; | |
| 1694 | + } | |
| 1695 | + } | |
| 1696 | + | |
| 1697 | + if (fd.italicFileName != null) { | |
| 1698 | + italicFile = getPathName(fd.italicFileName); | |
| 1699 | + if (italicFile == null) { | |
| 1700 | + failure = true; | |
| 1701 | + } | |
| 1702 | + } | |
| 1703 | + | |
| 1704 | + if (fd.boldItalicFileName != null) { | |
| 1705 | + boldItalicFile = getPathName(fd.boldItalicFileName); | |
| 1706 | + if (boldItalicFile == null) { | |
| 1707 | + failure = true; | |
| 1708 | + } | |
| 1709 | + } | |
| 1710 | + | |
| 1711 | + if (failure) { | |
| 1712 | + if (FontUtilities.isLogging()) { | |
| 1713 | + FontUtilities.getLogger(). | |
| 1714 | + info("Hardcoded file missing looking for " + lcName); | |
| 1715 | + } | |
| 1716 | + platformFontMap.remove(firstWord); | |
| 1717 | + return null; | |
| 1718 | + } | |
| 1719 | + | |
| 1720 | + /* Some of these may be null,as not all styles have to exist */ | |
| 1721 | + final String[] files = { | |
| 1722 | + plainFile, boldFile, italicFile, boldItalicFile } ; | |
| 1723 | + | |
| 1724 | + failure = java.security.AccessController.doPrivileged( | |
| 1725 | + new java.security.PrivilegedAction<Boolean>() { | |
| 1726 | + public Boolean run() { | |
| 1727 | + for (int i=0; i<files.length; i++) { | |
| 1728 | + if (files[i] == null) { | |
| 1729 | + continue; | |
| 1730 | + } | |
| 1731 | + File f = new File(files[i]); | |
| 1732 | + if (!f.exists()) { | |
| 1733 | + return Boolean.TRUE; | |
| 1734 | + } | |
| 1735 | + } | |
| 1736 | + return Boolean.FALSE; | |
| 1737 | + } | |
| 1738 | + }); | |
| 1739 | + | |
| 1740 | + if (failure) { | |
| 1741 | + if (FontUtilities.isLogging()) { | |
| 1742 | + FontUtilities.getLogger(). | |
| 1743 | + info("Hardcoded file missing looking for " + lcName); | |
| 1744 | + } | |
| 1745 | + platformFontMap.remove(firstWord); | |
| 1746 | + return null; | |
| 1747 | + } | |
| 1748 | + | |
| 1749 | + /* If we reach here we know that we have all the files we | |
| 1750 | + * expect, so all should be fine so long as the contents | |
| 1751 | + * are what we'd expect. Now on to registering the fonts. | |
| 1752 | + * Currently this code only looks for TrueType fonts, so format | |
| 1753 | + * and rank can be specified without looking at the filename. | |
| 1754 | + */ | |
| 1755 | + Font2D font = null; | |
| 1756 | + for (int f=0;f<files.length;f++) { | |
| 1757 | + if (files[f] == null) { | |
| 1758 | + continue; | |
| 1759 | + } | |
| 1760 | + PhysicalFont pf = | |
| 1761 | + registerFontFile(files[f], null, | |
| 1762 | + FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK); | |
| 1763 | + if (f == styleIndex) { | |
| 1764 | + font = pf; | |
| 1765 | + } | |
| 1766 | + } | |
| 1767 | + | |
| 1768 | + | |
| 1769 | + /* Two general cases need a bit more work here. | |
| 1770 | + * 1) If font is null, then it was perhaps a request for a | |
| 1771 | + * non-existent font, such as "Tahoma Italic", or a family name - | |
| 1772 | + * where family and full name of the plain font differ. | |
| 1773 | + * Fall back to finding the closest one in the family. | |
| 1774 | + * This could still fail if a client specified "Segoe" instead of | |
| 1775 | + * "Segoe UI". | |
| 1776 | + * 2) The request is of the form "MyFont Bold", style=Font.ITALIC, | |
| 1777 | + * and so we want to see if there's a Bold Italic font, or | |
| 1778 | + * "MyFamily", style=Font.BOLD, and we may have matched the plain, | |
| 1779 | + * but now need to revise that to the BOLD font. | |
| 1780 | + */ | |
| 1781 | + FontFamily fontFamily = FontFamily.getFamily(fd.familyName); | |
| 1782 | + if (fontFamily != null) { | |
| 1783 | + if (font == null) { | |
| 1784 | + font = fontFamily.getFont(style); | |
| 1785 | + if (font == null) { | |
| 1786 | + font = fontFamily.getClosestStyle(style); | |
| 1787 | + } | |
| 1788 | + } else if (style > 0 && style != font.style) { | |
| 1789 | + style |= font.style; | |
| 1790 | + font = fontFamily.getFont(style); | |
| 1791 | + if (font == null) { | |
| 1792 | + font = fontFamily.getClosestStyle(style); | |
| 1793 | + } | |
| 1794 | + } | |
| 1795 | + } | |
| 1796 | + | |
| 1797 | + return font; | |
| 1798 | + } | |
| 1799 | + private synchronized HashMap<String,String> getFullNameToFileMap() { | |
| 1800 | + if (fontToFileMap == null) { | |
| 1801 | + | |
| 1802 | + pathDirs = getPlatformFontDirs(noType1Font); | |
| 1803 | + | |
| 1804 | + fontToFileMap = new HashMap<String,String>(100); | |
| 1805 | + fontToFamilyNameMap = new HashMap<String,String>(100); | |
| 1806 | + familyToFontListMap = new HashMap<String,ArrayList<String>>(50); | |
| 1807 | + populateFontFileNameMap(fontToFileMap, | |
| 1808 | + fontToFamilyNameMap, | |
| 1809 | + familyToFontListMap, | |
| 1810 | + Locale.ENGLISH); | |
| 1811 | + if (FontUtilities.isWindows) { | |
| 1812 | + resolveWindowsFonts(); | |
| 1813 | + } | |
| 1814 | + if (FontUtilities.isLogging()) { | |
| 1815 | + logPlatformFontInfo(); | |
| 1816 | + } | |
| 1817 | + } | |
| 1818 | + return fontToFileMap; | |
| 1819 | + } | |
| 1820 | + | |
| 1821 | + private void logPlatformFontInfo() { | |
| 1822 | + PlatformLogger logger = FontUtilities.getLogger(); | |
| 1823 | + for (int i=0; i< pathDirs.length;i++) { | |
| 1824 | + logger.info("fontdir="+pathDirs[i]); | |
| 1825 | + } | |
| 1826 | + for (String keyName : fontToFileMap.keySet()) { | |
| 1827 | + logger.info("font="+keyName+" file="+ fontToFileMap.get(keyName)); | |
| 1828 | + } | |
| 1829 | + for (String keyName : fontToFamilyNameMap.keySet()) { | |
| 1830 | + logger.info("font="+keyName+" family="+ | |
| 1831 | + fontToFamilyNameMap.get(keyName)); | |
| 1832 | + } | |
| 1833 | + for (String keyName : familyToFontListMap.keySet()) { | |
| 1834 | + logger.info("family="+keyName+ " fonts="+ | |
| 1835 | + familyToFontListMap.get(keyName)); | |
| 1836 | + } | |
| 1837 | + } | |
| 1838 | + | |
| 1839 | + /* Note this return list excludes logical fonts and JRE fonts */ | |
| 1840 | + protected String[] getFontNamesFromPlatform() { | |
| 1841 | + if (getFullNameToFileMap().size() == 0) { | |
| 1842 | + return null; | |
| 1843 | + } | |
| 1844 | + checkForUnreferencedFontFiles(); | |
| 1845 | + /* This odd code with TreeMap is used to preserve a historical | |
| 1846 | + * behaviour wrt the sorting order .. */ | |
| 1847 | + ArrayList<String> fontNames = new ArrayList<String>(); | |
| 1848 | + for (ArrayList<String> a : familyToFontListMap.values()) { | |
| 1849 | + for (String s : a) { | |
| 1850 | + fontNames.add(s); | |
| 1851 | + } | |
| 1852 | + } | |
| 1853 | + return fontNames.toArray(STR_ARRAY); | |
| 1854 | + } | |
| 1855 | + | |
| 1856 | + public boolean gotFontsFromPlatform() { | |
| 1857 | + return getFullNameToFileMap().size() != 0; | |
| 1858 | + } | |
| 1859 | + | |
| 1860 | + public String getFileNameForFontName(String fontName) { | |
| 1861 | + String fontNameLC = fontName.toLowerCase(Locale.ENGLISH); | |
| 1862 | + return fontToFileMap.get(fontNameLC); | |
| 1863 | + } | |
| 1864 | + | |
| 1865 | + private PhysicalFont registerFontFile(String file) { | |
| 1866 | + if (new File(file).isAbsolute() && | |
| 1867 | + !registeredFonts.contains(file)) { | |
| 1868 | + int fontFormat = FONTFORMAT_NONE; | |
| 1869 | + int fontRank = Font2D.UNKNOWN_RANK; | |
| 1870 | + if (ttFilter.accept(null, file)) { | |
| 1871 | + fontFormat = FONTFORMAT_TRUETYPE; | |
| 1872 | + fontRank = Font2D.TTF_RANK; | |
| 1873 | + } else if | |
| 1874 | + (t1Filter.accept(null, file)) { | |
| 1875 | + fontFormat = FONTFORMAT_TYPE1; | |
| 1876 | + fontRank = Font2D.TYPE1_RANK; | |
| 1877 | + } | |
| 1878 | + if (fontFormat == FONTFORMAT_NONE) { | |
| 1879 | + return null; | |
| 1880 | + } | |
| 1881 | + return registerFontFile(file, null, fontFormat, false, fontRank); | |
| 1882 | + } | |
| 1883 | + return null; | |
| 1884 | + } | |
| 1885 | + | |
| 1886 | + /* Used to register any font files that are found by platform APIs | |
| 1887 | + * that weren't previously found in the standard font locations. | |
| 1888 | + * the isAbsolute() check is needed since that's whats stored in the | |
| 1889 | + * set, and on windows, the fonts in the system font directory that | |
| 1890 | + * are in the fontToFileMap are just basenames. We don't want to try | |
| 1891 | + * to register those again, but we do want to register other registry | |
| 1892 | + * installed fonts. | |
| 1893 | + */ | |
| 1894 | + protected void registerOtherFontFiles(HashSet registeredFontFiles) { | |
| 1895 | + if (getFullNameToFileMap().size() == 0) { | |
| 1896 | + return; | |
| 1897 | + } | |
| 1898 | + for (String file : fontToFileMap.values()) { | |
| 1899 | + registerFontFile(file); | |
| 1900 | + } | |
| 1901 | + } | |
| 1902 | + | |
| 1903 | + public boolean | |
| 1904 | + getFamilyNamesFromPlatform(TreeMap<String,String> familyNames, | |
| 1905 | + Locale requestedLocale) { | |
| 1906 | + if (getFullNameToFileMap().size() == 0) { | |
| 1907 | + return false; | |
| 1908 | + } | |
| 1909 | + checkForUnreferencedFontFiles(); | |
| 1910 | + for (String name : fontToFamilyNameMap.values()) { | |
| 1911 | + familyNames.put(name.toLowerCase(requestedLocale), name); | |
| 1912 | + } | |
| 1913 | + return true; | |
| 1914 | + } | |
| 1915 | + | |
| 1916 | + /* Path may be absolute or a base file name relative to one of | |
| 1917 | + * the platform font directories | |
| 1918 | + */ | |
| 1919 | + private String getPathName(final String s) { | |
| 1920 | + File f = new File(s); | |
| 1921 | + if (f.isAbsolute()) { | |
| 1922 | + return s; | |
| 1923 | + } else if (pathDirs.length==1) { | |
| 1924 | + return pathDirs[0] + File.separator + s; | |
| 1925 | + } else { | |
| 1926 | + String path = java.security.AccessController.doPrivileged( | |
| 1927 | + new java.security.PrivilegedAction<String>() { | |
| 1928 | + public String run() { | |
| 1929 | + for (int p=0; p<pathDirs.length; p++) { | |
| 1930 | + File f = new File(pathDirs[p] +File.separator+ s); | |
| 1931 | + if (f.exists()) { | |
| 1932 | + return f.getAbsolutePath(); | |
| 1933 | + } | |
| 1934 | + } | |
| 1935 | + return null; | |
| 1936 | + } | |
| 1937 | + }); | |
| 1938 | + if (path != null) { | |
| 1939 | + return path; | |
| 1940 | + } | |
| 1941 | + } | |
| 1942 | + return s; // shouldn't happen, but harmless | |
| 1943 | + } | |
| 1944 | + | |
| 1945 | + /* lcName is required to be lower case for use as a key. | |
| 1946 | + * lcName may be a full name, or a family name, and style may | |
| 1947 | + * be specified in addition to either of these. So be sure to | |
| 1948 | + * get the right one. Since an app *could* ask for "Foo Regular" | |
| 1949 | + * and later ask for "Foo Italic", if we don't register all the | |
| 1950 | + * styles, then logic in findFont2D may try to style the original | |
| 1951 | + * so we register the entire family if we get a match here. | |
| 1952 | + * This is still a big win because this code is invoked where | |
| 1953 | + * otherwise we would register all fonts. | |
| 1954 | + * It's also useful for the case where "Foo Bold" was specified with | |
| 1955 | + * style Font.ITALIC, as we would want in that case to try to return | |
| 1956 | + * "Foo Bold Italic" if it exists, and it is only by locating "Foo Bold" | |
| 1957 | + * and opening it that we really "know" it's Bold, and can look for | |
| 1958 | + * a font that supports that and the italic style. | |
| 1959 | + * The code in here is not overtly windows-specific but in fact it | |
| 1960 | + * is unlikely to be useful as is on other platforms. It is maintained | |
| 1961 | + * in this shared source file to be close to its sole client and | |
| 1962 | + * because so much of the logic is intertwined with the logic in | |
| 1963 | + * findFont2D. | |
| 1964 | + */ | |
| 1965 | + private Font2D findFontFromPlatform(String lcName, int style) { | |
| 1966 | + if (getFullNameToFileMap().size() == 0) { | |
| 1967 | + return null; | |
| 1968 | + } | |
| 1969 | + | |
| 1970 | + ArrayList<String> family = null; | |
| 1971 | + String fontFile = null; | |
| 1972 | + String familyName = fontToFamilyNameMap.get(lcName); | |
| 1973 | + if (familyName != null) { | |
| 1974 | + fontFile = fontToFileMap.get(lcName); | |
| 1975 | + family = familyToFontListMap.get | |
| 1976 | + (familyName.toLowerCase(Locale.ENGLISH)); | |
| 1977 | + } else { | |
| 1978 | + family = familyToFontListMap.get(lcName); // is lcName is a family? | |
| 1979 | + if (family != null && family.size() > 0) { | |
| 1980 | + String lcFontName = family.get(0).toLowerCase(Locale.ENGLISH); | |
| 1981 | + if (lcFontName != null) { | |
| 1982 | + familyName = fontToFamilyNameMap.get(lcFontName); | |
| 1983 | + } | |
| 1984 | + } | |
| 1985 | + } | |
| 1986 | + if (family == null || familyName == null) { | |
| 1987 | + return null; | |
| 1988 | + } | |
| 1989 | + String [] fontList = (String[])family.toArray(STR_ARRAY); | |
| 1990 | + if (fontList.length == 0) { | |
| 1991 | + return null; | |
| 1992 | + } | |
| 1993 | + | |
| 1994 | + /* first check that for every font in this family we can find | |
| 1995 | + * a font file. The specific reason for doing this is that | |
| 1996 | + * in at least one case on Windows a font has the face name "David" | |
| 1997 | + * but the registry entry is "David Regular". That is the "unique" | |
| 1998 | + * name of the font but in other cases the registry contains the | |
| 1999 | + * "full" name. See the specifications of name ids 3 and 4 in the | |
| 2000 | + * TrueType 'name' table. | |
| 2001 | + * In general this could cause a problem that we fail to register | |
| 2002 | + * if we all members of a family that we may end up mapping to | |
| 2003 | + * the wrong font member: eg return Bold when Plain is needed. | |
| 2004 | + */ | |
| 2005 | + for (int f=0;f<fontList.length;f++) { | |
| 2006 | + String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH); | |
| 2007 | + String fileName = fontToFileMap.get(fontNameLC); | |
| 2008 | + if (fileName == null) { | |
| 2009 | + if (FontUtilities.isLogging()) { | |
| 2010 | + FontUtilities.getLogger() | |
| 2011 | + .info("Platform lookup : No file for font " + | |
| 2012 | + fontList[f] + " in family " +familyName); | |
| 2013 | + } | |
| 2014 | + return null; | |
| 2015 | + } | |
| 2016 | + } | |
| 2017 | + | |
| 2018 | + /* Currently this code only looks for TrueType fonts, so format | |
| 2019 | + * and rank can be specified without looking at the filename. | |
| 2020 | + */ | |
| 2021 | + PhysicalFont physicalFont = null; | |
| 2022 | + if (fontFile != null) { | |
| 2023 | + physicalFont = registerFontFile(getPathName(fontFile), null, | |
| 2024 | + FONTFORMAT_TRUETYPE, false, | |
| 2025 | + Font2D.TTF_RANK); | |
| 2026 | + } | |
| 2027 | + /* Register all fonts in this family. */ | |
| 2028 | + for (int f=0;f<fontList.length;f++) { | |
| 2029 | + String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH); | |
| 2030 | + String fileName = fontToFileMap.get(fontNameLC); | |
| 2031 | + if (fontFile != null && fontFile.equals(fileName)) { | |
| 2032 | + continue; | |
| 2033 | + } | |
| 2034 | + /* Currently this code only looks for TrueType fonts, so format | |
| 2035 | + * and rank can be specified without looking at the filename. | |
| 2036 | + */ | |
| 2037 | + registerFontFile(getPathName(fileName), null, | |
| 2038 | + FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK); | |
| 2039 | + } | |
| 2040 | + | |
| 2041 | + Font2D font = null; | |
| 2042 | + FontFamily fontFamily = FontFamily.getFamily(familyName); | |
| 2043 | + /* Handle case where request "MyFont Bold", style=Font.ITALIC */ | |
| 2044 | + if (physicalFont != null) { | |
| 2045 | + style |= physicalFont.style; | |
| 2046 | + } | |
| 2047 | + if (fontFamily != null) { | |
| 2048 | + font = fontFamily.getFont(style); | |
| 2049 | + if (font == null) { | |
| 2050 | + font = fontFamily.getClosestStyle(style); | |
| 2051 | + } | |
| 2052 | + } | |
| 2053 | + return font; | |
| 2054 | + } | |
| 2055 | + | |
| 2056 | + private ConcurrentHashMap<String, Font2D> fontNameCache = | |
| 2057 | + new ConcurrentHashMap<String, Font2D>(); | |
| 2058 | + | |
| 2059 | + /* | |
| 2060 | + * The client supplies a name and a style. | |
| 2061 | + * The name could be a family name, or a full name. | |
| 2062 | + * A font may exist with the specified style, or it may | |
| 2063 | + * exist only in some other style. For non-native fonts the scaler | |
| 2064 | + * may be able to emulate the required style. | |
| 2065 | + */ | |
| 2066 | + public Font2D findFont2D(String name, int style, int fallback) { | |
| 2067 | + String lowerCaseName = name.toLowerCase(Locale.ENGLISH); | |
| 2068 | + String mapName = lowerCaseName + dotStyleStr(style); | |
| 2069 | + Font2D font; | |
| 2070 | + | |
| 2071 | + /* If preferLocaleFonts() or preferProportionalFonts() has been | |
| 2072 | + * called we may be using an alternate set of composite fonts in this | |
| 2073 | + * app context. The presence of a pre-built name map indicates whether | |
| 2074 | + * this is so, and gives access to the alternate composite for the | |
| 2075 | + * name. | |
| 2076 | + */ | |
| 2077 | + if (_usingPerAppContextComposites) { | |
| 2078 | + ConcurrentHashMap<String, Font2D> altNameCache = | |
| 2079 | + (ConcurrentHashMap<String, Font2D>) | |
| 2080 | + AppContext.getAppContext().get(CompositeFont.class); | |
| 2081 | + if (altNameCache != null) { | |
| 2082 | + font = (Font2D)altNameCache.get(mapName); | |
| 2083 | + } else { | |
| 2084 | + font = null; | |
| 2085 | + } | |
| 2086 | + } else { | |
| 2087 | + font = fontNameCache.get(mapName); | |
| 2088 | + } | |
| 2089 | + if (font != null) { | |
| 2090 | + return font; | |
| 2091 | + } | |
| 2092 | + | |
| 2093 | + if (FontUtilities.isLogging()) { | |
| 2094 | + FontUtilities.getLogger().info("Search for font: " + name); | |
| 2095 | + } | |
| 2096 | + | |
| 2097 | + // The check below is just so that the bitmap fonts being set by | |
| 2098 | + // AWT and Swing thru the desktop properties do not trigger the | |
| 2099 | + // the load fonts case. The two bitmap fonts are now mapped to | |
| 2100 | + // appropriate equivalents for serif and sansserif. | |
| 2101 | + // Note that the cost of this comparison is only for the first | |
| 2102 | + // call until the map is filled. | |
| 2103 | + if (FontUtilities.isWindows) { | |
| 2104 | + if (lowerCaseName.equals("ms sans serif")) { | |
| 2105 | + name = "sansserif"; | |
| 2106 | + } else if (lowerCaseName.equals("ms serif")) { | |
| 2107 | + name = "serif"; | |
| 2108 | + } | |
| 2109 | + } | |
| 2110 | + | |
| 2111 | + /* This isn't intended to support a client passing in the | |
| 2112 | + * string default, but if a client passes in null for the name | |
| 2113 | + * the java.awt.Font class internally substitutes this name. | |
| 2114 | + * So we need to recognise it here to prevent a loadFonts | |
| 2115 | + * on the unrecognised name. The only potential problem with | |
| 2116 | + * this is it would hide any real font called "default"! | |
| 2117 | + * But that seems like a potential problem we can ignore for now. | |
| 2118 | + */ | |
| 2119 | + if (lowerCaseName.equals("default")) { | |
| 2120 | + name = "dialog"; | |
| 2121 | + } | |
| 2122 | + | |
| 2123 | + /* First see if its a family name. */ | |
| 2124 | + FontFamily family = FontFamily.getFamily(name); | |
| 2125 | + if (family != null) { | |
| 2126 | + font = family.getFontWithExactStyleMatch(style); | |
| 2127 | + if (font == null) { | |
| 2128 | + font = findDeferredFont(name, style); | |
| 2129 | + } | |
| 2130 | + if (font == null) { | |
| 2131 | + font = family.getFont(style); | |
| 2132 | + } | |
| 2133 | + if (font == null) { | |
| 2134 | + font = family.getClosestStyle(style); | |
| 2135 | + } | |
| 2136 | + if (font != null) { | |
| 2137 | + fontNameCache.put(mapName, font); | |
| 2138 | + return font; | |
| 2139 | + } | |
| 2140 | + } | |
| 2141 | + | |
| 2142 | + /* If it wasn't a family name, it should be a full name of | |
| 2143 | + * either a composite, or a physical font | |
| 2144 | + */ | |
| 2145 | + font = fullNameToFont.get(lowerCaseName); | |
| 2146 | + if (font != null) { | |
| 2147 | + /* Check that the requested style matches the matched font's style. | |
| 2148 | + * But also match style automatically if the requested style is | |
| 2149 | + * "plain". This because the existing behaviour is that the fonts | |
| 2150 | + * listed via getAllFonts etc always list their style as PLAIN. | |
| 2151 | + * This does lead to non-commutative behaviours where you might | |
| 2152 | + * start with "Lucida Sans Regular" and ask for a BOLD version | |
| 2153 | + * and get "Lucida Sans DemiBold" but if you ask for the PLAIN | |
| 2154 | + * style of "Lucida Sans DemiBold" you get "Lucida Sans DemiBold". | |
| 2155 | + * This consistent however with what happens if you have a bold | |
| 2156 | + * version of a font and no plain version exists - alg. styling | |
| 2157 | + * doesn't "unbolden" the font. | |
| 2158 | + */ | |
| 2159 | + if (font.style == style || style == Font.PLAIN) { | |
| 2160 | + fontNameCache.put(mapName, font); | |
| 2161 | + return font; | |
| 2162 | + } else { | |
| 2163 | + /* If it was a full name like "Lucida Sans Regular", but | |
| 2164 | + * the style requested is "bold", then we want to see if | |
| 2165 | + * there's the appropriate match against another font in | |
| 2166 | + * that family before trying to load all fonts, or applying a | |
| 2167 | + * algorithmic styling | |
| 2168 | + */ | |
| 2169 | + family = FontFamily.getFamily(font.getFamilyName(null)); | |
| 2170 | + if (family != null) { | |
| 2171 | + Font2D familyFont = family.getFont(style|font.style); | |
| 2172 | + /* We exactly matched the requested style, use it! */ | |
| 2173 | + if (familyFont != null) { | |
| 2174 | + fontNameCache.put(mapName, familyFont); | |
| 2175 | + return familyFont; | |
| 2176 | + } else { | |
| 2177 | + /* This next call is designed to support the case | |
| 2178 | + * where bold italic is requested, and if we must | |
| 2179 | + * style, then base it on either bold or italic - | |
| 2180 | + * not on plain! | |
| 2181 | + */ | |
| 2182 | + familyFont = family.getClosestStyle(style|font.style); | |
| 2183 | + if (familyFont != null) { | |
| 2184 | + /* The next check is perhaps one | |
| 2185 | + * that shouldn't be done. ie if we get this | |
| 2186 | + * far we have probably as close a match as we | |
| 2187 | + * are going to get. We could load all fonts to | |
| 2188 | + * see if somehow some parts of the family are | |
| 2189 | + * loaded but not all of it. | |
| 2190 | + */ | |
| 2191 | + if (familyFont.canDoStyle(style|font.style)) { | |
| 2192 | + fontNameCache.put(mapName, familyFont); | |
| 2193 | + return familyFont; | |
| 2194 | + } | |
| 2195 | + } | |
| 2196 | + } | |
| 2197 | + } | |
| 2198 | + } | |
| 2199 | + } | |
| 2200 | + | |
| 2201 | + if (FontUtilities.isWindows) { | |
| 2202 | + | |
| 2203 | + font = findFontFromPlatformMap(lowerCaseName, style); | |
| 2204 | + if (FontUtilities.isLogging()) { | |
| 2205 | + FontUtilities.getLogger() | |
| 2206 | + .info("findFontFromPlatformMap returned " + font); | |
| 2207 | + } | |
| 2208 | + if (font != null) { | |
| 2209 | + fontNameCache.put(mapName, font); | |
| 2210 | + return font; | |
| 2211 | + } | |
| 2212 | + | |
| 2213 | + /* Don't want Windows to return a Lucida Sans font from | |
| 2214 | + * C:\Windows\Fonts | |
| 2215 | + */ | |
| 2216 | + if (deferredFontFiles.size() > 0) { | |
| 2217 | + font = findJREDeferredFont(lowerCaseName, style); | |
| 2218 | + if (font != null) { | |
| 2219 | + fontNameCache.put(mapName, font); | |
| 2220 | + return font; | |
| 2221 | + } | |
| 2222 | + } | |
| 2223 | + font = findFontFromPlatform(lowerCaseName, style); | |
| 2224 | + if (font != null) { | |
| 2225 | + if (FontUtilities.isLogging()) { | |
| 2226 | + FontUtilities.getLogger() | |
| 2227 | + .info("Found font via platform API for request:\"" + | |
| 2228 | + name + "\":, style="+style+ | |
| 2229 | + " found font: " + font); | |
| 2230 | + } | |
| 2231 | + fontNameCache.put(mapName, font); | |
| 2232 | + return font; | |
| 2233 | + } | |
| 2234 | + } | |
| 2235 | + | |
| 2236 | + /* If reach here and no match has been located, then if there are | |
| 2237 | + * uninitialised deferred fonts, load as many of those as needed | |
| 2238 | + * to find the deferred font. If none is found through that | |
| 2239 | + * search continue on. | |
| 2240 | + * There is possibly a minor issue when more than one | |
| 2241 | + * deferred font implements the same font face. Since deferred | |
| 2242 | + * fonts are only those in font configuration files, this is a | |
| 2243 | + * controlled situation, the known case being Solaris euro_fonts | |
| 2244 | + * versions of Arial, Times New Roman, Courier New. However | |
| 2245 | + * the larger font will transparently replace the smaller one | |
| 2246 | + * - see addToFontList() - when it is needed by the composite font. | |
| 2247 | + */ | |
| 2248 | + if (deferredFontFiles.size() > 0) { | |
| 2249 | + font = findDeferredFont(name, style); | |
| 2250 | + if (font != null) { | |
| 2251 | + fontNameCache.put(mapName, font); | |
| 2252 | + return font; | |
| 2253 | + } | |
| 2254 | + } | |
| 2255 | + | |
| 2256 | + /* Some apps use deprecated 1.0 names such as helvetica and courier. On | |
| 2257 | + * Solaris these are Type1 fonts in /usr/openwin/lib/X11/fonts/Type1. | |
| 2258 | + * If running on Solaris will register all the fonts in this | |
| 2259 | + * directory. | |
| 2260 | + * May as well register the whole directory without actually testing | |
| 2261 | + * the font name is one of the deprecated names as the next step would | |
| 2262 | + * load all fonts which are in this directory anyway. | |
| 2263 | + * In the event that this lookup is successful it potentially "hides" | |
| 2264 | + * TrueType versions of such fonts that are elsewhere but since they | |
| 2265 | + * do not exist on Solaris this is not a problem. | |
| 2266 | + * Set a flag to indicate we've done this registration to avoid | |
| 2267 | + * repetition and more seriously, to avoid recursion. | |
| 2268 | + */ | |
| 2269 | + if (FontUtilities.isSolaris &&!loaded1dot0Fonts) { | |
| 2270 | + /* "timesroman" is a special case since that's not the | |
| 2271 | + * name of any known font on Solaris or elsewhere. | |
| 2272 | + */ | |
| 2273 | + if (lowerCaseName.equals("timesroman")) { | |
| 2274 | + font = findFont2D("serif", style, fallback); | |
| 2275 | + fontNameCache.put(mapName, font); | |
| 2276 | + } | |
| 2277 | + register1dot0Fonts(); | |
| 2278 | + loaded1dot0Fonts = true; | |
| 2279 | + Font2D ff = findFont2D(name, style, fallback); | |
| 2280 | + return ff; | |
| 2281 | + } | |
| 2282 | + | |
| 2283 | + /* We check for application registered fonts before | |
| 2284 | + * explicitly loading all fonts as if necessary the registration | |
| 2285 | + * code will have done so anyway. And we don't want to needlessly | |
| 2286 | + * load the actual files for all fonts. | |
| 2287 | + * Just as for installed fonts we check for family before fullname. | |
| 2288 | + * We do not add these fonts to fontNameCache for the | |
| 2289 | + * app context case which eliminates the overhead of a per context | |
| 2290 | + * cache for these. | |
| 2291 | + */ | |
| 2292 | + | |
| 2293 | + if (fontsAreRegistered || fontsAreRegisteredPerAppContext) { | |
| 2294 | + Hashtable<String, FontFamily> familyTable = null; | |
| 2295 | + Hashtable<String, Font2D> nameTable; | |
| 2296 | + | |
| 2297 | + if (fontsAreRegistered) { | |
| 2298 | + familyTable = createdByFamilyName; | |
| 2299 | + nameTable = createdByFullName; | |
| 2300 | + } else { | |
| 2301 | + AppContext appContext = AppContext.getAppContext(); | |
| 2302 | + familyTable = | |
| 2303 | + (Hashtable<String,FontFamily>)appContext.get(regFamilyKey); | |
| 2304 | + nameTable = | |
| 2305 | + (Hashtable<String,Font2D>)appContext.get(regFullNameKey); | |
| 2306 | + } | |
| 2307 | + | |
| 2308 | + family = familyTable.get(lowerCaseName); | |
| 2309 | + if (family != null) { | |
| 2310 | + font = family.getFontWithExactStyleMatch(style); | |
| 2311 | + if (font == null) { | |
| 2312 | + font = family.getFont(style); | |
| 2313 | + } | |
| 2314 | + if (font == null) { | |
| 2315 | + font = family.getClosestStyle(style); | |
| 2316 | + } | |
| 2317 | + if (font != null) { | |
| 2318 | + if (fontsAreRegistered) { | |
| 2319 | + fontNameCache.put(mapName, font); | |
| 2320 | + } | |
| 2321 | + return font; | |
| 2322 | + } | |
| 2323 | + } | |
| 2324 | + font = nameTable.get(lowerCaseName); | |
| 2325 | + if (font != null) { | |
| 2326 | + if (fontsAreRegistered) { | |
| 2327 | + fontNameCache.put(mapName, font); | |
| 2328 | + } | |
| 2329 | + return font; | |
| 2330 | + } | |
| 2331 | + } | |
| 2332 | + | |
| 2333 | + /* If reach here and no match has been located, then if all fonts | |
| 2334 | + * are not yet loaded, do so, and then recurse. | |
| 2335 | + */ | |
| 2336 | + if (!loadedAllFonts) { | |
| 2337 | + if (FontUtilities.isLogging()) { | |
| 2338 | + FontUtilities.getLogger() | |
| 2339 | + .info("Load fonts looking for:" + name); | |
| 2340 | + } | |
| 2341 | + loadFonts(); | |
| 2342 | + loadedAllFonts = true; | |
| 2343 | + return findFont2D(name, style, fallback); | |
| 2344 | + } | |
| 2345 | + | |
| 2346 | + if (!loadedAllFontFiles) { | |
| 2347 | + if (FontUtilities.isLogging()) { | |
| 2348 | + FontUtilities.getLogger() | |
| 2349 | + .info("Load font files looking for:" + name); | |
| 2350 | + } | |
| 2351 | + loadFontFiles(); | |
| 2352 | + loadedAllFontFiles = true; | |
| 2353 | + return findFont2D(name, style, fallback); | |
| 2354 | + } | |
| 2355 | + | |
| 2356 | + /* The primary name is the locale default - ie not US/English but | |
| 2357 | + * whatever is the default in this locale. This is the way it always | |
| 2358 | + * has been but may be surprising to some developers if "Arial Regular" | |
| 2359 | + * were hard-coded in their app and yet "Arial Regular" was not the | |
| 2360 | + * default name. Fortunately for them, as a consequence of the JDK | |
| 2361 | + * supporting returning names and family names for arbitrary locales, | |
| 2362 | + * we also need to support searching all localised names for a match. | |
| 2363 | + * But because this case of the name used to reference a font is not | |
| 2364 | + * the same as the default for this locale is rare, it makes sense to | |
| 2365 | + * search a much shorter list of default locale names and only go to | |
| 2366 | + * a longer list of names in the event that no match was found. | |
| 2367 | + * So add here code which searches localised names too. | |
| 2368 | + * As in 1.4.x this happens only after loading all fonts, which | |
| 2369 | + * is probably the right order. | |
| 2370 | + */ | |
| 2371 | + if ((font = findFont2DAllLocales(name, style)) != null) { | |
| 2372 | + fontNameCache.put(mapName, font); | |
| 2373 | + return font; | |
| 2374 | + } | |
| 2375 | + | |
| 2376 | + /* Perhaps its a "compatibility" name - timesroman, helvetica, | |
| 2377 | + * or courier, which 1.0 apps used for logical fonts. | |
| 2378 | + * We look for these "late" after a loadFonts as we must not | |
| 2379 | + * hide real fonts of these names. | |
| 2380 | + * Map these appropriately: | |
| 2381 | + * On windows this means according to the rules specified by the | |
| 2382 | + * FontConfiguration : do it only for encoding==Cp1252 | |
| 2383 | + * | |
| 2384 | + * REMIND: this is something we plan to remove. | |
| 2385 | + */ | |
| 2386 | + if (FontUtilities.isWindows) { | |
| 2387 | + String compatName = | |
| 2388 | + getFontConfiguration().getFallbackFamilyName(name, null); | |
| 2389 | + if (compatName != null) { | |
| 2390 | + font = findFont2D(compatName, style, fallback); | |
| 2391 | + fontNameCache.put(mapName, font); | |
| 2392 | + return font; | |
| 2393 | + } | |
| 2394 | + } else if (lowerCaseName.equals("timesroman")) { | |
| 2395 | + font = findFont2D("serif", style, fallback); | |
| 2396 | + fontNameCache.put(mapName, font); | |
| 2397 | + return font; | |
| 2398 | + } else if (lowerCaseName.equals("helvetica")) { | |
| 2399 | + font = findFont2D("sansserif", style, fallback); | |
| 2400 | + fontNameCache.put(mapName, font); | |
| 2401 | + return font; | |
| 2402 | + } else if (lowerCaseName.equals("courier")) { | |
| 2403 | + font = findFont2D("monospaced", style, fallback); | |
| 2404 | + fontNameCache.put(mapName, font); | |
| 2405 | + return font; | |
| 2406 | + } | |
| 2407 | + | |
| 2408 | + if (FontUtilities.isLogging()) { | |
| 2409 | + FontUtilities.getLogger().info("No font found for:" + name); | |
| 2410 | + } | |
| 2411 | + | |
| 2412 | + switch (fallback) { | |
| 2413 | + case PHYSICAL_FALLBACK: return getDefaultPhysicalFont(); | |
| 2414 | + case LOGICAL_FALLBACK: return getDefaultLogicalFont(style); | |
| 2415 | + default: return null; | |
| 2416 | + } | |
| 2417 | + } | |
| 2418 | + | |
| 2419 | + /* | |
| 2420 | + * Workaround for apps which are dependent on a font metrics bug | |
| 2421 | + * in JDK 1.1. This is an unsupported win32 private setting. | |
| 2422 | + * Left in for a customer - do not remove. | |
| 2423 | + */ | |
| 2424 | + public boolean usePlatformFontMetrics() { | |
| 2425 | + return usePlatformFontMetrics; | |
| 2426 | + } | |
| 2427 | + | |
| 2428 | + public int getNumFonts() { | |
| 2429 | + return physicalFonts.size()+maxCompFont; | |
| 2430 | + } | |
| 2431 | + | |
| 2432 | + private static boolean fontSupportsEncoding(Font font, String encoding) { | |
| 2433 | + return FontUtilities.getFont2D(font).supportsEncoding(encoding); | |
| 2434 | + } | |
| 2435 | + | |
| 2436 | + protected abstract String getFontPath(boolean noType1Fonts); | |
| 2437 | + | |
| 2438 | + private Thread fileCloser = null; | |
| 2439 | + Vector<File> tmpFontFiles = null; | |
| 2440 | + | |
| 2441 | + public Font2D createFont2D(File fontFile, int fontFormat, | |
| 2442 | + boolean isCopy, CreatedFontTracker tracker) | |
| 2443 | + throws FontFormatException { | |
| 2444 | + | |
| 2445 | + String fontFilePath = fontFile.getPath(); | |
| 2446 | + FileFont font2D = null; | |
| 2447 | + final File fFile = fontFile; | |
| 2448 | + final CreatedFontTracker _tracker = tracker; | |
| 2449 | + try { | |
| 2450 | + switch (fontFormat) { | |
| 2451 | + case Font.TRUETYPE_FONT: | |
| 2452 | + font2D = new TrueTypeFont(fontFilePath, null, 0, true); | |
| 2453 | + break; | |
| 2454 | + case Font.TYPE1_FONT: | |
| 2455 | + font2D = new Type1Font(fontFilePath, null, isCopy); | |
| 2456 | + break; | |
| 2457 | + default: | |
| 2458 | + throw new FontFormatException("Unrecognised Font Format"); | |
| 2459 | + } | |
| 2460 | + } catch (FontFormatException e) { | |
| 2461 | + if (isCopy) { | |
| 2462 | + java.security.AccessController.doPrivileged( | |
| 2463 | + new java.security.PrivilegedAction() { | |
| 2464 | + public Object run() { | |
| 2465 | + if (_tracker != null) { | |
| 2466 | + _tracker.subBytes((int)fFile.length()); | |
| 2467 | + } | |
| 2468 | + fFile.delete(); | |
| 2469 | + return null; | |
| 2470 | + } | |
| 2471 | + }); | |
| 2472 | + } | |
| 2473 | + throw(e); | |
| 2474 | + } | |
| 2475 | + if (isCopy) { | |
| 2476 | + font2D.setFileToRemove(fontFile, tracker); | |
| 2477 | + synchronized (FontManager.class) { | |
| 2478 | + | |
| 2479 | + if (tmpFontFiles == null) { | |
| 2480 | + tmpFontFiles = new Vector<File>(); | |
| 2481 | + } | |
| 2482 | + tmpFontFiles.add(fontFile); | |
| 2483 | + | |
| 2484 | + if (fileCloser == null) { | |
| 2485 | + final Runnable fileCloserRunnable = new Runnable() { | |
| 2486 | + public void run() { | |
| 2487 | + java.security.AccessController.doPrivileged( | |
| 2488 | + new java.security.PrivilegedAction() { | |
| 2489 | + public Object run() { | |
| 2490 | + | |
| 2491 | + for (int i=0;i<CHANNELPOOLSIZE;i++) { | |
| 2492 | + if (fontFileCache[i] != null) { | |
| 2493 | + try { | |
| 2494 | + fontFileCache[i].close(); | |
| 2495 | + } catch (Exception e) { | |
| 2496 | + } | |
| 2497 | + } | |
| 2498 | + } | |
| 2499 | + if (tmpFontFiles != null) { | |
| 2500 | + File[] files = new File[tmpFontFiles.size()]; | |
| 2501 | + files = tmpFontFiles.toArray(files); | |
| 2502 | + for (int f=0; f<files.length;f++) { | |
| 2503 | + try { | |
| 2504 | + files[f].delete(); | |
| 2505 | + } catch (Exception e) { | |
| 2506 | + } | |
| 2507 | + } | |
| 2508 | + } | |
| 2509 | + | |
| 2510 | + return null; | |
| 2511 | + } | |
| 2512 | + | |
| 2513 | + }); | |
| 2514 | + } | |
| 2515 | + }; | |
| 2516 | + java.security.AccessController.doPrivileged( | |
| 2517 | + new java.security.PrivilegedAction() { | |
| 2518 | + public Object run() { | |
| 2519 | + /* The thread must be a member of a thread group | |
| 2520 | + * which will not get GCed before VM exit. | |
| 2521 | + * Make its parent the top-level thread group. | |
| 2522 | + */ | |
| 2523 | + ThreadGroup tg = | |
| 2524 | + Thread.currentThread().getThreadGroup(); | |
| 2525 | + for (ThreadGroup tgn = tg; | |
| 2526 | + tgn != null; | |
| 2527 | + tg = tgn, tgn = tg.getParent()); | |
| 2528 | + fileCloser = new Thread(tg, fileCloserRunnable); | |
| 2529 | + fileCloser.setContextClassLoader(null); | |
| 2530 | + Runtime.getRuntime().addShutdownHook(fileCloser); | |
| 2531 | + return null; | |
| 2532 | + } | |
| 2533 | + }); | |
| 2534 | + } | |
| 2535 | + } | |
| 2536 | + } | |
| 2537 | + return font2D; | |
| 2538 | + } | |
| 2539 | + | |
| 2540 | + /* remind: used in X11GraphicsEnvironment and called often enough | |
| 2541 | + * that we ought to obsolete this code | |
| 2542 | + */ | |
| 2543 | + public synchronized String getFullNameByFileName(String fileName) { | |
| 2544 | + PhysicalFont[] physFonts = getPhysicalFonts(); | |
| 2545 | + for (int i=0;i<physFonts.length;i++) { | |
| 2546 | + if (physFonts[i].platName.equals(fileName)) { | |
| 2547 | + return (physFonts[i].getFontName(null)); | |
| 2548 | + } | |
| 2549 | + } | |
| 2550 | + return null; | |
| 2551 | + } | |
| 2552 | + | |
| 2553 | + /* | |
| 2554 | + * This is called when font is determined to be invalid/bad. | |
| 2555 | + * It designed to be called (for example) by the font scaler | |
| 2556 | + * when in processing a font file it is discovered to be incorrect. | |
| 2557 | + * This is different than the case where fonts are discovered to | |
| 2558 | + * be incorrect during initial verification, as such fonts are | |
| 2559 | + * never registered. | |
| 2560 | + * Handles to this font held are re-directed to a default font. | |
| 2561 | + * This default may not be an ideal substitute buts it better than | |
| 2562 | + * crashing This code assumes a PhysicalFont parameter as it doesn't | |
| 2563 | + * make sense for a Composite to be "bad". | |
| 2564 | + */ | |
| 2565 | + public synchronized void deRegisterBadFont(Font2D font2D) { | |
| 2566 | + if (!(font2D instanceof PhysicalFont)) { | |
| 2567 | + /* We should never reach here, but just in case */ | |
| 2568 | + return; | |
| 2569 | + } else { | |
| 2570 | + if (FontUtilities.isLogging()) { | |
| 2571 | + FontUtilities.getLogger() | |
| 2572 | + .severe("Deregister bad font: " + font2D); | |
| 2573 | + } | |
| 2574 | + replaceFont((PhysicalFont)font2D, getDefaultPhysicalFont()); | |
| 2575 | + } | |
| 2576 | + } | |
| 2577 | + | |
| 2578 | + /* | |
| 2579 | + * This encapsulates all the work that needs to be done when a | |
| 2580 | + * Font2D is replaced by a different Font2D. | |
| 2581 | + */ | |
| 2582 | + public synchronized void replaceFont(PhysicalFont oldFont, | |
| 2583 | + PhysicalFont newFont) { | |
| 2584 | + | |
| 2585 | + if (oldFont.handle.font2D != oldFont) { | |
| 2586 | + /* already done */ | |
| 2587 | + return; | |
| 2588 | + } | |
| 2589 | + | |
| 2590 | + /* If we try to replace the font with itself, that won't work, | |
| 2591 | + * so pick any alternative physical font | |
| 2592 | + */ | |
| 2593 | + if (oldFont == newFont) { | |
| 2594 | + if (FontUtilities.isLogging()) { | |
| 2595 | + FontUtilities.getLogger() | |
| 2596 | + .severe("Can't replace bad font with itself " + oldFont); | |
| 2597 | + } | |
| 2598 | + PhysicalFont[] physFonts = getPhysicalFonts(); | |
| 2599 | + for (int i=0; i<physFonts.length;i++) { | |
| 2600 | + if (physFonts[i] != newFont) { | |
| 2601 | + newFont = physFonts[i]; | |
| 2602 | + break; | |
| 2603 | + } | |
| 2604 | + } | |
| 2605 | + if (oldFont == newFont) { | |
| 2606 | + if (FontUtilities.isLogging()) { | |
| 2607 | + FontUtilities.getLogger() | |
| 2608 | + .severe("This is bad. No good physicalFonts found."); | |
| 2609 | + } | |
| 2610 | + return; | |
| 2611 | + } | |
| 2612 | + } | |
| 2613 | + | |
| 2614 | + /* eliminate references to this font, so it won't be located | |
| 2615 | + * by future callers, and will be eligible for GC when all | |
| 2616 | + * references are removed | |
| 2617 | + */ | |
| 2618 | + oldFont.handle.font2D = newFont; | |
| 2619 | + physicalFonts.remove(oldFont.fullName); | |
| 2620 | + fullNameToFont.remove(oldFont.fullName.toLowerCase(Locale.ENGLISH)); | |
| 2621 | + FontFamily.remove(oldFont); | |
| 2622 | + | |
| 2623 | + if (localeFullNamesToFont != null) { | |
| 2624 | + Map.Entry[] mapEntries = | |
| 2625 | + (Map.Entry[])localeFullNamesToFont.entrySet(). | |
| 2626 | + toArray(new Map.Entry[0]); | |
| 2627 | + /* Should I be replacing these, or just I just remove | |
| 2628 | + * the names from the map? | |
| 2629 | + */ | |
| 2630 | + for (int i=0; i<mapEntries.length;i++) { | |
| 2631 | + if (mapEntries[i].getValue() == oldFont) { | |
| 2632 | + try { | |
| 2633 | + mapEntries[i].setValue(newFont); | |
| 2634 | + } catch (Exception e) { | |
| 2635 | + /* some maps don't support this operation. | |
| 2636 | + * In this case just give up and remove the entry. | |
| 2637 | + */ | |
| 2638 | + localeFullNamesToFont.remove(mapEntries[i].getKey()); | |
| 2639 | + } | |
| 2640 | + } | |
| 2641 | + } | |
| 2642 | + } | |
| 2643 | + | |
| 2644 | + for (int i=0; i<maxCompFont; i++) { | |
| 2645 | + /* Deferred initialization of composites shouldn't be | |
| 2646 | + * a problem for this case, since a font must have been | |
| 2647 | + * initialised to be discovered to be bad. | |
| 2648 | + * Some JRE composites on Solaris use two versions of the same | |
| 2649 | + * font. The replaced font isn't bad, just "smaller" so there's | |
| 2650 | + * no need to make the slot point to the new font. | |
| 2651 | + * Since composites have a direct reference to the Font2D (not | |
| 2652 | + * via a handle) making this substitution is not safe and could | |
| 2653 | + * cause an additional problem and so this substitution is | |
| 2654 | + * warranted only when a font is truly "bad" and could cause | |
| 2655 | + * a crash. So we now replace it only if its being substituted | |
| 2656 | + * with some font other than a fontconfig rank font | |
| 2657 | + * Since in practice a substitution will have the same rank | |
| 2658 | + * this may never happen, but the code is safer even if its | |
| 2659 | + * also now a no-op. | |
| 2660 | + * The only obvious "glitch" from this stems from the current | |
| 2661 | + * implementation that when asked for the number of glyphs in a | |
| 2662 | + * composite it lies and returns the number in slot 0 because | |
| 2663 | + * composite glyphs aren't contiguous. Since we live with that | |
| 2664 | + * we can live with the glitch that depending on how it was | |
| 2665 | + * initialised a composite may return different values for this. | |
| 2666 | + * Fixing the issues with composite glyph ids is tricky as | |
| 2667 | + * there are exclusion ranges and unlike other fonts even the | |
| 2668 | + * true "numGlyphs" isn't a contiguous range. Likely the only | |
| 2669 | + * solution is an API that returns an array of glyph ranges | |
| 2670 | + * which takes precedence over the existing API. That might | |
| 2671 | + * also need to address excluding ranges which represent a | |
| 2672 | + * code point supported by an earlier component. | |
| 2673 | + */ | |
| 2674 | + if (newFont.getRank() > Font2D.FONT_CONFIG_RANK) { | |
| 2675 | + compFonts[i].replaceComponentFont(oldFont, newFont); | |
| 2676 | + } | |
| 2677 | + } | |
| 2678 | + } | |
| 2679 | + | |
| 2680 | + private synchronized void loadLocaleNames() { | |
| 2681 | + if (localeFullNamesToFont != null) { | |
| 2682 | + return; | |
| 2683 | + } | |
| 2684 | + localeFullNamesToFont = new HashMap<String, TrueTypeFont>(); | |
| 2685 | + Font2D[] fonts = getRegisteredFonts(); | |
| 2686 | + for (int i=0; i<fonts.length; i++) { | |
| 2687 | + if (fonts[i] instanceof TrueTypeFont) { | |
| 2688 | + TrueTypeFont ttf = (TrueTypeFont)fonts[i]; | |
| 2689 | + String[] fullNames = ttf.getAllFullNames(); | |
| 2690 | + for (int n=0; n<fullNames.length; n++) { | |
| 2691 | + localeFullNamesToFont.put(fullNames[n], ttf); | |
| 2692 | + } | |
| 2693 | + FontFamily family = FontFamily.getFamily(ttf.familyName); | |
| 2694 | + if (family != null) { | |
| 2695 | + FontFamily.addLocaleNames(family, ttf.getAllFamilyNames()); | |
| 2696 | + } | |
| 2697 | + } | |
| 2698 | + } | |
| 2699 | + } | |
| 2700 | + | |
| 2701 | + /* This replicate the core logic of findFont2D but operates on | |
| 2702 | + * all the locale names. This hasn't been merged into findFont2D to | |
| 2703 | + * keep the logic simpler and reduce overhead, since this case is | |
| 2704 | + * almost never used. The main case in which it is called is when | |
| 2705 | + * a bogus font name is used and we need to check all possible names | |
| 2706 | + * before returning the default case. | |
| 2707 | + */ | |
| 2708 | + private Font2D findFont2DAllLocales(String name, int style) { | |
| 2709 | + | |
| 2710 | + if (FontUtilities.isLogging()) { | |
| 2711 | + FontUtilities.getLogger() | |
| 2712 | + .info("Searching localised font names for:" + name); | |
| 2713 | + } | |
| 2714 | + | |
| 2715 | + /* If reach here and no match has been located, then if we have | |
| 2716 | + * not yet built the map of localeFullNamesToFont for TT fonts, do so | |
| 2717 | + * now. This method must be called after all fonts have been loaded. | |
| 2718 | + */ | |
| 2719 | + if (localeFullNamesToFont == null) { | |
| 2720 | + loadLocaleNames(); | |
| 2721 | + } | |
| 2722 | + String lowerCaseName = name.toLowerCase(); | |
| 2723 | + Font2D font = null; | |
| 2724 | + | |
| 2725 | + /* First see if its a family name. */ | |
| 2726 | + FontFamily family = FontFamily.getLocaleFamily(lowerCaseName); | |
| 2727 | + if (family != null) { | |
| 2728 | + font = family.getFont(style); | |
| 2729 | + if (font == null) { | |
| 2730 | + font = family.getClosestStyle(style); | |
| 2731 | + } | |
| 2732 | + if (font != null) { | |
| 2733 | + return font; | |
| 2734 | + } | |
| 2735 | + } | |
| 2736 | + | |
| 2737 | + /* If it wasn't a family name, it should be a full name. */ | |
| 2738 | + synchronized (this) { | |
| 2739 | + font = localeFullNamesToFont.get(name); | |
| 2740 | + } | |
| 2741 | + if (font != null) { | |
| 2742 | + if (font.style == style || style == Font.PLAIN) { | |
| 2743 | + return font; | |
| 2744 | + } else { | |
| 2745 | + family = FontFamily.getFamily(font.getFamilyName(null)); | |
| 2746 | + if (family != null) { | |
| 2747 | + Font2D familyFont = family.getFont(style); | |
| 2748 | + /* We exactly matched the requested style, use it! */ | |
| 2749 | + if (familyFont != null) { | |
| 2750 | + return familyFont; | |
| 2751 | + } else { | |
| 2752 | + familyFont = family.getClosestStyle(style); | |
| 2753 | + if (familyFont != null) { | |
| 2754 | + /* The next check is perhaps one | |
| 2755 | + * that shouldn't be done. ie if we get this | |
| 2756 | + * far we have probably as close a match as we | |
| 2757 | + * are going to get. We could load all fonts to | |
| 2758 | + * see if somehow some parts of the family are | |
| 2759 | + * loaded but not all of it. | |
| 2760 | + * This check is commented out for now. | |
| 2761 | + */ | |
| 2762 | + if (!familyFont.canDoStyle(style)) { | |
| 2763 | + familyFont = null; | |
| 2764 | + } | |
| 2765 | + return familyFont; | |
| 2766 | + } | |
| 2767 | + } | |
| 2768 | + } | |
| 2769 | + } | |
| 2770 | + } | |
| 2771 | + return font; | |
| 2772 | + } | |
| 2773 | + | |
| 2774 | + /* Supporting "alternate" composite fonts on 2D graphics objects | |
| 2775 | + * is accessed by the application by calling methods on the local | |
| 2776 | + * GraphicsEnvironment. The overall implementation is described | |
| 2777 | + * in one place, here, since otherwise the implementation is spread | |
| 2778 | + * around it may be difficult to track. | |
| 2779 | + * The methods below call into SunGraphicsEnvironment which creates a | |
| 2780 | + * new FontConfiguration instance. The FontConfiguration class, | |
| 2781 | + * and its platform sub-classes are updated to take parameters requesting | |
| 2782 | + * these behaviours. This is then used to create new composite font | |
| 2783 | + * instances. Since this calls the initCompositeFont method in | |
| 2784 | + * SunGraphicsEnvironment it performs the same initialization as is | |
| 2785 | + * performed normally. There may be some duplication of effort, but | |
| 2786 | + * that code is already written to be able to perform properly if called | |
| 2787 | + * to duplicate work. The main difference is that if we detect we are | |
| 2788 | + * running in an applet/browser/Java plugin environment these new fonts | |
| 2789 | + * are not placed in the "default" maps but into an AppContext instance. | |
| 2790 | + * The font lookup mechanism in java.awt.Font.getFont2D() is also updated | |
| 2791 | + * so that look-up for composite fonts will in that case always | |
| 2792 | + * do a lookup rather than returning a cached result. | |
| 2793 | + * This is inefficient but necessary else singleton java.awt.Font | |
| 2794 | + * instances would not retrieve the correct Font2D for the appcontext. | |
| 2795 | + * sun.font.FontManager.findFont2D is also updated to that it uses | |
| 2796 | + * a name map cache specific to that appcontext. | |
| 2797 | + * | |
| 2798 | + * Getting an AppContext is expensive, so there is a global variable | |
| 2799 | + * that records whether these methods have ever been called and can | |
| 2800 | + * avoid the expense for almost all applications. Once the correct | |
| 2801 | + * CompositeFont is associated with the Font, everything should work | |
| 2802 | + * through existing mechanisms. | |
| 2803 | + * A special case is that GraphicsEnvironment.getAllFonts() must | |
| 2804 | + * return an AppContext specific list. | |
| 2805 | + * | |
| 2806 | + * Calling the methods below is "heavyweight" but it is expected that | |
| 2807 | + * these methods will be called very rarely. | |
| 2808 | + * | |
| 2809 | + * If _usingPerAppContextComposites is true, we are in "applet" | |
| 2810 | + * (eg browser) enviroment and at least one context has selected | |
| 2811 | + * an alternate composite font behaviour. | |
| 2812 | + * If _usingAlternateComposites is true, we are not in an "applet" | |
| 2813 | + * environment and the (single) application has selected | |
| 2814 | + * an alternate composite font behaviour. | |
| 2815 | + * | |
| 2816 | + * - Printing: The implementation delegates logical fonts to an AWT | |
| 2817 | + * mechanism which cannot use these alternate configurations. | |
| 2818 | + * We can detect that alternate fonts are in use and back-off to 2D, but | |
| 2819 | + * that uses outlines. Much of this can be fixed with additional work | |
| 2820 | + * but that may have to wait. The results should be correct, just not | |
| 2821 | + * optimal. | |
| 2822 | + */ | |
| 2823 | + private static final Object altJAFontKey = new Object(); | |
| 2824 | + private static final Object localeFontKey = new Object(); | |
| 2825 | + private static final Object proportionalFontKey = new Object(); | |
| 2826 | + private boolean _usingPerAppContextComposites = false; | |
| 2827 | + private boolean _usingAlternateComposites = false; | |
| 2828 | + | |
| 2829 | + /* These values are used only if we are running as a standalone | |
| 2830 | + * application, as determined by maybeMultiAppContext(); | |
| 2831 | + */ | |
| 2832 | + private static boolean gAltJAFont = false; | |
| 2833 | + private boolean gLocalePref = false; | |
| 2834 | + private boolean gPropPref = false; | |
| 2835 | + | |
| 2836 | + /* This method doesn't check if alternates are selected in this app | |
| 2837 | + * context. Its used by the FontMetrics caching code which in such | |
| 2838 | + * a case cannot retrieve a cached metrics solely on the basis of | |
| 2839 | + * the Font.equals() method since it needs to also check if the Font2D | |
| 2840 | + * is the same. | |
| 2841 | + * We also use non-standard composites for Swing native L&F fonts on | |
| 2842 | + * Windows. In that case the policy is that the metrics reported are | |
| 2843 | + * based solely on the physical font in the first slot which is the | |
| 2844 | + * visible java.awt.Font. So in that case the metrics cache which tests | |
| 2845 | + * the Font does what we want. In the near future when we expand the GTK | |
| 2846 | + * logical font definitions we may need to revisit this if GTK reports | |
| 2847 | + * combined metrics instead. For now though this test can be simple. | |
| 2848 | + */ | |
| 2849 | + public boolean maybeUsingAlternateCompositeFonts() { | |
| 2850 | + return _usingAlternateComposites || _usingPerAppContextComposites; | |
| 2851 | + } | |
| 2852 | + | |
| 2853 | + public boolean usingAlternateCompositeFonts() { | |
| 2854 | + return (_usingAlternateComposites || | |
| 2855 | + (_usingPerAppContextComposites && | |
| 2856 | + AppContext.getAppContext().get(CompositeFont.class) != null)); | |
| 2857 | + } | |
| 2858 | + | |
| 2859 | + private static boolean maybeMultiAppContext() { | |
| 2860 | + Boolean appletSM = (Boolean) | |
| 2861 | + java.security.AccessController.doPrivileged( | |
| 2862 | + new java.security.PrivilegedAction() { | |
| 2863 | + public Object run() { | |
| 2864 | + SecurityManager sm = System.getSecurityManager(); | |
| 2865 | + return new Boolean | |
| 2866 | + (sm instanceof sun.applet.AppletSecurity); | |
| 2867 | + } | |
| 2868 | + }); | |
| 2869 | + return appletSM.booleanValue(); | |
| 2870 | + } | |
| 2871 | + | |
| 2872 | + /* Modifies the behaviour of a subsequent call to preferLocaleFonts() | |
| 2873 | + * to use Mincho instead of Gothic for dialoginput in JA locales | |
| 2874 | + * on windows. Not needed on other platforms. | |
| 2875 | + */ | |
| 2876 | + public synchronized void useAlternateFontforJALocales() { | |
| 2877 | + if (FontUtilities.isLogging()) { | |
| 2878 | + FontUtilities.getLogger() | |
| 2879 | + .info("Entered useAlternateFontforJALocales()."); | |
| 2880 | + } | |
| 2881 | + if (!FontUtilities.isWindows) { | |
| 2882 | + return; | |
| 2883 | + } | |
| 2884 | + | |
| 2885 | + if (!maybeMultiAppContext()) { | |
| 2886 | + gAltJAFont = true; | |
| 2887 | + } else { | |
| 2888 | + AppContext appContext = AppContext.getAppContext(); | |
| 2889 | + appContext.put(altJAFontKey, altJAFontKey); | |
| 2890 | + } | |
| 2891 | + } | |
| 2892 | + | |
| 2893 | + public boolean usingAlternateFontforJALocales() { | |
| 2894 | + if (!maybeMultiAppContext()) { | |
| 2895 | + return gAltJAFont; | |
| 2896 | + } else { | |
| 2897 | + AppContext appContext = AppContext.getAppContext(); | |
| 2898 | + return appContext.get(altJAFontKey) == altJAFontKey; | |
| 2899 | + } | |
| 2900 | + } | |
| 2901 | + | |
| 2902 | + public synchronized void preferLocaleFonts() { | |
| 2903 | + if (FontUtilities.isLogging()) { | |
| 2904 | + FontUtilities.getLogger().info("Entered preferLocaleFonts()."); | |
| 2905 | + } | |
| 2906 | + /* Test if re-ordering will have any effect */ | |
| 2907 | + if (!FontConfiguration.willReorderForStartupLocale()) { | |
| 2908 | + return; | |
| 2909 | + } | |
| 2910 | + | |
| 2911 | + if (!maybeMultiAppContext()) { | |
| 2912 | + if (gLocalePref == true) { | |
| 2913 | + return; | |
| 2914 | + } | |
| 2915 | + gLocalePref = true; | |
| 2916 | + createCompositeFonts(fontNameCache, gLocalePref, gPropPref); | |
| 2917 | + _usingAlternateComposites = true; | |
| 2918 | + } else { | |
| 2919 | + AppContext appContext = AppContext.getAppContext(); | |
| 2920 | + if (appContext.get(localeFontKey) == localeFontKey) { | |
| 2921 | + return; | |
| 2922 | + } | |
| 2923 | + appContext.put(localeFontKey, localeFontKey); | |
| 2924 | + boolean acPropPref = | |
| 2925 | + appContext.get(proportionalFontKey) == proportionalFontKey; | |
| 2926 | + ConcurrentHashMap<String, Font2D> | |
| 2927 | + altNameCache = new ConcurrentHashMap<String, Font2D> (); | |
| 2928 | + /* If there is an existing hashtable, we can drop it. */ | |
| 2929 | + appContext.put(CompositeFont.class, altNameCache); | |
| 2930 | + _usingPerAppContextComposites = true; | |
| 2931 | + createCompositeFonts(altNameCache, true, acPropPref); | |
| 2932 | + } | |
| 2933 | + } | |
| 2934 | + | |
| 2935 | + public synchronized void preferProportionalFonts() { | |
| 2936 | + if (FontUtilities.isLogging()) { | |
| 2937 | + FontUtilities.getLogger() | |
| 2938 | + .info("Entered preferProportionalFonts()."); | |
| 2939 | + } | |
| 2940 | + /* If no proportional fonts are configured, there's no need | |
| 2941 | + * to take any action. | |
| 2942 | + */ | |
| 2943 | + if (!FontConfiguration.hasMonoToPropMap()) { | |
| 2944 | + return; | |
| 2945 | + } | |
| 2946 | + | |
| 2947 | + if (!maybeMultiAppContext()) { | |
| 2948 | + if (gPropPref == true) { | |
| 2949 | + return; | |
| 2950 | + } | |
| 2951 | + gPropPref = true; | |
| 2952 | + createCompositeFonts(fontNameCache, gLocalePref, gPropPref); | |
| 2953 | + _usingAlternateComposites = true; | |
| 2954 | + } else { | |
| 2955 | + AppContext appContext = AppContext.getAppContext(); | |
| 2956 | + if (appContext.get(proportionalFontKey) == proportionalFontKey) { | |
| 2957 | + return; | |
| 2958 | + } | |
| 2959 | + appContext.put(proportionalFontKey, proportionalFontKey); | |
| 2960 | + boolean acLocalePref = | |
| 2961 | + appContext.get(localeFontKey) == localeFontKey; | |
| 2962 | + ConcurrentHashMap<String, Font2D> | |
| 2963 | + altNameCache = new ConcurrentHashMap<String, Font2D> (); | |
| 2964 | + /* If there is an existing hashtable, we can drop it. */ | |
| 2965 | + appContext.put(CompositeFont.class, altNameCache); | |
| 2966 | + _usingPerAppContextComposites = true; | |
| 2967 | + createCompositeFonts(altNameCache, acLocalePref, true); | |
| 2968 | + } | |
| 2969 | + } | |
| 2970 | + | |
| 2971 | + private static HashSet<String> installedNames = null; | |
| 2972 | + private static HashSet<String> getInstalledNames() { | |
| 2973 | + if (installedNames == null) { | |
| 2974 | + Locale l = getSystemStartupLocale(); | |
| 2975 | + SunFontManager fontManager = SunFontManager.getInstance(); | |
| 2976 | + String[] installedFamilies = | |
| 2977 | + fontManager.getInstalledFontFamilyNames(l); | |
| 2978 | + Font[] installedFonts = fontManager.getAllInstalledFonts(); | |
| 2979 | + HashSet<String> names = new HashSet<String>(); | |
| 2980 | + for (int i=0; i<installedFamilies.length; i++) { | |
| 2981 | + names.add(installedFamilies[i].toLowerCase(l)); | |
| 2982 | + } | |
| 2983 | + for (int i=0; i<installedFonts.length; i++) { | |
| 2984 | + names.add(installedFonts[i].getFontName(l).toLowerCase(l)); | |
| 2985 | + } | |
| 2986 | + installedNames = names; | |
| 2987 | + } | |
| 2988 | + return installedNames; | |
| 2989 | + } | |
| 2990 | + | |
| 2991 | + /* Keys are used to lookup per-AppContext Hashtables */ | |
| 2992 | + private static final Object regFamilyKey = new Object(); | |
| 2993 | + private static final Object regFullNameKey = new Object(); | |
| 2994 | + private Hashtable<String,FontFamily> createdByFamilyName; | |
| 2995 | + private Hashtable<String,Font2D> createdByFullName; | |
| 2996 | + private boolean fontsAreRegistered = false; | |
| 2997 | + private boolean fontsAreRegisteredPerAppContext = false; | |
| 2998 | + | |
| 2999 | + public boolean registerFont(Font font) { | |
| 3000 | + /* This method should not be called with "null". | |
| 3001 | + * It is the caller's responsibility to ensure that. | |
| 3002 | + */ | |
| 3003 | + if (font == null) { | |
| 3004 | + return false; | |
| 3005 | + } | |
| 3006 | + | |
| 3007 | + /* Initialise these objects only once we start to use this API */ | |
| 3008 | + synchronized (regFamilyKey) { | |
| 3009 | + if (createdByFamilyName == null) { | |
| 3010 | + createdByFamilyName = new Hashtable<String,FontFamily>(); | |
| 3011 | + createdByFullName = new Hashtable<String,Font2D>(); | |
| 3012 | + } | |
| 3013 | + } | |
| 3014 | + | |
| 3015 | + if (! FontAccess.getFontAccess().isCreatedFont(font)) { | |
| 3016 | + return false; | |
| 3017 | + } | |
| 3018 | + /* We want to ensure that this font cannot override existing | |
| 3019 | + * installed fonts. Check these conditions : | |
| 3020 | + * - family name is not that of an installed font | |
| 3021 | + * - full name is not that of an installed font | |
| 3022 | + * - family name is not the same as the full name of an installed font | |
| 3023 | + * - full name is not the same as the family name of an installed font | |
| 3024 | + * The last two of these may initially look odd but the reason is | |
| 3025 | + * that (unfortunately) Font constructors do not distinuguish these. | |
| 3026 | + * An extreme example of such a problem would be a font which has | |
| 3027 | + * family name "Dialog.Plain" and full name of "Dialog". | |
| 3028 | + * The one arguably overly stringent restriction here is that if an | |
| 3029 | + * application wants to supply a new member of an existing family | |
| 3030 | + * It will get rejected. But since the JRE can perform synthetic | |
| 3031 | + * styling in many cases its not necessary. | |
| 3032 | + * We don't apply the same logic to registered fonts. If apps want | |
| 3033 | + * to do this lets assume they have a reason. It won't cause problems | |
| 3034 | + * except for themselves. | |
| 3035 | + */ | |
| 3036 | + HashSet<String> names = getInstalledNames(); | |
| 3037 | + Locale l = getSystemStartupLocale(); | |
| 3038 | + String familyName = font.getFamily(l).toLowerCase(); | |
| 3039 | + String fullName = font.getFontName(l).toLowerCase(); | |
| 3040 | + if (names.contains(familyName) || names.contains(fullName)) { | |
| 3041 | + return false; | |
| 3042 | + } | |
| 3043 | + | |
| 3044 | + /* Checks passed, now register the font */ | |
| 3045 | + Hashtable<String,FontFamily> familyTable; | |
| 3046 | + Hashtable<String,Font2D> fullNameTable; | |
| 3047 | + if (!maybeMultiAppContext()) { | |
| 3048 | + familyTable = createdByFamilyName; | |
| 3049 | + fullNameTable = createdByFullName; | |
| 3050 | + fontsAreRegistered = true; | |
| 3051 | + } else { | |
| 3052 | + AppContext appContext = AppContext.getAppContext(); | |
| 3053 | + familyTable = | |
| 3054 | + (Hashtable<String,FontFamily>)appContext.get(regFamilyKey); | |
| 3055 | + fullNameTable = | |
| 3056 | + (Hashtable<String,Font2D>)appContext.get(regFullNameKey); | |
| 3057 | + if (familyTable == null) { | |
| 3058 | + familyTable = new Hashtable<String,FontFamily>(); | |
| 3059 | + fullNameTable = new Hashtable<String,Font2D>(); | |
| 3060 | + appContext.put(regFamilyKey, familyTable); | |
| 3061 | + appContext.put(regFullNameKey, fullNameTable); | |
| 3062 | + } | |
| 3063 | + fontsAreRegisteredPerAppContext = true; | |
| 3064 | + } | |
| 3065 | + /* Create the FontFamily and add font to the tables */ | |
| 3066 | + Font2D font2D = FontUtilities.getFont2D(font); | |
| 3067 | + int style = font2D.getStyle(); | |
| 3068 | + FontFamily family = familyTable.get(familyName); | |
| 3069 | + if (family == null) { | |
| 3070 | + family = new FontFamily(font.getFamily(l)); | |
| 3071 | + familyTable.put(familyName, family); | |
| 3072 | + } | |
| 3073 | + /* Remove name cache entries if not using app contexts. | |
| 3074 | + * To accommodate a case where code may have registered first a plain | |
| 3075 | + * family member and then used it and is now registering a bold family | |
| 3076 | + * member, we need to remove all members of the family, so that the | |
| 3077 | + * new style can get picked up rather than continuing to synthesise. | |
| 3078 | + */ | |
| 3079 | + if (fontsAreRegistered) { | |
| 3080 | + removeFromCache(family.getFont(Font.PLAIN)); | |
| 3081 | + removeFromCache(family.getFont(Font.BOLD)); | |
| 3082 | + removeFromCache(family.getFont(Font.ITALIC)); | |
| 3083 | + removeFromCache(family.getFont(Font.BOLD|Font.ITALIC)); | |
| 3084 | + removeFromCache(fullNameTable.get(fullName)); | |
| 3085 | + } | |
| 3086 | + family.setFont(font2D, style); | |
| 3087 | + fullNameTable.put(fullName, font2D); | |
| 3088 | + return true; | |
| 3089 | + } | |
| 3090 | + | |
| 3091 | + /* Remove from the name cache all references to the Font2D */ | |
| 3092 | + private void removeFromCache(Font2D font) { | |
| 3093 | + if (font == null) { | |
| 3094 | + return; | |
| 3095 | + } | |
| 3096 | + String[] keys = (String[])(fontNameCache.keySet().toArray(STR_ARRAY)); | |
| 3097 | + for (int k=0; k<keys.length;k++) { | |
| 3098 | + if (fontNameCache.get(keys[k]) == font) { | |
| 3099 | + fontNameCache.remove(keys[k]); | |
| 3100 | + } | |
| 3101 | + } | |
| 3102 | + } | |
| 3103 | + | |
| 3104 | + // It may look odd to use TreeMap but its more convenient to the caller. | |
| 3105 | + public TreeMap<String, String> getCreatedFontFamilyNames() { | |
| 3106 | + | |
| 3107 | + Hashtable<String,FontFamily> familyTable; | |
| 3108 | + if (fontsAreRegistered) { | |
| 3109 | + familyTable = createdByFamilyName; | |
| 3110 | + } else if (fontsAreRegisteredPerAppContext) { | |
| 3111 | + AppContext appContext = AppContext.getAppContext(); | |
| 3112 | + familyTable = | |
| 3113 | + (Hashtable<String,FontFamily>)appContext.get(regFamilyKey); | |
| 3114 | + } else { | |
| 3115 | + return null; | |
| 3116 | + } | |
| 3117 | + | |
| 3118 | + Locale l = getSystemStartupLocale(); | |
| 3119 | + synchronized (familyTable) { | |
| 3120 | + TreeMap<String, String> map = new TreeMap<String, String>(); | |
| 3121 | + for (FontFamily f : familyTable.values()) { | |
| 3122 | + Font2D font2D = f.getFont(Font.PLAIN); | |
| 3123 | + if (font2D == null) { | |
| 3124 | + font2D = f.getClosestStyle(Font.PLAIN); | |
| 3125 | + } | |
| 3126 | + String name = font2D.getFamilyName(l); | |
| 3127 | + map.put(name.toLowerCase(l), name); | |
| 3128 | + } | |
| 3129 | + return map; | |
| 3130 | + } | |
| 3131 | + } | |
| 3132 | + | |
| 3133 | + public Font[] getCreatedFonts() { | |
| 3134 | + | |
| 3135 | + Hashtable<String,Font2D> nameTable; | |
| 3136 | + if (fontsAreRegistered) { | |
| 3137 | + nameTable = createdByFullName; | |
| 3138 | + } else if (fontsAreRegisteredPerAppContext) { | |
| 3139 | + AppContext appContext = AppContext.getAppContext(); | |
| 3140 | + nameTable = | |
| 3141 | + (Hashtable<String,Font2D>)appContext.get(regFullNameKey); | |
| 3142 | + } else { | |
| 3143 | + return null; | |
| 3144 | + } | |
| 3145 | + | |
| 3146 | + Locale l = getSystemStartupLocale(); | |
| 3147 | + synchronized (nameTable) { | |
| 3148 | + Font[] fonts = new Font[nameTable.size()]; | |
| 3149 | + int i=0; | |
| 3150 | + for (Font2D font2D : nameTable.values()) { | |
| 3151 | + fonts[i++] = new Font(font2D.getFontName(l), Font.PLAIN, 1); | |
| 3152 | + } | |
| 3153 | + return fonts; | |
| 3154 | + } | |
| 3155 | + } | |
| 3156 | + | |
| 3157 | + | |
| 3158 | + protected String[] getPlatformFontDirs(boolean noType1Fonts) { | |
| 3159 | + | |
| 3160 | + /* First check if we already initialised path dirs */ | |
| 3161 | + if (pathDirs != null) { | |
| 3162 | + return pathDirs; | |
| 3163 | + } | |
| 3164 | + | |
| 3165 | + String path = getPlatformFontPath(noType1Fonts); | |
| 3166 | + StringTokenizer parser = | |
| 3167 | + new StringTokenizer(path, File.pathSeparator); | |
| 3168 | + ArrayList<String> pathList = new ArrayList<String>(); | |
| 3169 | + try { | |
| 3170 | + while (parser.hasMoreTokens()) { | |
| 3171 | + pathList.add(parser.nextToken()); | |
| 3172 | + } | |
| 3173 | + } catch (NoSuchElementException e) { | |
| 3174 | + } | |
| 3175 | + pathDirs = pathList.toArray(new String[0]); | |
| 3176 | + return pathDirs; | |
| 3177 | + } | |
| 3178 | + | |
| 3179 | + /** | |
| 3180 | + * Returns an array of two strings. The first element is the | |
| 3181 | + * name of the font. The second element is the file name. | |
| 3182 | + */ | |
| 3183 | + public abstract String[] getDefaultPlatformFont(); | |
| 3184 | + | |
| 3185 | + // Begin: Refactored from SunGraphicsEnviroment. | |
| 3186 | + | |
| 3187 | + /* | |
| 3188 | + * helper function for registerFonts | |
| 3189 | + */ | |
| 3190 | + private void addDirFonts(String dirName, File dirFile, | |
| 3191 | + FilenameFilter filter, | |
| 3192 | + int fontFormat, boolean useJavaRasterizer, | |
| 3193 | + int fontRank, | |
| 3194 | + boolean defer, boolean resolveSymLinks) { | |
| 3195 | + String[] ls = dirFile.list(filter); | |
| 3196 | + if (ls == null || ls.length == 0) { | |
| 3197 | + return; | |
| 3198 | + } | |
| 3199 | + String[] fontNames = new String[ls.length]; | |
| 3200 | + String[][] nativeNames = new String[ls.length][]; | |
| 3201 | + int fontCount = 0; | |
| 3202 | + | |
| 3203 | + for (int i=0; i < ls.length; i++ ) { | |
| 3204 | + File theFile = new File(dirFile, ls[i]); | |
| 3205 | + String fullName = null; | |
| 3206 | + if (resolveSymLinks) { | |
| 3207 | + try { | |
| 3208 | + fullName = theFile.getCanonicalPath(); | |
| 3209 | + } catch (IOException e) { | |
| 3210 | + } | |
| 3211 | + } | |
| 3212 | + if (fullName == null) { | |
| 3213 | + fullName = dirName + File.separator + ls[i]; | |
| 3214 | + } | |
| 3215 | + | |
| 3216 | + // REMIND: case compare depends on platform | |
| 3217 | + if (registeredFontFiles.contains(fullName)) { | |
| 3218 | + continue; | |
| 3219 | + } | |
| 3220 | + | |
| 3221 | + if (badFonts != null && badFonts.contains(fullName)) { | |
| 3222 | + if (FontUtilities.debugFonts()) { | |
| 3223 | + FontUtilities.getLogger() | |
| 3224 | + .warning("skip bad font " + fullName); | |
| 3225 | + } | |
| 3226 | + continue; // skip this font file. | |
| 3227 | + } | |
| 3228 | + | |
| 3229 | + registeredFontFiles.add(fullName); | |
| 3230 | + | |
| 3231 | + if (FontUtilities.debugFonts() | |
| 3232 | + && FontUtilities.getLogger().isLoggable(PlatformLogger.INFO)) { | |
| 3233 | + String message = "Registering font " + fullName; | |
| 3234 | + String[] natNames = getNativeNames(fullName, null); | |
| 3235 | + if (natNames == null) { | |
| 3236 | + message += " with no native name"; | |
| 3237 | + } else { | |
| 3238 | + message += " with native name(s) " + natNames[0]; | |
| 3239 | + for (int nn = 1; nn < natNames.length; nn++) { | |
| 3240 | + message += ", " + natNames[nn]; | |
| 3241 | + } | |
| 3242 | + } | |
| 3243 | + FontUtilities.getLogger().info(message); | |
| 3244 | + } | |
| 3245 | + fontNames[fontCount] = fullName; | |
| 3246 | + nativeNames[fontCount++] = getNativeNames(fullName, null); | |
| 3247 | + } | |
| 3248 | + registerFonts(fontNames, nativeNames, fontCount, fontFormat, | |
| 3249 | + useJavaRasterizer, fontRank, defer); | |
| 3250 | + return; | |
| 3251 | + } | |
| 3252 | + | |
| 3253 | + protected String[] getNativeNames(String fontFileName, | |
| 3254 | + String platformName) { | |
| 3255 | + return null; | |
| 3256 | + } | |
| 3257 | + | |
| 3258 | + /** | |
| 3259 | + * Returns a file name for the physical font represented by this platform | |
| 3260 | + * font name. The default implementation tries to obtain the file name | |
| 3261 | + * from the font configuration. | |
| 3262 | + * Subclasses may override to provide information from other sources. | |
| 3263 | + */ | |
| 3264 | + protected String getFileNameFromPlatformName(String platformFontName) { | |
| 3265 | + return fontConfig.getFileNameFromPlatformName(platformFontName); | |
| 3266 | + } | |
| 3267 | + | |
| 3268 | + /** | |
| 3269 | + * Return the default font configuration. | |
| 3270 | + */ | |
| 3271 | + public FontConfiguration getFontConfiguration() { | |
| 3272 | + return fontConfig; | |
| 3273 | + } | |
| 3274 | + | |
| 3275 | + /* A call to this method should be followed by a call to | |
| 3276 | + * registerFontDirs(..) | |
| 3277 | + */ | |
| 3278 | + public String getPlatformFontPath(boolean noType1Font) { | |
| 3279 | + if (fontPath == null) { | |
| 3280 | + fontPath = getFontPath(noType1Font); | |
| 3281 | + } | |
| 3282 | + return fontPath; | |
| 3283 | + } | |
| 3284 | + | |
| 3285 | + public static boolean isOpenJDK() { | |
| 3286 | + return FontUtilities.isOpenJDK; | |
| 3287 | + } | |
| 3288 | + | |
| 3289 | + protected void loadFonts() { | |
| 3290 | + if (discoveredAllFonts) { | |
| 3291 | + return; | |
| 3292 | + } | |
| 3293 | + /* Use lock specific to the font system */ | |
| 3294 | + synchronized (this) { | |
| 3295 | + if (FontUtilities.debugFonts()) { | |
| 3296 | + Thread.dumpStack(); | |
| 3297 | + FontUtilities.getLogger() | |
| 3298 | + .info("SunGraphicsEnvironment.loadFonts() called"); | |
| 3299 | + } | |
| 3300 | + initialiseDeferredFonts(); | |
| 3301 | + | |
| 3302 | + java.security.AccessController.doPrivileged( | |
| 3303 | + new java.security.PrivilegedAction() { | |
| 3304 | + public Object run() { | |
| 3305 | + if (fontPath == null) { | |
| 3306 | + fontPath = getPlatformFontPath(noType1Font); | |
| 3307 | + registerFontDirs(fontPath); | |
| 3308 | + } | |
| 3309 | + if (fontPath != null) { | |
| 3310 | + // this will find all fonts including those already | |
| 3311 | + // registered. But we have checks in place to prevent | |
| 3312 | + // double registration. | |
| 3313 | + if (! gotFontsFromPlatform()) { | |
| 3314 | + registerFontsOnPath(fontPath, false, | |
| 3315 | + Font2D.UNKNOWN_RANK, | |
| 3316 | + false, true); | |
| 3317 | + loadedAllFontFiles = true; | |
| 3318 | + } | |
| 3319 | + } | |
| 3320 | + registerOtherFontFiles(registeredFontFiles); | |
| 3321 | + discoveredAllFonts = true; | |
| 3322 | + return null; | |
| 3323 | + } | |
| 3324 | + }); | |
| 3325 | + } | |
| 3326 | + } | |
| 3327 | + | |
| 3328 | + protected void registerFontDirs(String pathName) { | |
| 3329 | + return; | |
| 3330 | + } | |
| 3331 | + | |
| 3332 | + private void registerFontsOnPath(String pathName, | |
| 3333 | + boolean useJavaRasterizer, int fontRank, | |
| 3334 | + boolean defer, boolean resolveSymLinks) { | |
| 3335 | + | |
| 3336 | + StringTokenizer parser = new StringTokenizer(pathName, | |
| 3337 | + File.pathSeparator); | |
| 3338 | + try { | |
| 3339 | + while (parser.hasMoreTokens()) { | |
| 3340 | + registerFontsInDir(parser.nextToken(), | |
| 3341 | + useJavaRasterizer, fontRank, | |
| 3342 | + defer, resolveSymLinks); | |
| 3343 | + } | |
| 3344 | + } catch (NoSuchElementException e) { | |
| 3345 | + } | |
| 3346 | + } | |
| 3347 | + | |
| 3348 | + /* Called to register fall back fonts */ | |
| 3349 | + public void registerFontsInDir(String dirName) { | |
| 3350 | + registerFontsInDir(dirName, true, Font2D.JRE_RANK, true, false); | |
| 3351 | + } | |
| 3352 | + | |
| 3353 | + private void registerFontsInDir(String dirName, boolean useJavaRasterizer, | |
| 3354 | + int fontRank, | |
| 3355 | + boolean defer, boolean resolveSymLinks) { | |
| 3356 | + File pathFile = new File(dirName); | |
| 3357 | + addDirFonts(dirName, pathFile, ttFilter, | |
| 3358 | + FONTFORMAT_TRUETYPE, useJavaRasterizer, | |
| 3359 | + fontRank==Font2D.UNKNOWN_RANK ? | |
| 3360 | + Font2D.TTF_RANK : fontRank, | |
| 3361 | + defer, resolveSymLinks); | |
| 3362 | + addDirFonts(dirName, pathFile, t1Filter, | |
| 3363 | + FONTFORMAT_TYPE1, useJavaRasterizer, | |
| 3364 | + fontRank==Font2D.UNKNOWN_RANK ? | |
| 3365 | + Font2D.TYPE1_RANK : fontRank, | |
| 3366 | + defer, resolveSymLinks); | |
| 3367 | + } | |
| 3368 | + | |
| 3369 | + protected void registerFontDir(String path) { | |
| 3370 | + } | |
| 3371 | + | |
| 3372 | + /** | |
| 3373 | + * Returns file name for default font, either absolute | |
| 3374 | + * or relative as needed by registerFontFile. | |
| 3375 | + */ | |
| 3376 | + public synchronized String getDefaultFontFile() { | |
| 3377 | + if (defaultFontFileName == null) { | |
| 3378 | + initDefaultFonts(); | |
| 3379 | + } | |
| 3380 | + return defaultFontFileName; | |
| 3381 | + } | |
| 3382 | + | |
| 3383 | + private void initDefaultFonts() { | |
| 3384 | + if (!isOpenJDK()) { | |
| 3385 | + defaultFontName = lucidaFontName; | |
| 3386 | + if (useAbsoluteFontFileNames()) { | |
| 3387 | + defaultFontFileName = | |
| 3388 | + jreFontDirName + File.separator + FontUtilities.LUCIDA_FILE_NAME; | |
| 3389 | + } else { | |
| 3390 | + defaultFontFileName = FontUtilities.LUCIDA_FILE_NAME; | |
| 3391 | + } | |
| 3392 | + } | |
| 3393 | + } | |
| 3394 | + | |
| 3395 | + /** | |
| 3396 | + * Whether registerFontFile expects absolute or relative | |
| 3397 | + * font file names. | |
| 3398 | + */ | |
| 3399 | + protected boolean useAbsoluteFontFileNames() { | |
| 3400 | + return true; | |
| 3401 | + } | |
| 3402 | + | |
| 3403 | + /** | |
| 3404 | + * Creates this environment's FontConfiguration. | |
| 3405 | + */ | |
| 3406 | + protected abstract FontConfiguration createFontConfiguration(); | |
| 3407 | + | |
| 3408 | + public abstract FontConfiguration | |
| 3409 | + createFontConfiguration(boolean preferLocaleFonts, | |
| 3410 | + boolean preferPropFonts); | |
| 3411 | + | |
| 3412 | + /** | |
| 3413 | + * Returns face name for default font, or null if | |
| 3414 | + * no face names are used for CompositeFontDescriptors | |
| 3415 | + * for this platform. | |
| 3416 | + */ | |
| 3417 | + public synchronized String getDefaultFontFaceName() { | |
| 3418 | + if (defaultFontName == null) { | |
| 3419 | + initDefaultFonts(); | |
| 3420 | + } | |
| 3421 | + return defaultFontName; | |
| 3422 | + } | |
| 3423 | + | |
| 3424 | + public void loadFontFiles() { | |
| 3425 | + loadFonts(); | |
| 3426 | + if (loadedAllFontFiles) { | |
| 3427 | + return; | |
| 3428 | + } | |
| 3429 | + /* Use lock specific to the font system */ | |
| 3430 | + synchronized (this) { | |
| 3431 | + if (FontUtilities.debugFonts()) { | |
| 3432 | + Thread.dumpStack(); | |
| 3433 | + FontUtilities.getLogger().info("loadAllFontFiles() called"); | |
| 3434 | + } | |
| 3435 | + java.security.AccessController.doPrivileged( | |
| 3436 | + new java.security.PrivilegedAction() { | |
| 3437 | + public Object run() { | |
| 3438 | + if (fontPath == null) { | |
| 3439 | + fontPath = getPlatformFontPath(noType1Font); | |
| 3440 | + } | |
| 3441 | + if (fontPath != null) { | |
| 3442 | + // this will find all fonts including those already | |
| 3443 | + // registered. But we have checks in place to prevent | |
| 3444 | + // double registration. | |
| 3445 | + registerFontsOnPath(fontPath, false, | |
| 3446 | + Font2D.UNKNOWN_RANK, | |
| 3447 | + false, true); | |
| 3448 | + } | |
| 3449 | + loadedAllFontFiles = true; | |
| 3450 | + return null; | |
| 3451 | + } | |
| 3452 | + }); | |
| 3453 | + } | |
| 3454 | + } | |
| 3455 | + | |
| 3456 | + /* | |
| 3457 | + * This method asks the font configuration API for all platform names | |
| 3458 | + * used as components of composite/logical fonts and iterates over these | |
| 3459 | + * looking up their corresponding file name and registers these fonts. | |
| 3460 | + * It also ensures that the fonts are accessible via platform APIs. | |
| 3461 | + * The composites themselves are then registered. | |
| 3462 | + */ | |
| 3463 | + private void | |
| 3464 | + initCompositeFonts(FontConfiguration fontConfig, | |
| 3465 | + ConcurrentHashMap<String, Font2D> altNameCache) { | |
| 3466 | + | |
| 3467 | + if (FontUtilities.isLogging()) { | |
| 3468 | + FontUtilities.getLogger() | |
| 3469 | + .info("Initialising composite fonts"); | |
| 3470 | + } | |
| 3471 | + | |
| 3472 | + int numCoreFonts = fontConfig.getNumberCoreFonts(); | |
| 3473 | + String[] fcFonts = fontConfig.getPlatformFontNames(); | |
| 3474 | + for (int f=0; f<fcFonts.length; f++) { | |
| 3475 | + String platformFontName = fcFonts[f]; | |
| 3476 | + String fontFileName = | |
| 3477 | + getFileNameFromPlatformName(platformFontName); | |
| 3478 | + String[] nativeNames = null; | |
| 3479 | + if (fontFileName == null | |
| 3480 | + || fontFileName.equals(platformFontName)) { | |
| 3481 | + /* No file located, so register using the platform name, | |
| 3482 | + * i.e. as a native font. | |
| 3483 | + */ | |
| 3484 | + fontFileName = platformFontName; | |
| 3485 | + } else { | |
| 3486 | + if (f < numCoreFonts) { | |
| 3487 | + /* If platform APIs also need to access the font, add it | |
| 3488 | + * to a set to be registered with the platform too. | |
| 3489 | + * This may be used to add the parent directory to the X11 | |
| 3490 | + * font path if its not already there. See the docs for the | |
| 3491 | + * subclass implementation. | |
| 3492 | + * This is now mainly for the benefit of X11-based AWT | |
| 3493 | + * But for historical reasons, 2D initialisation code | |
| 3494 | + * makes these calls. | |
| 3495 | + * If the fontconfiguration file is properly set up | |
| 3496 | + * so that all fonts are mapped to files and all their | |
| 3497 | + * appropriate directories are specified, then this | |
| 3498 | + * method will be low cost as it will return after | |
| 3499 | + * a test that finds a null lookup map. | |
| 3500 | + */ | |
| 3501 | + addFontToPlatformFontPath(platformFontName); | |
| 3502 | + } | |
| 3503 | + nativeNames = getNativeNames(fontFileName, platformFontName); | |
| 3504 | + } | |
| 3505 | + /* Uncomment these two lines to "generate" the XLFD->filename | |
| 3506 | + * mappings needed to speed start-up on Solaris. | |
| 3507 | + * Augment this with the appendedpathname and the mappings | |
| 3508 | + * for native (F3) fonts | |
| 3509 | + */ | |
| 3510 | + //String platName = platformFontName.replaceAll(" ", "_"); | |
| 3511 | + //System.out.println("filename."+platName+"="+fontFileName); | |
| 3512 | + registerFontFile(fontFileName, nativeNames, | |
| 3513 | + Font2D.FONT_CONFIG_RANK, true); | |
| 3514 | + | |
| 3515 | + | |
| 3516 | + } | |
| 3517 | + /* This registers accumulated paths from the calls to | |
| 3518 | + * addFontToPlatformFontPath(..) and any specified by | |
| 3519 | + * the font configuration. Rather than registering | |
| 3520 | + * the fonts it puts them in a place and form suitable for | |
| 3521 | + * the Toolkit to pick up and use if a toolkit is initialised, | |
| 3522 | + * and if it uses X11 fonts. | |
| 3523 | + */ | |
| 3524 | + registerPlatformFontsUsedByFontConfiguration(); | |
| 3525 | + | |
| 3526 | + CompositeFontDescriptor[] compositeFontInfo | |
| 3527 | + = fontConfig.get2DCompositeFontInfo(); | |
| 3528 | + for (int i = 0; i < compositeFontInfo.length; i++) { | |
| 3529 | + CompositeFontDescriptor descriptor = compositeFontInfo[i]; | |
| 3530 | + String[] componentFileNames = descriptor.getComponentFileNames(); | |
| 3531 | + String[] componentFaceNames = descriptor.getComponentFaceNames(); | |
| 3532 | + | |
| 3533 | + /* It would be better eventually to handle this in the | |
| 3534 | + * FontConfiguration code which should also remove duplicate slots | |
| 3535 | + */ | |
| 3536 | + if (missingFontFiles != null) { | |
| 3537 | + for (int ii=0; ii<componentFileNames.length; ii++) { | |
| 3538 | + if (missingFontFiles.contains(componentFileNames[ii])) { | |
| 3539 | + componentFileNames[ii] = getDefaultFontFile(); | |
| 3540 | + componentFaceNames[ii] = getDefaultFontFaceName(); | |
| 3541 | + } | |
| 3542 | + } | |
| 3543 | + } | |
| 3544 | + | |
| 3545 | + /* FontConfiguration needs to convey how many fonts it has added | |
| 3546 | + * as fallback component fonts which should not affect metrics. | |
| 3547 | + * The core component count will be the number of metrics slots. | |
| 3548 | + * This does not preclude other mechanisms for adding | |
| 3549 | + * fall back component fonts to the composite. | |
| 3550 | + */ | |
| 3551 | + if (altNameCache != null) { | |
| 3552 | + SunFontManager.registerCompositeFont( | |
| 3553 | + descriptor.getFaceName(), | |
| 3554 | + componentFileNames, componentFaceNames, | |
| 3555 | + descriptor.getCoreComponentCount(), | |
| 3556 | + descriptor.getExclusionRanges(), | |
| 3557 | + descriptor.getExclusionRangeLimits(), | |
| 3558 | + true, | |
| 3559 | + altNameCache); | |
| 3560 | + } else { | |
| 3561 | + registerCompositeFont(descriptor.getFaceName(), | |
| 3562 | + componentFileNames, componentFaceNames, | |
| 3563 | + descriptor.getCoreComponentCount(), | |
| 3564 | + descriptor.getExclusionRanges(), | |
| 3565 | + descriptor.getExclusionRangeLimits(), | |
| 3566 | + true); | |
| 3567 | + } | |
| 3568 | + if (FontUtilities.debugFonts()) { | |
| 3569 | + FontUtilities.getLogger() | |
| 3570 | + .info("registered " + descriptor.getFaceName()); | |
| 3571 | + } | |
| 3572 | + } | |
| 3573 | + } | |
| 3574 | + | |
| 3575 | + /** | |
| 3576 | + * Notifies graphics environment that the logical font configuration | |
| 3577 | + * uses the given platform font name. The graphics environment may | |
| 3578 | + * use this for platform specific initialization. | |
| 3579 | + */ | |
| 3580 | + protected void addFontToPlatformFontPath(String platformFontName) { | |
| 3581 | + } | |
| 3582 | + | |
| 3583 | + protected void registerFontFile(String fontFileName, String[] nativeNames, | |
| 3584 | + int fontRank, boolean defer) { | |
| 3585 | +// REMIND: case compare depends on platform | |
| 3586 | + if (registeredFontFiles.contains(fontFileName)) { | |
| 3587 | + return; | |
| 3588 | + } | |
| 3589 | + int fontFormat; | |
| 3590 | + if (ttFilter.accept(null, fontFileName)) { | |
| 3591 | + fontFormat = FONTFORMAT_TRUETYPE; | |
| 3592 | + } else if (t1Filter.accept(null, fontFileName)) { | |
| 3593 | + fontFormat = FONTFORMAT_TYPE1; | |
| 3594 | + } else { | |
| 3595 | + fontFormat = FONTFORMAT_NATIVE; | |
| 3596 | + } | |
| 3597 | + registeredFontFiles.add(fontFileName); | |
| 3598 | + if (defer) { | |
| 3599 | + registerDeferredFont(fontFileName, fontFileName, nativeNames, | |
| 3600 | + fontFormat, false, fontRank); | |
| 3601 | + } else { | |
| 3602 | + registerFontFile(fontFileName, nativeNames, fontFormat, false, | |
| 3603 | + fontRank); | |
| 3604 | + } | |
| 3605 | + } | |
| 3606 | + | |
| 3607 | + protected void registerPlatformFontsUsedByFontConfiguration() { | |
| 3608 | + } | |
| 3609 | + | |
| 3610 | + /* | |
| 3611 | + * A GE may verify whether a font file used in a fontconfiguration | |
| 3612 | + * exists. If it doesn't then either we may substitute the default | |
| 3613 | + * font, or perhaps elide it altogether from the composite font. | |
| 3614 | + * This makes some sense on windows where the font file is only | |
| 3615 | + * likely to be in one place. But on other OSes, eg Linux, the file | |
| 3616 | + * can move around depending. So there we probably don't want to assume | |
| 3617 | + * its missing and so won't add it to this list. | |
| 3618 | + * If this list - missingFontFiles - is non-null then the composite | |
| 3619 | + * font initialisation logic tests to see if a font file is in that | |
| 3620 | + * set. | |
| 3621 | + * Only one thread should be able to add to this set so we don't | |
| 3622 | + * synchronize. | |
| 3623 | + */ | |
| 3624 | + protected void addToMissingFontFileList(String fileName) { | |
| 3625 | + if (missingFontFiles == null) { | |
| 3626 | + missingFontFiles = new HashSet<String>(); | |
| 3627 | + } | |
| 3628 | + missingFontFiles.add(fileName); | |
| 3629 | + } | |
| 3630 | + | |
| 3631 | + /* | |
| 3632 | + * This is for use only within getAllFonts(). | |
| 3633 | + * Fonts listed in the fontconfig files for windows were all | |
| 3634 | + * on the "deferred" initialisation list. They were registered | |
| 3635 | + * either in the course of the application, or in the call to | |
| 3636 | + * loadFonts() within getAllFonts(). The fontconfig file specifies | |
| 3637 | + * the names of the fonts using the English names. If there's a | |
| 3638 | + * different name in the execution locale, then the platform will | |
| 3639 | + * report that, and we will construct the font with both names, and | |
| 3640 | + * thereby enumerate it twice. This happens for Japanese fonts listed | |
| 3641 | + * in the windows fontconfig, when run in the JA locale. The solution | |
| 3642 | + * is to rely (in this case) on the platform's font->file mapping to | |
| 3643 | + * determine that this name corresponds to a file we already registered. | |
| 3644 | + * This works because | |
| 3645 | + * - we know when we get here all deferred fonts are already initialised | |
| 3646 | + * - when we register a font file, we register all fonts in it. | |
| 3647 | + * - we know the fontconfig fonts are all in the windows registry | |
| 3648 | + */ | |
| 3649 | + private boolean isNameForRegisteredFile(String fontName) { | |
| 3650 | + String fileName = getFileNameForFontName(fontName); | |
| 3651 | + if (fileName == null) { | |
| 3652 | + return false; | |
| 3653 | + } | |
| 3654 | + return registeredFontFiles.contains(fileName); | |
| 3655 | + } | |
| 3656 | + | |
| 3657 | + /* | |
| 3658 | + * This invocation is not in a privileged block because | |
| 3659 | + * all privileged operations (reading files and properties) | |
| 3660 | + * was conducted on the creation of the GE | |
| 3661 | + */ | |
| 3662 | + public void | |
| 3663 | + createCompositeFonts(ConcurrentHashMap<String, Font2D> altNameCache, | |
| 3664 | + boolean preferLocale, | |
| 3665 | + boolean preferProportional) { | |
| 3666 | + | |
| 3667 | + FontConfiguration fontConfig = | |
| 3668 | + createFontConfiguration(preferLocale, preferProportional); | |
| 3669 | + initCompositeFonts(fontConfig, altNameCache); | |
| 3670 | + } | |
| 3671 | + | |
| 3672 | + /** | |
| 3673 | + * Returns all fonts installed in this environment. | |
| 3674 | + */ | |
| 3675 | + public Font[] getAllInstalledFonts() { | |
| 3676 | + if (allFonts == null) { | |
| 3677 | + loadFonts(); | |
| 3678 | + TreeMap fontMapNames = new TreeMap(); | |
| 3679 | + /* warning: the number of composite fonts could change dynamically | |
| 3680 | + * if applications are allowed to create them. "allfonts" could | |
| 3681 | + * then be stale. | |
| 3682 | + */ | |
| 3683 | + Font2D[] allfonts = getRegisteredFonts(); | |
| 3684 | + for (int i=0; i < allfonts.length; i++) { | |
| 3685 | + if (!(allfonts[i] instanceof NativeFont)) { | |
| 3686 | + fontMapNames.put(allfonts[i].getFontName(null), | |
| 3687 | + allfonts[i]); | |
| 3688 | + } | |
| 3689 | + } | |
| 3690 | + | |
| 3691 | + String[] platformNames = getFontNamesFromPlatform(); | |
| 3692 | + if (platformNames != null) { | |
| 3693 | + for (int i=0; i<platformNames.length; i++) { | |
| 3694 | + if (!isNameForRegisteredFile(platformNames[i])) { | |
| 3695 | + fontMapNames.put(platformNames[i], null); | |
| 3696 | + } | |
| 3697 | + } | |
| 3698 | + } | |
| 3699 | + | |
| 3700 | + String[] fontNames = null; | |
| 3701 | + if (fontMapNames.size() > 0) { | |
| 3702 | + fontNames = new String[fontMapNames.size()]; | |
| 3703 | + Object [] keyNames = fontMapNames.keySet().toArray(); | |
| 3704 | + for (int i=0; i < keyNames.length; i++) { | |
| 3705 | + fontNames[i] = (String)keyNames[i]; | |
| 3706 | + } | |
| 3707 | + } | |
| 3708 | + Font[] fonts = new Font[fontNames.length]; | |
| 3709 | + for (int i=0; i < fontNames.length; i++) { | |
| 3710 | + fonts[i] = new Font(fontNames[i], Font.PLAIN, 1); | |
| 3711 | + Font2D f2d = (Font2D)fontMapNames.get(fontNames[i]); | |
| 3712 | + if (f2d != null) { | |
| 3713 | + FontAccess.getFontAccess().setFont2D(fonts[i], f2d.handle); | |
| 3714 | + } | |
| 3715 | + } | |
| 3716 | + allFonts = fonts; | |
| 3717 | + } | |
| 3718 | + | |
| 3719 | + Font []copyFonts = new Font[allFonts.length]; | |
| 3720 | + System.arraycopy(allFonts, 0, copyFonts, 0, allFonts.length); | |
| 3721 | + return copyFonts; | |
| 3722 | + } | |
| 3723 | + | |
| 3724 | + /** | |
| 3725 | + * Get a list of installed fonts in the requested {@link Locale}. | |
| 3726 | + * The list contains the fonts Family Names. | |
| 3727 | + * If Locale is null, the default locale is used. | |
| 3728 | + * | |
| 3729 | + * @param requestedLocale, if null the default locale is used. | |
| 3730 | + * @return list of installed fonts in the system. | |
| 3731 | + */ | |
| 3732 | + public String[] getInstalledFontFamilyNames(Locale requestedLocale) { | |
| 3733 | + if (requestedLocale == null) { | |
| 3734 | + requestedLocale = Locale.getDefault(); | |
| 3735 | + } | |
| 3736 | + if (allFamilies != null && lastDefaultLocale != null && | |
| 3737 | + requestedLocale.equals(lastDefaultLocale)) { | |
| 3738 | + String[] copyFamilies = new String[allFamilies.length]; | |
| 3739 | + System.arraycopy(allFamilies, 0, copyFamilies, | |
| 3740 | + 0, allFamilies.length); | |
| 3741 | + return copyFamilies; | |
| 3742 | + } | |
| 3743 | + | |
| 3744 | + TreeMap<String,String> familyNames = new TreeMap<String,String>(); | |
| 3745 | + // these names are always there and aren't localised | |
| 3746 | + String str; | |
| 3747 | + str = Font.SERIF; familyNames.put(str.toLowerCase(), str); | |
| 3748 | + str = Font.SANS_SERIF; familyNames.put(str.toLowerCase(), str); | |
| 3749 | + str = Font.MONOSPACED; familyNames.put(str.toLowerCase(), str); | |
| 3750 | + str = Font.DIALOG; familyNames.put(str.toLowerCase(), str); | |
| 3751 | + str = Font.DIALOG_INPUT; familyNames.put(str.toLowerCase(), str); | |
| 3752 | + | |
| 3753 | + /* Platform APIs may be used to get the set of available family | |
| 3754 | + * names for the current default locale so long as it is the same | |
| 3755 | + * as the start-up system locale, rather than loading all fonts. | |
| 3756 | + */ | |
| 3757 | + if (requestedLocale.equals(getSystemStartupLocale()) && | |
| 3758 | + getFamilyNamesFromPlatform(familyNames, requestedLocale)) { | |
| 3759 | + /* Augment platform names with JRE font family names */ | |
| 3760 | + getJREFontFamilyNames(familyNames, requestedLocale); | |
| 3761 | + } else { | |
| 3762 | + loadFontFiles(); | |
| 3763 | + Font2D[] physicalfonts = getPhysicalFonts(); | |
| 3764 | + for (int i=0; i < physicalfonts.length; i++) { | |
| 3765 | + if (!(physicalfonts[i] instanceof NativeFont)) { | |
| 3766 | + String name = | |
| 3767 | + physicalfonts[i].getFamilyName(requestedLocale); | |
| 3768 | + familyNames.put(name.toLowerCase(requestedLocale), name); | |
| 3769 | + } | |
| 3770 | + } | |
| 3771 | + } | |
| 3772 | + | |
| 3773 | + String[] retval = new String[familyNames.size()]; | |
| 3774 | + Object [] keyNames = familyNames.keySet().toArray(); | |
| 3775 | + for (int i=0; i < keyNames.length; i++) { | |
| 3776 | + retval[i] = (String)familyNames.get(keyNames[i]); | |
| 3777 | + } | |
| 3778 | + if (requestedLocale.equals(Locale.getDefault())) { | |
| 3779 | + lastDefaultLocale = requestedLocale; | |
| 3780 | + allFamilies = new String[retval.length]; | |
| 3781 | + System.arraycopy(retval, 0, allFamilies, 0, allFamilies.length); | |
| 3782 | + } | |
| 3783 | + return retval; | |
| 3784 | + } | |
| 3785 | + | |
| 3786 | + public void register1dot0Fonts() { | |
| 3787 | + java.security.AccessController.doPrivileged( | |
| 3788 | + new java.security.PrivilegedAction() { | |
| 3789 | + public Object run() { | |
| 3790 | + String type1Dir = "/usr/openwin/lib/X11/fonts/Type1"; | |
| 3791 | + registerFontsInDir(type1Dir, true, Font2D.TYPE1_RANK, | |
| 3792 | + false, false); | |
| 3793 | + return null; | |
| 3794 | + } | |
| 3795 | + }); | |
| 3796 | + } | |
| 3797 | + | |
| 3798 | + /* Really we need only the JRE fonts family names, but there's little | |
| 3799 | + * overhead in doing this the easy way by adding all the currently | |
| 3800 | + * known fonts. | |
| 3801 | + */ | |
| 3802 | + protected void getJREFontFamilyNames(TreeMap<String,String> familyNames, | |
| 3803 | + Locale requestedLocale) { | |
| 3804 | + registerDeferredJREFonts(jreFontDirName); | |
| 3805 | + Font2D[] physicalfonts = getPhysicalFonts(); | |
| 3806 | + for (int i=0; i < physicalfonts.length; i++) { | |
| 3807 | + if (!(physicalfonts[i] instanceof NativeFont)) { | |
| 3808 | + String name = | |
| 3809 | + physicalfonts[i].getFamilyName(requestedLocale); | |
| 3810 | + familyNames.put(name.toLowerCase(requestedLocale), name); | |
| 3811 | + } | |
| 3812 | + } | |
| 3813 | + } | |
| 3814 | + | |
| 3815 | + /** | |
| 3816 | + * Default locale can be changed but we need to know the initial locale | |
| 3817 | + * as that is what is used by native code. Changing Java default locale | |
| 3818 | + * doesn't affect that. | |
| 3819 | + * Returns the locale in use when using native code to communicate | |
| 3820 | + * with platform APIs. On windows this is known as the "system" locale, | |
| 3821 | + * and it is usually the same as the platform locale, but not always, | |
| 3822 | + * so this method also checks an implementation property used only | |
| 3823 | + * on windows and uses that if set. | |
| 3824 | + */ | |
| 3825 | + private static Locale systemLocale = null; | |
| 3826 | + private static Locale getSystemStartupLocale() { | |
| 3827 | + if (systemLocale == null) { | |
| 3828 | + systemLocale = (Locale) | |
| 3829 | + java.security.AccessController.doPrivileged( | |
| 3830 | + new java.security.PrivilegedAction() { | |
| 3831 | + public Object run() { | |
| 3832 | + /* On windows the system locale may be different than the | |
| 3833 | + * user locale. This is an unsupported configuration, but | |
| 3834 | + * in that case we want to return a dummy locale that will | |
| 3835 | + * never cause a match in the usage of this API. This is | |
| 3836 | + * important because Windows documents that the family | |
| 3837 | + * names of fonts are enumerated using the language of | |
| 3838 | + * the system locale. BY returning a dummy locale in that | |
| 3839 | + * case we do not use the platform API which would not | |
| 3840 | + * return us the names we want. | |
| 3841 | + */ | |
| 3842 | + String fileEncoding = System.getProperty("file.encoding", ""); | |
| 3843 | + String sysEncoding = System.getProperty("sun.jnu.encoding"); | |
| 3844 | + if (sysEncoding != null && !sysEncoding.equals(fileEncoding)) { | |
| 3845 | + return Locale.ROOT; | |
| 3846 | + } | |
| 3847 | + | |
| 3848 | + String language = System.getProperty("user.language", "en"); | |
| 3849 | + String country = System.getProperty("user.country",""); | |
| 3850 | + String variant = System.getProperty("user.variant",""); | |
| 3851 | + return new Locale(language, country, variant); | |
| 3852 | + } | |
| 3853 | + }); | |
| 3854 | + } | |
| 3855 | + return systemLocale; | |
| 3856 | + } | |
| 3857 | + | |
| 3858 | + void addToPool(FileFont font) { | |
| 3859 | + | |
| 3860 | + FileFont fontFileToClose = null; | |
| 3861 | + int freeSlot = -1; | |
| 3862 | + | |
| 3863 | + synchronized (fontFileCache) { | |
| 3864 | + /* Avoid duplicate entries in the pool, and don't close() it, | |
| 3865 | + * since this method is called only from within open(). | |
| 3866 | + * Seeing a duplicate is most likely to happen if the thread | |
| 3867 | + * was interrupted during a read, forcing perhaps repeated | |
| 3868 | + * close and open calls and it eventually it ends up pointing | |
| 3869 | + * at the same slot. | |
| 3870 | + */ | |
| 3871 | + for (int i=0;i<CHANNELPOOLSIZE;i++) { | |
| 3872 | + if (fontFileCache[i] == font) { | |
| 3873 | + return; | |
| 3874 | + } | |
| 3875 | + if (fontFileCache[i] == null && freeSlot < 0) { | |
| 3876 | + freeSlot = i; | |
| 3877 | + } | |
| 3878 | + } | |
| 3879 | + if (freeSlot >= 0) { | |
| 3880 | + fontFileCache[freeSlot] = font; | |
| 3881 | + return; | |
| 3882 | + } else { | |
| 3883 | + /* replace with new font. */ | |
| 3884 | + fontFileToClose = fontFileCache[lastPoolIndex]; | |
| 3885 | + fontFileCache[lastPoolIndex] = font; | |
| 3886 | + /* lastPoolIndex is updated so that the least recently opened | |
| 3887 | + * file will be closed next. | |
| 3888 | + */ | |
| 3889 | + lastPoolIndex = (lastPoolIndex+1) % CHANNELPOOLSIZE; | |
| 3890 | + } | |
| 3891 | + } | |
| 3892 | + /* Need to close the font file outside of the synchronized block, | |
| 3893 | + * since its possible some other thread is in an open() call on | |
| 3894 | + * this font file, and could be holding its lock and the pool lock. | |
| 3895 | + * Releasing the pool lock allows that thread to continue, so it can | |
| 3896 | + * then release the lock on this font, allowing the close() call | |
| 3897 | + * below to proceed. | |
| 3898 | + * Also, calling close() is safe because any other thread using | |
| 3899 | + * the font we are closing() synchronizes all reading, so we | |
| 3900 | + * will not close the file while its in use. | |
| 3901 | + */ | |
| 3902 | + if (fontFileToClose != null) { | |
| 3903 | + fontFileToClose.close(); | |
| 3904 | + } | |
| 3905 | + } | |
| 3906 | + | |
| 3907 | + protected FontUIResource getFontConfigFUIR(String family, int style, | |
| 3908 | + int size) | |
| 3909 | + { | |
| 3910 | + return new FontUIResource(family, style, size); | |
| 3911 | + } | |
| 3912 | +} | |