| /* | |
| * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.awt; | |
| import java.awt.GraphicsEnvironment; | |
| import java.io.BufferedReader; | |
| import java.io.File; | |
| import java.io.FileReader; | |
| import java.io.IOException; | |
| import java.io.StreamTokenizer; | |
| import java.util.HashMap; | |
| import java.util.HashSet; | |
| import java.util.Locale; | |
| import java.util.Map; | |
| import java.util.NoSuchElementException; | |
| import java.util.StringTokenizer; | |
| import java.util.Vector; | |
| import javax.swing.plaf.FontUIResource; | |
| import sun.font.MFontConfiguration; | |
| import sun.font.CompositeFont; | |
| import sun.font.FontManager; | |
| import sun.font.SunFontManager; | |
| import sun.font.FcFontConfiguration; | |
| import sun.font.FontAccess; | |
| import sun.font.FontUtilities; | |
| import sun.font.NativeFont; | |
| /** | |
| * The X11 implementation of {@link FontManager}. | |
| */ | |
| public final class X11FontManager extends FcFontManager { | |
| // constants identifying XLFD and font ID fields | |
| private static final int FOUNDRY_FIELD = 1; | |
| private static final int FAMILY_NAME_FIELD = 2; | |
| private static final int WEIGHT_NAME_FIELD = 3; | |
| private static final int SLANT_FIELD = 4; | |
| private static final int SETWIDTH_NAME_FIELD = 5; | |
| private static final int ADD_STYLE_NAME_FIELD = 6; | |
| private static final int PIXEL_SIZE_FIELD = 7; | |
| private static final int POINT_SIZE_FIELD = 8; | |
| private static final int RESOLUTION_X_FIELD = 9; | |
| private static final int RESOLUTION_Y_FIELD = 10; | |
| private static final int SPACING_FIELD = 11; | |
| private static final int AVERAGE_WIDTH_FIELD = 12; | |
| private static final int CHARSET_REGISTRY_FIELD = 13; | |
| private static final int CHARSET_ENCODING_FIELD = 14; | |
| /* | |
| * fontNameMap is a map from a fontID (which is a substring of an XLFD like | |
| * "-monotype-arial-bold-r-normal-iso8859-7") | |
| * to font file path like | |
| * /usr/openwin/lib/locale/iso_8859_7/X11/fonts/TrueType/ArialBoldItalic.ttf | |
| * It's used in a couple of methods like | |
| * getFileNameFomPlatformName(..) to help locate the font file. | |
| * We use this substring of a full XLFD because the font configuration files | |
| * define the XLFDs in a way that's easier to make into a request. | |
| * E.g., the -0-0-0-0-p-0- reported by X is -*-%d-*-*-p-*- in the font | |
| * configuration files. We need to remove that part for comparisons. | |
| */ | |
| private static Map<String, String> fontNameMap = new HashMap<>(); | |
| /* | |
| * xlfdMap is a map from a platform path like | |
| * /usr/openwin/lib/locale/ja/X11/fonts/TT/HG-GothicB.ttf to an XLFD like | |
| * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0" | |
| * Because there may be multiple native names, because the font is used | |
| * to support multiple X encodings for example, the value of an entry in | |
| * this map is always a vector where we store all the native names. | |
| * For fonts which we don't understand the key isn't a pathname, its | |
| * the full XLFD string like :- | |
| * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0" | |
| */ | |
| private static Map<String, Vector<String>> xlfdMap = new HashMap<>(); | |
| /* xFontDirsMap is also a map from a font ID to a font filepath. | |
| * The difference from fontNameMap is just that it does not have | |
| * resolved symbolic links. Normally this is not interesting except | |
| * that we need to know the directory in which a font was found to | |
| * add it to the X font server path, since although the files may | |
| * be linked, the fonts.dir is different and specific to the encoding | |
| * handled by that directory. This map is nulled out after use to free | |
| * heap space. If the optimal path is taken, such that all fonts in | |
| * font configuration files are referenced by filename, then the font | |
| * dir can be directly derived as its parent directory. | |
| * If a font is used by two XLFDs, each corresponding to a different | |
| * X11 font directory, then precautions must be taken to include both | |
| * directories. | |
| */ | |
| private static Map<String, String> xFontDirsMap; | |
| /* | |
| * This is the set of font directories needed to be on the X font path | |
| * to enable AWT heavyweights to find all of the font configuration fonts. | |
| * It is populated by : | |
| * - awtfontpath entries in the fontconfig.properties | |
| * - parent directories of "core" fonts used in the fontconfig.properties | |
| * - looking up font dirs in the xFontDirsMap where the key is a fontID | |
| * (cut down version of the XLFD read from the font configuration file). | |
| * This set is nulled out after use to free heap space. | |
| */ | |
| private static HashSet<String> fontConfigDirs = null; | |
| /* | |
| * Used to eliminate redundant work. When a font directory is | |
| * registered it added to this list. Subsequent registrations for the | |
| * same directory can then be skipped by checking this Map. | |
| * Access to this map is not synchronised here since creation | |
| * of the singleton GE instance is already synchronised and that is | |
| * the only code path that accesses this map. | |
| */ | |
| private static HashMap<String, Object> registeredDirs = new HashMap<>(); | |
| /* Array of directories to be added to the X11 font path. | |
| * Used by static method called from Toolkits which use X11 fonts. | |
| * Specifically this means MToolkit | |
| */ | |
| private static String[] fontdirs = null; | |
| public static X11FontManager getInstance() { | |
| return (X11FontManager) SunFontManager.getInstance(); | |
| } | |
| /** | |
| * Takes family name property in the following format: | |
| * "-linotype-helvetica-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1" | |
| * and returns the name of the corresponding physical font. | |
| * This code is used to resolve font configuration fonts, and expects | |
| * only to get called for these fonts. | |
| */ | |
| @Override | |
| public String getFileNameFromPlatformName(String platName) { | |
| /* If the FontConfig file doesn't use xlfds, or its | |
| * FcFontConfiguration, this may be already a file name. | |
| */ | |
| if (platName.startsWith("/")) { | |
| return platName; | |
| } | |
| String fileName = null; | |
| String fontID = specificFontIDForName(platName); | |
| /* If the font filename has been explicitly assigned in the | |
| * font configuration file, use it. This avoids accessing | |
| * the wrong fonts on Linux, where different fonts (some | |
| * of which may not be usable by 2D) may share the same | |
| * specific font ID. It may also speed up the lookup. | |
| */ | |
| fileName = super.getFileNameFromPlatformName(platName); | |
| if (fileName != null) { | |
| if (isHeadless() && fileName.startsWith("-")) { | |
| /* if it's headless, no xlfd should be used */ | |
| return null; | |
| } | |
| if (fileName.startsWith("/")) { | |
| /* If a path is assigned in the font configuration file, | |
| * it is required that the config file also specify using the | |
| * new awtfontpath key the X11 font directories | |
| * which must be added to the X11 font path to support | |
| * AWT access to that font. For that reason we no longer | |
| * have code here to add the parent directory to the list | |
| * of font config dirs, since the parent directory may not | |
| * be sufficient if fonts are symbolically linked to a | |
| * different directory. | |
| * | |
| * Add this XLFD (platform name) to the list of known | |
| * ones for this file. | |
| */ | |
| Vector<String> xVal = xlfdMap.get(fileName); | |
| if (xVal == null) { | |
| /* Try to be robust on Linux distros which move fonts | |
| * around by verifying that the fileName represents a | |
| * file that exists. If it doesn't, set it to null | |
| * to trigger a search. | |
| */ | |
| if (getFontConfiguration().needToSearchForFile(fileName)) { | |
| fileName = null; | |
| } | |
| if (fileName != null) { | |
| xVal = new Vector<>(); | |
| xVal.add(platName); | |
| xlfdMap.put(fileName, xVal); | |
| } | |
| } else { | |
| if (!xVal.contains(platName)) { | |
| xVal.add(platName); | |
| } | |
| } | |
| } | |
| if (fileName != null) { | |
| fontNameMap.put(fontID, fileName); | |
| return fileName; | |
| } | |
| } | |
| if (fontID != null) { | |
| fileName = fontNameMap.get(fontID); | |
| if (fontPath == null && | |
| (fileName == null || !fileName.startsWith("/"))) { | |
| if (FontUtilities.debugFonts()) { | |
| FontUtilities.logWarning("** Registering all font paths because " + | |
| "can't find file for " + platName); | |
| } | |
| fontPath = getPlatformFontPath(noType1Font); | |
| registerFontDirs(fontPath); | |
| if (FontUtilities.debugFonts()) { | |
| FontUtilities.logWarning("** Finished registering all font paths"); | |
| } | |
| fileName = fontNameMap.get(fontID); | |
| } | |
| if (fileName == null && !isHeadless()) { | |
| /* Query X11 directly to see if this font is available | |
| * as a native font. | |
| */ | |
| fileName = getX11FontName(platName); | |
| } | |
| if (fileName == null) { | |
| fontID = switchFontIDForName(platName); | |
| fileName = fontNameMap.get(fontID); | |
| } | |
| if (fileName != null) { | |
| fontNameMap.put(fontID, fileName); | |
| } | |
| } | |
| return fileName; | |
| } | |
| @Override | |
| protected String[] getNativeNames(String fontFileName, | |
| String platformName) { | |
| Vector<String> nativeNames; | |
| if ((nativeNames=xlfdMap.get(fontFileName))==null) { | |
| if (platformName == null) { | |
| return null; | |
| } else { | |
| /* back-stop so that at least the name used in the | |
| * font configuration file is known as a native name | |
| */ | |
| String []natNames = new String[1]; | |
| natNames[0] = platformName; | |
| return natNames; | |
| } | |
| } else { | |
| int len = nativeNames.size(); | |
| return nativeNames.toArray(new String[len]); | |
| } | |
| } | |
| /* NOTE: this method needs to be executed in a privileged context. | |
| * The superclass constructor which is the primary caller of | |
| * this method executes entirely in such a context. Additionally | |
| * the loadFonts() method does too. So all should be well. | |
| */ | |
| @Override | |
| protected void registerFontDir(String path) { | |
| /* fonts.dir file format looks like :- | |
| * 47 | |
| * Arial.ttf -monotype-arial-regular-r-normal--0-0-0-0-p-0-iso8859-1 | |
| * Arial-Bold.ttf -monotype-arial-bold-r-normal--0-0-0-0-p-0-iso8859-1 | |
| * ... | |
| */ | |
| if (FontUtilities.debugFonts()) { | |
| FontUtilities.logInfo("ParseFontDir " + path); | |
| } | |
| File fontsDotDir = new File(path + File.separator + "fonts.dir"); | |
| if (!fontsDotDir.canRead()) { | |
| return; | |
| } | |
| try (FileReader fr = new FileReader(fontsDotDir)) { | |
| BufferedReader br = new BufferedReader(fr, 8192); | |
| StreamTokenizer st = new StreamTokenizer(br); | |
| st.eolIsSignificant(true); | |
| int ttype = st.nextToken(); | |
| if (ttype == StreamTokenizer.TT_NUMBER) { | |
| int numEntries = (int)st.nval; | |
| ttype = st.nextToken(); | |
| if (ttype == StreamTokenizer.TT_EOL) { | |
| st.resetSyntax(); | |
| st.wordChars(32, 127); | |
| st.wordChars(128 + 32, 255); | |
| st.whitespaceChars(0, 31); | |
| for (int i=0; i < numEntries; i++) { | |
| ttype = st.nextToken(); | |
| if (ttype == StreamTokenizer.TT_EOF) { | |
| break; | |
| } | |
| if (ttype != StreamTokenizer.TT_WORD) { | |
| break; | |
| } | |
| int breakPos = st.sval.indexOf(' '); | |
| if (breakPos <= 0) { | |
| /* On TurboLinux 8.0 a fonts.dir file had | |
| * a line with integer value "24" which | |
| * appeared to be the number of remaining | |
| * entries in the file. This didn't add to | |
| * the value on the first line of the file. | |
| * Seemed like XFree86 didn't like this line | |
| * much either. It failed to parse the file. | |
| * Ignore lines like this completely, and | |
| * don't let them count as an entry. | |
| */ | |
| numEntries++; | |
| ttype = st.nextToken(); | |
| if (ttype != StreamTokenizer.TT_EOL) { | |
| break; | |
| } | |
| continue; | |
| } | |
| if (st.sval.charAt(0) == '!') { | |
| /* TurboLinux 8.0 comment line: ignore. | |
| * can't use st.commentChar('!') to just | |
| * skip because this line mustn't count | |
| * against numEntries. | |
| */ | |
| numEntries++; | |
| ttype = st.nextToken(); | |
| if (ttype != StreamTokenizer.TT_EOL) { | |
| break; | |
| } | |
| continue; | |
| } | |
| String fileName = st.sval.substring(0, breakPos); | |
| /* TurboLinux 8.0 uses some additional syntax to | |
| * indicate algorithmic styling values. | |
| * Ignore ':' separated files at the beginning | |
| * of the fileName | |
| */ | |
| int lastColon = fileName.lastIndexOf(':'); | |
| if (lastColon > 0) { | |
| if (lastColon+1 >= fileName.length()) { | |
| continue; | |
| } | |
| fileName = fileName.substring(lastColon+1); | |
| } | |
| String fontPart = st.sval.substring(breakPos+1); | |
| String fontID = specificFontIDForName(fontPart); | |
| String sVal = fontNameMap.get(fontID); | |
| if (FontUtilities.debugFonts()) { | |
| FontUtilities.logInfo("file=" + fileName + | |
| " xlfd=" + fontPart); | |
| FontUtilities.logInfo("fontID=" + fontID + | |
| " sVal=" + sVal); | |
| } | |
| String fullPath; | |
| try { | |
| File file = new File(path,fileName); | |
| /* we may have a resolved symbolic link | |
| * this becomes important for an xlfd we | |
| * still need to know the location it was | |
| * found to update the X server font path | |
| * for use by AWT heavyweights - and when 2D | |
| * wants to use the native rasteriser. | |
| */ | |
| if (xFontDirsMap == null) { | |
| xFontDirsMap = new HashMap<>(); | |
| } | |
| xFontDirsMap.put(fontID, path); | |
| fullPath = file.getCanonicalPath(); | |
| } catch (IOException e) { | |
| fullPath = path + File.separator + fileName; | |
| } | |
| Vector<String> xVal = xlfdMap.get(fullPath); | |
| if (FontUtilities.debugFonts()) { | |
| FontUtilities.logInfo("fullPath=" + fullPath + | |
| " xVal=" + xVal); | |
| } | |
| if ((xVal == null || !xVal.contains(fontPart)) && | |
| (sVal == null) || !sVal.startsWith("/")) { | |
| if (FontUtilities.debugFonts()) { | |
| FontUtilities.logInfo("Map fontID:"+fontID + | |
| "to file:" + fullPath); | |
| } | |
| fontNameMap.put(fontID, fullPath); | |
| if (xVal == null) { | |
| xVal = new Vector<>(); | |
| xlfdMap.put (fullPath, xVal); | |
| } | |
| xVal.add(fontPart); | |
| } | |
| ttype = st.nextToken(); | |
| if (ttype != StreamTokenizer.TT_EOL) { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } catch (IOException ioe) { | |
| } | |
| } | |
| @Override | |
| public void loadFonts() { | |
| super.loadFonts(); | |
| /* These maps are greatly expanded during a loadFonts but | |
| * can be reset to their initial state afterwards. | |
| * Since preferLocaleFonts() and preferProportionalFonts() will | |
| * trigger a partial repopulating from the FontConfiguration | |
| * it has to be the initial (empty) state for the latter two, not | |
| * simply nulling out. | |
| * xFontDirsMap is a special case in that the implementation | |
| * will typically not ever need to initialise it so it can be null. | |
| */ | |
| xFontDirsMap = null; | |
| xlfdMap = new HashMap<>(1); | |
| fontNameMap = new HashMap<>(1); | |
| } | |
| private static String getX11FontName(String platName) { | |
| String xlfd = platName.replaceAll("%d", "*"); | |
| if (NativeFont.fontExists(xlfd)) { | |
| return xlfd; | |
| } else { | |
| return null; | |
| } | |
| } | |
| private boolean isHeadless() { | |
| GraphicsEnvironment ge = | |
| GraphicsEnvironment.getLocalGraphicsEnvironment(); | |
| return GraphicsEnvironment.isHeadless(); | |
| } | |
| private String specificFontIDForName(String name) { | |
| int[] hPos = new int[14]; | |
| int hyphenCnt = 1; | |
| int pos = 1; | |
| while (pos != -1 && hyphenCnt < 14) { | |
| pos = name.indexOf('-', pos); | |
| if (pos != -1) { | |
| hPos[hyphenCnt++] = pos; | |
| pos++; | |
| } | |
| } | |
| if (hyphenCnt != 14) { | |
| if (FontUtilities.debugFonts()) { | |
| FontUtilities.logSevere("Font Configuration Font ID is malformed:" + name); | |
| } | |
| return name; // what else can we do? | |
| } | |
| String sb = name.substring(hPos[FAMILY_NAME_FIELD-1], hPos[SETWIDTH_NAME_FIELD]) | |
| + name.substring(hPos[CHARSET_REGISTRY_FIELD-1]); | |
| String retval = sb.toLowerCase(Locale.ENGLISH); | |
| return retval; | |
| } | |
| private String switchFontIDForName(String name) { | |
| int[] hPos = new int[14]; | |
| int hyphenCnt = 1; | |
| int pos = 1; | |
| while (pos != -1 && hyphenCnt < 14) { | |
| pos = name.indexOf('-', pos); | |
| if (pos != -1) { | |
| hPos[hyphenCnt++] = pos; | |
| pos++; | |
| } | |
| } | |
| if (hyphenCnt != 14) { | |
| if (FontUtilities.debugFonts()) { | |
| FontUtilities.logSevere("Font Configuration Font ID is malformed:" + name); | |
| } | |
| return name; // what else can we do? | |
| } | |
| String slant = name.substring(hPos[SLANT_FIELD-1]+1, | |
| hPos[SLANT_FIELD]); | |
| String family = name.substring(hPos[FAMILY_NAME_FIELD-1]+1, | |
| hPos[FAMILY_NAME_FIELD]); | |
| String registry = name.substring(hPos[CHARSET_REGISTRY_FIELD-1]+1, | |
| hPos[CHARSET_REGISTRY_FIELD]); | |
| String encoding = name.substring(hPos[CHARSET_ENCODING_FIELD-1]+1); | |
| if (slant.equals("i")) { | |
| slant = "o"; | |
| } else if (slant.equals("o")) { | |
| slant = "i"; | |
| } | |
| // workaround for #4471000 | |
| if (family.equals("itc zapfdingbats") | |
| && registry.equals("sun") | |
| && encoding.equals("fontspecific")){ | |
| registry = "adobe"; | |
| } | |
| String sb = name.substring(hPos[FAMILY_NAME_FIELD-1], hPos[SLANT_FIELD-1]+1) | |
| + slant | |
| + name.substring(hPos[SLANT_FIELD], hPos[SETWIDTH_NAME_FIELD]+1) | |
| + registry | |
| + name.substring(hPos[CHARSET_ENCODING_FIELD-1]); | |
| String retval = sb.toLowerCase(Locale.ENGLISH); | |
| return retval; | |
| } | |
| /** | |
| * Returns the face name for the given XLFD. | |
| */ | |
| public String getFileNameFromXLFD(String name) { | |
| String fileName = null; | |
| String fontID = specificFontIDForName(name); | |
| if (fontID != null) { | |
| fileName = fontNameMap.get(fontID); | |
| if (fileName == null) { | |
| fontID = switchFontIDForName(name); | |
| fileName = fontNameMap.get(fontID); | |
| } | |
| if (fileName == null) { | |
| fileName = getDefaultFontFile(); | |
| } | |
| } | |
| return fileName; | |
| } | |
| /* Register just the paths, (it doesn't register the fonts). | |
| * If a font configuration file has specified a baseFontPath | |
| * fontPath is just those directories, unless on usage we | |
| * find it doesn't contain what we need for the logical fonts. | |
| * Otherwise, we register all the paths on Solaris, because | |
| * the fontPath we have here is the complete one from | |
| * parsing /var/sadm/install/contents, not just | |
| * what's on the X font path (may be this should be | |
| * changed). | |
| * But for now what it means is that if we didn't do | |
| * this then if the font weren't listed anywhere on the | |
| * less complete font path we'd trigger loadFonts which | |
| * actually registers the fonts. This may actually be | |
| * the right thing tho' since that would also set up | |
| * the X font path without which we wouldn't be able to | |
| * display some "native" fonts. | |
| * So something to revisit is that probably fontPath | |
| * here ought to be only the X font path + jre font dir. | |
| * loadFonts should have a separate native call to | |
| * get the rest of the platform font path. | |
| * | |
| * Registering the directories can now be avoided in the | |
| * font configuration initialisation when filename entries | |
| * exist in the font configuration file for all fonts. | |
| * (Perhaps a little confusingly a filename entry is | |
| * actually keyed using the XLFD used in the font entries, | |
| * and it maps *to* a real filename). | |
| * In the event any are missing, registration of all | |
| * directories will be invoked to find the real files. | |
| * | |
| * But registering the directory performed other | |
| * functions such as filling in the map of all native names | |
| * for the font. So when this method isn't invoked, they still | |
| * must be found. This is mitigated by getNativeNames now | |
| * being able to return at least the platform name, but mostly | |
| * by ensuring that when a filename key is found, that | |
| * xlfd key is stored as one of the set of platform names | |
| * for the font. Its a set because typical font configuration | |
| * files reference the same CJK font files using multiple | |
| * X11 encodings. For the code that adds this to the map | |
| * see X11GE.getFileNameFromPlatformName(..) | |
| * If you don't get all of these then some code points may | |
| * not use the Xserver, and will not get the PCF bitmaps | |
| * that are available for some point sizes. | |
| * So, in the event that there is such a problem, | |
| * unconditionally making this call may be necessary, at | |
| * some cost to JRE start-up | |
| */ | |
| @Override | |
| protected void registerFontDirs(String pathName) { | |
| StringTokenizer parser = new StringTokenizer(pathName, | |
| File.pathSeparator); | |
| try { | |
| while (parser.hasMoreTokens()) { | |
| String dirPath = parser.nextToken(); | |
| if (dirPath != null && !registeredDirs.containsKey(dirPath)) { | |
| registeredDirs.put(dirPath, null); | |
| registerFontDir(dirPath); | |
| } | |
| } | |
| } catch (NoSuchElementException e) { | |
| } | |
| } | |
| // An X font spec (xlfd) includes an encoding. The same TrueType font file | |
| // may be referenced from different X font directories in font.dir files | |
| // to support use in multiple encodings by X apps. | |
| // So for the purposes of font configuration logical fonts where AWT | |
| // heavyweights need to access the font via X APIs we need to ensure that | |
| // the directory for precisely the encodings needed by this are added to | |
| // the x font path. This requires that we note the platform names | |
| // specified in font configuration files and use that to identify the | |
| // X font directory that contains a font.dir file for that platform name | |
| // and add it to the X font path (if display is local) | |
| // Here we make use of an already built map of xlfds to font locations | |
| // to add the font location to the set of those required to build the | |
| // x font path needed by AWT. | |
| // These are added to the x font path later. | |
| // All this is necessary because on Solaris the font.dir directories | |
| // may contain not real font files, but symbolic links to the actual | |
| // location but that location is not suitable for the x font path, since | |
| // it probably doesn't have a font.dir at all and certainly not one | |
| // with the required encodings | |
| // If the fontconfiguration file is properly set up so that all fonts | |
| // are mapped to files then we will never trigger initialising | |
| // xFontDirsMap (it will be null). In this case the awtfontpath entries | |
| // must specify all the X11 directories needed by AWT. | |
| @Override | |
| protected void addFontToPlatformFontPath(String platformName) { | |
| // Lazily initialize fontConfigDirs. | |
| getPlatformFontPathFromFontConfig(); | |
| if (xFontDirsMap != null) { | |
| String fontID = specificFontIDForName(platformName); | |
| String dirName = xFontDirsMap.get(fontID); | |
| if (dirName != null) { | |
| fontConfigDirs.add(dirName); | |
| } | |
| } | |
| return; | |
| } | |
| private void getPlatformFontPathFromFontConfig() { | |
| if (fontConfigDirs == null) { | |
| fontConfigDirs = getFontConfiguration().getAWTFontPathSet(); | |
| if (FontUtilities.debugFonts() && fontConfigDirs != null) { | |
| String[] names = fontConfigDirs.toArray(new String[0]); | |
| for (int i=0;i<names.length;i++) { | |
| FontUtilities.logInfo("awtfontpath : " + names[i]); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| protected void registerPlatformFontsUsedByFontConfiguration() { | |
| // Lazily initialize fontConfigDirs. | |
| getPlatformFontPathFromFontConfig(); | |
| if (fontConfigDirs == null) { | |
| return; | |
| } | |
| if (FontUtilities.isLinux) { | |
| fontConfigDirs.add(jreLibDirName+File.separator+"oblique-fonts"); | |
| } | |
| fontdirs = fontConfigDirs.toArray(new String[0]); | |
| } | |
| // Implements SunGraphicsEnvironment.createFontConfiguration. | |
| @Override | |
| protected FontConfiguration createFontConfiguration() { | |
| /* The logic here decides whether to use a preconfigured | |
| * fontconfig.properties file, or synthesise one using platform APIs. | |
| * On Solaris we try to use the | |
| * pre-configured ones, but if the files it specifies are missing | |
| * we fail-safe to synthesising one. This might happen if Solaris | |
| * changes its fonts. | |
| * For Linux we require an exact match of distro and version to | |
| * use the preconfigured file. | |
| * If synthesising fails, we fall back to any preconfigured file | |
| * and do the best we can. | |
| */ | |
| FontConfiguration mFontConfig = new MFontConfiguration(this); | |
| if ((FontUtilities.isLinux && !mFontConfig.foundOsSpecificFile())) { | |
| FcFontConfiguration fcFontConfig = | |
| new FcFontConfiguration(this); | |
| if (fcFontConfig.init()) { | |
| return fcFontConfig; | |
| } | |
| } | |
| mFontConfig.init(); | |
| return mFontConfig; | |
| } | |
| @Override | |
| public FontConfiguration | |
| createFontConfiguration(boolean preferLocaleFonts, | |
| boolean preferPropFonts) { | |
| return new MFontConfiguration(this, | |
| preferLocaleFonts, preferPropFonts); | |
| } | |
| @Override | |
| protected synchronized String getFontPath(boolean noType1Fonts) { | |
| isHeadless(); // make sure GE is inited, as its the X11 lock. | |
| return getFontPathNative(noType1Fonts, true); | |
| } | |
| @Override | |
| protected FontUIResource getFontConfigFUIR(String family, int style, int size) { | |
| CompositeFont font2D = getFontConfigManager().getFontConfigFont(family, style); | |
| if (font2D == null) { // Not expected, just a precaution. | |
| return new FontUIResource(family, style, size); | |
| } | |
| /* The name of the font will be that of the physical font in slot, | |
| * but by setting the handle to that of the CompositeFont it | |
| * renders as that CompositeFont. | |
| * It also needs to be marked as a created font which is the | |
| * current mechanism to signal that deriveFont etc must copy | |
| * the handle from the original font. | |
| */ | |
| FontUIResource fuir = | |
| new FontUIResource(font2D.getFamilyName(null), style, size); | |
| FontAccess.getFontAccess().setFont2D(fuir, font2D.handle); | |
| FontAccess.getFontAccess().setCreatedFont(fuir); | |
| return fuir; | |
| } | |
| } |