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