com.sun.javafx.font.LogicalFont Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2011, 2024, 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 com.sun.javafx.font;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.sun.javafx.geom.transform.BaseTransform;
/**
* This acts as a factory class for the 12 logical composite font
* resources which are available as well as providing the implementation
* of the resource.
*/
public class LogicalFont implements CompositeFontResource {
public static final String SYSTEM = "System";
public static final String SERIF = "Serif";
public static final String SANS_SERIF = "SansSerif";
public static final String MONOSPACED = "Monospaced";
public static final String STYLE_REGULAR = "Regular";
public static final String STYLE_BOLD = "Bold";
public static final String STYLE_ITALIC = "Italic";
public static final String STYLE_BOLD_ITALIC = "Bold Italic";
private static final Map CANONICAL_FAMILY_MAP = Map.of(
"system", SYSTEM,
"serif", SERIF,
"sansserif", SANS_SERIF,
"sans-serif", SANS_SERIF, // css style
"dialog", SANS_SERIF,
"default", SANS_SERIF,
"monospaced", MONOSPACED,
"monospace", MONOSPACED, // css style
"dialoginput", MONOSPACED);
static boolean isLogicalFont(String name) {
int spaceIndex = name.indexOf(' ');
if (spaceIndex != -1) name = name.substring(0, spaceIndex);
return CANONICAL_FAMILY_MAP.get(name) != null;
}
private static String getCanonicalFamilyName(String name) {
if (name == null) {
return SANS_SERIF;
}
String lcName = name.toLowerCase();
return CANONICAL_FAMILY_MAP.get(lcName);
}
static LogicalFont[] logicalFonts = new LogicalFont[16];
static PGFont getLogicalFont(String familyName, boolean bold,
boolean italic, float size) {
String canonicalFamilyName = getCanonicalFamilyName(familyName);
if (canonicalFamilyName == null) {
return null;
}
int fontIndex = 0;
if (canonicalFamilyName.equals(SANS_SERIF)) {
fontIndex = 0;
} else if (canonicalFamilyName.equals(SERIF)) {
fontIndex = 4;
} else if (canonicalFamilyName.equals(MONOSPACED)) {
fontIndex = 8;
} else {
fontIndex = 12;
}
if (bold) {
fontIndex +=1;
}
if (italic) {
fontIndex +=2;
}
LogicalFont font = logicalFonts[fontIndex];
if (font == null) {
font = new LogicalFont(canonicalFamilyName, bold, italic);
logicalFonts[fontIndex] = font;
}
return new PrismFont(font, font.getFullName(), size);
}
static PGFont getLogicalFont(String fullName, float size) {
/* Need to parse this to find the family portion, for which
* we will allow the various spellings, and the style portion
* which must be exactly one of those we understand. The matching
* is however case insensitive.
* Don't allow an absence of style, we want people to be
* in the habit of distinguishing family and full name usage.
* None of the family names we understand have a space, so look
* for a space to delimit the family and style.
*/
int spaceIndex = fullName.indexOf(' ');
if (spaceIndex == -1 || spaceIndex == fullName.length()-1) {
return null;
}
String family = fullName.substring(0, spaceIndex);
String canonicalFamily = getCanonicalFamilyName(family);
if (canonicalFamily == null) {
return null;
}
String style = fullName.substring(spaceIndex+1).toLowerCase();
boolean bold=false, italic=false;
if (style.equals("regular")) {
// nothing to do
} else if (style.equals("bold")) {
bold = true;
} else if (style.equals("italic")) {
italic = true;
} else if (style.equals("bold italic")) {
bold = true;
italic = true;
} else {
return null;
}
return getLogicalFont(canonicalFamily, bold, italic, size);
}
boolean isBold, isItalic;
private String fullName, familyName, styleName;
private String physicalFamily;
private String physicalFullName;
private String physicalFileName;
private LogicalFont(String family, boolean bold, boolean italic) {
familyName = family;
isBold = bold;
isItalic = italic;
if (!bold && !italic) {
styleName = STYLE_REGULAR;
} else if (bold && !italic) {
styleName = STYLE_BOLD;
} else if (!bold && italic) {
styleName = STYLE_ITALIC;
} else {
styleName = STYLE_BOLD_ITALIC;
}
fullName = familyName + " " + styleName;
if (PrismFontFactory.isLinux) {
FontConfigManager.FcCompFont fcCompFont =
FontConfigManager.getFontConfigFont(family, bold, italic);
if (fcCompFont != null) {
physicalFullName = fcCompFont.firstFont.fullName;
physicalFileName = fcCompFont.firstFont.fontFile;
}
} else {
physicalFamily = PrismFontFactory.getSystemFont(familyName);
}
}
private FontResource slot0FontResource;
private FontResource getSlot0Resource() {
if (slot0FontResource == null) {
PrismFontFactory factory = PrismFontFactory.getFontFactory();
if (physicalFamily != null) {
slot0FontResource = factory.getFontResource(physicalFamily,
isBold,
isItalic, false);
} else {
slot0FontResource = factory.getFontResource(physicalFullName,
physicalFileName,
false);
}
// Its unlikely but possible that this font isn't installed.
if (slot0FontResource == null) {
slot0FontResource = factory.getDefaultFontResource(false);
}
}
return slot0FontResource;
}
volatile private String[] linkedFontNames;
volatile private String[] linkedFontFiles;
volatile private FontResource[] fallbacks;
volatile private FontResource[] nativeFallbacks;
private void getLinkedFonts() {
if (fallbacks == null) {
PrismFontFactory factory = PrismFontFactory.getFontFactory();
FontFallbackInfo fallbackInfo = factory.getFallbacks(getSlot0Resource());
linkedFontNames = fallbackInfo.getFontNames();
linkedFontFiles = fallbackInfo.getFontFiles();
fallbacks = fallbackInfo.getFonts();
}
}
@Override
public int getNumSlots() {
getLinkedFonts();
int num = fallbacks.length;
if (nativeFallbacks != null) {
num += nativeFallbacks.length;
}
return num + 1;
}
private int getSlotForFontNoCreate(String fontName) {
if (fontName.equals(getSlot0Resource().getFullName())) {
return 0;
}
getLinkedFonts();
int i = 1;
for (String linkedFontName : linkedFontNames) {
if (fontName.equalsIgnoreCase(linkedFontName)) {
return i;
}
i++;
}
if (nativeFallbacks != null) {
for (FontResource nativeFallback : nativeFallbacks) {
if (fontName.equalsIgnoreCase(nativeFallback.getFullName())) {
return i;
}
i++;
}
}
return -1;
}
@Override
public int getSlotForFont(String fontName) {
int slot = getSlotForFontNoCreate(fontName);
if (slot >= 0) {
return slot;
}
PrismFontFactory factory = PrismFontFactory.getFontFactory();
FontResource fr = factory.getFontResource(fontName, null, false);
if (fr == null) {
if (PrismFontFactory.debugFonts) {
System.err.println("\t Font name not supported \"" + fontName + "\".");
}
return -1;
}
slot = getSlotForFontNoCreate(fr.getFullName());
if (slot >= 0) {
return slot;
}
/* Add the font to the list of native fallbacks */
return addNativeFallback(fr);
}
private int addNativeFallback(FontResource fr) {
int ns = getNumSlots();
if (ns >= 0x7E) {
/* There are 8bits (0xFF) reserved in a glyph code to store the slot
* number. The first bit cannot be set to avoid negative values
* (leaving 0x7F). The extra -1 (leaving 0x7E) is to account for
* the primary font resource in PrismCompositeFontResource.
*/
if (PrismFontFactory.debugFonts) {
System.err.println("\tToo many font fallbacks!");
}
return -1;
}
/* Add the font to the list of native fallbacks */
FontResource[] tmp;
if (nativeFallbacks == null) {
tmp = new FontResource[1];
} else {
tmp = new FontResource[nativeFallbacks.length + 1];
System.arraycopy(nativeFallbacks, 0, tmp, 0, nativeFallbacks.length);
}
tmp[tmp.length - 1] = fr;
nativeFallbacks = tmp;
return ns;
}
@Override
public int addSlotFont(FontResource fr) {
if (fr == null) {
return -1;
}
int slot = getSlotForFont(fr.getFullName());
if (slot >= 0) {
return slot;
} else {
return addNativeFallback(fr);
}
}
@Override
public FontResource getSlotResource(int slot) {
if (slot == 0) {
return getSlot0Resource();
} else {
getLinkedFonts();
slot = slot - 1;
if (slot >= fallbacks.length) {
slot = slot - fallbacks.length;
if (nativeFallbacks == null || slot >= nativeFallbacks.length) {
return null;
}
return nativeFallbacks[slot];
}
if (fallbacks[slot] == null) {
String file = linkedFontFiles[slot];
String name = linkedFontNames[slot];
fallbacks[slot] =
PrismFontFactory.getFontFactory().
getFontResource(name, file, false);
if (fallbacks[slot] == null) {
fallbacks[slot] = getSlot0Resource();
}
}
return fallbacks[slot];
}
}
@Override
public String getFullName() {
return fullName;
}
@Override
public String getPSName() {
return fullName;
}
@Override
public String getFamilyName() {
return familyName;
}
@Override
public String getStyleName() {
return styleName;
}
@Override
public String getLocaleFullName() {
return fullName;
}
@Override
public String getLocaleFamilyName() {
return familyName;
}
@Override
public String getLocaleStyleName() {
return styleName;
}
@Override
public boolean isBold() {
return getSlotResource(0).isBold();
}
@Override
public boolean isItalic() {
return getSlotResource(0).isItalic();
}
@Override
public String getFileName() {
return getSlotResource(0).getFileName();
}
@Override
public int getFeatures() {
return getSlotResource(0).getFeatures();
}
@Override
public Object getPeer() {
return null;
}
@Override
public boolean isEmbeddedFont() {
return getSlotResource(0).isEmbeddedFont();
}
@Override
public void setPeer(Object peer) {
throw new UnsupportedOperationException("Not supported");
}
@Override
public float[] getGlyphBoundingBox(int glyphCode,
float size, float[] retArr) {
int slot = (glyphCode >>> 24);
int slotglyphCode = glyphCode & CompositeGlyphMapper.GLYPHMASK;
FontResource slotResource = getSlotResource(slot);
return slotResource.getGlyphBoundingBox(slotglyphCode, size, retArr);
}
@Override
public float getAdvance(int glyphCode, float size) {
int slot = (glyphCode >>> 24);
int slotglyphCode = glyphCode & CompositeGlyphMapper.GLYPHMASK;
FontResource slotResource = getSlotResource(slot);
return slotResource.getAdvance(slotglyphCode, size);
}
CompositeGlyphMapper mapper;
@Override
public CharToGlyphMapper getGlyphMapper() {
//return getSlot0Resource().getGlyphMapper();
if (mapper == null) {
mapper = new CompositeGlyphMapper(this);
}
return mapper;
}
Map> strikeMap = new ConcurrentHashMap<>();
@Override
public Map> getStrikeMap() {
return strikeMap;
}
@Override
public int getDefaultAAMode() {
return getSlot0Resource().getDefaultAAMode();
}
@Override
public FontStrike getStrike(float size, BaseTransform transform) {
return getStrike(size, transform, getDefaultAAMode());
}
@Override
public FontStrike getStrike(float size, BaseTransform transform,
int aaMode) {
FontStrikeDesc desc= new FontStrikeDesc(size, transform, aaMode);
WeakReference ref = strikeMap.get(desc);
CompositeStrike strike = null;
if (ref != null) {
strike = (CompositeStrike)ref.get();
}
if (strike == null) {
strike = new CompositeStrike(this, size, transform, aaMode, desc);
if (strike.disposer != null) {
ref = Disposer.addRecord(strike, strike.disposer);
} else {
ref = new WeakReference<>(strike);
}
strikeMap.put(desc, ref);
}
return strike;
}
// Family 0 = SansSerif, 1 = Serif, 2 = Monospaced, 3 = System
private static final int SANS_SERIF_INDEX = 0;
private static final int SERIF_INDEX = 1;
private static final int MONOSPACED_INDEX = 2;
private static final int SYSTEM_INDEX = 3;
// Within a family styles are in the usual order
static String[][] logFamilies = null;
private static void buildFamily(String[] fullNames, String family) {
fullNames[0] = family + " " + STYLE_REGULAR;
fullNames[1] = family + " " + STYLE_BOLD;
fullNames[2] = family + " " + STYLE_ITALIC;
fullNames[3] = family + " " + STYLE_BOLD_ITALIC;
}
private static void buildFamilies() {
if (logFamilies == null) {
String[][] tmpFamilies = new String[SYSTEM_INDEX+1][4];
buildFamily(tmpFamilies[SANS_SERIF_INDEX], SANS_SERIF);
buildFamily(tmpFamilies[SERIF_INDEX], SERIF);
buildFamily(tmpFamilies[MONOSPACED_INDEX], MONOSPACED);
buildFamily(tmpFamilies[SYSTEM_INDEX], SYSTEM);
logFamilies = tmpFamilies;
}
}
static void addFamilies(ArrayList familyList) {
familyList.add(SANS_SERIF);
familyList.add(SERIF);
familyList.add(MONOSPACED);
familyList.add(SYSTEM);
}
static void addFullNames(ArrayList fullNames) {
buildFamilies();
for (int f = 0; f < logFamilies.length; f++) {
for (int n = 0; n < logFamilies[f].length; n++) {
fullNames.add(logFamilies[f][n]);
}
}
}
static String[] getFontsInFamily(String family) {
String canonicalFamily = getCanonicalFamilyName(family);
if (canonicalFamily == null) {
return null;
}
buildFamilies();
if (canonicalFamily.equals(SANS_SERIF)) {
return logFamilies[SANS_SERIF_INDEX];
} else if (canonicalFamily.equals(SERIF)) {
return logFamilies[SERIF_INDEX];
} else if (canonicalFamily.equals(MONOSPACED)) {
return logFamilies[MONOSPACED_INDEX];
} else {
return logFamilies[SYSTEM_INDEX];
}
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof LogicalFont)) {
return false;
}
final LogicalFont other = (LogicalFont)obj;
return this.fullName.equals(other.fullName);
}
private int hash;
@Override
public int hashCode() {
if (hash != 0) {
return hash;
}
else {
hash = fullName.hashCode();
return hash;
}
}
}