1687N/A/*
3909N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
1687N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1687N/A *
1687N/A * This code is free software; you can redistribute it and/or modify it
1687N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
1687N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
1687N/A *
1687N/A * This code is distributed in the hope that it will be useful, but WITHOUT
1687N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1687N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1687N/A * version 2 for more details (a copy is included in the LICENSE file that
1687N/A * accompanied this code).
1687N/A *
1687N/A * You should have received a copy of the GNU General Public License version
1687N/A * 2 along with this work; if not, write to the Free Software Foundation,
1687N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1687N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
1687N/A */
1687N/A
1687N/Apackage sun.font;
1687N/A
1687N/Aimport java.util.Locale;
1687N/A
1687N/Aimport sun.awt.SunHints;
1687N/Aimport sun.awt.SunToolkit;
1696N/Aimport sun.util.logging.PlatformLogger;
1687N/A
1687N/A/**
1687N/A * Small utility class to manage FontConfig.
1687N/A */
1687N/Apublic class FontConfigManager {
1687N/A
1687N/A static boolean fontConfigFailed = false;
1687N/A
1687N/A /* This is populated by native */
1687N/A private static final FontConfigInfo fcInfo = new FontConfigInfo();
1687N/A
1687N/A /* Begin support for GTK Look and Feel - query libfontconfig and
1687N/A * return a composite Font to Swing that uses the desktop font(s).
1687N/A */
1687N/A
1687N/A /* These next three classes are just data structures.
1687N/A */
1687N/A public static class FontConfigFont {
1687N/A public String familyName; // eg Bitstream Vera Sans
1687N/A public String styleStr; // eg Bold
1687N/A public String fullName; // eg Bitstream Vera Sans Bold
1687N/A public String fontFile; // eg /usr/X11/lib/fonts/foo.ttf
1687N/A }
1687N/A
1687N/A public static class FcCompFont {
1687N/A public String fcName; // eg sans
1687N/A public String fcFamily; // eg sans
1687N/A public String jdkName; // eg sansserif
1687N/A public int style; // eg 0=PLAIN
1687N/A public FontConfigFont firstFont;
1687N/A public FontConfigFont[] allFonts;
1687N/A //boolean preferBitmaps; // if embedded bitmaps preferred over AA
1687N/A public CompositeFont compFont; // null if not yet created/known.
1687N/A }
1687N/A
1687N/A public static class FontConfigInfo {
1687N/A public int fcVersion;
1687N/A public String[] cacheDirs = new String[4];
1687N/A }
1687N/A
1687N/A /* fontconfig recognises slants roman, italic, as well as oblique,
1687N/A * and a slew of weights, where the ones that matter here are
1687N/A * regular and bold.
1687N/A * To fully qualify what we want, we can for example ask for (eg)
1687N/A * Font.PLAIN : "serif:regular:roman"
1687N/A * Font.BOLD : "serif:bold:roman"
1687N/A * Font.ITALIC : "serif:regular:italic"
1687N/A * Font.BOLD|Font.ITALIC : "serif:bold:italic"
1687N/A */
1687N/A private static String[] fontConfigNames = {
1687N/A "sans:regular:roman",
1687N/A "sans:bold:roman",
1687N/A "sans:regular:italic",
1687N/A "sans:bold:italic",
1687N/A
1687N/A "serif:regular:roman",
1687N/A "serif:bold:roman",
1687N/A "serif:regular:italic",
1687N/A "serif:bold:italic",
1687N/A
1687N/A "monospace:regular:roman",
1687N/A "monospace:bold:roman",
1687N/A "monospace:regular:italic",
1687N/A "monospace:bold:italic",
1687N/A };
1687N/A
1687N/A /* This array has the array elements created in Java code and is
1687N/A * passed down to native to be filled in.
1687N/A */
1687N/A private FcCompFont[] fontConfigFonts;
1687N/A
1687N/A /**
1687N/A * Instantiates a new FontConfigManager getting the default instance
1687N/A * of FontManager from the FontManagerFactory.
1687N/A */
1687N/A public FontConfigManager() {
1687N/A }
1687N/A
1687N/A public static String[] getFontConfigNames() {
1687N/A return fontConfigNames;
1687N/A }
1687N/A
1687N/A /* Called from code that needs to know what are the AA settings
1687N/A * that apps using FC would pick up for the default desktop font.
1687N/A * Note apps can change the default desktop font. etc, so this
1687N/A * isn't certain to be right but its going to correct for most cases.
1687N/A * Native return values map to the text aa values in sun.awt.SunHints.
1687N/A * which is used to look up the renderinghint value object.
1687N/A */
1687N/A public static Object getFontConfigAAHint() {
1687N/A return getFontConfigAAHint("sans");
1687N/A }
1687N/A
1687N/A /* This is public solely so that for debugging purposes it can be called
1687N/A * with other names, which might (eg) include a size, eg "sans-24"
1687N/A * The return value is a text aa rendering hint value.
1687N/A * Normally we should call the no-args version.
1687N/A */
1687N/A public static Object getFontConfigAAHint(String fcFamily) {
1687N/A if (FontUtilities.isWindows) {
1687N/A return null;
1687N/A } else {
1687N/A int hint = getFontConfigAASettings(getFCLocaleStr(), fcFamily);
1687N/A if (hint < 0) {
1687N/A return null;
1687N/A } else {
1687N/A return SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,
1687N/A hint);
1687N/A }
1687N/A }
1687N/A }
1687N/A
1687N/A
1687N/A private static String getFCLocaleStr() {
1687N/A Locale l = SunToolkit.getStartupLocale();
1687N/A String localeStr = l.getLanguage();
1687N/A String country = l.getCountry();
1687N/A if (!country.equals("")) {
1687N/A localeStr = localeStr + "-" + country;
1687N/A }
1687N/A return localeStr;
1687N/A }
1687N/A
1687N/A /* This does cause the native libfontconfig to be loaded and unloaded,
1687N/A * but it does not incur the overhead of initialisation of its
1687N/A * data structures, so shouldn't have a measurable impact.
1687N/A */
1687N/A public static native int getFontConfigVersion();
1687N/A
1687N/A /* This can be made public if it's needed to force a re-read
1687N/A * rather than using the cached values. The re-read would be needed
1687N/A * only if some event signalled that the fontconfig has changed.
1687N/A * In that event this method would need to return directly the array
1687N/A * to be used by the caller in case it subsequently changed.
1687N/A */
1687N/A public synchronized void initFontConfigFonts(boolean includeFallbacks) {
1687N/A
1687N/A if (fontConfigFonts != null) {
1687N/A if (!includeFallbacks || (fontConfigFonts[0].allFonts != null)) {
1687N/A return;
1687N/A }
1687N/A }
1687N/A
1687N/A if (FontUtilities.isWindows || fontConfigFailed) {
1687N/A return;
1687N/A }
1687N/A
1687N/A long t0 = 0;
1687N/A if (FontUtilities.isLogging()) {
1687N/A t0 = System.nanoTime();
1687N/A }
1687N/A
1687N/A String[] fontConfigNames = FontConfigManager.getFontConfigNames();
1687N/A FcCompFont[] fontArr = new FcCompFont[fontConfigNames.length];
1687N/A
1687N/A for (int i = 0; i< fontArr.length; i++) {
1687N/A fontArr[i] = new FcCompFont();
1687N/A fontArr[i].fcName = fontConfigNames[i];
1687N/A int colonPos = fontArr[i].fcName.indexOf(':');
1687N/A fontArr[i].fcFamily = fontArr[i].fcName.substring(0, colonPos);
1687N/A fontArr[i].jdkName = FontUtilities.mapFcName(fontArr[i].fcFamily);
1687N/A fontArr[i].style = i % 4; // depends on array order.
1687N/A }
1687N/A getFontConfig(getFCLocaleStr(), fcInfo, fontArr, includeFallbacks);
1689N/A FontConfigFont anyFont = null;
1687N/A /* If don't find anything (eg no libfontconfig), then just return */
1687N/A for (int i = 0; i< fontArr.length; i++) {
1687N/A FcCompFont fci = fontArr[i];
1687N/A if (fci.firstFont == null) {
1687N/A if (FontUtilities.isLogging()) {
1696N/A PlatformLogger logger = FontUtilities.getLogger();
1689N/A logger.info("Fontconfig returned no font for " +
1689N/A fontArr[i].fcName);
1687N/A }
1687N/A fontConfigFailed = true;
1689N/A } else if (anyFont == null) {
1689N/A anyFont = fci.firstFont;
1689N/A }
1689N/A }
1689N/A
1689N/A if (anyFont == null) {
1689N/A if (FontUtilities.isLogging()) {
1696N/A PlatformLogger logger = FontUtilities.getLogger();
1689N/A logger.info("Fontconfig returned no fonts at all.");
1687N/A }
3346N/A fontConfigFailed = true;
3346N/A return;
1689N/A } else if (fontConfigFailed) {
1689N/A for (int i = 0; i< fontArr.length; i++) {
1689N/A if (fontArr[i].firstFont == null) {
1689N/A fontArr[i].firstFont = anyFont;
1689N/A }
1689N/A }
1687N/A }
1689N/A
1687N/A fontConfigFonts = fontArr;
1687N/A
1687N/A if (FontUtilities.isLogging()) {
1687N/A
1696N/A PlatformLogger logger = FontUtilities.getLogger();
1687N/A
1687N/A long t1 = System.nanoTime();
1687N/A logger.info("Time spent accessing fontconfig="
1687N/A + ((t1 - t0) / 1000000) + "ms.");
1687N/A
1687N/A for (int i = 0; i< fontConfigFonts.length; i++) {
1687N/A FcCompFont fci = fontConfigFonts[i];
1687N/A logger.info("FC font " + fci.fcName+" maps to family " +
1687N/A fci.firstFont.familyName +
1687N/A " in file " + fci.firstFont.fontFile);
1687N/A if (fci.allFonts != null) {
1687N/A for (int f=0;f<fci.allFonts.length;f++) {
1687N/A FontConfigFont fcf = fci.allFonts[f];
1687N/A logger.info("Family=" + fcf.familyName +
1687N/A " Style="+ fcf.styleStr +
1687N/A " Fullname="+fcf.fullName +
1687N/A " File="+fcf.fontFile);
1687N/A }
1687N/A }
1687N/A }
1687N/A }
1687N/A }
1687N/A
1687N/A public PhysicalFont registerFromFcInfo(FcCompFont fcInfo) {
1687N/A
1687N/A SunFontManager fm = SunFontManager.getInstance();
1687N/A
1687N/A /* If it's a TTC file we need to know that as we will need to
1687N/A * make sure we return the right font */
1687N/A String fontFile = fcInfo.firstFont.fontFile;
1687N/A int offset = fontFile.length()-4;
1687N/A if (offset <= 0) {
1687N/A return null;
1687N/A }
1687N/A String ext = fontFile.substring(offset).toLowerCase();
1687N/A boolean isTTC = ext.equals(".ttc");
1687N/A
1687N/A /* If this file is already registered, can just return its font.
1687N/A * However we do need to check in case it's a TTC as we need
1687N/A * a specific font, so rather than directly returning it, let
1687N/A * findFont2D resolve that.
1687N/A */
1687N/A PhysicalFont physFont = fm.getRegisteredFontFile(fontFile);
1687N/A if (physFont != null) {
1687N/A if (isTTC) {
1687N/A Font2D f2d = fm.findFont2D(fcInfo.firstFont.familyName,
1687N/A fcInfo.style,
1687N/A FontManager.NO_FALLBACK);
1687N/A if (f2d instanceof PhysicalFont) { /* paranoia */
1687N/A return (PhysicalFont)f2d;
1687N/A } else {
1687N/A return null;
1687N/A }
1687N/A } else {
1687N/A return physFont;
1687N/A }
1687N/A }
1687N/A
1687N/A /* If the font may hide a JRE font (eg fontconfig says it is
1687N/A * Lucida Sans), we want to use the JRE version, so make it
1687N/A * point to the JRE font.
1687N/A */
1687N/A physFont = fm.findJREDeferredFont(fcInfo.firstFont.familyName,
1687N/A fcInfo.style);
1687N/A
1687N/A /* It is also possible the font file is on the "deferred" list,
1687N/A * in which case we can just initialise it now.
1687N/A */
1687N/A if (physFont == null &&
1687N/A fm.isDeferredFont(fontFile) == true) {
1687N/A physFont = fm.initialiseDeferredFont(fcInfo.firstFont.fontFile);
1687N/A /* use findFont2D to get the right font from TTC's */
1687N/A if (physFont != null) {
1687N/A if (isTTC) {
1687N/A Font2D f2d = fm.findFont2D(fcInfo.firstFont.familyName,
1687N/A fcInfo.style,
1687N/A FontManager.NO_FALLBACK);
1687N/A if (f2d instanceof PhysicalFont) { /* paranoia */
1687N/A return (PhysicalFont)f2d;
1687N/A } else {
1687N/A return null;
1687N/A }
1687N/A } else {
1687N/A return physFont;
1687N/A }
1687N/A }
1687N/A }
1687N/A
1687N/A /* In the majority of cases we reach here, and need to determine
1687N/A * the type and rank to register the font.
1687N/A */
1687N/A if (physFont == null) {
1687N/A int fontFormat = SunFontManager.FONTFORMAT_NONE;
1687N/A int fontRank = Font2D.UNKNOWN_RANK;
1687N/A
1687N/A if (ext.equals(".ttf") || isTTC) {
1687N/A fontFormat = SunFontManager.FONTFORMAT_TRUETYPE;
1687N/A fontRank = Font2D.TTF_RANK;
1687N/A } else if (ext.equals(".pfa") || ext.equals(".pfb")) {
1687N/A fontFormat = SunFontManager.FONTFORMAT_TYPE1;
1687N/A fontRank = Font2D.TYPE1_RANK;
1687N/A }
1687N/A physFont = fm.registerFontFile(fcInfo.firstFont.fontFile, null,
1687N/A fontFormat, true, fontRank);
1687N/A }
1687N/A return physFont;
1687N/A }
1687N/A
1687N/A /*
1687N/A * We need to return a Composite font which has as the font in
1687N/A * its first slot one obtained from fontconfig.
1687N/A */
1687N/A public CompositeFont getFontConfigFont(String name, int style) {
1687N/A
1687N/A name = name.toLowerCase();
1687N/A
1687N/A initFontConfigFonts(false);
5477N/A if (fontConfigFonts == null) {
5477N/A // This avoids an immediate NPE if fontconfig look up failed
5477N/A // but doesn't guarantee this is a recoverable situation.
5477N/A return null;
5477N/A }
1687N/A
1687N/A FcCompFont fcInfo = null;
1687N/A for (int i=0; i<fontConfigFonts.length; i++) {
1687N/A if (name.equals(fontConfigFonts[i].fcFamily) &&
1687N/A style == fontConfigFonts[i].style) {
1687N/A fcInfo = fontConfigFonts[i];
1687N/A break;
1687N/A }
1687N/A }
1687N/A if (fcInfo == null) {
1687N/A fcInfo = fontConfigFonts[0];
1687N/A }
1687N/A
1687N/A if (FontUtilities.isLogging()) {
1687N/A FontUtilities.getLogger()
1687N/A .info("FC name=" + name + " style=" + style +
1687N/A " uses " + fcInfo.firstFont.familyName +
1687N/A " in file: " + fcInfo.firstFont.fontFile);
1687N/A }
1687N/A
1687N/A if (fcInfo.compFont != null) {
1687N/A return fcInfo.compFont;
1687N/A }
1687N/A
1687N/A /* jdkFont is going to be used for slots 1..N and as a fallback.
1687N/A * Slot 0 will be the physical font from fontconfig.
1687N/A */
1687N/A FontManager fm = FontManagerFactory.getInstance();
1687N/A CompositeFont jdkFont = (CompositeFont)
1687N/A fm.findFont2D(fcInfo.jdkName, style, FontManager.LOGICAL_FALLBACK);
1687N/A
1687N/A if (fcInfo.firstFont.familyName == null ||
1687N/A fcInfo.firstFont.fontFile == null) {
1687N/A return (fcInfo.compFont = jdkFont);
1687N/A }
1687N/A
1687N/A /* First, see if the family and exact style is already registered.
1687N/A * If it is, use it. If it's not, then try to register it.
1687N/A * If that registration fails (signalled by null) just return the
1687N/A * regular JDK composite.
1687N/A * Algorithmically styled fonts won't match on exact style, so
1687N/A * will fall through this code, but the regisration code will
1687N/A * find that file already registered and return its font.
1687N/A */
1687N/A FontFamily family = FontFamily.getFamily(fcInfo.firstFont.familyName);
1687N/A PhysicalFont physFont = null;
1687N/A if (family != null) {
1687N/A Font2D f2D = family.getFontWithExactStyleMatch(fcInfo.style);
1687N/A if (f2D instanceof PhysicalFont) {
1687N/A physFont = (PhysicalFont)f2D;
1687N/A }
1687N/A }
1687N/A
1687N/A if (physFont == null ||
1687N/A !fcInfo.firstFont.fontFile.equals(physFont.platName)) {
1687N/A physFont = registerFromFcInfo(fcInfo);
1687N/A if (physFont == null) {
1687N/A return (fcInfo.compFont = jdkFont);
1687N/A }
1687N/A family = FontFamily.getFamily(physFont.getFamilyName(null));
1687N/A }
1687N/A
1687N/A /* Now register the fonts in the family (the other styles) after
1687N/A * checking that they aren't already registered and are actually in
1687N/A * a different file. They may be the same file in CJK cases.
1687N/A * For cases where they are different font files - eg as is common for
1687N/A * Latin fonts, then we rely on fontconfig to report these correctly.
1687N/A * Assume that all styles of this font are found by fontconfig,
1687N/A * so we can find all the family members which must be registered
1687N/A * together to prevent synthetic styling.
1687N/A */
1687N/A for (int i=0; i<fontConfigFonts.length; i++) {
1687N/A FcCompFont fc = fontConfigFonts[i];
1687N/A if (fc != fcInfo &&
1687N/A physFont.getFamilyName(null).equals(fc.firstFont.familyName) &&
1687N/A !fc.firstFont.fontFile.equals(physFont.platName) &&
1687N/A family.getFontWithExactStyleMatch(fc.style) == null) {
1687N/A
1687N/A registerFromFcInfo(fontConfigFonts[i]);
1687N/A }
1687N/A }
1687N/A
1687N/A /* Now we have a physical font. We will back this up with the JDK
1687N/A * logical font (sansserif, serif, or monospaced) that corresponds
1687N/A * to the Pango/GTK/FC logical font name.
1687N/A */
1687N/A return (fcInfo.compFont = new CompositeFont(physFont, jdkFont));
1687N/A }
1687N/A
1687N/A /**
1687N/A *
1687N/A * @param locale
1687N/A * @param fcFamily
1687N/A * @return
1687N/A */
1687N/A public FcCompFont[] getFontConfigFonts() {
1687N/A return fontConfigFonts;
1687N/A }
1687N/A
1687N/A /* Return an array of FcCompFont structs describing the primary
1687N/A * font located for each of fontconfig/GTK/Pango's logical font names.
1687N/A */
1687N/A private static native void getFontConfig(String locale,
1687N/A FontConfigInfo fcInfo,
1687N/A FcCompFont[] fonts,
1687N/A boolean includeFallbacks);
1687N/A
1687N/A void populateFontConfig(FcCompFont[] fcInfo) {
1687N/A fontConfigFonts = fcInfo;
1687N/A }
1687N/A
1687N/A FcCompFont[] loadFontConfig() {
1687N/A initFontConfigFonts(true);
1687N/A return fontConfigFonts;
1687N/A }
1687N/A
1687N/A FontConfigInfo getFontConfigInfo() {
1687N/A initFontConfigFonts(true);
1687N/A return fcInfo;
1687N/A }
1687N/A
1687N/A private static native int
1687N/A getFontConfigAASettings(String locale, String fcFamily);
1687N/A}