Path: blob/master/src/java.desktop/unix/classes/sun/font/FontConfigManager.java
41153 views
/*1* Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.font;2627import java.util.Locale;2829import sun.awt.SunHints;30import sun.awt.SunToolkit;31import sun.util.logging.PlatformLogger;3233/**34* Small utility class to manage FontConfig.35*/36public class FontConfigManager {3738static boolean fontConfigFailed = false;3940/* This is populated by native */41private static final FontConfigInfo fcInfo = new FontConfigInfo();4243/* Begin support for GTK Look and Feel - query libfontconfig and44* return a composite Font to Swing that uses the desktop font(s).45*/4647/* These next three classes are just data structures.48*/49public static class FontConfigFont {50public String familyName; // eg Bitstream Vera Sans51public String styleStr; // eg Bold52public String fullName; // eg Bitstream Vera Sans Bold53public String fontFile; // eg /usr/X11/lib/fonts/foo.ttf54}5556public static class FcCompFont {57public String fcName; // eg sans58public String fcFamily; // eg sans59public String jdkName; // eg sansserif60public int style; // eg 0=PLAIN61public FontConfigFont firstFont;62public FontConfigFont[] allFonts;63//boolean preferBitmaps; // if embedded bitmaps preferred over AA64public CompositeFont compFont; // null if not yet created/known.65}6667public static class FontConfigInfo {68public int fcVersion;69public String[] cacheDirs = new String[4];70}7172/* fontconfig recognises slants roman, italic, as well as oblique,73* and a slew of weights, where the ones that matter here are74* regular and bold.75* To fully qualify what we want, we can for example ask for (eg)76* Font.PLAIN : "serif:regular:roman"77* Font.BOLD : "serif:bold:roman"78* Font.ITALIC : "serif:regular:italic"79* Font.BOLD|Font.ITALIC : "serif:bold:italic"80*/81private static String[] fontConfigNames = {82"sans:regular:roman",83"sans:bold:roman",84"sans:regular:italic",85"sans:bold:italic",8687"serif:regular:roman",88"serif:bold:roman",89"serif:regular:italic",90"serif:bold:italic",9192"monospace:regular:roman",93"monospace:bold:roman",94"monospace:regular:italic",95"monospace:bold:italic",96};9798/* This array has the array elements created in Java code and is99* passed down to native to be filled in.100*/101private FcCompFont[] fontConfigFonts;102103/**104* Instantiates a new FontConfigManager getting the default instance105* of FontManager from the FontManagerFactory.106*/107public FontConfigManager() {108}109110/* Called from code that needs to know what are the AA settings111* that apps using FC would pick up for the default desktop font.112* Note apps can change the default desktop font. etc, so this113* isn't certain to be right but its going to correct for most cases.114* Native return values map to the text aa values in sun.awt.SunHints.115* which is used to look up the renderinghint value object.116*/117public static Object getFontConfigAAHint() {118return getFontConfigAAHint("sans");119}120121/* This is public solely so that for debugging purposes it can be called122* with other names, which might (eg) include a size, eg "sans-24"123* The return value is a text aa rendering hint value.124* Normally we should call the no-args version.125*/126public static Object getFontConfigAAHint(String fcFamily) {127if (FontUtilities.isWindows) {128return null;129} else {130int hint = getFontConfigAASettings(getFCLocaleStr(), fcFamily);131if (hint < 0) {132return null;133} else {134return SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,135hint);136}137}138}139140141private static String getFCLocaleStr() {142Locale l = SunToolkit.getStartupLocale();143String localeStr = l.getLanguage();144String country = l.getCountry();145if (!country.isEmpty()) {146localeStr = localeStr + "-" + country;147}148return localeStr;149}150151/* This does cause the native libfontconfig to be loaded and unloaded,152* but it does not incur the overhead of initialisation of its153* data structures, so shouldn't have a measurable impact.154*/155public static native int getFontConfigVersion();156157/* This can be made public if it's needed to force a re-read158* rather than using the cached values. The re-read would be needed159* only if some event signalled that the fontconfig has changed.160* In that event this method would need to return directly the array161* to be used by the caller in case it subsequently changed.162*/163public synchronized void initFontConfigFonts(boolean includeFallbacks) {164165if (fontConfigFonts != null) {166if (!includeFallbacks || (fontConfigFonts[0].allFonts != null)) {167return;168}169}170171if (FontUtilities.isWindows || fontConfigFailed) {172return;173}174175long t0 = 0;176if (FontUtilities.isLogging()) {177t0 = System.nanoTime();178}179180FcCompFont[] fontArr = new FcCompFont[fontConfigNames.length];181182for (int i = 0; i< fontArr.length; i++) {183fontArr[i] = new FcCompFont();184fontArr[i].fcName = fontConfigNames[i];185int colonPos = fontArr[i].fcName.indexOf(':');186fontArr[i].fcFamily = fontArr[i].fcName.substring(0, colonPos);187fontArr[i].jdkName = FontUtilities.mapFcName(fontArr[i].fcFamily);188fontArr[i].style = i % 4; // depends on array order.189}190getFontConfig(getFCLocaleStr(), fcInfo, fontArr, includeFallbacks);191FontConfigFont anyFont = null;192/* If don't find anything (eg no libfontconfig), then just return */193for (int i = 0; i< fontArr.length; i++) {194FcCompFont fci = fontArr[i];195if (fci.firstFont == null) {196if (FontUtilities.isLogging()) {197FontUtilities.logInfo("Fontconfig returned no font for " + fontArr[i].fcName);198}199fontConfigFailed = true;200} else if (anyFont == null) {201anyFont = fci.firstFont;202}203}204205if (anyFont == null) {206if (FontUtilities.isLogging()) {207FontUtilities.logInfo("Fontconfig returned no fonts at all.");208}209fontConfigFailed = true;210return;211} else if (fontConfigFailed) {212for (int i = 0; i< fontArr.length; i++) {213if (fontArr[i].firstFont == null) {214fontArr[i].firstFont = anyFont;215}216}217}218219fontConfigFonts = fontArr;220221if (FontUtilities.isLogging()) {222long t1 = System.nanoTime();223FontUtilities.logInfo("Time spent accessing fontconfig="224+ ((t1 - t0) / 1000000) + "ms.");225226for (int i = 0; i< fontConfigFonts.length; i++) {227FcCompFont fci = fontConfigFonts[i];228FontUtilities.logInfo("FC font " + fci.fcName+" maps to family " +229fci.firstFont.familyName +230" in file " + fci.firstFont.fontFile);231if (fci.allFonts != null) {232for (int f=0;f<fci.allFonts.length;f++) {233FontConfigFont fcf = fci.allFonts[f];234FontUtilities.logInfo("Family=" + fcf.familyName +235" Style="+ fcf.styleStr +236" Fullname="+fcf.fullName +237" File="+fcf.fontFile);238}239}240}241}242}243244public PhysicalFont registerFromFcInfo(FcCompFont fcInfo) {245246SunFontManager fm = SunFontManager.getInstance();247248/* If it's a TTC file we need to know that as we will need to249* make sure we return the right font */250String fontFile = fcInfo.firstFont.fontFile;251int offset = fontFile.length()-4;252if (offset <= 0) {253return null;254}255String ext = fontFile.substring(offset).toLowerCase();256boolean isTTC = ext.equals(".ttc");257258/* If this file is already registered, can just return its font.259* However we do need to check in case it's a TTC as we need260* a specific font, so rather than directly returning it, let261* findFont2D resolve that.262*/263PhysicalFont physFont = fm.getRegisteredFontFile(fontFile);264if (physFont != null) {265if (isTTC) {266Font2D f2d = fm.findFont2D(fcInfo.firstFont.familyName,267fcInfo.style,268FontManager.NO_FALLBACK);269if (f2d instanceof PhysicalFont) { /* paranoia */270return (PhysicalFont)f2d;271} else {272return null;273}274} else {275return physFont;276}277}278279/* If the font may hide a JRE font, we want to use the JRE version,280* so make it point to the JRE font.281*/282physFont = fm.findJREDeferredFont(fcInfo.firstFont.familyName,283fcInfo.style);284285/* It is also possible the font file is on the "deferred" list,286* in which case we can just initialise it now.287*/288if (physFont == null &&289fm.isDeferredFont(fontFile) == true) {290physFont = fm.initialiseDeferredFont(fcInfo.firstFont.fontFile);291/* use findFont2D to get the right font from TTC's */292if (physFont != null) {293if (isTTC) {294Font2D f2d = fm.findFont2D(fcInfo.firstFont.familyName,295fcInfo.style,296FontManager.NO_FALLBACK);297if (f2d instanceof PhysicalFont) { /* paranoia */298return (PhysicalFont)f2d;299} else {300return null;301}302} else {303return physFont;304}305}306}307308/* In the majority of cases we reach here, and need to determine309* the type and rank to register the font.310*/311if (physFont == null) {312int fontFormat = SunFontManager.FONTFORMAT_NONE;313int fontRank = Font2D.UNKNOWN_RANK;314315if (ext.equals(".ttf") || isTTC) {316fontFormat = SunFontManager.FONTFORMAT_TRUETYPE;317fontRank = Font2D.TTF_RANK;318} else if (ext.equals(".pfa") || ext.equals(".pfb")) {319fontFormat = SunFontManager.FONTFORMAT_TYPE1;320fontRank = Font2D.TYPE1_RANK;321}322physFont = fm.registerFontFile(fcInfo.firstFont.fontFile, null,323fontFormat, true, fontRank);324}325return physFont;326}327328/*329* We need to return a Composite font which has as the font in330* its first slot one obtained from fontconfig.331*/332public CompositeFont getFontConfigFont(String name, int style) {333334name = name.toLowerCase();335336initFontConfigFonts(false);337if (fontConfigFonts == null) {338// This avoids an immediate NPE if fontconfig look up failed339// but doesn't guarantee this is a recoverable situation.340return null;341}342343FcCompFont fcInfo = null;344for (int i=0; i<fontConfigFonts.length; i++) {345if (name.equals(fontConfigFonts[i].fcFamily) &&346style == fontConfigFonts[i].style) {347fcInfo = fontConfigFonts[i];348break;349}350}351if (fcInfo == null) {352fcInfo = fontConfigFonts[0];353}354355if (FontUtilities.isLogging()) {356FontUtilities.logInfo("FC name=" + name + " style=" + style +357" uses " + fcInfo.firstFont.familyName +358" in file: " + fcInfo.firstFont.fontFile);359}360361if (fcInfo.compFont != null) {362return fcInfo.compFont;363}364365/* jdkFont is going to be used for slots 1..N and as a fallback.366* Slot 0 will be the physical font from fontconfig.367*/368FontManager fm = FontManagerFactory.getInstance();369CompositeFont jdkFont = (CompositeFont)370fm.findFont2D(fcInfo.jdkName, style, FontManager.LOGICAL_FALLBACK);371372if (fcInfo.firstFont.familyName == null ||373fcInfo.firstFont.fontFile == null) {374return (fcInfo.compFont = jdkFont);375}376377/* First, see if the family and exact style is already registered.378* If it is, use it. If it's not, then try to register it.379* If that registration fails (signalled by null) just return the380* regular JDK composite.381* Algorithmically styled fonts won't match on exact style, so382* will fall through this code, but the regisration code will383* find that file already registered and return its font.384*/385FontFamily family = FontFamily.getFamily(fcInfo.firstFont.familyName);386PhysicalFont physFont = null;387if (family != null) {388Font2D f2D = family.getFontWithExactStyleMatch(fcInfo.style);389if (f2D instanceof PhysicalFont) {390physFont = (PhysicalFont)f2D;391}392}393394if (physFont == null ||395!fcInfo.firstFont.fontFile.equals(physFont.platName)) {396physFont = registerFromFcInfo(fcInfo);397if (physFont == null) {398return (fcInfo.compFont = jdkFont);399}400family = FontFamily.getFamily(physFont.getFamilyName(null));401}402403/* Now register the fonts in the family (the other styles) after404* checking that they aren't already registered and are actually in405* a different file. They may be the same file in CJK cases.406* For cases where they are different font files - eg as is common for407* Latin fonts, then we rely on fontconfig to report these correctly.408* Assume that all styles of this font are found by fontconfig,409* so we can find all the family members which must be registered410* together to prevent synthetic styling.411*/412for (int i=0; i<fontConfigFonts.length; i++) {413FcCompFont fc = fontConfigFonts[i];414if (fc != fcInfo &&415physFont.getFamilyName(null).equals(fc.firstFont.familyName) &&416!fc.firstFont.fontFile.equals(physFont.platName) &&417family.getFontWithExactStyleMatch(fc.style) == null) {418419registerFromFcInfo(fontConfigFonts[i]);420}421}422423/* Now we have a physical font. We will back this up with the JDK424* logical font (sansserif, serif, or monospaced) that corresponds425* to the Pango/GTK/FC logical font name.426*/427return (fcInfo.compFont = new CompositeFont(physFont, jdkFont));428}429430public FcCompFont[] getFontConfigFonts() {431return fontConfigFonts;432}433434/* Return an array of FcCompFont structs describing the primary435* font located for each of fontconfig/GTK/Pango's logical font names.436*/437private static native void getFontConfig(String locale,438FontConfigInfo fcInfo,439FcCompFont[] fonts,440boolean includeFallbacks);441442void populateFontConfig(FcCompFont[] fcInfo) {443fontConfigFonts = fcInfo;444}445446FcCompFont[] loadFontConfig() {447initFontConfigFonts(true);448return fontConfigFonts;449}450451FontConfigInfo getFontConfigInfo() {452initFontConfigFonts(true);453return fcInfo;454}455456private static native int457getFontConfigAASettings(String locale, String fcFamily);458}459460461