History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/font/SunFontManager.java (1 of 18352 files). Show all

Unified · Split

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