@Override
 public String[] getPlatformFontNames() {
   HashSet<String> nameSet = new HashSet<String>();
   X11FontManager fm = (X11FontManager) fontManager;
   FontConfigManager fcm = fm.getFontConfigManager();
   FcCompFont[] fcCompFonts = fcm.loadFontConfig();
   for (int i = 0; i < fcCompFonts.length; i++) {
     for (int j = 0; j < fcCompFonts[i].allFonts.length; j++) {
       nameSet.add(fcCompFonts[i].allFonts[j].fontFile);
     }
   }
   return nameSet.toArray(new String[0]);
 }
 private void writeFcInfo() {
   Properties props = new Properties();
   props.setProperty("version", fileVersion);
   X11FontManager fm = (X11FontManager) fontManager;
   FontConfigManager fcm = fm.getFontConfigManager();
   FontConfigInfo fcInfo = fcm.getFontConfigInfo();
   props.setProperty("fcversion", Integer.toString(fcInfo.fcVersion));
   if (fcInfo.cacheDirs != null) {
     for (int i = 0; i < fcInfo.cacheDirs.length; i++) {
       if (fcInfo.cacheDirs[i] != null) {
         props.setProperty("cachedir." + i, fcInfo.cacheDirs[i]);
       }
     }
   }
   for (int i = 0; i < fcCompFonts.length; i++) {
     FcCompFont fci = fcCompFonts[i];
     String styleKey = fci.jdkName + "." + fci.style;
     props.setProperty(styleKey + ".length", Integer.toString(fci.allFonts.length));
     for (int j = 0; j < fci.allFonts.length; j++) {
       props.setProperty(styleKey + "." + j + ".family", fci.allFonts[j].familyName);
       props.setProperty(styleKey + "." + j + ".file", fci.allFonts[j].fontFile);
     }
   }
   try {
     /* This writes into a temp file then renames when done.
      * Since the rename is an atomic action within the same
      * directory no client will ever see a partially written file.
      */
     File fcInfoFile = getFcInfoFile();
     File dir = fcInfoFile.getParentFile();
     dir.mkdirs();
     File tempFile = Files.createTempFile(dir.toPath(), "fcinfo", null).toFile();
     FileOutputStream fos = new FileOutputStream(tempFile);
     props.store(fos, "JDK Font Configuration Generated File: *Do Not Edit*");
     fos.close();
     boolean renamed = tempFile.renameTo(fcInfoFile);
     if (!renamed && FontUtilities.debugFonts()) {
       System.out.println("rename failed");
       warning("Failed renaming file to " + getFcInfoFile());
     }
   } catch (Exception e) {
     if (FontUtilities.debugFonts()) {
       warning("IOException writing to " + getFcInfoFile());
     }
   }
 }
  @Override
  public CompositeFontDescriptor[] get2DCompositeFontInfo() {

    X11FontManager fm = (X11FontManager) fontManager;
    FontConfigManager fcm = fm.getFontConfigManager();
    FcCompFont[] fcCompFonts = fcm.loadFontConfig();

    CompositeFontDescriptor[] result = new CompositeFontDescriptor[NUM_FONTS * NUM_STYLES];

    for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) {
      String fontName = publicFontNames[fontIndex];

      for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) {

        String faceName = fontName + "." + styleNames[styleIndex];
        FontConfigFont[] fcFonts = getFcFontList(fcCompFonts, fontNames[fontIndex], styleIndex);

        int numFonts = fcFonts.length;
        // fall back fonts listed in the lib/fonts/fallback directory
        if (installedFallbackFontFiles != null) {
          numFonts += installedFallbackFontFiles.length;
        }

        String[] fileNames = new String[numFonts];

        int index;
        for (index = 0; index < fcFonts.length; index++) {
          fileNames[index] = fcFonts[index].fontFile;
        }

        if (installedFallbackFontFiles != null) {
          System.arraycopy(
              installedFallbackFontFiles,
              0,
              fileNames,
              fcFonts.length,
              installedFallbackFontFiles.length);
        }

        result[fontIndex * NUM_STYLES + styleIndex] =
            new CompositeFontDescriptor(faceName, 1, null, fileNames, null, null);
      }
    }
    return result;
  }
  @Override
  public synchronized boolean init() {
    if (fcCompFonts != null) {
      return true;
    }

    setFontConfiguration();
    readFcInfo();
    X11FontManager fm = (X11FontManager) fontManager;
    FontConfigManager fcm = fm.getFontConfigManager();
    if (fcCompFonts == null) {
      fcCompFonts = fcm.loadFontConfig();
      if (fcCompFonts != null) {
        try {
          writeFcInfo();
        } catch (Exception e) {
          if (FontUtilities.debugFonts()) {
            warning("Exception writing fcInfo " + e);
          }
        }
      } else if (FontUtilities.debugFonts()) {
        warning("Failed to get info from libfontconfig");
      }
    } else {
      fcm.populateFontConfig(fcCompFonts);
    }

    if (fcCompFonts == null) {
      return false; // couldn't load fontconfig.
    }

    // NB already in a privileged block from SGE
    String javaHome = System.getProperty("java.home");
    if (javaHome == null) {
      throw new Error("java.home property not set");
    }
    String javaLib = javaHome + File.separator + "lib";
    getInstalledFallbackFonts(javaLib);

    return true;
  }
  /* We want to be able to use this cache instead of invoking
   * fontconfig except when we can detect the system cache has changed.
   * But there doesn't seem to be a way to find the location of
   * the system cache.
   */
  private void readFcInfo() {
    File fcFile = getFcInfoFile();
    if (!fcFile.exists()) {
      return;
    }
    Properties props = new Properties();
    X11FontManager fm = (X11FontManager) fontManager;
    FontConfigManager fcm = fm.getFontConfigManager();
    try {
      FileInputStream fis = new FileInputStream(fcFile);
      props.load(fis);
      fis.close();
    } catch (IOException e) {
      if (FontUtilities.debugFonts()) {
        warning("IOException reading from " + fcFile.toString());
      }
      return;
    }
    String version = (String) props.get("version");
    if (version == null || !version.equals(fileVersion)) {
      return;
    }

    // If there's a new, different fontconfig installed on the
    // system, we invalidate our fontconfig file.
    String fcVersionStr = (String) props.get("fcversion");
    if (fcVersionStr != null) {
      int fcVersion;
      try {
        fcVersion = Integer.parseInt(fcVersionStr);
        if (fcVersion != 0 && fcVersion != fcm.getFontConfigVersion()) {
          return;
        }
      } catch (Exception e) {
        if (FontUtilities.debugFonts()) {
          warning("Exception parsing version " + fcVersionStr);
        }
        return;
      }
    }

    // If we can locate the fontconfig cache dirs, then compare the
    // time stamp of those with our properties file. If we are out
    // of date then re-generate.
    long lastModified = fcFile.lastModified();
    int cacheDirIndex = 0;
    while (cacheDirIndex < 4) { // should never be more than 2 anyway.
      String dir = (String) props.get("cachedir." + cacheDirIndex);
      if (dir == null) {
        break;
      }
      File dirFile = new File(dir);
      if (dirFile.exists() && dirFile.lastModified() > lastModified) {
        return;
      }
      cacheDirIndex++;
    }

    String[] names = {"sansserif", "serif", "monospaced"};
    String[] fcnames = {"sans", "serif", "monospace"};
    int namesLen = names.length;
    int numStyles = 4;
    FcCompFont[] fci = new FcCompFont[namesLen * numStyles];

    try {
      for (int i = 0; i < namesLen; i++) {
        for (int s = 0; s < numStyles; s++) {
          int index = i * numStyles + s;
          fci[index] = new FcCompFont();
          String key = names[i] + "." + s;
          fci[index].jdkName = names[i];
          fci[index].fcFamily = fcnames[i];
          fci[index].style = s;
          String lenStr = (String) props.get(key + ".length");
          int nfonts = Integer.parseInt(lenStr);
          if (nfonts <= 0) {
            return; // bad file
          }
          fci[index].allFonts = new FontConfigFont[nfonts];
          for (int f = 0; f < nfonts; f++) {
            fci[index].allFonts[f] = new FontConfigFont();
            String fkey = key + "." + f + ".family";
            String family = (String) props.get(fkey);
            fci[index].allFonts[f].familyName = family;
            fkey = key + "." + f + ".file";
            String file = (String) props.get(fkey);
            if (file == null) {
              return; // bad file
            }
            fci[index].allFonts[f].fontFile = file;
          }
          fci[index].firstFont = fci[index].allFonts[0];
        }
      }
      fcCompFonts = fci;
    } catch (Throwable t) {
      if (FontUtilities.debugFonts()) {
        warning(t.toString());
      }
    }
  }