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.misc.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 default:
1165
1166 }
1167 if (FontUtilities.isLogging()) {
1168 FontUtilities.getLogger()
1169 .info("Registered file " + fileName + " as font " +
1170 physicalFont + " rank=" + fontRank);
1171 }
1172 } catch (FontFormatException ffe) {
1173 if (FontUtilities.isLogging()) {
1174 FontUtilities.getLogger().warning("Unusable font: " +
1175 fileName + " " + ffe.toString());
1176 }
1177 }
1178 if (physicalFont != null &&
1179 fontFormat != FONTFORMAT_NATIVE) {
1180 registeredFonts.put(fileName, physicalFont);
1181 }
1182 return physicalFont;
1183 }
1184
1185 public void registerFonts(String[] fileNames,
1186 String[][] nativeNames,
1187 int fontCount,
1188 int fontFormat,
1189 boolean useJavaRasterizer,
1190 int fontRank, boolean defer) {
1191
1192 for (int i=0; i < fontCount; i++) {
1193 if (defer) {
1194 registerDeferredFont(fileNames[i],fileNames[i], nativeNames[i],
1195 fontFormat, useJavaRasterizer, fontRank);
1196 } else {
1197 registerFontFile(fileNames[i], nativeNames[i],
1198 fontFormat, useJavaRasterizer, fontRank);
1199 }
1200 }
1201 }
1202
1203 /*
1204 * This is the Physical font used when some other font on the system
1205 * can't be located. There has to be at least one font or the font
1206 * system is not useful and the graphics environment cannot sustain
1207 * the Java platform.
1208 */
1209 public PhysicalFont getDefaultPhysicalFont() {
1210 if (defaultPhysicalFont == null) {
1211 /* findFont2D will load all fonts before giving up the search.
1212 * If the JRE Lucida isn't found (eg because the JRE fonts
1213 * directory is missing), it could find another version of Lucida
1214 * from the host system. This is OK because at that point we are
1215 * trying to gracefully handle/recover from a system
1216 * misconfiguration and this is probably a reasonable substitution.
1217 */
1218 defaultPhysicalFont = (PhysicalFont)
1219 findFont2D("Lucida Sans Regular", Font.PLAIN, NO_FALLBACK);
1220 if (defaultPhysicalFont == null) {
1221 defaultPhysicalFont = (PhysicalFont)
1222 findFont2D("Arial", Font.PLAIN, NO_FALLBACK);
1223 }
1224 if (defaultPhysicalFont == null) {
1225 /* Because of the findFont2D call above, if we reach here, we
1226 * know all fonts have already been loaded, just accept any
1227 * match at this point. If this fails we are in real trouble
1228 * and I don't know how to recover from there being absolutely
1229 * no fonts anywhere on the system.
1230 */
1231 Iterator<PhysicalFont> i = physicalFonts.values().iterator();
1232 if (i.hasNext()) {
1233 defaultPhysicalFont = i.next();
1234 } else {
1235 throw new Error("Probable fatal error:No fonts found.");
1236 }
1237 }
1238 }
1239 return defaultPhysicalFont;
1240 }
1241
1242 public Font2D getDefaultLogicalFont(int style) {
1243 return findFont2D("dialog", style, NO_FALLBACK);
1244 }
1245
1246 /*
1247 * return String representation of style prepended with "."
1248 * This is useful for performance to avoid unnecessary string operations.
1249 */
1250 private static String dotStyleStr(int num) {
1251 switch(num){
1252 case Font.BOLD:
1253 return ".bold";
1254 case Font.ITALIC:
1255 return ".italic";
1256 case Font.ITALIC | Font.BOLD:
1257 return ".bolditalic";
1258 default:
1259 return ".plain";
1260 }
1261 }
1262
1263 /* This is implemented only on windows and is called from code that
1264 * executes only on windows. This isn't pretty but its not a precedent
1265 * in this file. This very probably should be cleaned up at some point.
1266 */
1267 protected void
1268 populateFontFileNameMap(HashMap<String,String> fontToFileMap,
1269 HashMap<String,String> fontToFamilyNameMap,
1270 HashMap<String,ArrayList<String>>
1271 familyToFontListMap,
1272 Locale locale) {
1273 }
1274
1275 /* Obtained from Platform APIs (windows only)
1276 * Map from lower-case font full name to basename of font file.
1277 * Eg "arial bold" -> ARIALBD.TTF.
1278 * For TTC files, there is a mapping for each font in the file.
1279 */
1280 private HashMap<String,String> fontToFileMap = null;
1281
1282 /* Obtained from Platform APIs (windows only)
1283 * Map from lower-case font full name to the name of its font family
1284 * Eg "arial bold" -> "Arial"
1285 */
1286 private HashMap<String,String> fontToFamilyNameMap = null;
1287
1288 /* Obtained from Platform APIs (windows only)
1289 * Map from a lower-case family name to a list of full names of
1290 * the member fonts, eg:
1291 * "arial" -> ["Arial", "Arial Bold", "Arial Italic","Arial Bold Italic"]
1292 */
1293 private HashMap<String,ArrayList<String>> familyToFontListMap= null;
1294
1295 /* The directories which contain platform fonts */
1296 private String[] pathDirs = null;
1297
1298 private boolean haveCheckedUnreferencedFontFiles;
1299
1300 private String[] getFontFilesFromPath(boolean noType1) {
1301 final FilenameFilter filter;
1302 if (noType1) {
1303 filter = ttFilter;
1304 } else {
1305 filter = new TTorT1Filter();
1306 }
1307 return (String[])AccessController.doPrivileged(new PrivilegedAction<Object>() {
1308 public Object run() {
1309 if (pathDirs.length == 1) {
1310 File dir = new File(pathDirs[0]);
1311 String[] files = dir.list(filter);
1312 if (files == null) {
1313 return new String[0];
1314 }
1315 for (int f=0; f<files.length; f++) {
1316 files[f] = files[f].toLowerCase();
1317 }
1318 return files;
1319 } else {
1320 ArrayList<String> fileList = new ArrayList<String>();
1321 for (int i = 0; i< pathDirs.length; i++) {
1322 File dir = new File(pathDirs[i]);
1323 String[] files = dir.list(filter);
1324 if (files == null) {
1325 continue;
1326 }
1327 for (int f=0; f<files.length ; f++) {
1328 fileList.add(files[f].toLowerCase());
1329 }
1330 }
1331 return fileList.toArray(STR_ARRAY);
1332 }
1333 }
1334 });
1335 }
1336
1337 /* This is needed since some windows registry names don't match
1338 * the font names.
1339 * - UPC styled font names have a double space, but the
1340 * registry entry mapping to a file doesn't.
1341 * - Marlett is in a hidden file not listed in the registry
1342 * - The registry advertises that the file david.ttf contains a
1343 * font with the full name "David Regular" when in fact its
1344 * just "David".
1345 * Directly fix up these known cases as this is faster.
1346 * If a font which doesn't match these known cases has no file,
1347 * it may be a font that has been temporarily added to the known set
1348 * or it may be an installed font with a missing registry entry.
1349 * Installed fonts are those in the windows font directories.
1350 * Make a best effort attempt to locate these.
1351 * We obtain the list of TrueType fonts in these directories and
1352 * filter out all the font files we already know about from the registry.
1353 * What remains may be "bad" fonts, duplicate fonts, or perhaps the
1354 * missing font(s) we are looking for.
1355 * Open each of these files to find out.
1356 */
1357 private void resolveWindowsFonts() {
1358
1359 ArrayList<String> unmappedFontNames = null;
1360 for (String font : fontToFamilyNameMap.keySet()) {
1361 String file = fontToFileMap.get(font);
1362 if (file == null) {
1363 if (font.indexOf(" ") > 0) {
1364 String newName = font.replaceFirst(" ", " ");
1365 file = fontToFileMap.get(newName);
1366 /* If this name exists and isn't for a valid name
1367 * replace the mapping to the file with this font
1368 */
1369 if (file != null &&
1370 !fontToFamilyNameMap.containsKey(newName)) {
1371 fontToFileMap.remove(newName);
1372 fontToFileMap.put(font, file);
1373 }
1374 } else if (font.equals("marlett")) {
1375 fontToFileMap.put(font, "marlett.ttf");
1376 } else if (font.equals("david")) {
1377 file = fontToFileMap.get("david regular");
1378 if (file != null) {
1379 fontToFileMap.remove("david regular");
1380 fontToFileMap.put("david", file);
1381 }
1382 } else {
1383 if (unmappedFontNames == null) {
1384 unmappedFontNames = new ArrayList<String>();
1385 }
1386 unmappedFontNames.add(font);
1387 }
1388 }
1389 }
1390
1391 if (unmappedFontNames != null) {
1392 HashSet<String> unmappedFontFiles = new HashSet<String>();
1393
1394 /* Every font key in fontToFileMap ought to correspond to a
1395 * font key in fontToFamilyNameMap. Entries that don't seem
1396 * to correspond are likely fonts that were named differently
1397 * by GDI than in the registry. One known cause of this is when
1398 * Windows has had its regional settings changed so that from
1399 * GDI we get a localised (eg Chinese or Japanese) name for the
1400 * font, but the registry retains the English version of the name
1401 * that corresponded to the "install" locale for windows.
1402 * Since we are in this code block because there are unmapped
1403 * font names, we can look to find unused font->file mappings
1404 * and then open the files to read the names. We don't generally
1405 * want to open font files, as its a performance hit, but this
1406 * occurs only for a small number of fonts on specific system
1407 * configs - ie is believed that a "true" Japanese windows would
1408 * have JA names in the registry too.
1409 * Clone fontToFileMap and remove from the clone all keys which
1410 * match a fontToFamilyNameMap key. What remains maps to the
1411 * files we want to open to find the fonts GDI returned.
1412 * A font in such a file is added to the fontToFileMap after
1413 * checking its one of the unmappedFontNames we are looking for.
1414 * The original name that didn't map is removed from fontToFileMap
1415 * so essentially this "fixes up" fontToFileMap to use the same
1416 * name as GDI.
1417 * Also note that typically the fonts for which this occurs in
1418 * CJK locales are TTC fonts and not all fonts in a TTC may have
1419 * localised names. Eg MSGOTHIC.TTC contains 3 fonts and one of
1420 * them "MS UI Gothic" has no JA name whereas the other two do.
1421 * So not every font in these files is unmapped or new.
1422 */
1423 @SuppressWarnings("unchecked")
1424 HashMap<String,String> ffmapCopy =
1425 (HashMap<String,String>)(fontToFileMap.clone());
1426 for (String key : fontToFamilyNameMap.keySet()) {
1427 ffmapCopy.remove(key);
1428 }
1429 for (String key : ffmapCopy.keySet()) {
1430 unmappedFontFiles.add(ffmapCopy.get(key));
1431 fontToFileMap.remove(key);
1432 }
1433
1434 resolveFontFiles(unmappedFontFiles, unmappedFontNames);
1435
1436 /* If there are still unmapped font names, this means there's
1437 * something that wasn't in the registry. We need to get all
1438 * the font files directly and look at the ones that weren't
1439 * found in the registry.
1440 */
1441 if (unmappedFontNames.size() > 0) {
1442
1443 /* getFontFilesFromPath() returns all lower case names.
1444 * To compare we also need lower case
1445 * versions of the names from the registry.
1446 */
1447 ArrayList<String> registryFiles = new ArrayList<String>();
1448
1449 for (String regFile : fontToFileMap.values()) {
1450 registryFiles.add(regFile.toLowerCase());
1451 }
1452 /* We don't look for Type1 files here as windows will
1453 * not enumerate these, so aren't useful in reconciling
1454 * GDI's unmapped files. We do find these later when
1455 * we enumerate all fonts.
1456 */
1457 for (String pathFile : getFontFilesFromPath(true)) {
1458 if (!registryFiles.contains(pathFile)) {
1459 unmappedFontFiles.add(pathFile);
1460 }
1461 }
1462
1463 resolveFontFiles(unmappedFontFiles, unmappedFontNames);
1464 }
1465
1466 /* remove from the set of names that will be returned to the
1467 * user any fonts that can't be mapped to files.
1468 */
1469 if (unmappedFontNames.size() > 0) {
1470 int sz = unmappedFontNames.size();
1471 for (int i=0; i<sz; i++) {
1472 String name = unmappedFontNames.get(i);
1473 String familyName = fontToFamilyNameMap.get(name);
1474 if (familyName != null) {
1475 ArrayList<String> family = familyToFontListMap.get(familyName);
1476 if (family != null) {
1477 if (family.size() <= 1) {
1478 familyToFontListMap.remove(familyName);
1479 }
1480 }
1481 }
1482 fontToFamilyNameMap.remove(name);
1483 if (FontUtilities.isLogging()) {
1484 FontUtilities.getLogger()
1485 .info("No file for font:" + name);
1486 }
1487 }
1488 }
1489 }
1490 }
1491
1492 /**
1493 * In some cases windows may have fonts in the fonts folder that
1494 * don't show up in the registry or in the GDI calls to enumerate fonts.
1495 * The only way to find these is to list the directory. We invoke this
1496 * only in getAllFonts/Families, so most searches for a specific
1497 * font that is satisfied by the GDI/registry calls don't take the
1498 * additional hit of listing the directory. This hit is small enough
1499 * that its not significant in these 'enumerate all the fonts' cases.
1500 * The basic approach is to cross-reference the files windows found
1501 * with the ones in the directory listing approach, and for each
1502 * in the latter list that is missing from the former list, register it.
1503 */
1504 private synchronized void checkForUnreferencedFontFiles() {
1505 if (haveCheckedUnreferencedFontFiles) {
1506 return;
1507 }
1508 haveCheckedUnreferencedFontFiles = true;
1509 if (!FontUtilities.isWindows) {
1510 return;
1511 }
1512 /* getFontFilesFromPath() returns all lower case names.
1513 * To compare we also need lower case
1514 * versions of the names from the registry.
1515 */
1516 ArrayList<String> registryFiles = new ArrayList<String>();
1517 for (String regFile : fontToFileMap.values()) {
1518 registryFiles.add(regFile.toLowerCase());
1519 }
1520
1521 /* To avoid any issues with concurrent modification, create
1522 * copies of the existing maps, add the new fonts into these
1523 * and then replace the references to the old ones with the
1524 * new maps. ConcurrentHashmap is another option but its a lot
1525 * more changes and with this exception, these maps are intended
1526 * to be static.
1527 */
1528 HashMap<String,String> fontToFileMap2 = null;
1529 HashMap<String,String> fontToFamilyNameMap2 = null;
1530 HashMap<String,ArrayList<String>> familyToFontListMap2 = null;;
1531
1532 for (String pathFile : getFontFilesFromPath(false)) {
1533 if (!registryFiles.contains(pathFile)) {
1534 if (FontUtilities.isLogging()) {
1535 FontUtilities.getLogger()
1536 .info("Found non-registry file : " + pathFile);
1537 }
1538 PhysicalFont f = registerFontFile(getPathName(pathFile));
1539 if (f == null) {
1540 continue;
1541 }
1542 if (fontToFileMap2 == null) {
1543 fontToFileMap2 = new HashMap<String,String>(fontToFileMap);
1544 fontToFamilyNameMap2 =
1545 new HashMap<String,String>(fontToFamilyNameMap);
1546 familyToFontListMap2 = new
1547 HashMap<String,ArrayList<String>>(familyToFontListMap);
1548 }
1549 String fontName = f.getFontName(null);
1550 String family = f.getFamilyName(null);
1551 String familyLC = family.toLowerCase();
1552 fontToFamilyNameMap2.put(fontName, family);
1553 fontToFileMap2.put(fontName, pathFile);
1554 ArrayList<String> fonts = familyToFontListMap2.get(familyLC);
1555 if (fonts == null) {
1556 fonts = new ArrayList<String>();
1557 } else {
1558 fonts = new ArrayList<String>(fonts);
1559 }
1560 fonts.add(fontName);
1561 familyToFontListMap2.put(familyLC, fonts);
1562 }
1563 }
1564 if (fontToFileMap2 != null) {
1565 fontToFileMap = fontToFileMap2;
1566 familyToFontListMap = familyToFontListMap2;
1567 fontToFamilyNameMap = fontToFamilyNameMap2;
1568 }
1569 }
1570
1571 private void resolveFontFiles(HashSet<String> unmappedFiles,
1572 ArrayList<String> unmappedFonts) {
1573
1574 Locale l = SunToolkit.getStartupLocale();
1575
1576 for (String file : unmappedFiles) {
1577 try {
1578 int fn = 0;
1579 TrueTypeFont ttf;
1580 String fullPath = getPathName(file);
1581 if (FontUtilities.isLogging()) {
1582 FontUtilities.getLogger()
1583 .info("Trying to resolve file " + fullPath);
1584 }
1585 do {
1586 ttf = new TrueTypeFont(fullPath, null, fn++, false);
1587 // prefer the font's locale name.
1588 String fontName = ttf.getFontName(l).toLowerCase();
1589 if (unmappedFonts.contains(fontName)) {
1590 fontToFileMap.put(fontName, file);
1591 unmappedFonts.remove(fontName);
1592 if (FontUtilities.isLogging()) {
1593 FontUtilities.getLogger()
1594 .info("Resolved absent registry entry for " +
1595 fontName + " located in " + fullPath);
1596 }
1597 }
1598 }
1599 while (fn < ttf.getFontCount());
1600 } catch (Exception e) {
1601 }
1602 }
1603 }
1604
1605 /* Hardwire the English names and expected file names of fonts
1606 * commonly used at start up. Avoiding until later even the small
1607 * cost of calling platform APIs to locate these can help.
1608 * The code that registers these fonts needs to "bail" if any
1609 * of the files do not exist, so it will verify the existence of
1610 * all non-null file names first.
1611 * They are added in to a map with nominally the first
1612 * word in the name of the family as the key. In all the cases
1613 * we are using the the family name is a single word, and as is
1614 * more or less required the family name is the initial sequence
1615 * in a full name. So lookup first finds the matching description,
1616 * then registers the whole family, returning the right font.
1617 */
1618 public static class FamilyDescription {
1619 public String familyName;
1620 public String plainFullName;
1621 public String boldFullName;
1622 public String italicFullName;
1623 public String boldItalicFullName;
1624 public String plainFileName;
1625 public String boldFileName;
1626 public String italicFileName;
1627 public String boldItalicFileName;
1628 }
1629
1630 static HashMap<String, FamilyDescription> platformFontMap;
1631
1632 /**
1633 * default implementation does nothing.
1634 */
1635 public HashMap<String, FamilyDescription> populateHardcodedFileNameMap() {
1636 return new HashMap<String, FamilyDescription>(0);
1637 }
1638
1639 Font2D findFontFromPlatformMap(String lcName, int style) {
1640 if (platformFontMap == null) {
1641 platformFontMap = populateHardcodedFileNameMap();
1642 }
1643
1644 if (platformFontMap == null || platformFontMap.size() == 0) {
1645 return null;
1646 }
1647
1648 int spaceIndex = lcName.indexOf(' ');
1649 String firstWord = lcName;
1650 if (spaceIndex > 0) {
1651 firstWord = lcName.substring(0, spaceIndex);
1652 }
1653
1654 FamilyDescription fd = platformFontMap.get(firstWord);
1655 if (fd == null) {
1656 return null;
1657 }
1658 /* Once we've established that its at least the first word,
1659 * we need to dig deeper to make sure its a match for either
1660 * a full name, or the family name, to make sure its not
1661 * a request for some other font that just happens to start
1662 * with the same first word.
1663 */
1664 int styleIndex = -1;
1665 if (lcName.equalsIgnoreCase(fd.plainFullName)) {
1666 styleIndex = 0;
1667 } else if (lcName.equalsIgnoreCase(fd.boldFullName)) {
1668 styleIndex = 1;
1669 } else if (lcName.equalsIgnoreCase(fd.italicFullName)) {
1670 styleIndex = 2;
1671 } else if (lcName.equalsIgnoreCase(fd.boldItalicFullName)) {
1672 styleIndex = 3;
1673 }
1674 if (styleIndex == -1 && !lcName.equalsIgnoreCase(fd.familyName)) {
1675 return null;
1676 }
1677
1678 String plainFile = null, boldFile = null,
1679 italicFile = null, boldItalicFile = null;
1680
1681 boolean failure = false;
1682 /* In a terminal server config, its possible that getPathName()
1683 * will return null, if the file doesn't exist, hence the null
1684 * checks on return. But in the normal client config we need to
1685 * follow this up with a check to see if all the files really
1686 * exist for the non-null paths.
1687 */
1688 getPlatformFontDirs(noType1Font);
1689
1690 if (fd.plainFileName != null) {
1691 plainFile = getPathName(fd.plainFileName);
1692 if (plainFile == null) {
1693 failure = true;
1694 }
1695 }
1696
1697 if (fd.boldFileName != null) {
1698 boldFile = getPathName(fd.boldFileName);
1699 if (boldFile == null) {
1700 failure = true;
1701 }
1702 }
1703
1704 if (fd.italicFileName != null) {
1705 italicFile = getPathName(fd.italicFileName);
1706 if (italicFile == null) {
1707 failure = true;
1708 }
1709 }
1710
1711 if (fd.boldItalicFileName != null) {
1712 boldItalicFile = getPathName(fd.boldItalicFileName);
1713 if (boldItalicFile == null) {
1714 failure = true;
1715 }
1716 }
1717
1718 if (failure) {
1719 if (FontUtilities.isLogging()) {
1720 FontUtilities.getLogger().
1721 info("Hardcoded file missing looking for " + lcName);
1722 }
1723 platformFontMap.remove(firstWord);
1724 return null;
1725 }
1726
1727 /* Some of these may be null,as not all styles have to exist */
1728 final String[] files = {
1729 plainFile, boldFile, italicFile, boldItalicFile } ;
1730
1731 failure = java.security.AccessController.doPrivileged(
1732 new java.security.PrivilegedAction<Boolean>() {
1733 public Boolean run() {
1734 for (int i=0; i<files.length; i++) {
1735 if (files[i] == null) {
1736 continue;
1737 }
1738 File f = new File(files[i]);
1739 if (!f.exists()) {
1740 return Boolean.TRUE;
1741 }
1742 }
1743 return Boolean.FALSE;
1744 }
1745 });
1746
1747 if (failure) {
1748 if (FontUtilities.isLogging()) {
1749 FontUtilities.getLogger().
1750 info("Hardcoded file missing looking for " + lcName);
1751 }
1752 platformFontMap.remove(firstWord);
1753 return null;
1754 }
1755
1756 /* If we reach here we know that we have all the files we
1757 * expect, so all should be fine so long as the contents
1758 * are what we'd expect. Now on to registering the fonts.
1759 * Currently this code only looks for TrueType fonts, so format
1760 * and rank can be specified without looking at the filename.
1761 */
1762 Font2D font = null;
1763 for (int f=0;f<files.length;f++) {
1764 if (files[f] == null) {
1765 continue;
1766 }
1767 PhysicalFont pf =
1768 registerFontFile(files[f], null,
1769 FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK);
1770 if (f == styleIndex) {
1771 font = pf;
1772 }
1773 }
1774
1775
1776 /* Two general cases need a bit more work here.
1777 * 1) If font is null, then it was perhaps a request for a
1778 * non-existent font, such as "Tahoma Italic", or a family name -
1779 * where family and full name of the plain font differ.
1780 * Fall back to finding the closest one in the family.
1781 * This could still fail if a client specified "Segoe" instead of
1782 * "Segoe UI".
1783 * 2) The request is of the form "MyFont Bold", style=Font.ITALIC,
1784 * and so we want to see if there's a Bold Italic font, or
1785 * "MyFamily", style=Font.BOLD, and we may have matched the plain,
1786 * but now need to revise that to the BOLD font.
1787 */
1788 FontFamily fontFamily = FontFamily.getFamily(fd.familyName);
1789 if (fontFamily != null) {
1790 if (font == null) {
1791 font = fontFamily.getFont(style);
1792 if (font == null) {
1793 font = fontFamily.getClosestStyle(style);
1794 }
1795 } else if (style > 0 && style != font.style) {
1796 style |= font.style;
1797 font = fontFamily.getFont(style);
1798 if (font == null) {
1799 font = fontFamily.getClosestStyle(style);
1800 }
1801 }
1802 }
1803
1804 return font;
1805 }
1806 private synchronized HashMap<String,String> getFullNameToFileMap() {
1807 if (fontToFileMap == null) {
1808
1809 pathDirs = getPlatformFontDirs(noType1Font);
1810
1811 fontToFileMap = new HashMap<String,String>(100);
1812 fontToFamilyNameMap = new HashMap<String,String>(100);
1813 familyToFontListMap = new HashMap<String,ArrayList<String>>(50);
1814 populateFontFileNameMap(fontToFileMap,
1815 fontToFamilyNameMap,
1816 familyToFontListMap,
1817 Locale.ENGLISH);
1818 if (FontUtilities.isWindows) {
1819 resolveWindowsFonts();
1820 }
1821 if (FontUtilities.isLogging()) {
1822 logPlatformFontInfo();
1823 }
1824 }
1825 return fontToFileMap;
1826 }
1827
1828 private void logPlatformFontInfo() {
1829 PlatformLogger logger = FontUtilities.getLogger();
1830 for (int i=0; i< pathDirs.length;i++) {
1831 logger.info("fontdir="+pathDirs[i]);
1832 }
1833 for (String keyName : fontToFileMap.keySet()) {
1834 logger.info("font="+keyName+" file="+ fontToFileMap.get(keyName));
1835 }
1836 for (String keyName : fontToFamilyNameMap.keySet()) {
1837 logger.info("font="+keyName+" family="+
1838 fontToFamilyNameMap.get(keyName));
1839 }
1840 for (String keyName : familyToFontListMap.keySet()) {
1841 logger.info("family="+keyName+ " fonts="+
1842 familyToFontListMap.get(keyName));
1843 }
1844 }
1845
1846 /* Note this return list excludes logical fonts and JRE fonts */
1847 protected String[] getFontNamesFromPlatform() {
1848 if (getFullNameToFileMap().size() == 0) {
1849 return null;
1850 }
1851 checkForUnreferencedFontFiles();
1852 /* This odd code with TreeMap is used to preserve a historical
1853 * behaviour wrt the sorting order .. */
1854 ArrayList<String> fontNames = new ArrayList<String>();
1855 for (ArrayList<String> a : familyToFontListMap.values()) {
1856 for (String s : a) {
1857 fontNames.add(s);
1858 }
1859 }
1860 return fontNames.toArray(STR_ARRAY);
1861 }
1862
1863 public boolean gotFontsFromPlatform() {
1864 return getFullNameToFileMap().size() != 0;
1865 }
1866
1867 public String getFileNameForFontName(String fontName) {
1868 String fontNameLC = fontName.toLowerCase(Locale.ENGLISH);
1869 return fontToFileMap.get(fontNameLC);
1870 }
1871
1872 private PhysicalFont registerFontFile(String file) {
1873 if (new File(file).isAbsolute() &&
1874 !registeredFonts.contains(file)) {
1875 int fontFormat = FONTFORMAT_NONE;
1876 int fontRank = Font2D.UNKNOWN_RANK;
1877 if (ttFilter.accept(null, file)) {
1878 fontFormat = FONTFORMAT_TRUETYPE;
1879 fontRank = Font2D.TTF_RANK;
1880 } else if
1881 (t1Filter.accept(null, file)) {
1882 fontFormat = FONTFORMAT_TYPE1;
1883 fontRank = Font2D.TYPE1_RANK;
1884 }
1885 if (fontFormat == FONTFORMAT_NONE) {
1886 return null;
1887 }
1888 return registerFontFile(file, null, fontFormat, false, fontRank);
1889 }
1890 return null;
1891 }
1892
1893 /* Used to register any font files that are found by platform APIs
1894 * that weren't previously found in the standard font locations.
1895 * the isAbsolute() check is needed since that's whats stored in the
1896 * set, and on windows, the fonts in the system font directory that
1897 * are in the fontToFileMap are just basenames. We don't want to try
1898 * to register those again, but we do want to register other registry
1899 * installed fonts.
1900 */
1901 protected void registerOtherFontFiles(HashSet<String> registeredFontFiles) {
1902 if (getFullNameToFileMap().size() == 0) {
1903 return;
1904 }
1905 for (String file : fontToFileMap.values()) {
1906 registerFontFile(file);
1907 }
1908 }
1909
1910 public boolean
1911 getFamilyNamesFromPlatform(TreeMap<String,String> familyNames,
1912 Locale requestedLocale) {
1913 if (getFullNameToFileMap().size() == 0) {
1914 return false;
1915 }
1916 checkForUnreferencedFontFiles();
1917 for (String name : fontToFamilyNameMap.values()) {
1918 familyNames.put(name.toLowerCase(requestedLocale), name);
1919 }
1920 return true;
1921 }
1922
1923 /* Path may be absolute or a base file name relative to one of
1924 * the platform font directories
1925 */
1926 private String getPathName(final String s) {
1927 File f = new File(s);
1928 if (f.isAbsolute()) {
1929 return s;
1930 } else if (pathDirs.length==1) {
1931 return pathDirs[0] + File.separator + s;
1932 } else {
1933 String path = java.security.AccessController.doPrivileged(
1934 new java.security.PrivilegedAction<String>() {
1935 public String run() {
1936 for (int p=0; p<pathDirs.length; p++) {
1937 File f = new File(pathDirs[p] +File.separator+ s);
1938 if (f.exists()) {
1939 return f.getAbsolutePath();
1940 }
1941 }
1942 return null;
1943 }
1944 });
1945 if (path != null) {
1946 return path;
1947 }
1948 }
1949 return s; // shouldn't happen, but harmless
1950 }
1951
1952 /* lcName is required to be lower case for use as a key.
1953 * lcName may be a full name, or a family name, and style may
1954 * be specified in addition to either of these. So be sure to
1955 * get the right one. Since an app *could* ask for "Foo Regular"
1956 * and later ask for "Foo Italic", if we don't register all the
1957 * styles, then logic in findFont2D may try to style the original
1958 * so we register the entire family if we get a match here.
1959 * This is still a big win because this code is invoked where
1960 * otherwise we would register all fonts.
1961 * It's also useful for the case where "Foo Bold" was specified with
1962 * style Font.ITALIC, as we would want in that case to try to return
1963 * "Foo Bold Italic" if it exists, and it is only by locating "Foo Bold"
1964 * and opening it that we really "know" it's Bold, and can look for
1965 * a font that supports that and the italic style.
1966 * The code in here is not overtly windows-specific but in fact it
1967 * is unlikely to be useful as is on other platforms. It is maintained
1968 * in this shared source file to be close to its sole client and
1969 * because so much of the logic is intertwined with the logic in
1970 * findFont2D.
1971 */
1972 private Font2D findFontFromPlatform(String lcName, int style) {
1973 if (getFullNameToFileMap().size() == 0) {
1974 return null;
1975 }
1976
1977 ArrayList<String> family = null;
1978 String fontFile = null;
1979 String familyName = fontToFamilyNameMap.get(lcName);
1980 if (familyName != null) {
1981 fontFile = fontToFileMap.get(lcName);
1982 family = familyToFontListMap.get
1983 (familyName.toLowerCase(Locale.ENGLISH));
1984 } else {
1985 family = familyToFontListMap.get(lcName); // is lcName is a family?
1986 if (family != null && family.size() > 0) {
1987 String lcFontName = family.get(0).toLowerCase(Locale.ENGLISH);
1988 if (lcFontName != null) {
1989 familyName = fontToFamilyNameMap.get(lcFontName);
1990 }
1991 }
1992 }
1993 if (family == null || familyName == null) {
1994 return null;
1995 }
1996 String [] fontList = family.toArray(STR_ARRAY);
1997 if (fontList.length == 0) {
1998 return null;
1999 }
2000
2001 /* first check that for every font in this family we can find
2002 * a font file. The specific reason for doing this is that
2003 * in at least one case on Windows a font has the face name "David"
2004 * but the registry entry is "David Regular". That is the "unique"
2005 * name of the font but in other cases the registry contains the
2006 * "full" name. See the specifications of name ids 3 and 4 in the
2007 * TrueType 'name' table.
2008 * In general this could cause a problem that we fail to register
2009 * if we all members of a family that we may end up mapping to
2010 * the wrong font member: eg return Bold when Plain is needed.
2011 */
2012 for (int f=0;f<fontList.length;f++) {
2013 String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH);
2014 String fileName = fontToFileMap.get(fontNameLC);
2015 if (fileName == null) {
2016 if (FontUtilities.isLogging()) {
2017 FontUtilities.getLogger()
2018 .info("Platform lookup : No file for font " +
2019 fontList[f] + " in family " +familyName);
2020 }
2021 return null;
2022 }
2023 }
2024
2025 /* Currently this code only looks for TrueType fonts, so format
2026 * and rank can be specified without looking at the filename.
2027 */
2028 PhysicalFont physicalFont = null;
2029 if (fontFile != null) {
2030 physicalFont = registerFontFile(getPathName(fontFile), null,
2031 FONTFORMAT_TRUETYPE, false,
2032 Font2D.TTF_RANK);
2033 }
2034 /* Register all fonts in this family. */
2035 for (int f=0;f<fontList.length;f++) {
2036 String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH);
2037 String fileName = fontToFileMap.get(fontNameLC);
2038 if (fontFile != null && fontFile.equals(fileName)) {
2039 continue;
2040 }
2041 /* Currently this code only looks for TrueType fonts, so format
2042 * and rank can be specified without looking at the filename.
2043 */
2044 registerFontFile(getPathName(fileName), null,
2045 FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK);
2046 }
2047
2048 Font2D font = null;
2049 FontFamily fontFamily = FontFamily.getFamily(familyName);
2050 /* Handle case where request "MyFont Bold", style=Font.ITALIC */
2051 if (physicalFont != null) {
2052 style |= physicalFont.style;
2053 }
2054 if (fontFamily != null) {
2055 font = fontFamily.getFont(style);
2056 if (font == null) {
2057 font = fontFamily.getClosestStyle(style);
2058 }
2059 }
2060 return font;
2061 }
2062
2063 private ConcurrentHashMap<String, Font2D> fontNameCache =
2064 new ConcurrentHashMap<String, Font2D>();
2065
2066 /*
2067 * The client supplies a name and a style.
2068 * The name could be a family name, or a full name.
2069 * A font may exist with the specified style, or it may
2070 * exist only in some other style. For non-native fonts the scaler
2071 * may be able to emulate the required style.
2072 */
2073 public Font2D findFont2D(String name, int style, int fallback) {
2074 String lowerCaseName = name.toLowerCase(Locale.ENGLISH);
2075 String mapName = lowerCaseName + dotStyleStr(style);
2076 Font2D font;
2077
2078 /* If preferLocaleFonts() or preferProportionalFonts() has been
2079 * called we may be using an alternate set of composite fonts in this
2080 * app context. The presence of a pre-built name map indicates whether
2081 * this is so, and gives access to the alternate composite for the
2082 * name.
2083 */
2084 if (_usingPerAppContextComposites) {
2085 @SuppressWarnings("unchecked")
2086 ConcurrentHashMap<String, Font2D> altNameCache =
2087 (ConcurrentHashMap<String, Font2D>)
2088 AppContext.getAppContext().get(CompositeFont.class);
2089 if (altNameCache != null) {
2090 font = altNameCache.get(mapName);
2091 } else {
2092 font = null;
2093 }
2094 } else {
2095 font = fontNameCache.get(mapName);
2096 }
2097 if (font != null) {
2098 return font;
2099 }
2100
2101 if (FontUtilities.isLogging()) {
2102 FontUtilities.getLogger().info("Search for font: " + name);
2103 }
2104
2105 // The check below is just so that the bitmap fonts being set by
2106 // AWT and Swing thru the desktop properties do not trigger the
2107 // the load fonts case. The two bitmap fonts are now mapped to
2108 // appropriate equivalents for serif and sansserif.
2109 // Note that the cost of this comparison is only for the first
2110 // call until the map is filled.
2111 if (FontUtilities.isWindows) {
2112 if (lowerCaseName.equals("ms sans serif")) {
2113 name = "sansserif";
2114 } else if (lowerCaseName.equals("ms serif")) {
2115 name = "serif";
2116 }
2117 }
2118
2119 /* This isn't intended to support a client passing in the
2120 * string default, but if a client passes in null for the name
2121 * the java.awt.Font class internally substitutes this name.
2122 * So we need to recognise it here to prevent a loadFonts
2123 * on the unrecognised name. The only potential problem with
2124 * this is it would hide any real font called "default"!
2125 * But that seems like a potential problem we can ignore for now.
2126 */
2127 if (lowerCaseName.equals("default")) {
2128 name = "dialog";
2129 }
2130
2131 /* First see if its a family name. */
2132 FontFamily family = FontFamily.getFamily(name);
2133 if (family != null) {
2134 font = family.getFontWithExactStyleMatch(style);
2135 if (font == null) {
2136 font = findDeferredFont(name, style);
2137 }
2138 if (font == null) {
2139 font = family.getFont(style);
2140 }
2141 if (font == null) {
2142 font = family.getClosestStyle(style);
2143 }
2144 if (font != null) {
2145 fontNameCache.put(mapName, font);
2146 return font;
2147 }
2148 }
2149
2150 /* If it wasn't a family name, it should be a full name of
2151 * either a composite, or a physical font
2152 */
2153 font = fullNameToFont.get(lowerCaseName);
2154 if (font != null) {
2155 /* Check that the requested style matches the matched font's style.
2156 * But also match style automatically if the requested style is
2157 * "plain". This because the existing behaviour is that the fonts
2158 * listed via getAllFonts etc always list their style as PLAIN.
2159 * This does lead to non-commutative behaviours where you might
2160 * start with "Lucida Sans Regular" and ask for a BOLD version
2161 * and get "Lucida Sans DemiBold" but if you ask for the PLAIN
2162 * style of "Lucida Sans DemiBold" you get "Lucida Sans DemiBold".
2163 * This consistent however with what happens if you have a bold
2164 * version of a font and no plain version exists - alg. styling
2165 * doesn't "unbolden" the font.
2166 */
2167 if (font.style == style || style == Font.PLAIN) {
2168 fontNameCache.put(mapName, font);
2169 return font;
2170 } else {
2171 /* If it was a full name like "Lucida Sans Regular", but
2172 * the style requested is "bold", then we want to see if
2173 * there's the appropriate match against another font in
2174 * that family before trying to load all fonts, or applying a
2175 * algorithmic styling
2176 */
2177 family = FontFamily.getFamily(font.getFamilyName(null));
2178 if (family != null) {
2179 Font2D familyFont = family.getFont(style|font.style);
2180 /* We exactly matched the requested style, use it! */
2181 if (familyFont != null) {
2182 fontNameCache.put(mapName, familyFont);
2183 return familyFont;
2184 } else {
2185 /* This next call is designed to support the case
2186 * where bold italic is requested, and if we must
2187 * style, then base it on either bold or italic -
2188 * not on plain!
2189 */
2190 familyFont = family.getClosestStyle(style|font.style);
2191 if (familyFont != null) {
2192 /* The next check is perhaps one
2193 * that shouldn't be done. ie if we get this
2194 * far we have probably as close a match as we
2195 * are going to get. We could load all fonts to
2196 * see if somehow some parts of the family are
2197 * loaded but not all of it.
2198 */
2199 if (familyFont.canDoStyle(style|font.style)) {
2200 fontNameCache.put(mapName, familyFont);
2201 return familyFont;
2202 }
2203 }
2204 }
2205 }
2206 }
2207 }
2208
2209 if (FontUtilities.isWindows) {
2210
2211 font = findFontFromPlatformMap(lowerCaseName, style);
2212 if (FontUtilities.isLogging()) {
2213 FontUtilities.getLogger()
2214 .info("findFontFromPlatformMap returned " + font);
2215 }
2216 if (font != null) {
2217 fontNameCache.put(mapName, font);
2218 return font;
2219 }
2220
2221 /* Don't want Windows to return a Lucida Sans font from
2222 * C:\Windows\Fonts
2223 */
2224 if (deferredFontFiles.size() > 0) {
2225 font = findJREDeferredFont(lowerCaseName, style);
2226 if (font != null) {
2227 fontNameCache.put(mapName, font);
2228 return font;
2229 }
2230 }
2231 font = findFontFromPlatform(lowerCaseName, style);
2232 if (font != null) {
2233 if (FontUtilities.isLogging()) {
2234 FontUtilities.getLogger()
2235 .info("Found font via platform API for request:\"" +
2236 name + "\":, style="+style+
2237 " found font: " + font);
2238 }
2239 fontNameCache.put(mapName, font);
2240 return font;
2241 }
2242 }
2243
2244 /* If reach here and no match has been located, then if there are
2245 * uninitialised deferred fonts, load as many of those as needed
2246 * to find the deferred font. If none is found through that
2247 * search continue on.
2248 * There is possibly a minor issue when more than one
2249 * deferred font implements the same font face. Since deferred
2250 * fonts are only those in font configuration files, this is a
2251 * controlled situation, the known case being Solaris euro_fonts
2252 * versions of Arial, Times New Roman, Courier New. However
2253 * the larger font will transparently replace the smaller one
2254 * - see addToFontList() - when it is needed by the composite font.
2255 */
2256 if (deferredFontFiles.size() > 0) {
2257 font = findDeferredFont(name, style);
2258 if (font != null) {
2259 fontNameCache.put(mapName, font);
2260 return font;
2261 }
2262 }
2263
2264 /* Some apps use deprecated 1.0 names such as helvetica and courier. On
2265 * Solaris these are Type1 fonts in /usr/openwin/lib/X11/fonts/Type1.
2266 * If running on Solaris will register all the fonts in this
2267 * directory.
2268 * May as well register the whole directory without actually testing
2269 * the font name is one of the deprecated names as the next step would
2270 * load all fonts which are in this directory anyway.
2271 * In the event that this lookup is successful it potentially "hides"
2272 * TrueType versions of such fonts that are elsewhere but since they
2273 * do not exist on Solaris this is not a problem.
2274 * Set a flag to indicate we've done this registration to avoid
2275 * repetition and more seriously, to avoid recursion.
2276 */
2277 if (FontUtilities.isSolaris &&!loaded1dot0Fonts) {
2278 /* "timesroman" is a special case since that's not the
2279 * name of any known font on Solaris or elsewhere.
2280 */
2281 if (lowerCaseName.equals("timesroman")) {
2282 font = findFont2D("serif", style, fallback);
2283 fontNameCache.put(mapName, font);
2284 }
2285 register1dot0Fonts();
2286 loaded1dot0Fonts = true;
2287 Font2D ff = findFont2D(name, style, fallback);
2288 return ff;
2289 }
2290
2291 /* We check for application registered fonts before
2292 * explicitly loading all fonts as if necessary the registration
2293 * code will have done so anyway. And we don't want to needlessly
2294 * load the actual files for all fonts.
2295 * Just as for installed fonts we check for family before fullname.
2296 * We do not add these fonts to fontNameCache for the
2297 * app context case which eliminates the overhead of a per context
2298 * cache for these.
2299 */
2300
2301 if (fontsAreRegistered || fontsAreRegisteredPerAppContext) {
2302 Hashtable<String, FontFamily> familyTable = null;
2303 Hashtable<String, Font2D> nameTable;
2304
2305 if (fontsAreRegistered) {
2306 familyTable = createdByFamilyName;
2307 nameTable = createdByFullName;
2308 } else {
2309 AppContext appContext = AppContext.getAppContext();
2310 @SuppressWarnings("unchecked")
2311 Hashtable<String,FontFamily> tmp1 =
2312 (Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
2313 familyTable = tmp1;
2314
2315 @SuppressWarnings("unchecked")
2316 Hashtable<String, Font2D> tmp2 =
2317 (Hashtable<String,Font2D>)appContext.get(regFullNameKey);
2318 nameTable = tmp2;
2319 }
2320
2321 family = familyTable.get(lowerCaseName);
2322 if (family != null) {
2323 font = family.getFontWithExactStyleMatch(style);
2324 if (font == null) {
2325 font = family.getFont(style);
2326 }
2327 if (font == null) {
2328 font = family.getClosestStyle(style);
2329 }
2330 if (font != null) {
2331 if (fontsAreRegistered) {
2332 fontNameCache.put(mapName, font);
2333 }
2334 return font;
2335 }
2336 }
2337 font = nameTable.get(lowerCaseName);
2338 if (font != null) {
2339 if (fontsAreRegistered) {
2340 fontNameCache.put(mapName, font);
2341 }
2342 return font;
2343 }
2344 }
2345
2346 /* If reach here and no match has been located, then if all fonts
2347 * are not yet loaded, do so, and then recurse.
2348 */
2349 if (!loadedAllFonts) {
2350 if (FontUtilities.isLogging()) {
2351 FontUtilities.getLogger()
2352 .info("Load fonts looking for:" + name);
2353 }
2354 loadFonts();
2355 loadedAllFonts = true;
2356 return findFont2D(name, style, fallback);
2357 }
2358
2359 if (!loadedAllFontFiles) {
2360 if (FontUtilities.isLogging()) {
2361 FontUtilities.getLogger()
2362 .info("Load font files looking for:" + name);
2363 }
2364 loadFontFiles();
2365 loadedAllFontFiles = true;
2366 return findFont2D(name, style, fallback);
2367 }
2368
2369 /* The primary name is the locale default - ie not US/English but
2370 * whatever is the default in this locale. This is the way it always
2371 * has been but may be surprising to some developers if "Arial Regular"
2372 * were hard-coded in their app and yet "Arial Regular" was not the
2373 * default name. Fortunately for them, as a consequence of the JDK
2374 * supporting returning names and family names for arbitrary locales,
2375 * we also need to support searching all localised names for a match.
2376 * But because this case of the name used to reference a font is not
2377 * the same as the default for this locale is rare, it makes sense to
2378 * search a much shorter list of default locale names and only go to
2379 * a longer list of names in the event that no match was found.
2380 * So add here code which searches localised names too.
2381 * As in 1.4.x this happens only after loading all fonts, which
2382 * is probably the right order.
2383 */
2384 if ((font = findFont2DAllLocales(name, style)) != null) {
2385 fontNameCache.put(mapName, font);
2386 return font;
2387 }
2388
2389 /* Perhaps its a "compatibility" name - timesroman, helvetica,
2390 * or courier, which 1.0 apps used for logical fonts.
2391 * We look for these "late" after a loadFonts as we must not
2392 * hide real fonts of these names.
2393 * Map these appropriately:
2394 * On windows this means according to the rules specified by the
2395 * FontConfiguration : do it only for encoding==Cp1252
2396 *
2397 * REMIND: this is something we plan to remove.
2398 */
2399 if (FontUtilities.isWindows) {
2400 String compatName =
2401 getFontConfiguration().getFallbackFamilyName(name, null);
2402 if (compatName != null) {
2403 font = findFont2D(compatName, style, fallback);
2404 fontNameCache.put(mapName, font);
2405 return font;
2406 }
2407 } else if (lowerCaseName.equals("timesroman")) {
2408 font = findFont2D("serif", style, fallback);
2409 fontNameCache.put(mapName, font);
2410 return font;
2411 } else if (lowerCaseName.equals("helvetica")) {
2412 font = findFont2D("sansserif", style, fallback);
2413 fontNameCache.put(mapName, font);
2414 return font;
2415 } else if (lowerCaseName.equals("courier")) {
2416 font = findFont2D("monospaced", style, fallback);
2417 fontNameCache.put(mapName, font);
2418 return font;
2419 }
2420
2421 if (FontUtilities.isLogging()) {
2422 FontUtilities.getLogger().info("No font found for:" + name);
2423 }
2424
2425 switch (fallback) {
2426 case PHYSICAL_FALLBACK: return getDefaultPhysicalFont();
2427 case LOGICAL_FALLBACK: return getDefaultLogicalFont(style);
2428 default: return null;
2429 }
2430 }
2431
2432 /*
2433 * Workaround for apps which are dependent on a font metrics bug
2434 * in JDK 1.1. This is an unsupported win32 private setting.
2435 * Left in for a customer - do not remove.
2436 */
2437 public boolean usePlatformFontMetrics() {
2438 return usePlatformFontMetrics;
2439 }
2440
2441 public int getNumFonts() {
2442 return physicalFonts.size()+maxCompFont;
2443 }
2444
2445 private static boolean fontSupportsEncoding(Font font, String encoding) {
2446 return FontUtilities.getFont2D(font).supportsEncoding(encoding);
2447 }
2448
2449 protected abstract String getFontPath(boolean noType1Fonts);
2450
2451 // MACOSX begin -- need to access this in subclass
2452 protected Thread fileCloser = null;
2453 // MACOSX end
2454 Vector<File> tmpFontFiles = null;
2455
2456 public Font2D createFont2D(File fontFile, int fontFormat,
2457 boolean isCopy, CreatedFontTracker tracker)
2458 throws FontFormatException {
2459
2460 String fontFilePath = fontFile.getPath();
2461 FileFont font2D = null;
2462 final File fFile = fontFile;
2463 final CreatedFontTracker _tracker = tracker;
2464 try {
2465 switch (fontFormat) {
2466 case Font.TRUETYPE_FONT:
2467 font2D = new TrueTypeFont(fontFilePath, null, 0, true);
2468 break;
2469 case Font.TYPE1_FONT:
2470 font2D = new Type1Font(fontFilePath, null, isCopy);
2471 break;
2472 default:
2473 throw new FontFormatException("Unrecognised Font Format");
2474 }
2475 } catch (FontFormatException e) {
2476 if (isCopy) {
2477 java.security.AccessController.doPrivileged(
2478 new java.security.PrivilegedAction<Object>() {
2479 public Object run() {
2480 if (_tracker != null) {
2481 _tracker.subBytes((int)fFile.length());
2482 }
2483 fFile.delete();
2484 return null;
2485 }
2486 });
2487 }
2488 throw(e);
2489 }
2490 if (isCopy) {
2491 font2D.setFileToRemove(fontFile, tracker);
2492 synchronized (FontManager.class) {
2493
2494 if (tmpFontFiles == null) {
2495 tmpFontFiles = new Vector<File>();
2496 }
2497 tmpFontFiles.add(fontFile);
2498
2499 if (fileCloser == null) {
2500 final Runnable fileCloserRunnable = new Runnable() {
2501 public void run() {
2502 java.security.AccessController.doPrivileged(
2503 new java.security.PrivilegedAction<Object>() {
2504 public Object run() {
2505
2506 for (int i=0;i<CHANNELPOOLSIZE;i++) {
2507 if (fontFileCache[i] != null) {
2508 try {
2509 fontFileCache[i].close();
2510 } catch (Exception e) {
2511 }
2512 }
2513 }
2514 if (tmpFontFiles != null) {
2515 File[] files = new File[tmpFontFiles.size()];
2516 files = tmpFontFiles.toArray(files);
2517 for (int f=0; f<files.length;f++) {
2518 try {
2519 files[f].delete();
2520 } catch (Exception e) {
2521 }
2522 }
2523 }
2524
2525 return null;
2526 }
2527
2528 });
2529 }
2530 };
2531 AccessController.doPrivileged(
2532 (PrivilegedAction<Void>) () -> {
2533 /* The thread must be a member of a thread group
2534 * which will not get GCed before VM exit.
2535 * Make its parent the top-level thread group.
2536 */
2537 ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();
2538 fileCloser = new Thread(rootTG, fileCloserRunnable);
2539 fileCloser.setContextClassLoader(null);
2540 Runtime.getRuntime().addShutdownHook(fileCloser);
2541 return null;
2542 });
2543 }
2544 }
2545 }
2546 return font2D;
2547 }
2548
2549 /* remind: used in X11GraphicsEnvironment and called often enough
2550 * that we ought to obsolete this code
2551 */
2552 public synchronized String getFullNameByFileName(String fileName) {
2553 PhysicalFont[] physFonts = getPhysicalFonts();
2554 for (int i=0;i<physFonts.length;i++) {
2555 if (physFonts[i].platName.equals(fileName)) {
2556 return (physFonts[i].getFontName(null));
2557 }
2558 }
2559 return null;
2560 }
2561
2562 /*
2563 * This is called when font is determined to be invalid/bad.
2564 * It designed to be called (for example) by the font scaler
2565 * when in processing a font file it is discovered to be incorrect.
2566 * This is different than the case where fonts are discovered to
2567 * be incorrect during initial verification, as such fonts are
2568 * never registered.
2569 * Handles to this font held are re-directed to a default font.
2570 * This default may not be an ideal substitute buts it better than
2571 * crashing This code assumes a PhysicalFont parameter as it doesn't
2572 * make sense for a Composite to be "bad".
2573 */
2574 public synchronized void deRegisterBadFont(Font2D font2D) {
2575 if (!(font2D instanceof PhysicalFont)) {
2576 /* We should never reach here, but just in case */
2577 return;
2578 } else {
2579 if (FontUtilities.isLogging()) {
2580 FontUtilities.getLogger()
2581 .severe("Deregister bad font: " + font2D);
2582 }
2583 replaceFont((PhysicalFont)font2D, getDefaultPhysicalFont());
2584 }
2585 }
2586
2587 /*
2588 * This encapsulates all the work that needs to be done when a
2589 * Font2D is replaced by a different Font2D.
2590 */
2591 public synchronized void replaceFont(PhysicalFont oldFont,
2592 PhysicalFont newFont) {
2593
2594 if (oldFont.handle.font2D != oldFont) {
2595 /* already done */
2596 return;
2597 }
2598
2599 /* If we try to replace the font with itself, that won't work,
2600 * so pick any alternative physical font
2601 */
2602 if (oldFont == newFont) {
2603 if (FontUtilities.isLogging()) {
2604 FontUtilities.getLogger()
2605 .severe("Can't replace bad font with itself " + oldFont);
2606 }
2607 PhysicalFont[] physFonts = getPhysicalFonts();
2608 for (int i=0; i<physFonts.length;i++) {
2609 if (physFonts[i] != newFont) {
2610 newFont = physFonts[i];
2611 break;
2612 }
2613 }
2614 if (oldFont == newFont) {
2615 if (FontUtilities.isLogging()) {
2616 FontUtilities.getLogger()
2617 .severe("This is bad. No good physicalFonts found.");
2618 }
2619 return;
2620 }
2621 }
2622
2623 /* eliminate references to this font, so it won't be located
2624 * by future callers, and will be eligible for GC when all
2625 * references are removed
2626 */
2627 oldFont.handle.font2D = newFont;
2628 physicalFonts.remove(oldFont.fullName);
2629 fullNameToFont.remove(oldFont.fullName.toLowerCase(Locale.ENGLISH));
2630 FontFamily.remove(oldFont);
2631 if (localeFullNamesToFont != null) {
2632 Map.Entry<?, ?>[] mapEntries = localeFullNamesToFont.entrySet().
2633 toArray(new Map.Entry<?, ?>[0]);
2634 /* Should I be replacing these, or just I just remove
2635 * the names from the map?
2636 */
2637 for (int i=0; i<mapEntries.length;i++) {
2638 if (mapEntries[i].getValue() == oldFont) {
2639 try {
2640 @SuppressWarnings("unchecked")
2641 Map.Entry<String, PhysicalFont> tmp = (Map.Entry<String, PhysicalFont>)mapEntries[i];
2642 tmp.setValue(newFont);
2643 } catch (Exception e) {
2644 /* some maps don't support this operation.
2645 * In this case just give up and remove the entry.
2646 */
2647 localeFullNamesToFont.remove(mapEntries[i].getKey());
2648 }
2649 }
2650 }
2651 }
2652
2653 for (int i=0; i<maxCompFont; i++) {
2654 /* Deferred initialization of composites shouldn't be
2655 * a problem for this case, since a font must have been
2656 * initialised to be discovered to be bad.
2657 * Some JRE composites on Solaris use two versions of the same
2658 * font. The replaced font isn't bad, just "smaller" so there's
2659 * no need to make the slot point to the new font.
2660 * Since composites have a direct reference to the Font2D (not
2661 * via a handle) making this substitution is not safe and could
2662 * cause an additional problem and so this substitution is
2663 * warranted only when a font is truly "bad" and could cause
2664 * a crash. So we now replace it only if its being substituted
2665 * with some font other than a fontconfig rank font
2666 * Since in practice a substitution will have the same rank
2667 * this may never happen, but the code is safer even if its
2668 * also now a no-op.
2669 * The only obvious "glitch" from this stems from the current
2670 * implementation that when asked for the number of glyphs in a
2671 * composite it lies and returns the number in slot 0 because
2672 * composite glyphs aren't contiguous. Since we live with that
2673 * we can live with the glitch that depending on how it was
2674 * initialised a composite may return different values for this.
2675 * Fixing the issues with composite glyph ids is tricky as
2676 * there are exclusion ranges and unlike other fonts even the
2677 * true "numGlyphs" isn't a contiguous range. Likely the only
2678 * solution is an API that returns an array of glyph ranges
2679 * which takes precedence over the existing API. That might
2680 * also need to address excluding ranges which represent a
2681 * code point supported by an earlier component.
2682 */
2683 if (newFont.getRank() > Font2D.FONT_CONFIG_RANK) {
2684 compFonts[i].replaceComponentFont(oldFont, newFont);
2685 }
2686 }
2687 }
2688
2689 private synchronized void loadLocaleNames() {
2690 if (localeFullNamesToFont != null) {
2691 return;
2692 }
2693 localeFullNamesToFont = new HashMap<String, TrueTypeFont>();
2694 Font2D[] fonts = getRegisteredFonts();
2695 for (int i=0; i<fonts.length; i++) {
2696 if (fonts[i] instanceof TrueTypeFont) {
2697 TrueTypeFont ttf = (TrueTypeFont)fonts[i];
2698 String[] fullNames = ttf.getAllFullNames();
2699 for (int n=0; n<fullNames.length; n++) {
2700 localeFullNamesToFont.put(fullNames[n], ttf);
2701 }
2702 FontFamily family = FontFamily.getFamily(ttf.familyName);
2703 if (family != null) {
2704 FontFamily.addLocaleNames(family, ttf.getAllFamilyNames());
2705 }
2706 }
2707 }
2708 }
2709
2710 /* This replicate the core logic of findFont2D but operates on
2711 * all the locale names. This hasn't been merged into findFont2D to
2712 * keep the logic simpler and reduce overhead, since this case is
2713 * almost never used. The main case in which it is called is when
2714 * a bogus font name is used and we need to check all possible names
2715 * before returning the default case.
2716 */
2717 private Font2D findFont2DAllLocales(String name, int style) {
2718
2719 if (FontUtilities.isLogging()) {
2720 FontUtilities.getLogger()
2721 .info("Searching localised font names for:" + name);
2722 }
2723
2724 /* If reach here and no match has been located, then if we have
2725 * not yet built the map of localeFullNamesToFont for TT fonts, do so
2726 * now. This method must be called after all fonts have been loaded.
2727 */
2728 if (localeFullNamesToFont == null) {
2729 loadLocaleNames();
2730 }
2731 String lowerCaseName = name.toLowerCase();
2732 Font2D font = null;
2733
2734 /* First see if its a family name. */
2735 FontFamily family = FontFamily.getLocaleFamily(lowerCaseName);
2736 if (family != null) {
2737 font = family.getFont(style);
2738 if (font == null) {
2739 font = family.getClosestStyle(style);
2740 }
2741 if (font != null) {
2742 return font;
2743 }
2744 }
2745
2746 /* If it wasn't a family name, it should be a full name. */
2747 synchronized (this) {
2748 font = localeFullNamesToFont.get(name);
2749 }
2750 if (font != null) {
2751 if (font.style == style || style == Font.PLAIN) {
2752 return font;
2753 } else {
2754 family = FontFamily.getFamily(font.getFamilyName(null));
2755 if (family != null) {
2756 Font2D familyFont = family.getFont(style);
2757 /* We exactly matched the requested style, use it! */
2758 if (familyFont != null) {
2759 return familyFont;
2760 } else {
2761 familyFont = family.getClosestStyle(style);
2762 if (familyFont != null) {
2763 /* The next check is perhaps one
2764 * that shouldn't be done. ie if we get this
2765 * far we have probably as close a match as we
2766 * are going to get. We could load all fonts to
2767 * see if somehow some parts of the family are
2768 * loaded but not all of it.
2769 * This check is commented out for now.
2770 */
2771 if (!familyFont.canDoStyle(style)) {
2772 familyFont = null;
2773 }
2774 return familyFont;
2775 }
2776 }
2777 }
2778 }
2779 }
2780 return font;
2781 }
2782
2783 /* Supporting "alternate" composite fonts on 2D graphics objects
2784 * is accessed by the application by calling methods on the local
2785 * GraphicsEnvironment. The overall implementation is described
2786 * in one place, here, since otherwise the implementation is spread
2787 * around it may be difficult to track.
2788 * The methods below call into SunGraphicsEnvironment which creates a
2789 * new FontConfiguration instance. The FontConfiguration class,
2790 * and its platform sub-classes are updated to take parameters requesting
2791 * these behaviours. This is then used to create new composite font
2792 * instances. Since this calls the initCompositeFont method in
2793 * SunGraphicsEnvironment it performs the same initialization as is
2794 * performed normally. There may be some duplication of effort, but
2795 * that code is already written to be able to perform properly if called
2796 * to duplicate work. The main difference is that if we detect we are
2797 * running in an applet/browser/Java plugin environment these new fonts
2798 * are not placed in the "default" maps but into an AppContext instance.
2799 * The font lookup mechanism in java.awt.Font.getFont2D() is also updated
2800 * so that look-up for composite fonts will in that case always
2801 * do a lookup rather than returning a cached result.
2802 * This is inefficient but necessary else singleton java.awt.Font
2803 * instances would not retrieve the correct Font2D for the appcontext.
2804 * sun.font.FontManager.findFont2D is also updated to that it uses
2805 * a name map cache specific to that appcontext.
2806 *
2807 * Getting an AppContext is expensive, so there is a global variable
2808 * that records whether these methods have ever been called and can
2809 * avoid the expense for almost all applications. Once the correct
2810 * CompositeFont is associated with the Font, everything should work
2811 * through existing mechanisms.
2812 * A special case is that GraphicsEnvironment.getAllFonts() must
2813 * return an AppContext specific list.
2814 *
2815 * Calling the methods below is "heavyweight" but it is expected that
2816 * these methods will be called very rarely.
2817 *
2818 * If _usingPerAppContextComposites is true, we are in "applet"
2819 * (eg browser) environment and at least one context has selected
2820 * an alternate composite font behaviour.
2821 * If _usingAlternateComposites is true, we are not in an "applet"
2822 * environment and the (single) application has selected
2823 * an alternate composite font behaviour.
2824 *
2825 * - Printing: The implementation delegates logical fonts to an AWT
2826 * mechanism which cannot use these alternate configurations.
2827 * We can detect that alternate fonts are in use and back-off to 2D, but
2828 * that uses outlines. Much of this can be fixed with additional work
2829 * but that may have to wait. The results should be correct, just not
2830 * optimal.
2831 */
2832 private static final Object altJAFontKey = new Object();
2833 private static final Object localeFontKey = new Object();
2834 private static final Object proportionalFontKey = new Object();
2835 private boolean _usingPerAppContextComposites = false;
2836 private boolean _usingAlternateComposites = false;
2837
2838 /* These values are used only if we are running as a standalone
2839 * application, as determined by maybeMultiAppContext();
2840 */
2841 private static boolean gAltJAFont = false;
2842 private boolean gLocalePref = false;
2843 private boolean gPropPref = false;
2844
2845 /* This method doesn't check if alternates are selected in this app
2846 * context. Its used by the FontMetrics caching code which in such
2847 * a case cannot retrieve a cached metrics solely on the basis of
2848 * the Font.equals() method since it needs to also check if the Font2D
2849 * is the same.
2850 * We also use non-standard composites for Swing native L&F fonts on
2851 * Windows. In that case the policy is that the metrics reported are
2852 * based solely on the physical font in the first slot which is the
2853 * visible java.awt.Font. So in that case the metrics cache which tests
2854 * the Font does what we want. In the near future when we expand the GTK
2855 * logical font definitions we may need to revisit this if GTK reports
2856 * combined metrics instead. For now though this test can be simple.
2857 */
2858 public boolean maybeUsingAlternateCompositeFonts() {
2859 return _usingAlternateComposites || _usingPerAppContextComposites;
2860 }
2861
2862 public boolean usingAlternateCompositeFonts() {
2863 return (_usingAlternateComposites ||
2864 (_usingPerAppContextComposites &&
2865 AppContext.getAppContext().get(CompositeFont.class) != null));
2866 }
2867
2868 private static boolean maybeMultiAppContext() {
2869 Boolean appletSM = (Boolean)
2870 java.security.AccessController.doPrivileged(
2871 new java.security.PrivilegedAction<Object>() {
2872 public Object run() {
2873 SecurityManager sm = System.getSecurityManager();
2874 return new Boolean
2875 (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 }
--- EOF ---