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