/*
* Copyright 2003-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.nodeEditor.cells;
import com.intellij.openapi.util.SystemInfo;
import jetbrains.mps.logging.Logger;
import jetbrains.mps.util.Pair;
import org.apache.log4j.LogManager;
import org.jetbrains.annotations.NotNull;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* User: shatalin
* Date: 06/08/14
*/
public class FontRegistry {
private static final Logger LOG = Logger.wrap(LogManager.getLogger(FontRegistry.class));
private static FontRegistry ourInstance;
public static FontRegistry getInstance() {
if (ourInstance == null) {
ourInstance = new FontRegistry();
}
return ourInstance;
}
private Map<String, FontFamily> myAvailableFonts;
private Map<String, Font> myFontsCache = new HashMap<String, Font>();
private Map<Font, FontMetrics> myFontMetricsCache = new HashMap<Font, FontMetrics>();
private FontRegistry() {
loadStyledFonts();
}
private void loadStyledFonts() {
if (!SystemInfo.isMac) {
return;
}
myAvailableFonts = new HashMap<String, FontFamily>();
GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
for (Font font : graphicsEnvironment.getAllFonts()) {
String fontFamilyName = font.getFamily();
FontFamily fontFamily = myAvailableFonts.get(fontFamilyName);
if (fontFamily == null) {
fontFamily = new FontFamily(fontFamilyName);
myAvailableFonts.put(fontFamilyName, fontFamily);
}
fontFamily.addFont(font.getName());
}
for (String fontFamilyName : myAvailableFonts.keySet().toArray(new String[myAvailableFonts.size()])) {
FontFamily fontFamily = myAvailableFonts.get(fontFamilyName);
if (!fontFamily.isValid()) {
myAvailableFonts.remove(fontFamilyName);
}
}
}
public Font getFont(String fontName, int style, int size) {
String key = fontName + "#" + style + "#" + size;
Font result = myFontsCache.get(key);
if (result == null) {
Pair<String, Integer> realFontName = getRealFontNameAndStyle(fontName, style);
result = new Font(realFontName.o1, realFontName.o2, size);
myFontsCache.put(key, result);
}
return result;
}
public boolean isFakeItalic(String fontName, int style) {
if (!SystemInfo.isMac || (style & Font.ITALIC) == 0) {
return false;
}
FontFamily fontEntry = myAvailableFonts.get(fontName);
return fontEntry == null || fontEntry.getFontName(style) == null;
}
public FontMetrics getFontMetrics(Font font) {
FontMetrics result = myFontMetricsCache.get(font);
if (result == null) {
result = Toolkit.getDefaultToolkit().getFontMetrics(font);
myFontMetricsCache.put(font, result);
}
return result;
}
private Pair<String, Integer> getRealFontNameAndStyle(String fontName, int style) {
if (SystemInfo.isMac && myAvailableFonts.containsKey(fontName)) {
FontFamily fontEntry = myAvailableFonts.get(fontName);
String styledFontName = fontEntry.getFontName(style);
if (styledFontName != null) {
return new Pair<String, Integer>(styledFontName, 0);
} else {
return new Pair<String, Integer>(fontEntry.getRegularFontName(), style);
}
}
return new Pair<String, Integer>(fontName, style);
}
private class FontFamily {
private String myFamilyName;
private List<String> myFontNames = new ArrayList<String>();
private String myRegularFontName;
private String myBoldFontName;
private String myItalicFontName;
private String myBoldItalicFontName;
public FontFamily(@NotNull String familyName) {
myFamilyName = familyName;
}
public void addFont(String fontName) {
myFontNames.add(fontName);
}
private String getCommonPrefix() {
assert myFontNames.size() > 1;
String commonPrefix = null;
for (String fontName : myFontNames) {
if (commonPrefix == null) {
commonPrefix = fontName;
continue;
}
while (!fontName.startsWith(commonPrefix) && !commonPrefix.isEmpty()) {
commonPrefix = commonPrefix.substring(0, commonPrefix.length() - 1);
}
}
return commonPrefix;
}
private void loadFontNames() {
if (myFontNames.isEmpty()) {
return;
}
if (myFontNames.size() == 1) {
myRegularFontName = myFontNames.get(0);
return;
}
String prefix = getCommonPrefix();
if (prefix.isEmpty()) {
myRegularFontName = myFontNames.get(0);
LOG.error(
"Common prefix was not found for font family \"" + myFamilyName + "\" using first available font as regular one: \"" + myRegularFontName + "\"");
return;
}
int prefixLength = prefix.length();
int regularSuffixLen = -1;
int italicSuffixLen = -1;
int boldSuffixLen = -1;
int boldItalicSuffixLen = -1;
for (String fontName : myFontNames) {
if (fontName.length() < prefixLength) {
LOG.error(
"Font name \"" + fontName + "\" registered in font family \"" + myFamilyName + "\" is shorter that the length of common prefix \"" + prefix +
"\". Skipping it.");
continue;
}
String fontSuffix = fontName.substring(prefixLength);
if (fontSuffix.isEmpty() || fontSuffix.contains("Regular") || fontSuffix.contains("Plain")) {
if (myRegularFontName == null || fontSuffix.length() < regularSuffixLen) {
myRegularFontName = fontName;
regularSuffixLen = fontSuffix.length();
}
continue;
}
boolean bold = fontSuffix.contains("Bold");
boolean italic = fontSuffix.contains("Italic") || fontSuffix.contains("Oblique");
if (bold & italic) {
if (myBoldItalicFontName == null || fontSuffix.length() < boldItalicSuffixLen) {
myBoldItalicFontName = fontName;
boldItalicSuffixLen = fontSuffix.length();
}
} else if (bold) {
if (myBoldFontName == null || fontSuffix.length() < boldSuffixLen) {
myBoldFontName = fontName;
boldSuffixLen = fontSuffix.length();
}
} else if (italic) {
if (myItalicFontName == null || fontSuffix.length() < italicSuffixLen) {
myItalicFontName = fontName;
italicSuffixLen = fontSuffix.length();
}
}
}
if (myRegularFontName == null) {
if (myBoldFontName != null) {
myRegularFontName = myBoldFontName;
} else if (myItalicFontName != null) {
myRegularFontName = myItalicFontName;
} else if (myBoldItalicFontName != null) {
myRegularFontName = myBoldItalicFontName;
} else {
myRegularFontName = myFontNames.get(0);
}
}
}
public boolean isValid() {
loadFontNames();
return myRegularFontName != null;
}
public String getRegularFontName() {
return myRegularFontName;
}
public String getFontName(int style) {
boolean bold = (style & Font.BOLD) != 0;
boolean italic = (style & Font.ITALIC) != 0;
if (bold & italic) {
return myBoldItalicFontName;
} else if (bold) {
return myBoldFontName;
} else if (italic) {
return myItalicFontName;
}
return myRegularFontName;
}
}
}