Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/font/FcFontConfiguration.java
32287 views
/*1* Copyright (c) 2008, 2014, 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.awt.Font;28import java.io.File;29import java.io.FileInputStream;30import java.io.FileOutputStream;31import java.io.IOException;32import java.net.InetAddress;33import java.net.UnknownHostException;34import java.nio.charset.Charset;35import java.nio.charset.StandardCharsets;36import java.nio.file.Files;37import java.util.HashMap;38import java.util.HashSet;39import java.util.Properties;40import java.util.Scanner;41import sun.awt.FcFontManager;42import sun.awt.FontConfiguration;43import sun.awt.FontDescriptor;44import sun.awt.SunToolkit;45import sun.font.CompositeFontDescriptor;46import sun.font.FontManager;47import sun.font.FontConfigManager.FontConfigInfo;48import sun.font.FontConfigManager.FcCompFont;49import sun.font.FontConfigManager.FontConfigFont;50import sun.java2d.SunGraphicsEnvironment;51import sun.util.logging.PlatformLogger;5253public class FcFontConfiguration extends FontConfiguration {5455/** Version of the cache file format understood by this code.56* Its part of the file name so that we can rev this at57* any time, even in a minor JDK update.58* It is stored as the value of the "version" property.59* This is distinct from the version of "libfontconfig" that generated60* the cached results, and which is the "fcversion" property in the file.61* {@code FontConfiguration.getVersion()} also returns a version string,62* and has meant the version of the fontconfiguration.properties file63* that was read. Since this class doesn't use such files, then what64* that really means is whether the methods on this class return65* values that are compatible with the classes that do directly read66* from such files. It is a compatible subset of version "1".67*/68private static final String fileVersion = "1";69private String fcInfoFileName = null;7071private FcCompFont[] fcCompFonts = null;7273public FcFontConfiguration(SunFontManager fm) {74super(fm);75init();76}7778/* This isn't called but is needed to satisfy super-class contract. */79public FcFontConfiguration(SunFontManager fm,80boolean preferLocaleFonts,81boolean preferPropFonts) {82super(fm, preferLocaleFonts, preferPropFonts);83init();84}8586@Override87public synchronized boolean init() {88if (fcCompFonts != null) {89return true;90}9192setFontConfiguration();93readFcInfo();94FcFontManager fm = (FcFontManager) fontManager;95FontConfigManager fcm = fm.getFontConfigManager();96if (fcCompFonts == null) {97fcCompFonts = fcm.loadFontConfig();98if (fcCompFonts != null) {99try {100writeFcInfo();101} catch (Exception e) {102if (FontUtilities.debugFonts()) {103warning("Exception writing fcInfo " + e);104}105}106} else if (FontUtilities.debugFonts()) {107warning("Failed to get info from libfontconfig");108}109} else {110fcm.populateFontConfig(fcCompFonts);111}112113if (fcCompFonts == null) {114return false; // couldn't load fontconfig.115}116117// NB already in a privileged block from SGE118String javaHome = System.getProperty("java.home");119if (javaHome == null) {120throw new Error("java.home property not set");121}122String javaLib = javaHome + File.separator + "lib";123getInstalledFallbackFonts(javaLib);124125return true;126}127128@Override129public String getFallbackFamilyName(String fontName,130String defaultFallback) {131// maintain compatibility with old font.properties files, which either132// had aliases for TimesRoman & Co. or defined mappings for them.133String compatibilityName = getCompatibilityFamilyName(fontName);134if (compatibilityName != null) {135return compatibilityName;136}137return defaultFallback;138}139140@Override141protected String142getFaceNameFromComponentFontName(String componentFontName) {143return null;144}145146@Override147protected String148getFileNameFromComponentFontName(String componentFontName) {149return null;150}151152@Override153public String getFileNameFromPlatformName(String platformName) {154/* Platform name is the file name, but rather than returning155* the arg, return null*/156return null;157}158159@Override160protected Charset getDefaultFontCharset(String fontName) {161return Charset.forName("ISO8859_1");162}163164@Override165protected String getEncoding(String awtFontName,166String characterSubsetName) {167return "default";168}169170@Override171protected void initReorderMap() {172reorderMap = new HashMap();173}174175@Override176protected FontDescriptor[] buildFontDescriptors(int fontIndex, int styleIndex) {177CompositeFontDescriptor[] cfi = get2DCompositeFontInfo();178int idx = fontIndex * NUM_STYLES + styleIndex;179String[] componentFaceNames = cfi[idx].getComponentFaceNames();180FontDescriptor[] ret = new FontDescriptor[componentFaceNames.length];181for (int i = 0; i < componentFaceNames.length; i++) {182ret[i] = new FontDescriptor(componentFaceNames[i], StandardCharsets.ISO_8859_1.newEncoder(), new int[0]);183}184185return ret;186}187188@Override189public int getNumberCoreFonts() {190return 1;191}192193@Override194public String[] getPlatformFontNames() {195HashSet<String> nameSet = new HashSet<String>();196FcFontManager fm = (FcFontManager) fontManager;197FontConfigManager fcm = fm.getFontConfigManager();198FcCompFont[] fcCompFonts = fcm.loadFontConfig();199for (int i=0; i<fcCompFonts.length; i++) {200for (int j=0; j<fcCompFonts[i].allFonts.length; j++) {201nameSet.add(fcCompFonts[i].allFonts[j].fontFile);202}203}204return nameSet.toArray(new String[0]);205}206207@Override208public String getExtraFontPath() {209return null;210}211212@Override213public boolean needToSearchForFile(String fileName) {214return false;215}216217private FontConfigFont[] getFcFontList(FcCompFont[] fcFonts,218String fontname, int style) {219220if (fontname.equals("dialog")) {221fontname = "sansserif";222} else if (fontname.equals("dialoginput")) {223fontname = "monospaced";224}225for (int i=0; i<fcFonts.length; i++) {226if (fontname.equals(fcFonts[i].jdkName) &&227style == fcFonts[i].style) {228return fcFonts[i].allFonts;229}230}231return fcFonts[0].allFonts;232}233234@Override235public CompositeFontDescriptor[] get2DCompositeFontInfo() {236237FcFontManager fm = (FcFontManager) fontManager;238FontConfigManager fcm = fm.getFontConfigManager();239FcCompFont[] fcCompFonts = fcm.loadFontConfig();240241CompositeFontDescriptor[] result =242new CompositeFontDescriptor[NUM_FONTS * NUM_STYLES];243244for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) {245String fontName = publicFontNames[fontIndex];246247for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) {248249String faceName = fontName + "." + styleNames[styleIndex];250FontConfigFont[] fcFonts =251getFcFontList(fcCompFonts,252fontNames[fontIndex], styleIndex);253254int numFonts = fcFonts.length;255// fall back fonts listed in the lib/fonts/fallback directory256if (installedFallbackFontFiles != null) {257numFonts += installedFallbackFontFiles.length;258}259260String[] fileNames = new String[numFonts];261String[] faceNames = new String[numFonts];262263int index;264for (index = 0; index < fcFonts.length; index++) {265fileNames[index] = fcFonts[index].fontFile;266faceNames[index] = fcFonts[index].familyName;267}268269if (installedFallbackFontFiles != null) {270System.arraycopy(installedFallbackFontFiles, 0,271fileNames, fcFonts.length,272installedFallbackFontFiles.length);273}274275result[fontIndex * NUM_STYLES + styleIndex]276= new CompositeFontDescriptor(277faceName,2781,279faceNames,280fileNames,281null, null);282}283}284return result;285}286287/**288* Gets the OS version string from a Linux release-specific file.289*/290private String getVersionString(File f){291try {292Scanner sc = new Scanner(f);293return sc.findInLine("(\\d)+((\\.)(\\d)+)*");294}295catch (Exception e){296}297return null;298}299300/**301* Sets the OS name and version from environment information.302*/303@Override304protected void setOsNameAndVersion() {305306super.setOsNameAndVersion();307308if (!osName.equals("Linux")) {309return;310}311try {312File f;313if ((f = new File("/etc/lsb-release")).canRead()) {314/* Ubuntu and (perhaps others) use only lsb-release.315* Syntax and encoding is compatible with java properties.316* For Ubuntu the ID is "Ubuntu".317*/318Properties props = new Properties();319props.load(new FileInputStream(f));320osName = props.getProperty("DISTRIB_ID");321osVersion = props.getProperty("DISTRIB_RELEASE");322} else if ((f = new File("/etc/redhat-release")).canRead()) {323osName = "RedHat";324osVersion = getVersionString(f);325} else if ((f = new File("/etc/SuSE-release")).canRead()) {326osName = "SuSE";327osVersion = getVersionString(f);328} else if ((f = new File("/etc/turbolinux-release")).canRead()) {329osName = "Turbo";330osVersion = getVersionString(f);331} else if ((f = new File("/etc/fedora-release")).canRead()) {332osName = "Fedora";333osVersion = getVersionString(f);334}335} catch (Exception e) {336if (FontUtilities.debugFonts()) {337warning("Exception identifying Linux distro.");338}339}340}341342private File getFcInfoFile() {343if (fcInfoFileName == null) {344// NB need security permissions to get true IP address, and345// we should have those as the whole initialisation is in a346// doPrivileged block. But in this case no exception is thrown,347// and it returns the loop back address, and so we end up with348// "localhost"349String hostname;350try {351hostname = InetAddress.getLocalHost().getHostName();352} catch (UnknownHostException e) {353hostname = "localhost";354}355String userDir = System.getProperty("user.home");356String version = System.getProperty("java.version");357String fs = File.separator;358String dir = userDir+fs+".java"+fs+"fonts"+fs+version;359String lang = SunToolkit.getStartupLocale().getLanguage();360String name = "fcinfo-"+fileVersion+"-"+hostname+"-"+361osName+"-"+osVersion+"-"+lang+".properties";362fcInfoFileName = dir+fs+name;363}364return new File(fcInfoFileName);365}366367private void writeFcInfo() {368Properties props = new Properties();369props.setProperty("version", fileVersion);370FcFontManager fm = (FcFontManager) fontManager;371FontConfigManager fcm = fm.getFontConfigManager();372FontConfigInfo fcInfo = fcm.getFontConfigInfo();373props.setProperty("fcversion", Integer.toString(fcInfo.fcVersion));374if (fcInfo.cacheDirs != null) {375for (int i=0;i<fcInfo.cacheDirs.length;i++) {376if (fcInfo.cacheDirs[i] != null) {377props.setProperty("cachedir."+i, fcInfo.cacheDirs[i]);378}379}380}381for (int i=0; i<fcCompFonts.length; i++) {382FcCompFont fci = fcCompFonts[i];383String styleKey = fci.jdkName+"."+fci.style;384props.setProperty(styleKey+".length",385Integer.toString(fci.allFonts.length));386for (int j=0; j<fci.allFonts.length; j++) {387props.setProperty(styleKey+"."+j+".family",388fci.allFonts[j].familyName);389props.setProperty(styleKey+"."+j+".file",390fci.allFonts[j].fontFile);391}392}393try {394/* This writes into a temp file then renames when done.395* Since the rename is an atomic action within the same396* directory no client will ever see a partially written file.397*/398File fcInfoFile = getFcInfoFile();399File dir = fcInfoFile.getParentFile();400dir.mkdirs();401File tempFile = Files.createTempFile(dir.toPath(), "fcinfo", null).toFile();402FileOutputStream fos = new FileOutputStream(tempFile);403props.store(fos,404"JDK Font Configuration Generated File: *Do Not Edit*");405fos.close();406boolean renamed = tempFile.renameTo(fcInfoFile);407if (!renamed && FontUtilities.debugFonts()) {408System.out.println("rename failed");409warning("Failed renaming file to "+ getFcInfoFile());410}411} catch (Exception e) {412if (FontUtilities.debugFonts()) {413warning("IOException writing to "+ getFcInfoFile());414}415}416}417418/* We want to be able to use this cache instead of invoking419* fontconfig except when we can detect the system cache has changed.420* But there doesn't seem to be a way to find the location of421* the system cache.422*/423private void readFcInfo() {424File fcFile = getFcInfoFile();425if (!fcFile.exists()) {426return;427}428Properties props = new Properties();429FcFontManager fm = (FcFontManager) fontManager;430FontConfigManager fcm = fm.getFontConfigManager();431try {432FileInputStream fis = new FileInputStream(fcFile);433props.load(fis);434fis.close();435} catch (IOException e) {436if (FontUtilities.debugFonts()) {437warning("IOException reading from "+fcFile.toString());438}439return;440}441String version = (String)props.get("version");442if (version == null || !version.equals(fileVersion)) {443return;444}445446// If there's a new, different fontconfig installed on the447// system, we invalidate our fontconfig file.448String fcVersionStr = (String)props.get("fcversion");449if (fcVersionStr != null) {450int fcVersion;451try {452fcVersion = Integer.parseInt(fcVersionStr);453if (fcVersion != 0 &&454fcVersion != fcm.getFontConfigVersion()) {455return;456}457} catch (Exception e) {458if (FontUtilities.debugFonts()) {459warning("Exception parsing version " + fcVersionStr);460}461return;462}463}464465// If we can locate the fontconfig cache dirs, then compare the466// time stamp of those with our properties file. If we are out467// of date then re-generate.468long lastModified = fcFile.lastModified();469int cacheDirIndex = 0;470while (cacheDirIndex<4) { // should never be more than 2 anyway.471String dir = (String)props.get("cachedir."+cacheDirIndex);472if (dir == null) {473break;474}475File dirFile = new File(dir);476if (dirFile.exists() && dirFile.lastModified() > lastModified) {477return;478}479cacheDirIndex++;480}481482String[] names = { "sansserif", "serif", "monospaced" };483String[] fcnames = { "sans", "serif", "monospace" };484int namesLen = names.length;485int numStyles = 4;486FcCompFont[] fci = new FcCompFont[namesLen*numStyles];487488try {489for (int i=0; i<namesLen; i++) {490for (int s=0; s<numStyles; s++) {491int index = i*numStyles+s;492fci[index] = new FcCompFont();493String key = names[i]+"."+s;494fci[index].jdkName = names[i];495fci[index].fcFamily = fcnames[i];496fci[index].style = s;497String lenStr = (String)props.get(key+".length");498int nfonts = Integer.parseInt(lenStr);499if (nfonts <= 0) {500return; // bad file501}502fci[index].allFonts = new FontConfigFont[nfonts];503for (int f=0; f<nfonts; f++) {504fci[index].allFonts[f] = new FontConfigFont();505String fkey = key+"."+f+".family";506String family = (String)props.get(fkey);507fci[index].allFonts[f].familyName = family;508fkey = key+"."+f+".file";509String file = (String)props.get(fkey);510if (file == null) {511return; // bad file512}513fci[index].allFonts[f].fontFile = file;514}515fci[index].firstFont = fci[index].allFonts[0];516517}518}519fcCompFonts = fci;520} catch (Throwable t) {521if (FontUtilities.debugFonts()) {522warning(t.toString());523}524}525}526527private static void warning(String msg) {528PlatformLogger logger = PlatformLogger.getLogger("sun.awt.FontConfiguration");529logger.warning(msg);530}531}532533534