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