alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (FontConfigManager.java)

This example Java source code file (FontConfigManager.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

awt, bitstream, bold, compositefont, fccompfont, font2d, fontconfigfont, fontconfiginfo, fontconfigmanager, object, physicalfont, platformlogger, sans, string, util, vera

The FontConfigManager.java Java example source code

/*
 * Copyright (c) 2008, 2013, 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.font;

import java.util.Locale;

import sun.awt.SunHints;
import sun.awt.SunToolkit;
import sun.util.logging.PlatformLogger;

/**
 * Small utility class to manage FontConfig.
 */
public class FontConfigManager {

    static boolean fontConfigFailed = false;

    /* This is populated by native */
    private static final FontConfigInfo fcInfo = new FontConfigInfo();

    /* Begin support for GTK Look and Feel - query libfontconfig and
     * return a composite Font to Swing that uses the desktop font(s).
     */

    /* These next three classes are just data structures.
     */
    public static class FontConfigFont {
        public String familyName;        // eg Bitstream Vera Sans
        public String styleStr;          // eg Bold
        public String fullName;          // eg Bitstream Vera Sans Bold
        public String fontFile;          // eg /usr/X11/lib/fonts/foo.ttf
    }

    public static class FcCompFont {
        public String fcName;            // eg sans
        public String fcFamily;          // eg sans
        public String jdkName;           // eg sansserif
        public int style;                // eg 0=PLAIN
        public FontConfigFont firstFont;
        public FontConfigFont[] allFonts;
        //boolean preferBitmaps;    // if embedded bitmaps preferred over AA
        public CompositeFont compFont;   // null if not yet created/known.
    }

    public static class FontConfigInfo {
        public int fcVersion;
        public String[] cacheDirs = new String[4];
    }

    /* fontconfig recognises slants roman, italic, as well as oblique,
     * and a slew of weights, where the ones that matter here are
     * regular and bold.
     * To fully qualify what we want, we can for example ask for (eg)
     * Font.PLAIN             : "serif:regular:roman"
     * Font.BOLD              : "serif:bold:roman"
     * Font.ITALIC            : "serif:regular:italic"
     * Font.BOLD|Font.ITALIC  : "serif:bold:italic"
     */
    private static String[] fontConfigNames = {
        "sans:regular:roman",
        "sans:bold:roman",
        "sans:regular:italic",
        "sans:bold:italic",

        "serif:regular:roman",
        "serif:bold:roman",
        "serif:regular:italic",
        "serif:bold:italic",

        "monospace:regular:roman",
        "monospace:bold:roman",
        "monospace:regular:italic",
        "monospace:bold:italic",
    };

    /* This array has the array elements created in Java code and is
     * passed down to native to be filled in.
     */
    private FcCompFont[] fontConfigFonts;

    /**
     * Instantiates a new FontConfigManager getting the default instance
     * of FontManager from the FontManagerFactory.
     */
    public FontConfigManager() {
    }

    /* Called from code that needs to know what are the AA settings
     * that apps using FC would pick up for the default desktop font.
     * Note apps can change the default desktop font. etc, so this
     * isn't certain to be right but its going to correct for most cases.
     * Native return values map to the text aa values in sun.awt.SunHints.
     * which is used to look up the renderinghint value object.
     */
    public static Object getFontConfigAAHint() {
        return getFontConfigAAHint("sans");
    }

    /* This is public solely so that for debugging purposes it can be called
     * with other names, which might (eg) include a size, eg "sans-24"
     * The return value is a text aa rendering hint value.
     * Normally we should call the no-args version.
     */
    public static Object getFontConfigAAHint(String fcFamily) {
        if (FontUtilities.isWindows) {
            return null;
        } else {
            int hint = getFontConfigAASettings(getFCLocaleStr(), fcFamily);
            if (hint < 0) {
                return null;
            } else {
                return SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,
                                          hint);
            }
        }
    }


    private static String getFCLocaleStr() {
        Locale l = SunToolkit.getStartupLocale();
        String localeStr = l.getLanguage();
        String country = l.getCountry();
        if (!country.equals("")) {
            localeStr = localeStr + "-" + country;
        }
        return localeStr;
    }

    /* This does cause the native libfontconfig to be loaded and unloaded,
     * but it does not incur the overhead of initialisation of its
     * data structures, so shouldn't have a measurable impact.
     */
    public static native int getFontConfigVersion();

    /* This can be made public if it's needed to force a re-read
     * rather than using the cached values. The re-read would be needed
     * only if some event signalled that the fontconfig has changed.
     * In that event this method would need to return directly the array
     * to be used by the caller in case it subsequently changed.
     */
    public synchronized void initFontConfigFonts(boolean includeFallbacks) {

        if (fontConfigFonts != null) {
            if (!includeFallbacks || (fontConfigFonts[0].allFonts != null)) {
                return;
            }
        }

        if (FontUtilities.isWindows || fontConfigFailed) {
            return;
        }

        long t0 = 0;
        if (FontUtilities.isLogging()) {
            t0 = System.nanoTime();
        }

        FcCompFont[] fontArr = new FcCompFont[fontConfigNames.length];

        for (int i = 0; i< fontArr.length; i++) {
            fontArr[i] = new FcCompFont();
            fontArr[i].fcName = fontConfigNames[i];
            int colonPos = fontArr[i].fcName.indexOf(':');
            fontArr[i].fcFamily = fontArr[i].fcName.substring(0, colonPos);
            fontArr[i].jdkName = FontUtilities.mapFcName(fontArr[i].fcFamily);
            fontArr[i].style = i % 4; // depends on array order.
        }
        getFontConfig(getFCLocaleStr(), fcInfo, fontArr, includeFallbacks);
        FontConfigFont anyFont = null;
        /* If don't find anything (eg no libfontconfig), then just return */
        for (int i = 0; i< fontArr.length; i++) {
            FcCompFont fci = fontArr[i];
            if (fci.firstFont == null) {
                if (FontUtilities.isLogging()) {
                    PlatformLogger logger = FontUtilities.getLogger();
                    logger.info("Fontconfig returned no font for " +
                                fontArr[i].fcName);
                }
                fontConfigFailed = true;
            } else if (anyFont == null) {
                anyFont = fci.firstFont;
            }
        }

        if (anyFont == null) {
            if (FontUtilities.isLogging()) {
                PlatformLogger logger = FontUtilities.getLogger();
                logger.info("Fontconfig returned no fonts at all.");
            }
            fontConfigFailed = true;
            return;
        } else if (fontConfigFailed) {
            for (int i = 0; i< fontArr.length; i++) {
                if (fontArr[i].firstFont == null) {
                    fontArr[i].firstFont = anyFont;
                }
            }
        }

        fontConfigFonts = fontArr;

        if (FontUtilities.isLogging()) {

            PlatformLogger logger = FontUtilities.getLogger();

            long t1 = System.nanoTime();
            logger.info("Time spent accessing fontconfig="
                        + ((t1 - t0) / 1000000) + "ms.");

            for (int i = 0; i< fontConfigFonts.length; i++) {
                FcCompFont fci = fontConfigFonts[i];
                logger.info("FC font " + fci.fcName+" maps to family " +
                            fci.firstFont.familyName +
                            " in file " + fci.firstFont.fontFile);
                if (fci.allFonts != null) {
                    for (int f=0;f<fci.allFonts.length;f++) {
                        FontConfigFont fcf = fci.allFonts[f];
                        logger.info("Family=" + fcf.familyName +
                                    " Style="+ fcf.styleStr +
                                    " Fullname="+fcf.fullName +
                                    " File="+fcf.fontFile);
                    }
                }
            }
        }
    }

    public PhysicalFont registerFromFcInfo(FcCompFont fcInfo) {

        SunFontManager fm = SunFontManager.getInstance();

        /* If it's a TTC file we need to know that as we will need to
         * make sure we return the right font */
        String fontFile = fcInfo.firstFont.fontFile;
        int offset = fontFile.length()-4;
        if (offset <= 0) {
            return null;
        }
        String ext = fontFile.substring(offset).toLowerCase();
        boolean isTTC = ext.equals(".ttc");

        /* If this file is already registered, can just return its font.
         * However we do need to check in case it's a TTC as we need
         * a specific font, so rather than directly returning it, let
         * findFont2D resolve that.
         */
        PhysicalFont physFont = fm.getRegisteredFontFile(fontFile);
        if (physFont != null) {
            if (isTTC) {
                Font2D f2d = fm.findFont2D(fcInfo.firstFont.familyName,
                                           fcInfo.style,
                                           FontManager.NO_FALLBACK);
                if (f2d instanceof PhysicalFont) { /* paranoia */
                    return (PhysicalFont)f2d;
                } else {
                    return null;
                }
            } else {
                return physFont;
            }
        }

        /* If the font may hide a JRE font (eg fontconfig says it is
         * Lucida Sans), we want to use the JRE version, so make it
         * point to the JRE font.
         */
        physFont = fm.findJREDeferredFont(fcInfo.firstFont.familyName,
                                          fcInfo.style);

        /* It is also possible the font file is on the "deferred" list,
         * in which case we can just initialise it now.
         */
        if (physFont == null &&
            fm.isDeferredFont(fontFile) == true) {
            physFont = fm.initialiseDeferredFont(fcInfo.firstFont.fontFile);
            /* use findFont2D to get the right font from TTC's */
            if (physFont != null) {
                if (isTTC) {
                    Font2D f2d = fm.findFont2D(fcInfo.firstFont.familyName,
                                               fcInfo.style,
                                               FontManager.NO_FALLBACK);
                    if (f2d instanceof PhysicalFont) { /* paranoia */
                        return (PhysicalFont)f2d;
                    } else {
                        return null;
                    }
                } else {
                    return physFont;
                }
            }
        }

        /* In the majority of cases we reach here, and need to determine
         * the type and rank to register the font.
         */
        if (physFont == null) {
            int fontFormat = SunFontManager.FONTFORMAT_NONE;
            int fontRank = Font2D.UNKNOWN_RANK;

            if (ext.equals(".ttf") || isTTC) {
                fontFormat = SunFontManager.FONTFORMAT_TRUETYPE;
                fontRank = Font2D.TTF_RANK;
            } else if (ext.equals(".pfa") || ext.equals(".pfb")) {
                fontFormat = SunFontManager.FONTFORMAT_TYPE1;
                fontRank = Font2D.TYPE1_RANK;
            }
            physFont = fm.registerFontFile(fcInfo.firstFont.fontFile, null,
                                      fontFormat, true, fontRank);
        }
        return physFont;
    }

    /*
     * We need to return a Composite font which has as the font in
     * its first slot one obtained from fontconfig.
     */
    public CompositeFont getFontConfigFont(String name, int style) {

        name = name.toLowerCase();

        initFontConfigFonts(false);
        if (fontConfigFonts == null) {
            // This avoids an immediate NPE if fontconfig look up failed
            // but doesn't guarantee this is a recoverable situation.
            return null;
        }

        FcCompFont fcInfo = null;
        for (int i=0; i<fontConfigFonts.length; i++) {
            if (name.equals(fontConfigFonts[i].fcFamily) &&
                style == fontConfigFonts[i].style) {
                fcInfo = fontConfigFonts[i];
                break;
            }
        }
        if (fcInfo == null) {
            fcInfo = fontConfigFonts[0];
        }

        if (FontUtilities.isLogging()) {
            FontUtilities.getLogger()
                          .info("FC name=" + name + " style=" + style +
                                " uses " + fcInfo.firstFont.familyName +
                                " in file: " + fcInfo.firstFont.fontFile);
        }

        if (fcInfo.compFont != null) {
            return fcInfo.compFont;
        }

        /* jdkFont is going to be used for slots 1..N and as a fallback.
         * Slot 0 will be the physical font from fontconfig.
         */
        FontManager fm = FontManagerFactory.getInstance();
        CompositeFont jdkFont = (CompositeFont)
            fm.findFont2D(fcInfo.jdkName, style, FontManager.LOGICAL_FALLBACK);

        if (fcInfo.firstFont.familyName == null ||
            fcInfo.firstFont.fontFile == null) {
            return (fcInfo.compFont = jdkFont);
        }

        /* First, see if the family and exact style is already registered.
         * If it is, use it. If it's not, then try to register it.
         * If that registration fails (signalled by null) just return the
         * regular JDK composite.
         * Algorithmically styled fonts won't match on exact style, so
         * will fall through this code, but the regisration code will
         * find that file already registered and return its font.
         */
        FontFamily family = FontFamily.getFamily(fcInfo.firstFont.familyName);
        PhysicalFont physFont = null;
        if (family != null) {
            Font2D f2D = family.getFontWithExactStyleMatch(fcInfo.style);
            if (f2D instanceof PhysicalFont) {
                physFont = (PhysicalFont)f2D;
            }
        }

        if (physFont == null ||
            !fcInfo.firstFont.fontFile.equals(physFont.platName)) {
            physFont = registerFromFcInfo(fcInfo);
            if (physFont == null) {
                return (fcInfo.compFont = jdkFont);
            }
            family = FontFamily.getFamily(physFont.getFamilyName(null));
        }

        /* Now register the fonts in the family (the other styles) after
         * checking that they aren't already registered and are actually in
         * a different file. They may be the same file in CJK cases.
         * For cases where they are different font files - eg as is common for
         * Latin fonts, then we rely on fontconfig to report these correctly.
         * Assume that all styles of this font are found by fontconfig,
         * so we can find all the family members which must be registered
         * together to prevent synthetic styling.
         */
        for (int i=0; i<fontConfigFonts.length; i++) {
            FcCompFont fc = fontConfigFonts[i];
            if (fc != fcInfo &&
                physFont.getFamilyName(null).equals(fc.firstFont.familyName) &&
                !fc.firstFont.fontFile.equals(physFont.platName) &&
                family.getFontWithExactStyleMatch(fc.style) == null) {

                registerFromFcInfo(fontConfigFonts[i]);
            }
        }

        /* Now we have a physical font. We will back this up with the JDK
         * logical font (sansserif, serif, or monospaced) that corresponds
         * to the Pango/GTK/FC logical font name.
         */
        return (fcInfo.compFont = new CompositeFont(physFont, jdkFont));
    }

    /**
     *
     * @param locale
     * @param fcFamily
     * @return
     */
    public FcCompFont[] getFontConfigFonts() {
        return fontConfigFonts;
    }

    /* Return an array of FcCompFont structs describing the primary
     * font located for each of fontconfig/GTK/Pango's logical font names.
     */
    private static native void getFontConfig(String locale,
                                             FontConfigInfo fcInfo,
                                             FcCompFont[] fonts,
                                             boolean includeFallbacks);

    void populateFontConfig(FcCompFont[] fcInfo) {
        fontConfigFonts = fcInfo;
    }

    FcCompFont[] loadFontConfig() {
        initFontConfigFonts(true);
        return fontConfigFonts;
    }

    FontConfigInfo getFontConfigInfo() {
        initFontConfigFonts(true);
        return fcInfo;
    }

    private static native int
    getFontConfigAASettings(String locale, String fcFamily);
}

Other Java examples (source code examples)

Here is a short list of links related to this Java FontConfigManager.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.