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.getLogger()
228                           .warning("** Registering all font paths because " +
229                                    "can't find file for " + platName);
230                 }
231                 fontPath = getPlatformFontPath(noType1Font);
232                 registerFontDirs(fontPath);
233                 if (FontUtilities.debugFonts()) {
234                     FontUtilities.getLogger()
235                             .warning("** Finished registering all font paths");
236                 }
237                 fileName = fontNameMap.get(fontID);
238             }
239             if (fileName == null && !isHeadless()) {
240                 /* Query X11 directly to see if this font is available
241                  * as a native font.
242                  */
243                 fileName = getX11FontName(platName);
244             }
245             if (fileName == null) {
246                 fontID = switchFontIDForName(platName);
247                 fileName = fontNameMap.get(fontID);
248             }
249             if (fileName != null) {
250                 fontNameMap.put(fontID, fileName);
251             }
252         }
253         return fileName;
254     }
255 
256     @Override
getNativeNames(String fontFileName, String platformName)257     protected String[] getNativeNames(String fontFileName,
258             String platformName) {
259         Vector<String> nativeNames;
260         if ((nativeNames=xlfdMap.get(fontFileName))==null) {
261             if (platformName == null) {
262                 return null;
263             } else {
264                 /* back-stop so that at least the name used in the
265                  * font configuration file is known as a native name
266                  */
267                 String []natNames = new String[1];
268                 natNames[0] = platformName;
269                 return natNames;
270             }
271         } else {
272             int len = nativeNames.size();
273             return nativeNames.toArray(new String[len]);
274         }
275     }
276 
277     /* NOTE: this method needs to be executed in a privileged context.
278      * The superclass constructor which is the primary caller of
279      * this method executes entirely in such a context. Additionally
280      * the loadFonts() method does too. So all should be well.
281 
282      */
283     @Override
registerFontDir(String path)284     protected void registerFontDir(String path) {
285         /* fonts.dir file format looks like :-
286          * 47
287          * Arial.ttf -monotype-arial-regular-r-normal--0-0-0-0-p-0-iso8859-1
288          * Arial-Bold.ttf -monotype-arial-bold-r-normal--0-0-0-0-p-0-iso8859-1
289          * ...
290          */
291         if (FontUtilities.debugFonts()) {
292             FontUtilities.getLogger().info("ParseFontDir " + path);
293         }
294         File fontsDotDir = new File(path + File.separator + "fonts.dir");
295         FileReader fr = null;
296         try {
297             if (fontsDotDir.canRead()) {
298                 fr = new FileReader(fontsDotDir);
299                 BufferedReader br = new BufferedReader(fr, 8192);
300                 StreamTokenizer st = new StreamTokenizer(br);
301                 st.eolIsSignificant(true);
302                 int ttype = st.nextToken();
303                 if (ttype == StreamTokenizer.TT_NUMBER) {
304                     int numEntries = (int)st.nval;
305                     ttype = st.nextToken();
306                     if (ttype == StreamTokenizer.TT_EOL) {
307                         st.resetSyntax();
308                         st.wordChars(32, 127);
309                         st.wordChars(128 + 32, 255);
310                         st.whitespaceChars(0, 31);
311 
312                         for (int i=0; i < numEntries; i++) {
313                             ttype = st.nextToken();
314                             if (ttype == StreamTokenizer.TT_EOF) {
315                                 break;
316                             }
317                             if (ttype != StreamTokenizer.TT_WORD) {
318                                 break;
319                             }
320                             int breakPos = st.sval.indexOf(' ');
321                             if (breakPos <= 0) {
322                                 /* On TurboLinux 8.0 a fonts.dir file had
323                                  * a line with integer value "24" which
324                                  * appeared to be the number of remaining
325                                  * entries in the file. This didn't add to
326                                  * the value on the first line of the file.
327                                  * Seemed like XFree86 didn't like this line
328                                  * much either. It failed to parse the file.
329                                  * Ignore lines like this completely, and
330                                  * don't let them count as an entry.
331                                  */
332                                 numEntries++;
333                                 ttype = st.nextToken();
334                                 if (ttype != StreamTokenizer.TT_EOL) {
335                                     break;
336                                 }
337 
338                                 continue;
339                             }
340                             if (st.sval.charAt(0) == '!') {
341                                 /* TurboLinux 8.0 comment line: ignore.
342                                  * can't use st.commentChar('!') to just
343                                  * skip because this line mustn't count
344                                  * against numEntries.
345                                  */
346                                 numEntries++;
347                                 ttype = st.nextToken();
348                                 if (ttype != StreamTokenizer.TT_EOL) {
349                                     break;
350                                 }
351                                 continue;
352                             }
353                             String fileName = st.sval.substring(0, breakPos);
354                             /* TurboLinux 8.0 uses some additional syntax to
355                              * indicate algorithmic styling values.
356                              * Ignore ':' separated files at the beginning
357                              * of the fileName
358                              */
359                             int lastColon = fileName.lastIndexOf(':');
360                             if (lastColon > 0) {
361                                 if (lastColon+1 >= fileName.length()) {
362                                     continue;
363                                 }
364                                 fileName = fileName.substring(lastColon+1);
365                             }
366                             String fontPart = st.sval.substring(breakPos+1);
367                             String fontID = specificFontIDForName(fontPart);
368                             String sVal = fontNameMap.get(fontID);
369 
370                             if (FontUtilities.debugFonts()) {
371                                 PlatformLogger logger = FontUtilities.getLogger();
372                                 logger.info("file=" + fileName +
373                                             " xlfd=" + fontPart);
374                                 logger.info("fontID=" + fontID +
375                                             " sVal=" + sVal);
376                             }
377                             String fullPath = null;
378                             try {
379                                 File file = new File(path,fileName);
380                                 /* we may have a resolved symbolic link
381                                  * this becomes important for an xlfd we
382                                  * still need to know the location it was
383                                  * found to update the X server font path
384                                  * for use by AWT heavyweights - and when 2D
385                                  * wants to use the native rasteriser.
386                                  */
387                                 if (xFontDirsMap == null) {
388                                     xFontDirsMap = new HashMap<>();
389                                 }
390                                 xFontDirsMap.put(fontID, path);
391                                 fullPath = file.getCanonicalPath();
392                             } catch (IOException e) {
393                                 fullPath = path + File.separator + fileName;
394                             }
395                             Vector<String> xVal = xlfdMap.get(fullPath);
396                             if (FontUtilities.debugFonts()) {
397                                 FontUtilities.getLogger()
398                                       .info("fullPath=" + fullPath +
399                                             " xVal=" + xVal);
400                             }
401                             if ((xVal == null || !xVal.contains(fontPart)) &&
402                                 (sVal == null) || !sVal.startsWith("/")) {
403                                 if (FontUtilities.debugFonts()) {
404                                     FontUtilities.getLogger()
405                                           .info("Map fontID:"+fontID +
406                                                 "to file:" + fullPath);
407                                 }
408                                 fontNameMap.put(fontID, fullPath);
409                                 if (xVal == null) {
410                                     xVal = new Vector<>();
411                                     xlfdMap.put (fullPath, xVal);
412                                 }
413                                 xVal.add(fontPart);
414                             }
415 
416                             ttype = st.nextToken();
417                             if (ttype != StreamTokenizer.TT_EOL) {
418                                 break;
419                             }
420                         }
421                     }
422                 }
423                 fr.close();
424             }
425         } catch (IOException ioe1) {
426         } finally {
427             if (fr != null) {
428                 try {
429                     fr.close();
430                 }  catch (IOException ioe2) {
431                 }
432             }
433         }
434     }
435 
436     @Override
loadFonts()437     public void loadFonts() {
438         super.loadFonts();
439         /* These maps are greatly expanded during a loadFonts but
440          * can be reset to their initial state afterwards.
441          * Since preferLocaleFonts() and preferProportionalFonts() will
442          * trigger a partial repopulating from the FontConfiguration
443          * it has to be the inital (empty) state for the latter two, not
444          * simply nulling out.
445          * xFontDirsMap is a special case in that the implementation
446          * will typically not ever need to initialise it so it can be null.
447          */
448         xFontDirsMap = null;
449         xlfdMap = new HashMap<>(1);
450         fontNameMap = new HashMap<>(1);
451     }
452 
getX11FontName(String platName)453     private static String getX11FontName(String platName) {
454         String xlfd = platName.replaceAll("%d", "*");
455         if (NativeFont.fontExists(xlfd)) {
456             return xlfd;
457         } else {
458             return null;
459         }
460     }
461 
isHeadless()462     private boolean isHeadless() {
463         GraphicsEnvironment ge =
464             GraphicsEnvironment.getLocalGraphicsEnvironment();
465         return GraphicsEnvironment.isHeadless();
466     }
467 
specificFontIDForName(String name)468     private String specificFontIDForName(String name) {
469 
470         int[] hPos = new int[14];
471         int hyphenCnt = 1;
472         int pos = 1;
473 
474         while (pos != -1 && hyphenCnt < 14) {
475             pos = name.indexOf('-', pos);
476             if (pos != -1) {
477                 hPos[hyphenCnt++] = pos;
478                     pos++;
479             }
480         }
481 
482         if (hyphenCnt != 14) {
483             if (FontUtilities.debugFonts()) {
484                 FontUtilities.getLogger()
485                     .severe("Font Configuration Font ID is malformed:" + name);
486             }
487             return name; // what else can we do?
488         }
489 
490         StringBuffer sb =
491             new StringBuffer(name.substring(hPos[FAMILY_NAME_FIELD-1],
492                                             hPos[SETWIDTH_NAME_FIELD]));
493         sb.append(name.substring(hPos[CHARSET_REGISTRY_FIELD-1]));
494         String retval = sb.toString().toLowerCase (Locale.ENGLISH);
495         return retval;
496     }
497 
switchFontIDForName(String name)498     private String switchFontIDForName(String name) {
499 
500         int[] hPos = new int[14];
501         int hyphenCnt = 1;
502         int pos = 1;
503 
504         while (pos != -1 && hyphenCnt < 14) {
505             pos = name.indexOf('-', pos);
506             if (pos != -1) {
507                 hPos[hyphenCnt++] = pos;
508                     pos++;
509             }
510         }
511 
512         if (hyphenCnt != 14) {
513             if (FontUtilities.debugFonts()) {
514                 FontUtilities.getLogger()
515                     .severe("Font Configuration Font ID is malformed:" + name);
516             }
517             return name; // what else can we do?
518         }
519 
520         String slant = name.substring(hPos[SLANT_FIELD-1]+1,
521                                            hPos[SLANT_FIELD]);
522         String family = name.substring(hPos[FAMILY_NAME_FIELD-1]+1,
523                                            hPos[FAMILY_NAME_FIELD]);
524         String registry = name.substring(hPos[CHARSET_REGISTRY_FIELD-1]+1,
525                                            hPos[CHARSET_REGISTRY_FIELD]);
526         String encoding = name.substring(hPos[CHARSET_ENCODING_FIELD-1]+1);
527 
528         if (slant.equals("i")) {
529             slant = "o";
530         } else if (slant.equals("o")) {
531             slant = "i";
532         }
533         // workaround for #4471000
534         if (family.equals("itc zapfdingbats")
535             && registry.equals("sun")
536             && encoding.equals("fontspecific")){
537             registry = "adobe";
538         }
539         StringBuffer sb =
540             new StringBuffer(name.substring(hPos[FAMILY_NAME_FIELD-1],
541                                             hPos[SLANT_FIELD-1]+1));
542         sb.append(slant);
543         sb.append(name.substring(hPos[SLANT_FIELD],
544                                  hPos[SETWIDTH_NAME_FIELD]+1));
545         sb.append(registry);
546         sb.append(name.substring(hPos[CHARSET_ENCODING_FIELD-1]));
547         String retval = sb.toString().toLowerCase (Locale.ENGLISH);
548         return retval;
549     }
550 
551     /**
552      * Returns the face name for the given XLFD.
553      */
getFileNameFromXLFD(String name)554     public String getFileNameFromXLFD(String name) {
555         String fileName = null;
556         String fontID = specificFontIDForName(name);
557         if (fontID != null) {
558             fileName = fontNameMap.get(fontID);
559             if (fileName == null) {
560                 fontID = switchFontIDForName(name);
561                 fileName = fontNameMap.get(fontID);
562             }
563             if (fileName == null) {
564                 fileName = getDefaultFontFile();
565             }
566         }
567         return fileName;
568     }
569 
570     /* Register just the paths, (it doesn't register the fonts).
571      * If a font configuration file has specified a baseFontPath
572      * fontPath is just those directories, unless on usage we
573      * find it doesn't contain what we need for the logical fonts.
574      * Otherwise, we register all the paths on Solaris, because
575      * the fontPath we have here is the complete one from
576      * parsing /var/sadm/install/contents, not just
577      * what's on the X font path (may be this should be
578      * changed).
579      * But for now what it means is that if we didn't do
580      * this then if the font weren't listed anywhere on the
581      * less complete font path we'd trigger loadFonts which
582      * actually registers the fonts. This may actually be
583      * the right thing tho' since that would also set up
584      * the X font path without which we wouldn't be able to
585      * display some "native" fonts.
586      * So something to revisit is that probably fontPath
587      * here ought to be only the X font path + jre font dir.
588      * loadFonts should have a separate native call to
589      * get the rest of the platform font path.
590      *
591      * Registering the directories can now be avoided in the
592      * font configuration initialisation when filename entries
593      * exist in the font configuration file for all fonts.
594      * (Perhaps a little confusingly a filename entry is
595      * actually keyed using the XLFD used in the font entries,
596      * and it maps *to* a real filename).
597      * In the event any are missing, registration of all
598      * directories will be invoked to find the real files.
599      *
600      * But registering the directory performed other
601      * functions such as filling in the map of all native names
602      * for the font. So when this method isn't invoked, they still
603      * must be found. This is mitigated by getNativeNames now
604      * being able to return at least the platform name, but mostly
605      * by ensuring that when a filename key is found, that
606      * xlfd key is stored as one of the set of platform names
607      * for the font. Its a set because typical font configuration
608      * files reference the same CJK font files using multiple
609      * X11 encodings. For the code that adds this to the map
610      * see X11GE.getFileNameFromPlatformName(..)
611      * If you don't get all of these then some code points may
612      * not use the Xserver, and will not get the PCF bitmaps
613      * that are available for some point sizes.
614      * So, in the event that there is such a problem,
615      * unconditionally making this call may be necessary, at
616      * some cost to JRE start-up
617      */
618     @Override
registerFontDirs(String pathName)619     protected void registerFontDirs(String pathName) {
620 
621         StringTokenizer parser = new StringTokenizer(pathName,
622                                                      File.pathSeparator);
623         try {
624             while (parser.hasMoreTokens()) {
625                 String dirPath = parser.nextToken();
626                 if (dirPath != null && !registeredDirs.containsKey(dirPath)) {
627                     registeredDirs.put(dirPath, null);
628                     registerFontDir(dirPath);
629                 }
630             }
631         } catch (NoSuchElementException e) {
632         }
633     }
634 
635     // An X font spec (xlfd) includes an encoding. The same TrueType font file
636     // may be referenced from different X font directories in font.dir files
637     // to support use in multiple encodings by X apps.
638     // So for the purposes of font configuration logical fonts where AWT
639     // heavyweights need to access the font via X APIs we need to ensure that
640     // the directory for precisely the encodings needed by this are added to
641     // the x font path. This requires that we note the platform names
642     // specified in font configuration files and use that to identify the
643     // X font directory that contains a font.dir file for that platform name
644     // and add it to the X font path (if display is local)
645     // Here we make use of an already built map of xlfds to font locations
646     // to add the font location to the set of those required to build the
647     // x font path needed by AWT.
648     // These are added to the x font path later.
649     // All this is necessary because on Solaris the font.dir directories
650     // may contain not real font files, but symbolic links to the actual
651     // location but that location is not suitable for the x font path, since
652     // it probably doesn't have a font.dir at all and certainly not one
653     // with the required encodings
654     // If the fontconfiguration file is properly set up so that all fonts
655     // are mapped to files then we will never trigger initialising
656     // xFontDirsMap (it will be null). In this case the awtfontpath entries
657     // must specify all the X11 directories needed by AWT.
658     @Override
addFontToPlatformFontPath(String platformName)659     protected void addFontToPlatformFontPath(String platformName) {
660         // Lazily initialize fontConfigDirs.
661         getPlatformFontPathFromFontConfig();
662         if (xFontDirsMap != null) {
663             String fontID = specificFontIDForName(platformName);
664             String dirName = xFontDirsMap.get(fontID);
665             if (dirName != null) {
666                 fontConfigDirs.add(dirName);
667             }
668         }
669         return;
670     }
671 
getPlatformFontPathFromFontConfig()672     private void getPlatformFontPathFromFontConfig() {
673         if (fontConfigDirs == null) {
674             fontConfigDirs = getFontConfiguration().getAWTFontPathSet();
675             if (FontUtilities.debugFonts() && fontConfigDirs != null) {
676                 String[] names = fontConfigDirs.toArray(new String[0]);
677                 for (int i=0;i<names.length;i++) {
678                     FontUtilities.getLogger().info("awtfontpath : " + names[i]);
679                 }
680             }
681         }
682     }
683 
684     @Override
registerPlatformFontsUsedByFontConfiguration()685     protected void registerPlatformFontsUsedByFontConfiguration() {
686         // Lazily initialize fontConfigDirs.
687         getPlatformFontPathFromFontConfig();
688         if (fontConfigDirs == null) {
689             return;
690         }
691         if (FontUtilities.isLinux || FontUtilities.isBSD) {
692             fontConfigDirs.add(jreLibDirName+File.separator+"oblique-fonts");
693         }
694         fontdirs = fontConfigDirs.toArray(new String[0]);
695     }
696 
697     // Implements SunGraphicsEnvironment.createFontConfiguration.
createFontConfiguration()698     protected FontConfiguration createFontConfiguration() {
699         /* The logic here decides whether to use a preconfigured
700          * fontconfig.properties file, or synthesise one using platform APIs.
701          * On Solaris we try to use the
702          * pre-configured ones, but if the files it specifies are missing
703          * we fail-safe to synthesising one. This might happen if Solaris
704          * changes its fonts.
705          * For Linux we require an exact match of distro and version to
706          * use the preconfigured file.
707          * If synthesising fails, we fall back to any preconfigured file
708          * and do the best we can.
709          */
710         FontConfiguration mFontConfig = new MFontConfiguration(this);
711         if ((FontUtilities.isLinux && !mFontConfig.foundOsSpecificFile()) ||
712             (FontUtilities.isBSD && !mFontConfig.foundOsSpecificFile())) {
713             FcFontConfiguration fcFontConfig =
714                 new FcFontConfiguration(this);
715             if (fcFontConfig.init()) {
716                 return fcFontConfig;
717             }
718         }
719         mFontConfig.init();
720         return mFontConfig;
721     }
722 
723     public FontConfiguration
createFontConfiguration(boolean preferLocaleFonts, boolean preferPropFonts)724         createFontConfiguration(boolean preferLocaleFonts,
725                                 boolean preferPropFonts) {
726 
727         return new MFontConfiguration(this,
728                                       preferLocaleFonts, preferPropFonts);
729     }
730 
getFontPath(boolean noType1Fonts)731     protected synchronized String getFontPath(boolean noType1Fonts) {
732         isHeadless(); // make sure GE is inited, as its the X11 lock.
733         return getFontPathNative(noType1Fonts, true);
734     }
735 
736     @Override
getFontConfigFUIR(String family, int style, int size)737     protected FontUIResource getFontConfigFUIR(String family, int style, int size) {
738 
739         CompositeFont font2D = getFontConfigManager().getFontConfigFont(family, style);
740 
741         if (font2D == null) { // Not expected, just a precaution.
742            return new FontUIResource(family, style, size);
743         }
744 
745         /* The name of the font will be that of the physical font in slot,
746          * but by setting the handle to that of the CompositeFont it
747          * renders as that CompositeFont.
748          * It also needs to be marked as a created font which is the
749          * current mechanism to signal that deriveFont etc must copy
750          * the handle from the original font.
751          */
752         FontUIResource fuir =
753             new FontUIResource(font2D.getFamilyName(null), style, size);
754         FontAccess.getFontAccess().setFont2D(fuir, font2D.handle);
755         FontAccess.getFontAccess().setCreatedFont(fuir);
756         return fuir;
757     }
758 }
759