1 /*
2  * Copyright (c) 2009, 2020, 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.awt;
27 
28 import java.awt.GraphicsEnvironment;
29 import java.io.BufferedReader;
30 import java.io.File;
31 import java.io.FileReader;
32 import java.io.IOException;
33 import java.io.StreamTokenizer;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.Locale;
37 import java.util.Map;
38 import java.util.NoSuchElementException;
39 import java.util.StringTokenizer;
40 import java.util.Vector;
41 
42 import javax.swing.plaf.FontUIResource;
43 import sun.font.MFontConfiguration;
44 import sun.font.CompositeFont;
45 import sun.font.FontManager;
46 import sun.font.SunFontManager;
47 import sun.font.FcFontConfiguration;
48 import sun.font.FontAccess;
49 import sun.font.FontUtilities;
50 import sun.font.NativeFont;
51 import sun.util.logging.PlatformLogger;
52 
53 /**
54  * The X11 implementation of {@link FontManager}.
55  */
56 public final class X11FontManager extends FcFontManager {
57 
58     // constants identifying XLFD and font ID fields
59     private static final int FOUNDRY_FIELD = 1;
60     private static final int FAMILY_NAME_FIELD = 2;
61     private static final int WEIGHT_NAME_FIELD = 3;
62     private static final int SLANT_FIELD = 4;
63     private static final int SETWIDTH_NAME_FIELD = 5;
64     private static final int ADD_STYLE_NAME_FIELD = 6;
65     private static final int PIXEL_SIZE_FIELD = 7;
66     private static final int POINT_SIZE_FIELD = 8;
67     private static final int RESOLUTION_X_FIELD = 9;
68     private static final int RESOLUTION_Y_FIELD = 10;
69     private static final int SPACING_FIELD = 11;
70     private static final int AVERAGE_WIDTH_FIELD = 12;
71     private static final int CHARSET_REGISTRY_FIELD = 13;
72     private static final int CHARSET_ENCODING_FIELD = 14;
73 
74     /*
75      * fontNameMap is a map from a fontID (which is a substring of an XLFD like
76      * "-monotype-arial-bold-r-normal-iso8859-7")
77      * to font file path like
78      * /usr/openwin/lib/locale/iso_8859_7/X11/fonts/TrueType/ArialBoldItalic.ttf
79      * It's used in a couple of methods like
80      * getFileNameFomPlatformName(..) to help locate the font file.
81      * We use this substring of a full XLFD because the font configuration files
82      * define the XLFDs in a way that's easier to make into a request.
83      * E.g., the -0-0-0-0-p-0- reported by X is -*-%d-*-*-p-*- in the font
84      * configuration files. We need to remove that part for comparisons.
85      */
86     private static Map<String, String> fontNameMap = new HashMap<>();
87 
88     /*
89      * xlfdMap is a map from a platform path like
90      * /usr/openwin/lib/locale/ja/X11/fonts/TT/HG-GothicB.ttf to an XLFD like
91      * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0"
92      * Because there may be multiple native names, because the font is used
93      * to support multiple X encodings for example, the value of an entry in
94      * this map is always a vector where we store all the native names.
95      * For fonts which we don't understand the key isn't a pathname, its
96      * the full XLFD string like :-
97      * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0"
98      */
99     private static Map<String, Vector<String>> xlfdMap = new HashMap<>();
100 
101     /* xFontDirsMap is also a map from a font ID to a font filepath.
102      * The difference from fontNameMap is just that it does not have
103      * resolved symbolic links. Normally this is not interesting except
104      * that we need to know the directory in which a font was found to
105      * add it to the X font server path, since although the files may
106      * be linked, the fonts.dir is different and specific to the encoding
107      * handled by that directory. This map is nulled out after use to free
108      * heap space. If the optimal path is taken, such that all fonts in
109      * font configuration files are referenced by filename, then the font
110      * dir can be directly derived as its parent directory.
111      * If a font is used by two XLFDs, each corresponding to a different
112      * X11 font directory, then precautions must be taken to include both
113      * directories.
114      */
115      private static Map<String, String> xFontDirsMap;
116 
117      /*
118       * This is the set of font directories needed to be on the X font path
119       * to enable AWT heavyweights to find all of the font configuration fonts.
120       * It is populated by :
121       * - awtfontpath entries in the fontconfig.properties
122       * - parent directories of "core" fonts used in the fontconfig.properties
123       * - looking up font dirs in the xFontDirsMap where the key is a fontID
124       *   (cut down version of the XLFD read from the font configuration file).
125       * This set is nulled out after use to free heap space.
126       */
127      private static HashSet<String> fontConfigDirs = null;
128 
129     /*
130      * Used to eliminate redundant work. When a font directory is
131      * registered it added to this list. Subsequent registrations for the
132      * same directory can then be skipped by checking this Map.
133      * Access to this map is not synchronised here since creation
134      * of the singleton GE instance is already synchronised and that is
135      * the only code path that accesses this map.
136      */
137      private static HashMap<String, Object> registeredDirs = new HashMap<>();
138 
139      /* Array of directories to be added to the X11 font path.
140       * Used by static method called from Toolkits which use X11 fonts.
141       * Specifically this means MToolkit
142       */
143      private static String[] fontdirs = null;
144 
getInstance()145     public static X11FontManager getInstance() {
146         return (X11FontManager) SunFontManager.getInstance();
147     }
148 
149     /**
150      * Takes family name property in the following format:
151      * "-linotype-helvetica-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1"
152      * and returns the name of the corresponding physical font.
153      * This code is used to resolve font configuration fonts, and expects
154      * only to get called for these fonts.
155      */
156     @Override
getFileNameFromPlatformName(String platName)157     public String getFileNameFromPlatformName(String platName) {
158 
159         /* If the FontConfig file doesn't use xlfds, or its
160          * FcFontConfiguration, this may be already a file name.
161          */
162         if (platName.startsWith("/")) {
163             return platName;
164         }
165 
166         String fileName = null;
167         String fontID = specificFontIDForName(platName);
168 
169         /* If the font filename has been explicitly assigned in the
170          * font configuration file, use it. This avoids accessing
171          * the wrong fonts on Linux, where different fonts (some
172          * of which may not be usable by 2D) may share the same
173          * specific font ID. It may also speed up the lookup.
174          */
175         fileName = super.getFileNameFromPlatformName(platName);
176         if (fileName != null) {
177             if (isHeadless() && fileName.startsWith("-")) {
178                 /* if it's headless, no xlfd should be used */
179                     return null;
180             }
181             if (fileName.startsWith("/")) {
182                 /* If a path is assigned in the font configuration file,
183                  * it is required that the config file also specify using the
184                  * new awtfontpath key the X11 font directories
185                  * which must be added to the X11 font path to support
186                  * AWT access to that font. For that reason we no longer
187                  * have code here to add the parent directory to the list
188                  * of font config dirs, since the parent directory may not
189                  * be sufficient if fonts are symbolically linked to a
190                  * different directory.
191                  *
192                  * Add this XLFD (platform name) to the list of known
193                  * ones for this file.
194                  */
195                 Vector<String> xVal = xlfdMap.get(fileName);
196                 if (xVal == null) {
197                     /* Try to be robust on Linux distros which move fonts
198                      * around by verifying that the fileName represents a
199                      * file that exists.  If it doesn't, set it to null
200                      * to trigger a search.
201                      */
202                     if (getFontConfiguration().needToSearchForFile(fileName)) {
203                         fileName = null;
204                     }
205                     if (fileName != null) {
206                         xVal = new Vector<>();
207                         xVal.add(platName);
208                         xlfdMap.put(fileName, xVal);
209                     }
210                 } else {
211                     if (!xVal.contains(platName)) {
212                         xVal.add(platName);
213                     }
214                 }
215             }
216             if (fileName != null) {
217                 fontNameMap.put(fontID, fileName);
218                 return fileName;
219             }
220         }
221 
222         if (fontID != null) {
223             fileName = fontNameMap.get(fontID);
224             if (fontPath == null &&
225                 (fileName == null || !fileName.startsWith("/"))) {
226                 if (FontUtilities.debugFonts()) {
227                     FontUtilities.logWarning("** Registering all font paths because " +
228                                              "can't find file for " + platName);
229                 }
230                 fontPath = getPlatformFontPath(noType1Font);
231                 registerFontDirs(fontPath);
232                 if (FontUtilities.debugFonts()) {
233                     FontUtilities.logWarning("** Finished registering all font paths");
234                 }
235                 fileName = fontNameMap.get(fontID);
236             }
237             if (fileName == null && !isHeadless()) {
238                 /* Query X11 directly to see if this font is available
239                  * as a native font.
240                  */
241                 fileName = getX11FontName(platName);
242             }
243             if (fileName == null) {
244                 fontID = switchFontIDForName(platName);
245                 fileName = fontNameMap.get(fontID);
246             }
247             if (fileName != null) {
248                 fontNameMap.put(fontID, fileName);
249             }
250         }
251         return fileName;
252     }
253 
254     @Override
getNativeNames(String fontFileName, String platformName)255     protected String[] getNativeNames(String fontFileName,
256             String platformName) {
257         Vector<String> nativeNames;
258         if ((nativeNames=xlfdMap.get(fontFileName))==null) {
259             if (platformName == null) {
260                 return null;
261             } else {
262                 /* back-stop so that at least the name used in the
263                  * font configuration file is known as a native name
264                  */
265                 String []natNames = new String[1];
266                 natNames[0] = platformName;
267                 return natNames;
268             }
269         } else {
270             int len = nativeNames.size();
271             return nativeNames.toArray(new String[len]);
272         }
273     }
274 
275     /* NOTE: this method needs to be executed in a privileged context.
276      * The superclass constructor which is the primary caller of
277      * this method executes entirely in such a context. Additionally
278      * the loadFonts() method does too. So all should be well.
279 
280      */
281     @Override
registerFontDir(String path)282     protected void registerFontDir(String path) {
283         /* fonts.dir file format looks like :-
284          * 47
285          * Arial.ttf -monotype-arial-regular-r-normal--0-0-0-0-p-0-iso8859-1
286          * Arial-Bold.ttf -monotype-arial-bold-r-normal--0-0-0-0-p-0-iso8859-1
287          * ...
288          */
289         if (FontUtilities.debugFonts()) {
290             FontUtilities.logInfo("ParseFontDir " + path);
291         }
292         File fontsDotDir = new File(path + File.separator + "fonts.dir");
293         FileReader fr = null;
294         try {
295             if (fontsDotDir.canRead()) {
296                 fr = new FileReader(fontsDotDir);
297                 BufferedReader br = new BufferedReader(fr, 8192);
298                 StreamTokenizer st = new StreamTokenizer(br);
299                 st.eolIsSignificant(true);
300                 int ttype = st.nextToken();
301                 if (ttype == StreamTokenizer.TT_NUMBER) {
302                     int numEntries = (int)st.nval;
303                     ttype = st.nextToken();
304                     if (ttype == StreamTokenizer.TT_EOL) {
305                         st.resetSyntax();
306                         st.wordChars(32, 127);
307                         st.wordChars(128 + 32, 255);
308                         st.whitespaceChars(0, 31);
309 
310                         for (int i=0; i < numEntries; i++) {
311                             ttype = st.nextToken();
312                             if (ttype == StreamTokenizer.TT_EOF) {
313                                 break;
314                             }
315                             if (ttype != StreamTokenizer.TT_WORD) {
316                                 break;
317                             }
318                             int breakPos = st.sval.indexOf(' ');
319                             if (breakPos <= 0) {
320                                 /* On TurboLinux 8.0 a fonts.dir file had
321                                  * a line with integer value "24" which
322                                  * appeared to be the number of remaining
323                                  * entries in the file. This didn't add to
324                                  * the value on the first line of the file.
325                                  * Seemed like XFree86 didn't like this line
326                                  * much either. It failed to parse the file.
327                                  * Ignore lines like this completely, and
328                                  * don't let them count as an entry.
329                                  */
330                                 numEntries++;
331                                 ttype = st.nextToken();
332                                 if (ttype != StreamTokenizer.TT_EOL) {
333                                     break;
334                                 }
335 
336                                 continue;
337                             }
338                             if (st.sval.charAt(0) == '!') {
339                                 /* TurboLinux 8.0 comment line: ignore.
340                                  * can't use st.commentChar('!') to just
341                                  * skip because this line mustn't count
342                                  * against numEntries.
343                                  */
344                                 numEntries++;
345                                 ttype = st.nextToken();
346                                 if (ttype != StreamTokenizer.TT_EOL) {
347                                     break;
348                                 }
349                                 continue;
350                             }
351                             String fileName = st.sval.substring(0, breakPos);
352                             /* TurboLinux 8.0 uses some additional syntax to
353                              * indicate algorithmic styling values.
354                              * Ignore ':' separated files at the beginning
355                              * of the fileName
356                              */
357                             int lastColon = fileName.lastIndexOf(':');
358                             if (lastColon > 0) {
359                                 if (lastColon+1 >= fileName.length()) {
360                                     continue;
361                                 }
362                                 fileName = fileName.substring(lastColon+1);
363                             }
364                             String fontPart = st.sval.substring(breakPos+1);
365                             String fontID = specificFontIDForName(fontPart);
366                             String sVal = fontNameMap.get(fontID);
367 
368                             if (FontUtilities.debugFonts()) {
369                                 FontUtilities.logInfo("file=" + fileName +
370                                             " xlfd=" + fontPart);
371                                 FontUtilities.logInfo("fontID=" + fontID +
372                                             " sVal=" + sVal);
373                             }
374                             String fullPath = null;
375                             try {
376                                 File file = new File(path,fileName);
377                                 /* we may have a resolved symbolic link
378                                  * this becomes important for an xlfd we
379                                  * still need to know the location it was
380                                  * found to update the X server font path
381                                  * for use by AWT heavyweights - and when 2D
382                                  * wants to use the native rasteriser.
383                                  */
384                                 if (xFontDirsMap == null) {
385                                     xFontDirsMap = new HashMap<>();
386                                 }
387                                 xFontDirsMap.put(fontID, path);
388                                 fullPath = file.getCanonicalPath();
389                             } catch (IOException e) {
390                                 fullPath = path + File.separator + fileName;
391                             }
392                             Vector<String> xVal = xlfdMap.get(fullPath);
393                             if (FontUtilities.debugFonts()) {
394                                 FontUtilities.logInfo("fullPath=" + fullPath +
395                                                       " xVal=" + xVal);
396                             }
397                             if ((xVal == null || !xVal.contains(fontPart)) &&
398                                 (sVal == null) || !sVal.startsWith("/")) {
399                                 if (FontUtilities.debugFonts()) {
400                                     FontUtilities.logInfo("Map fontID:"+fontID +
401                                                           "to file:" + fullPath);
402                                 }
403                                 fontNameMap.put(fontID, fullPath);
404                                 if (xVal == null) {
405                                     xVal = new Vector<>();
406                                     xlfdMap.put (fullPath, xVal);
407                                 }
408                                 xVal.add(fontPart);
409                             }
410 
411                             ttype = st.nextToken();
412                             if (ttype != StreamTokenizer.TT_EOL) {
413                                 break;
414                             }
415                         }
416                     }
417                 }
418                 fr.close();
419             }
420         } catch (IOException ioe1) {
421         } finally {
422             if (fr != null) {
423                 try {
424                     fr.close();
425                 }  catch (IOException ioe2) {
426                 }
427             }
428         }
429     }
430 
431     @Override
loadFonts()432     public void loadFonts() {
433         super.loadFonts();
434         /* These maps are greatly expanded during a loadFonts but
435          * can be reset to their initial state afterwards.
436          * Since preferLocaleFonts() and preferProportionalFonts() will
437          * trigger a partial repopulating from the FontConfiguration
438          * it has to be the inital (empty) state for the latter two, not
439          * simply nulling out.
440          * xFontDirsMap is a special case in that the implementation
441          * will typically not ever need to initialise it so it can be null.
442          */
443         xFontDirsMap = null;
444         xlfdMap = new HashMap<>(1);
445         fontNameMap = new HashMap<>(1);
446     }
447 
getX11FontName(String platName)448     private static String getX11FontName(String platName) {
449         String xlfd = platName.replaceAll("%d", "*");
450         if (NativeFont.fontExists(xlfd)) {
451             return xlfd;
452         } else {
453             return null;
454         }
455     }
456 
isHeadless()457     private boolean isHeadless() {
458         GraphicsEnvironment ge =
459             GraphicsEnvironment.getLocalGraphicsEnvironment();
460         return GraphicsEnvironment.isHeadless();
461     }
462 
specificFontIDForName(String name)463     private String specificFontIDForName(String name) {
464 
465         int[] hPos = new int[14];
466         int hyphenCnt = 1;
467         int pos = 1;
468 
469         while (pos != -1 && hyphenCnt < 14) {
470             pos = name.indexOf('-', pos);
471             if (pos != -1) {
472                 hPos[hyphenCnt++] = pos;
473                     pos++;
474             }
475         }
476 
477         if (hyphenCnt != 14) {
478             if (FontUtilities.debugFonts()) {
479                 FontUtilities.logSevere("Font Configuration Font ID is malformed:" + name);
480             }
481             return name; // what else can we do?
482         }
483 
484         StringBuffer sb =
485             new StringBuffer(name.substring(hPos[FAMILY_NAME_FIELD-1],
486                                             hPos[SETWIDTH_NAME_FIELD]));
487         sb.append(name.substring(hPos[CHARSET_REGISTRY_FIELD-1]));
488         String retval = sb.toString().toLowerCase (Locale.ENGLISH);
489         return retval;
490     }
491 
switchFontIDForName(String name)492     private String switchFontIDForName(String name) {
493 
494         int[] hPos = new int[14];
495         int hyphenCnt = 1;
496         int pos = 1;
497 
498         while (pos != -1 && hyphenCnt < 14) {
499             pos = name.indexOf('-', pos);
500             if (pos != -1) {
501                 hPos[hyphenCnt++] = pos;
502                     pos++;
503             }
504         }
505 
506         if (hyphenCnt != 14) {
507             if (FontUtilities.debugFonts()) {
508                 FontUtilities.logSevere("Font Configuration Font ID is malformed:" + name);
509             }
510             return name; // what else can we do?
511         }
512 
513         String slant = name.substring(hPos[SLANT_FIELD-1]+1,
514                                            hPos[SLANT_FIELD]);
515         String family = name.substring(hPos[FAMILY_NAME_FIELD-1]+1,
516                                            hPos[FAMILY_NAME_FIELD]);
517         String registry = name.substring(hPos[CHARSET_REGISTRY_FIELD-1]+1,
518                                            hPos[CHARSET_REGISTRY_FIELD]);
519         String encoding = name.substring(hPos[CHARSET_ENCODING_FIELD-1]+1);
520 
521         if (slant.equals("i")) {
522             slant = "o";
523         } else if (slant.equals("o")) {
524             slant = "i";
525         }
526         // workaround for #4471000
527         if (family.equals("itc zapfdingbats")
528             && registry.equals("sun")
529             && encoding.equals("fontspecific")){
530             registry = "adobe";
531         }
532         StringBuffer sb =
533             new StringBuffer(name.substring(hPos[FAMILY_NAME_FIELD-1],
534                                             hPos[SLANT_FIELD-1]+1));
535         sb.append(slant);
536         sb.append(name.substring(hPos[SLANT_FIELD],
537                                  hPos[SETWIDTH_NAME_FIELD]+1));
538         sb.append(registry);
539         sb.append(name.substring(hPos[CHARSET_ENCODING_FIELD-1]));
540         String retval = sb.toString().toLowerCase (Locale.ENGLISH);
541         return retval;
542     }
543 
544     /**
545      * Returns the face name for the given XLFD.
546      */
getFileNameFromXLFD(String name)547     public String getFileNameFromXLFD(String name) {
548         String fileName = null;
549         String fontID = specificFontIDForName(name);
550         if (fontID != null) {
551             fileName = fontNameMap.get(fontID);
552             if (fileName == null) {
553                 fontID = switchFontIDForName(name);
554                 fileName = fontNameMap.get(fontID);
555             }
556             if (fileName == null) {
557                 fileName = getDefaultFontFile();
558             }
559         }
560         return fileName;
561     }
562 
563     /* Register just the paths, (it doesn't register the fonts).
564      * If a font configuration file has specified a baseFontPath
565      * fontPath is just those directories, unless on usage we
566      * find it doesn't contain what we need for the logical fonts.
567      * Otherwise, we register all the paths on Solaris, because
568      * the fontPath we have here is the complete one from
569      * parsing /var/sadm/install/contents, not just
570      * what's on the X font path (may be this should be
571      * changed).
572      * But for now what it means is that if we didn't do
573      * this then if the font weren't listed anywhere on the
574      * less complete font path we'd trigger loadFonts which
575      * actually registers the fonts. This may actually be
576      * the right thing tho' since that would also set up
577      * the X font path without which we wouldn't be able to
578      * display some "native" fonts.
579      * So something to revisit is that probably fontPath
580      * here ought to be only the X font path + jre font dir.
581      * loadFonts should have a separate native call to
582      * get the rest of the platform font path.
583      *
584      * Registering the directories can now be avoided in the
585      * font configuration initialisation when filename entries
586      * exist in the font configuration file for all fonts.
587      * (Perhaps a little confusingly a filename entry is
588      * actually keyed using the XLFD used in the font entries,
589      * and it maps *to* a real filename).
590      * In the event any are missing, registration of all
591      * directories will be invoked to find the real files.
592      *
593      * But registering the directory performed other
594      * functions such as filling in the map of all native names
595      * for the font. So when this method isn't invoked, they still
596      * must be found. This is mitigated by getNativeNames now
597      * being able to return at least the platform name, but mostly
598      * by ensuring that when a filename key is found, that
599      * xlfd key is stored as one of the set of platform names
600      * for the font. Its a set because typical font configuration
601      * files reference the same CJK font files using multiple
602      * X11 encodings. For the code that adds this to the map
603      * see X11GE.getFileNameFromPlatformName(..)
604      * If you don't get all of these then some code points may
605      * not use the Xserver, and will not get the PCF bitmaps
606      * that are available for some point sizes.
607      * So, in the event that there is such a problem,
608      * unconditionally making this call may be necessary, at
609      * some cost to JRE start-up
610      */
611     @Override
registerFontDirs(String pathName)612     protected void registerFontDirs(String pathName) {
613 
614         StringTokenizer parser = new StringTokenizer(pathName,
615                                                      File.pathSeparator);
616         try {
617             while (parser.hasMoreTokens()) {
618                 String dirPath = parser.nextToken();
619                 if (dirPath != null && !registeredDirs.containsKey(dirPath)) {
620                     registeredDirs.put(dirPath, null);
621                     registerFontDir(dirPath);
622                 }
623             }
624         } catch (NoSuchElementException e) {
625         }
626     }
627 
628     // An X font spec (xlfd) includes an encoding. The same TrueType font file
629     // may be referenced from different X font directories in font.dir files
630     // to support use in multiple encodings by X apps.
631     // So for the purposes of font configuration logical fonts where AWT
632     // heavyweights need to access the font via X APIs we need to ensure that
633     // the directory for precisely the encodings needed by this are added to
634     // the x font path. This requires that we note the platform names
635     // specified in font configuration files and use that to identify the
636     // X font directory that contains a font.dir file for that platform name
637     // and add it to the X font path (if display is local)
638     // Here we make use of an already built map of xlfds to font locations
639     // to add the font location to the set of those required to build the
640     // x font path needed by AWT.
641     // These are added to the x font path later.
642     // All this is necessary because on Solaris the font.dir directories
643     // may contain not real font files, but symbolic links to the actual
644     // location but that location is not suitable for the x font path, since
645     // it probably doesn't have a font.dir at all and certainly not one
646     // with the required encodings
647     // If the fontconfiguration file is properly set up so that all fonts
648     // are mapped to files then we will never trigger initialising
649     // xFontDirsMap (it will be null). In this case the awtfontpath entries
650     // must specify all the X11 directories needed by AWT.
651     @Override
addFontToPlatformFontPath(String platformName)652     protected void addFontToPlatformFontPath(String platformName) {
653         // Lazily initialize fontConfigDirs.
654         getPlatformFontPathFromFontConfig();
655         if (xFontDirsMap != null) {
656             String fontID = specificFontIDForName(platformName);
657             String dirName = xFontDirsMap.get(fontID);
658             if (dirName != null) {
659                 fontConfigDirs.add(dirName);
660             }
661         }
662         return;
663     }
664 
getPlatformFontPathFromFontConfig()665     private void getPlatformFontPathFromFontConfig() {
666         if (fontConfigDirs == null) {
667             fontConfigDirs = getFontConfiguration().getAWTFontPathSet();
668             if (FontUtilities.debugFonts() && fontConfigDirs != null) {
669                 String[] names = fontConfigDirs.toArray(new String[0]);
670                 for (int i=0;i<names.length;i++) {
671                     FontUtilities.logInfo("awtfontpath : " + names[i]);
672                 }
673             }
674         }
675     }
676 
677     @Override
registerPlatformFontsUsedByFontConfiguration()678     protected void registerPlatformFontsUsedByFontConfiguration() {
679         // Lazily initialize fontConfigDirs.
680         getPlatformFontPathFromFontConfig();
681         if (fontConfigDirs == null) {
682             return;
683         }
684         if (FontUtilities.isLinux || FontUtilities.isBSD) {
685             fontConfigDirs.add(jreLibDirName+File.separator+"oblique-fonts");
686         }
687         fontdirs = fontConfigDirs.toArray(new String[0]);
688     }
689 
690     // Implements SunGraphicsEnvironment.createFontConfiguration.
createFontConfiguration()691     protected FontConfiguration createFontConfiguration() {
692         /* The logic here decides whether to use a preconfigured
693          * fontconfig.properties file, or synthesise one using platform APIs.
694          * On Solaris we try to use the
695          * pre-configured ones, but if the files it specifies are missing
696          * we fail-safe to synthesising one. This might happen if Solaris
697          * changes its fonts.
698          * For Linux we require an exact match of distro and version to
699          * use the preconfigured file.
700          * If synthesising fails, we fall back to any preconfigured file
701          * and do the best we can.
702          */
703         FontConfiguration mFontConfig = new MFontConfiguration(this);
704         if ((FontUtilities.isLinux && !mFontConfig.foundOsSpecificFile()) ||
705             (FontUtilities.isBSD && !mFontConfig.foundOsSpecificFile())) {
706             FcFontConfiguration fcFontConfig =
707                 new FcFontConfiguration(this);
708             if (fcFontConfig.init()) {
709                 return fcFontConfig;
710             }
711         }
712         mFontConfig.init();
713         return mFontConfig;
714     }
715 
716     public FontConfiguration
createFontConfiguration(boolean preferLocaleFonts, boolean preferPropFonts)717         createFontConfiguration(boolean preferLocaleFonts,
718                                 boolean preferPropFonts) {
719 
720         return new MFontConfiguration(this,
721                                       preferLocaleFonts, preferPropFonts);
722     }
723 
getFontPath(boolean noType1Fonts)724     protected synchronized String getFontPath(boolean noType1Fonts) {
725         isHeadless(); // make sure GE is inited, as its the X11 lock.
726         return getFontPathNative(noType1Fonts, true);
727     }
728 
729     @Override
getFontConfigFUIR(String family, int style, int size)730     protected FontUIResource getFontConfigFUIR(String family, int style, int size) {
731 
732         CompositeFont font2D = getFontConfigManager().getFontConfigFont(family, style);
733 
734         if (font2D == null) { // Not expected, just a precaution.
735            return new FontUIResource(family, style, size);
736         }
737 
738         /* The name of the font will be that of the physical font in slot,
739          * but by setting the handle to that of the CompositeFont it
740          * renders as that CompositeFont.
741          * It also needs to be marked as a created font which is the
742          * current mechanism to signal that deriveFont etc must copy
743          * the handle from the original font.
744          */
745         FontUIResource fuir =
746             new FontUIResource(font2D.getFamilyName(null), style, size);
747         FontAccess.getFontAccess().setFont2D(fuir, font2D.handle);
748         FontAccess.getFontAccess().setCreatedFont(fuir);
749         return fuir;
750     }
751 }
752