JBR-4072 Migrate font fallback implementation on macOS to cascade lists

(cherry picked from commit a155760e94)
This commit is contained in:
Dmitry Batrak
2021-12-03 16:02:54 +03:00
parent 09c200ccfd
commit d0d8a63e0b
26 changed files with 254 additions and 849 deletions

View File

@@ -128,12 +128,8 @@ public class CCharToGlyphMapper extends CharToGlyphMapper {
};
}
// This mapper returns either the glyph code, or if the character can be
// replaced on-the-fly using CoreText substitution; the negative unicode
// value. If this "glyph code int" is treated as an opaque code, it will
// strike and measure exactly as a real glyph code - whether the character
// is present or not. Missing characters for any font on the system will
// be returned as 0, as the getMissingGlyphCode() function above indicates.
// Missing characters for any font on the system will be returned as 0,
// as the getMissingGlyphCode() function above indicates.
private static native void nativeCharsToGlyphs(final long nativeFontPtr,
int count, char[] unicodes,
int[] glyphs);

View File

@@ -1,81 +0,0 @@
/*
* Copyright 2000-2018 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 sun.font;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
public final class CCompositeFont extends CompositeFont {
private final List<CFont> fallbackFonts = new ArrayList<>();
public CCompositeFont(CFont font) {
super(new PhysicalFont[]{font});
mapper = new CCompositeGlyphMapper(this);
}
@Override
public synchronized int getNumSlots() {
return super.getNumSlots();
}
@Override
public CFont getSlotFont(int slot) {
if (slot == 0) return (CFont) super.getSlotFont(0);
synchronized (this) {
return fallbackFonts.get(slot - 1);
}
}
@Override
synchronized FontStrike getStrike(FontStrikeDesc desc, boolean copy) {
return super.getStrike(desc, copy);
}
@Override
protected synchronized int getValidatedGlyphCode(int glyphCode) {
return super.getValidatedGlyphCode(glyphCode);
}
@Override
public boolean hasSupplementaryChars() {
return false;
}
@Override
public boolean useAAForPtSize(int ptsize) {
return true;
}
public synchronized int findSlot(String fontName) {
for (int slot = 0; slot < numSlots; slot++) {
CFont slotFont = getSlotFont(slot);
if (fontName.equals(slotFont.getNativeFontName())) {
return slot;
}
}
return -1;
}
public synchronized int addSlot(CFont font) {
int slot = findSlot(font.getNativeFontName());
if (slot >= 0) return slot;
fallbackFonts.add(font);
lastFontStrike = new SoftReference<>(null);
strikeCache.clear();
return numSlots++;
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright (c) 2015, 2018, 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.awt.*;
public final class CCompositeGlyphMapper extends CompositeGlyphMapper {
public CCompositeGlyphMapper(CCompositeFont compFont) {
super(compFont);
}
@Override
protected int convertToGlyph(int unicode) {
CCompositeFont compositeFont = (CCompositeFont) font;
CFont mainFont = (CFont) font.getSlotFont(0);
String[] fallbackFontInfo = new String[2];
int glyphCode = nativeCodePointToGlyph(mainFont.getNativeFontPtr(), unicode, fallbackFontInfo);
if (glyphCode == missingGlyph) {
setCachedGlyphCode(unicode, missingGlyph);
return missingGlyph;
}
String fallbackFontName = fallbackFontInfo[0];
String fallbackFontFamilyName = fallbackFontInfo[1];
if (fallbackFontName == null || fallbackFontFamilyName == null) {
int result = compositeGlyphCode(0, glyphCode);
setCachedGlyphCode(unicode, result);
return result;
}
int slot = compositeFont.findSlot(fallbackFontName);
if (slot < 0) {
Font2D fallbackFont = FontManagerFactory.getInstance().findFont2D(fallbackFontName,
Font.PLAIN, FontManager.NO_FALLBACK);
if (!(fallbackFont instanceof CFont) ||
!fallbackFontName.equals(((CFont) fallbackFont).getNativeFontName())) {
// Native font fallback mechanism can return "hidden" fonts - their names start with dot,
// and they are not returned in a list of fonts available in system, but they can still be used
// if requested explicitly.
fallbackFont = new CFont(fallbackFontName, fallbackFontFamilyName, null);
}
if (mainFont.isFakeItalic()) fallbackFont = ((CFont)fallbackFont).createItalicVariant(false);
slot = compositeFont.addSlot((CFont) fallbackFont);
}
int result = compositeGlyphCode(slot, glyphCode);
setCachedGlyphCode(unicode, result);
return result;
}
// This invokes native font fallback mechanism, returning information about font (its Postscript and family names)
// able to display a given character, and corresponding glyph code
private static native int nativeCodePointToGlyph(long nativeFontPtr, int codePoint, String[] result);
}

View File

@@ -31,6 +31,9 @@ import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;
// Right now this class is final to avoid a problem with native code.
// For some reason the JNI IsInstanceOf was not working correctly
@@ -175,14 +178,12 @@ public final class CFont extends PhysicalFont implements FontSubstitution, FontW
isFakeItalic = other.isFakeItalic;
}
public CFont createItalicVariant(boolean updateStyle) {
public CFont createItalicVariant() {
CFont font = new CFont(this, familyName);
font.nativeFontName = fullName;
font.fullName =
fullName + (style == Font.BOLD ? "" : "-") + "Italic-Derived";
if (updateStyle) {
font.style |= Font.ITALIC;
}
font.style |= Font.ITALIC;
font.isFakeItalic = true;
return font;
}
@@ -204,11 +205,51 @@ public final class CFont extends PhysicalFont implements FontSubstitution, FontW
return getCGFontPtrNative(getNativeFontPtr());
}
static native void getCascadeList(long nativeFontPtr, ArrayList<String> listOfString);
private CompositeFont createCompositeFont() {
ArrayList<String> listOfString = new ArrayList<String>();
getCascadeList(getNativeFontPtr(), listOfString);
Set<String> components = new LinkedHashSet<>(listOfString);
// In some italic cases the standard Mac cascade list is missing Arabic.
components.add("GeezaPro");
CFontManager fm = (CFontManager) FontManagerFactory.getInstance();
components.addAll(fm.getAdditionalFallbackVariants());
int numFonts = 1 + components.size();
PhysicalFont[] fonts = new PhysicalFont[numFonts];
fonts[0] = this;
int idx = 1;
if (FontUtilities.isLogging()) {
FontUtilities.logInfo("Cascading list for " + this + " :");
}
for (String s : components) {
if (FontUtilities.isLogging()) {
FontUtilities.logInfo("Fallback:" + s);
}
if (s.equals(".AppleSymbolsFB")) {
// Don't know why we get the weird name above .. replace.
s = "AppleSymbols";
}
Font2D f2d = fm.getOrCreateFallbackFont(s);
if (f2d == null || f2d == this) {
continue;
}
fonts[idx++] = (PhysicalFont)f2d;
}
if (idx < fonts.length) {
PhysicalFont[] orig = fonts;
fonts = new PhysicalFont[idx];
System.arraycopy(orig, 0, fonts, 0, idx);
}
return new CompositeFont(fonts);
}
private CompositeFont compFont;
public CompositeFont getCompositeFont2D() {
if (compFont == null) {
compFont = new CCompositeFont(this);
compFont = createCompositeFont();
}
return compFont;
}
@@ -236,14 +277,6 @@ public final class CFont extends PhysicalFont implements FontSubstitution, FontW
return new CStrike(this, desc);
}
boolean isFakeItalic() {
return isFakeItalic;
}
String getNativeFontName() {
return nativeFontName;
}
@Override
public String getTypographicSubfamilyName() {
return faceName == null ? super.getTypographicSubfamilyName() : faceName;
@@ -276,8 +309,4 @@ public final class CFont extends PhysicalFont implements FontSubstitution, FontW
", familyName: " + familyName + ", style: " + style +
" } aka: " + super.toString();
}
public Font2D createItalic() {
return this.createItalicVariant(true);
}
}

View File

@@ -30,20 +30,27 @@ import java.io.File;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import javax.swing.plaf.FontUIResource;
import sun.awt.FontConfiguration;
import sun.awt.HeadlessToolkit;
import sun.lwawt.macosx.*;
import sun.security.action.GetBooleanAction;
import sun.util.logging.PlatformLogger;
public final class CFontManager extends SunFontManager {
private static Hashtable<String, Font2D> genericFonts = new Hashtable<String, Font2D>();
private final Map<String, Font2D> fallbackFonts = new ConcurrentHashMap<>();
private final List<String> extFallbackVariants = new ArrayList<>();
@Override
protected FontConfiguration createFontConfiguration() {
@@ -230,6 +237,7 @@ public final class CFontManager extends SunFontManager {
public Object run() {
if (!loadedAllFonts) {
loadNativeFonts();
collectAdditionalFallbackVariants();
loadedAllFonts = true;
}
return null;
@@ -337,4 +345,40 @@ public final class CFontManager extends SunFontManager {
@Override
protected void populateFontFileNameMap(HashMap<String, String> fontToFileMap, HashMap<String, String> fontToFamilyNameMap,
HashMap<String, ArrayList<String>> familyToFontListMap, Locale locale) {}
@SuppressWarnings("removal")
private void collectAdditionalFallbackVariants() {
if (AccessController.doPrivileged(new GetBooleanAction("mac.ext.font.fallback"))) {
for (String fontName : genericFonts.keySet()) {
boolean accept = false;
if (fontName.equals("ArialUnicodeMS")) {
accept = true;
} else if (fontName.startsWith("NotoSans")) {
int pos = fontName.indexOf('-');
accept = pos == -1 || fontName.substring(pos + 1).equals("Regular");
}
if (accept) {
extFallbackVariants.add(fontName);
}
}
Collections.sort(extFallbackVariants); // ensure predictable order
}
}
List<String> getAdditionalFallbackVariants() {
return extFallbackVariants;
}
Font2D getOrCreateFallbackFont(String fontName) {
Font2D font2D = findFont2D(fontName, Font.PLAIN, FontManager.NO_FALLBACK);
if (font2D != null || fontName.startsWith(".")) {
return font2D;
} else {
// macOS doesn't list some system fonts in [NSFontManager availableFontFamilies] output,
// so they are not registered in font manager as part of 'loadNativeFonts'.
// These fonts are present in [NSFontManager availableFonts] output though,
// and can be accessed in the same way as other system fonts.
return fallbackFonts.computeIfAbsent(fontName, name -> new CFont(name, null, null));
}
}
}

View File

@@ -358,18 +358,14 @@ public final class CStrike extends PhysicalStrike {
}
}
// This class stores glyph pointers, and is indexed based on glyph codes,
// and negative unicode values. See the comments in
// CCharToGlyphMapper for more details on our glyph code strategy.
// This class stores glyph pointers, and is indexed based on glyph codes.
private static class GlyphInfoCache extends CStrikeDisposer {
private static final int FIRST_LAYER_SIZE = 256;
private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128
// rdar://problem/5204197
private final AtomicBoolean disposed = new AtomicBoolean(false);
private final long[] firstLayerCache;
private SparseBitShiftingTwoLayerArray secondLayerCache;
private HashMap<Integer, Long> generalCache;
GlyphInfoCache(final Font2D nativeFont, final FontStrikeDesc desc) {
@@ -378,19 +374,9 @@ public final class CStrike extends PhysicalStrike {
}
public synchronized long get(final int index) {
if (index < 0) {
if (-index < SECOND_LAYER_SIZE) {
// catch common unicodes
if (secondLayerCache == null) {
return 0L;
}
return secondLayerCache.get(-index);
}
} else {
if (index < FIRST_LAYER_SIZE) {
// catch common glyphcodes
return firstLayerCache[index];
}
if (index < FIRST_LAYER_SIZE) {
// catch common glyphcodes
return firstLayerCache[index];
}
if (generalCache == null) {
@@ -404,21 +390,10 @@ public final class CStrike extends PhysicalStrike {
}
public synchronized void put(final int index, final long value) {
if (index < 0) {
if (-index < SECOND_LAYER_SIZE) {
// catch common unicodes
if (secondLayerCache == null) {
secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128
}
secondLayerCache.put(-index, value);
return;
}
} else {
if (index < FIRST_LAYER_SIZE) {
// catch common glyphcodes
firstLayerCache[index] = value;
return;
}
if (index < FIRST_LAYER_SIZE) {
// catch common glyphcodes
firstLayerCache[index] = value;
return;
}
if (generalCache == null) {
@@ -441,14 +416,6 @@ public final class CStrike extends PhysicalStrike {
// clean out the first array
disposeLongArray(firstLayerCache);
// clean out the two layer arrays
if (secondLayerCache != null) {
final long[][] secondLayerLongArrayArray = secondLayerCache.cache;
for (final long[] longArray : secondLayerLongArrayArray) {
if (longArray != null) disposeLongArray(longArray);
}
}
// clean up everyone else
if (generalCache != null) {
for (Long aLong : generalCache.values()) {
@@ -485,56 +452,18 @@ public final class CStrike extends PhysicalStrike {
}
}
}
private static class SparseBitShiftingTwoLayerArray {
final long[][] cache;
final int shift;
final int secondLayerLength;
SparseBitShiftingTwoLayerArray(final int size, final int shift) {
this.shift = shift;
this.cache = new long[1 << shift][];
this.secondLayerLength = size >> shift;
}
public long get(final int index) {
final int firstIndex = index >> shift;
final long[] firstLayerRow = cache[firstIndex];
if (firstLayerRow == null) return 0L;
return firstLayerRow[index - (firstIndex * (1 << shift))];
}
public void put(final int index, final long value) {
final int firstIndex = index >> shift;
long[] firstLayerRow = cache[firstIndex];
if (firstLayerRow == null) {
cache[firstIndex] = firstLayerRow = new long[secondLayerLength];
}
firstLayerRow[index - (firstIndex * (1 << shift))] = value;
}
}
}
private static class GlyphAdvanceCache {
private static final int FIRST_LAYER_SIZE = 256;
private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128
private final float[] firstLayerCache = new float[FIRST_LAYER_SIZE];
private SparseBitShiftingTwoLayerArray secondLayerCache;
private HashMap<Integer, Float> generalCache;
public synchronized float get(final int index) {
if (index < 0) {
if (-index < SECOND_LAYER_SIZE) {
// catch common unicodes
if (secondLayerCache == null) return 0;
return secondLayerCache.get(-index);
}
} else {
if (index < FIRST_LAYER_SIZE) {
// catch common glyphcodes
return firstLayerCache[index];
}
if (index < FIRST_LAYER_SIZE) {
// catch common glyphcodes
return firstLayerCache[index];
}
if (generalCache == null) return 0;
@@ -544,21 +473,10 @@ public final class CStrike extends PhysicalStrike {
}
public synchronized void put(final int index, final float value) {
if (index < 0) {
if (-index < SECOND_LAYER_SIZE) {
// catch common unicodes
if (secondLayerCache == null) {
secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128
}
secondLayerCache.put(-index, value);
return;
}
} else {
if (index < FIRST_LAYER_SIZE) {
// catch common glyphcodes
firstLayerCache[index] = value;
return;
}
if (index < FIRST_LAYER_SIZE) {
// catch common glyphcodes
firstLayerCache[index] = value;
return;
}
if (generalCache == null) {
@@ -567,34 +485,5 @@ public final class CStrike extends PhysicalStrike {
generalCache.put(Integer.valueOf(index), Float.valueOf(value));
}
private static class SparseBitShiftingTwoLayerArray {
final float[][] cache;
final int shift;
final int secondLayerLength;
SparseBitShiftingTwoLayerArray(final int size, final int shift) {
this.shift = shift;
this.cache = new float[1 << shift][];
this.secondLayerLength = size >> shift;
}
public float get(final int index) {
final int firstIndex = index >> shift;
final float[] firstLayerRow = cache[firstIndex];
if (firstLayerRow == null) return 0L;
return firstLayerRow[index - (firstIndex * (1 << shift))];
}
public void put(final int index, final float value) {
final int firstIndex = index >> shift;
float[] firstLayerRow = cache[firstIndex];
if (firstLayerRow == null) {
cache[firstIndex] = firstLayerRow =
new float[secondLayerLength];
}
firstLayerRow[index - (firstIndex * (1 << shift))] = value;
}
}
}
}

View File

@@ -41,51 +41,6 @@ static const CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 };
#pragma mark --- CoreText Support ---
// Translates a Unicode into a CGGlyph/CTFontRef pair
// Returns the substituted font, and places the appropriate glyph into "glyphRef"
CTFontRef JavaCT_CopyCTFallbackFontAndGlyphForUnicode
(const AWTFont *font, const UTF16Char *charRef, CGGlyph *glyphRef, int count) {
CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font->fFont, charRef, count);
if (fallback == NULL)
{
// use the original font if we somehow got duped into trying to fallback something we can't
fallback = (CTFontRef)font->fFont;
CFRetain(fallback);
}
CTFontGetGlyphsForCharacters(fallback, charRef, glyphRef, count);
return fallback;
}
// Translates a Java glyph code int (might be a negative unicode value) into a CGGlyph/CTFontRef pair
// Returns the substituted font, and places the appropriate glyph into "glyph"
CTFontRef JavaCT_CopyCTFallbackFontAndGlyphForJavaGlyphCode
(const AWTFont *font, const jint glyphCode, CGGlyph *glyphRef)
{
// negative glyph codes are really unicodes, which were placed there by the mapper
// to indicate we should use CoreText to substitute the character
if (glyphCode >= 0)
{
*glyphRef = glyphCode;
CFRetain(font->fFont);
return (CTFontRef)font->fFont;
}
UTF16Char character = -glyphCode;
return JavaCT_CopyCTFallbackFontAndGlyphForUnicode(font, &character, glyphRef, 1);
}
// Breakup a 32 bit unicode value into the component surrogate pairs
void JavaCT_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]) {
int value = uniChar - 0x10000;
UTF16Char low_surrogate = (value & 0x3FF) | LO_SURROGATE_START;
UTF16Char high_surrogate = (((int)(value & 0xFFC00)) >> 10) | HI_SURROGATE_START;
charRef[0] = high_surrogate;
charRef[1] = low_surrogate;
}
/*
* Callback for CoreText which uses the CoreTextProviderStruct to feed CT UniChars
* We only use it for one-off lines, and don't attempt to fragment our strings
@@ -129,7 +84,7 @@ static NSDictionary* ctsDictionaryFor(const NSFont *font, BOOL useFractionalMetr
// Itterates though each glyph, and if a transform is present for that glyph, apply it to the CGContext, and strike the glyph.
// If there is no per-glyph transform, just strike the glyph. Advances must also be transformed on-the-spot as well.
void JavaCT_DrawGlyphVector
(const QuartzSDOps *qsdo, const AWTStrike *strike, const BOOL useSubstituion, const int uniChars[], const CGGlyph glyphs[], CGSize advances[], const jint g_gvTXIndicesAsInts[], const jdouble g_gvTransformsAsDoubles[], const CFIndex length)
(const QuartzSDOps *qsdo, const AWTStrike *strike, const CGGlyph glyphs[], CGSize advances[], const jint g_gvTXIndicesAsInts[], const jdouble g_gvTransformsAsDoubles[], const CFIndex length)
{
CGPoint pt = { 0, 0 };
@@ -137,49 +92,12 @@ void JavaCT_DrawGlyphVector
CGContextRef cgRef = qsdo->cgRef;
CGAffineTransform ctmText = CGContextGetTextMatrix(cgRef);
BOOL saved = false;
CGAffineTransform invTx = CGAffineTransformInvert(strike->fTx);
NSInteger i;
for (i = 0; i < length; i++)
{
CGGlyph glyph = glyphs[i];
int uniChar = uniChars[i];
// if we found a unichar instead of a glyph code, get the fallback font,
// find the glyph code for the fallback font, and set the font on the current context
if (uniChar != 0)
{
CTFontRef fallback;
if (uniChar > 0xFFFF) {
UTF16Char charRef[2];
JavaCT_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef);
CGGlyph glyphTmp[2];
fallback = JavaCT_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2);
glyph = glyphTmp[0];
} else {
const UTF16Char u = uniChar;
fallback = JavaCT_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, &u, (CGGlyph *)&glyph, 1);
}
if (fallback) {
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
CFRelease(fallback);
if (cgFallback) {
if (!saved) {
CGContextSaveGState(cgRef);
saved = true;
}
CGContextSetFont(cgRef, cgFallback);
CFRelease(cgFallback);
}
}
} else {
if (saved) {
CGContextRestoreGState(cgRef);
saved = false;
}
}
// if we have per-glyph transformations
int tin = (g_gvTXIndicesAsInts == NULL) ? -1 : (g_gvTXIndicesAsInts[i] - 1) * 6;
@@ -214,10 +132,6 @@ void JavaCT_DrawGlyphVector
pt.y += advances[i].height;
}
// reset the font on the context after striking a unicode with CoreText
if (saved) {
CGContextRestoreGState(cgRef);
}
}
// Using the Quartz Surface Data context, draw a hot-substituted character run
@@ -327,24 +241,16 @@ static jclass jc_StandardGlyphVector = NULL;
// Checks the GlyphVector Java object for any transforms that were applied to individual characters. If none are present,
// strike the glyphs immediately in Core Graphics. Otherwise, obtain the arrays, and defer to above.
static inline void doDrawGlyphsPipe_checkForPerGlyphTransforms
(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, BOOL useSubstituion, int *uniChars, CGGlyph *glyphs, CGSize *advances, size_t length)
(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, CGGlyph *glyphs, CGSize *advances, size_t length)
{
// if we have no character substitution, and no per-glyph transformations - strike now!
// if we have no per-glyph transformations - strike now!
GET_SGV_CLASS();
DECLARE_FIELD(jm_StandardGlyphVector_gti, jc_StandardGlyphVector, "gti", "Lsun/font/StandardGlyphVector$GlyphTransformInfo;");
jobject gti = (*env)->GetObjectField(env, gVector, jm_StandardGlyphVector_gti);
if (gti == 0)
{
if (useSubstituion)
{
// quasi-simple case, substitution, but no per-glyph transforms
JavaCT_DrawGlyphVector(qsdo, strike, TRUE, uniChars, glyphs, advances, NULL, NULL, length);
}
else
{
// fast path, straight to CG without per-glyph transforms
CGContextShowGlyphsWithAdvances(qsdo->cgRef, glyphs, advances, length);
}
// fast path, straight to CG without per-glyph transforms
CGContextShowGlyphsWithAdvances(qsdo->cgRef, glyphs, advances, length);
return;
}
@@ -369,8 +275,8 @@ static inline void doDrawGlyphsPipe_checkForPerGlyphTransforms
(*env)->DeleteLocalRef(env, g_gtiTXIndicesArray);
return;
}
// slowest case, we have per-glyph transforms, and possibly glyph substitution as well
JavaCT_DrawGlyphVector(qsdo, strike, useSubstituion, uniChars, glyphs, advances, g_gvTXIndicesAsInts, g_gvTransformsAsDoubles, length);
// slowest case, we have per-glyph transforms
JavaCT_DrawGlyphVector(qsdo, strike, glyphs, advances, g_gvTXIndicesAsInts, g_gvTransformsAsDoubles, length);
(*env)->ReleasePrimitiveArrayCritical(env, g_gtiTransformsArray, g_gvTransformsAsDoubles, JNI_ABORT);
(*env)->ReleasePrimitiveArrayCritical(env, g_gtiTXIndicesArray, g_gvTXIndicesAsInts, JNI_ABORT);
@@ -379,35 +285,11 @@ static inline void doDrawGlyphsPipe_checkForPerGlyphTransforms
(*env)->DeleteLocalRef(env, g_gtiTXIndicesArray);
}
// Retrieves advances for translated unicodes
// Uses "glyphs" as a temporary buffer for the glyph-to-unicode translation
void JavaCT_GetAdvancesForUnichars
(const NSFont *font, const int uniChars[], CGGlyph glyphs[], const size_t length, CGSize advances[])
{
// cycle over each spot, and if we discovered a unicode to substitute, we have to calculate the advance for it
size_t i;
for (i = 0; i < length; i++)
{
UniChar uniChar = uniChars[i];
if (uniChar == 0) continue;
CGGlyph glyph = 0;
const CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font, &uniChar, 1);
if (fallback) {
CTFontGetGlyphsForCharacters(fallback, &uniChar, &glyph, 1);
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &(advances[i]), 1);
CFRelease(fallback);
}
glyphs[i] = glyph;
}
}
// Fills the glyph buffer with glyphs from the GlyphVector object. Also checks to see if the glyph's positions have been
// already caculated from GlyphVector, or we simply ask Core Graphics to make some advances for us. Pre-calculated positions
// are translated into advances, since CG only understands advances.
static inline void doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers
(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, CGGlyph *glyphs, int *uniChars, CGSize *advances, size_t length, jintArray glyphsArray)
(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, CGGlyph *glyphs, CGSize *advances, size_t length, jintArray glyphsArray)
{
// fill the glyph buffer
jint *glyphsAsInts = (*env)->GetPrimitiveArrayCritical(env, glyphsArray, NULL);
@@ -415,24 +297,10 @@ static inline void doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers
return;
}
// if a glyph code from Java is negative, that means it is really a unicode value
// which we can use in CoreText to strike the character in another font
size_t i;
BOOL complex = NO;
for (i = 0; i < length; i++)
{
jint code = glyphsAsInts[i];
if (code < 0)
{
complex = YES;
uniChars[i] = -code;
glyphs[i] = 0;
}
else
{
uniChars[i] = 0;
glyphs[i] = code;
}
glyphs[i] = glyphsAsInts[i];
}
(*env)->ReleasePrimitiveArrayCritical(env, glyphsArray, glyphsAsInts, JNI_ABORT);
@@ -483,15 +351,10 @@ static inline void doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers
// there were no pre-calculated positions from the glyph buffer on the Java side
AWTFont *awtFont = strike->fAWTFont;
CTFontGetAdvancesForGlyphs((CTFontRef)awtFont->fFont, kCTFontDefaultOrientation, glyphs, advances, length);
if (complex)
{
JavaCT_GetAdvancesForUnichars(awtFont->fFont, uniChars, glyphs, length, advances);
}
}
// continue on to the next stage of the pipe
doDrawGlyphsPipe_checkForPerGlyphTransforms(env, qsdo, strike, gVector, complex, uniChars, glyphs, advances, length);
doDrawGlyphsPipe_checkForPerGlyphTransforms(env, qsdo, strike, gVector, glyphs, advances, length);
}
// Obtains the glyph array to determine the number of glyphs we are dealing with. If we are dealing a large number of glyphs,
@@ -515,18 +378,16 @@ static inline void doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc
{
// if we are small enough, fit everything onto the stack
CGGlyph glyphs[length];
int uniChars[length];
CGSize advances[length];
doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, uniChars, advances, length, glyphsArray);
doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, advances, length, glyphsArray);
}
else
{
// otherwise, we should malloc and free buffers for this large run
CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * length);
int *uniChars = (int *)malloc(sizeof(int) * length);
CGSize *advances = (CGSize *)malloc(sizeof(CGSize) * length);
if (glyphs == NULL || uniChars == NULL || advances == NULL)
if (glyphs == NULL || advances == NULL)
{
(*env)->DeleteLocalRef(env, glyphsArray);
[NSException raise:NSMallocException format:@"%s-%s:%d", __FILE__, __FUNCTION__, __LINE__];
@@ -534,10 +395,6 @@ static inline void doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc
{
free(glyphs);
}
if (uniChars)
{
free(uniChars);
}
if (advances)
{
free(advances);
@@ -545,10 +402,9 @@ static inline void doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc
return;
}
doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, uniChars, advances, length, glyphsArray);
doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, advances, length, glyphsArray);
free(glyphs);
free(uniChars);
free(advances);
}

View File

@@ -3341,6 +3341,64 @@ Java_sun_awt_FontDescriptor_initIDs
}
#endif
/*
* Class: sun_font_CFont
* Method: getCascadeList
* Signature: (JLjava/util/ArrayList;)V
*/
JNIEXPORT void JNICALL
Java_sun_font_CFont_getCascadeList
(JNIEnv *env, jclass cls, jlong awtFontPtr, jobject arrayListOfString)
{
JNI_COCOA_ENTER(env);
jclass alc = (*env)->FindClass(env, "java/util/ArrayList");
if (alc == NULL) return;
jmethodID addMID = (*env)->GetMethodID(env, alc, "add", "(Ljava/lang/Object;)Z");
if (addMID == NULL) return;
CFIndex i;
AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
NSFont* nsFont = awtFont->fFont;
#ifdef DEBUG
CFStringRef base = CTFontCopyFullName((CTFontRef)nsFont);
NSLog(@"BaseFont is : %@", (NSString*)base);
CFRelease(base);
#endif
bool anotherBaseFont = false;
if (awtFont->fFallbackBase != nil) {
nsFont = awtFont->fFallbackBase;
anotherBaseFont = true;
}
CTFontRef font = (CTFontRef)nsFont;
CFArrayRef codes = CFLocaleCopyISOLanguageCodes();
CFArrayRef fds = CTFontCopyDefaultCascadeListForLanguages(font, codes);
CFRelease(codes);
CFIndex cnt = CFArrayGetCount(fds);
for (i= anotherBaseFont ? -1 : 0; i<cnt; i++) {
CFStringRef fontname;
if (i < 0) {
fontname = CTFontCopyPostScriptName(font);
} else {
CTFontDescriptorRef ref = CFArrayGetValueAtIndex(fds, i);
fontname = CTFontDescriptorCopyAttribute(ref, kCTFontNameAttribute);
}
#ifdef DEBUG
NSLog(@"Font is : %@", (NSString*)fontname);
#endif
jstring jFontName = (jstring)NSStringToJavaString(env, fontname);
CFRelease(fontname);
(*env)->CallBooleanMethod(env, arrayListOfString, addMID, jFontName);
if ((*env)->ExceptionOccurred(env)) {
CFRelease(fds);
return;
}
(*env)->DeleteLocalRef(env, jFontName);
}
CFRelease(fds);
JNI_COCOA_EXIT(env);
}
static CFStringRef EMOJI_FONT_NAME = CFSTR("Apple Color Emoji");
bool IsEmojiFont(CTFontRef font)

View File

@@ -28,7 +28,6 @@
#import "sun_font_CStrikeDisposer.h"
#import "CGGlyphImages.h"
#import "CGGlyphOutlines.h"
#import "CoreTextSupport.h"
#import "JNIUtilities.h"
#include "fontscalerdefs.h"
#import "LWCToolkit.h"
@@ -160,12 +159,8 @@ JNI_COCOA_ENTER(env);
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
AWTFont *awtFont = awtStrike->fAWTFont;
// negative glyph codes are really unicodes, which were placed there by the mapper
// to indicate we should use CoreText to substitute the character
CGGlyph glyph;
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
CGGlyphImages_GetGlyphMetrics(fallback, &awtStrike->fAltTx, awtStrike->fSize, awtStrike->fStyle, &glyph, 1, NULL, &advance, IS_OSX_GT10_14);
CFRelease(fallback);
CGGlyph glyph = glyphCode;
CGGlyphImages_GetGlyphMetrics((CTFontRef)awtFont->fFont, &awtStrike->fAltTx, awtStrike->fSize, awtStrike->fStyle, &glyph, 1, NULL, &advance, IS_OSX_GT10_14);
advance = CGSizeApplyAffineTransform(advance, awtStrike->fFontTx);
if (!JRSFontStyleUsesFractionalMetrics(awtStrike->fStyle)) {
advance.width = round(advance.width);
@@ -195,14 +190,9 @@ JNI_COCOA_ENTER(env);
tx.tx += x;
tx.ty += y;
// negative glyph codes are really unicodes, which were placed there by the mapper
// to indicate we should use CoreText to substitute the character
CGGlyph glyph;
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
CGGlyph glyph = glyphCode;
CGRect bbox;
CGGlyphImages_GetGlyphMetrics(fallback, &tx, awtStrike->fSize, awtStrike->fStyle, &glyph, 1, &bbox, NULL, IS_OSX_GT10_14);
CFRelease(fallback);
CGGlyphImages_GetGlyphMetrics((CTFontRef)awtFont->fFont, &tx, awtStrike->fSize, awtStrike->fStyle, &glyph, 1, &bbox, NULL, IS_OSX_GT10_14);
// the origin of this bounding box is relative to the bottom-left corner baseline
CGFloat decender = -bbox.origin.y;
@@ -251,14 +241,12 @@ AWT_FONT_CLEANUP_CHECK(awtfont);
tx.tx += xPos;
tx.ty += yPos;
// get the right font and glyph for this "Java GlyphCode"
CGGlyph glyph;
const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);
CGGlyph glyph = glyphCode;
NSFont *font = awtfont->fFont;
// get the advance of this glyph
CGSize advance;
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
CTFontGetAdvancesForGlyphs((CTFontRef)font, kCTFontDefaultOrientation, &glyph, &advance, 1);
// Create AWTPath
path = AWTPathCreate(CGSizeMake(xPos, yPos));
@@ -267,8 +255,7 @@ AWT_FONT_CLEANUP_CHECK(path);
// Get the paths
tx = awtStrike->fTx;
tx = CGAffineTransformConcat(tx, sInverseTX);
AWTGetGlyphOutline(&glyph, (NSFont *)font, &advance, &tx, 0, 1, &path);
CFRelease(font);
AWTGetGlyphOutline(&glyph, font, &advance, &tx, 0, 1, &path);
pointCoords = (*env)->NewFloatArray(env, path->fNumberOfDataElements);
AWT_FONT_CLEANUP_CHECK(pointCoords);
@@ -322,19 +309,14 @@ JNIEXPORT void JNICALL Java_sun_font_CStrike_getNativeGlyphOutlineBounds
AWT_FONT_CLEANUP_SETUP;
AWT_FONT_CLEANUP_CHECK(awtfont);
// get the right font and glyph for this "Java GlyphCode"
CGGlyph glyph;
const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(
awtfont, glyphCode, &glyph);
CGGlyph glyph = glyphCode;
CGRect bbox = CTFontGetBoundingRectsForGlyphs(
font, kCTFontOrientationDefault, &glyph, NULL, 1);
(CTFontRef)awtfont->fFont, kCTFontOrientationDefault, &glyph, NULL, 1);
CGAffineTransform tx = CGAffineTransformConcat(awtStrike->fTx,
sInverseTX);
bbox = CGRectApplyAffineTransform (bbox, tx);
CFRelease(font);
jfloat *rawRectData =
(*env)->GetPrimitiveArrayCritical(env, rectData, NULL);

View File

@@ -29,7 +29,6 @@
#import "CoreTextSupport.h"
#import "sun_font_CCharToGlyphMapper.h"
#import "sun_font_CCompositeGlyphMapper.h"
/*
* Class: sun_font_CCharToGlyphMapper
@@ -114,28 +113,3 @@ JNI_COCOA_ENTER(env);
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_font_CCompositeGlyphMapper
* Method: nativeCodePointToGlyph
* Signature: (JI[Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL
Java_sun_font_CCompositeGlyphMapper_nativeCodePointToGlyph
(JNIEnv *env, jclass clazz, jlong awtFontPtr, jint codePoint, jobjectArray resultArray)
{
JNI_COCOA_ENTER(env);
AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
CFStringRef fontNames[] = {NULL, NULL};
CGGlyph glyph = CTS_CopyGlyphAndFontNamesForCodePoint(awtFont, (UnicodeScalarValue)codePoint, fontNames);
if (glyph > 0) {
jstring fontName = NSStringToJavaString(env, (NSString *)fontNames[0]);
(*env)->SetObjectArrayElement(env, resultArray, 0, fontName);
jstring fontFamilyName = NSStringToJavaString(env, (NSString *)fontNames[1]);
(*env)->SetObjectArrayElement(env, resultArray, 1, fontFamilyName);
}
if (fontNames[0]) CFRelease(fontNames[0]);
if (fontNames[1]) CFRelease(fontNames[1]);
return glyph;
JNI_COCOA_EXIT(env);
}

View File

@@ -27,7 +27,6 @@
#import "JNIUtilities.h"
#import "CGGlyphImages.h"
#import "CoreTextSupport.h"
#import "fontscalerdefs.h" // contains the definition of GlyphInfo struct
#import "sun_awt_SunHints.h"
@@ -739,78 +738,6 @@ CGGI_CreateImageForGlyph
/*
* CoreText path...
*/
static inline GlyphInfo *
CGGI_CreateImageForUnicode
(CGGI_GlyphCanvas *canvas, const AWTStrike *strike,
const CGGI_RenderingMode *mode, const UnicodeScalarValue uniChar,
const bool isCatalinaOrAbove)
{
// save the graphics state
CGContextSaveGState(canvas->context);
// text matrix is not considered part of graphics state
CGAffineTransform originalTx = CGContextGetTextMatrix(canvas->context);
// get the glyph, measure it using CG
CGGlyph glyph;
CTFontRef fallback;
if (uniChar > 0xFFFF) {
UTF16Char charRef[2];
CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef);
CGGlyph glyphTmp[2];
fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2);
glyph = glyphTmp[0];
} else {
UTF16Char charRef;
charRef = (UTF16Char) uniChar; // truncate.
fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
}
JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
CGGI_GlyphInfoDescriptor *glyphDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fallback);
bool subpixelResolution = mode->subpixelResolution && glyphDescriptor == &grey;
CGRect bbox;
CGSize advance;
CGGlyphImages_GetGlyphMetrics(fallback, &strike->fTx, strike->fSize, style, &glyph, 1, &bbox, &advance, isCatalinaOrAbove);
// create the Sun2D GlyphInfo we are going to strike into
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, glyphDescriptor, subpixelResolution);
// fix the context size, just in case the substituted character is unexpectedly large
CGGI_SizeCanvas(canvas, info->width * info->subpixelResolutionX, info->height * info->subpixelResolutionY, mode);
// align the transform for the real CoreText strike
CGContextSetTextMatrix(canvas->context, strike->fAltTx);
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
CGContextSetFont(canvas->context, cgFallback);
CFRelease(cgFallback);
// clean the canvas - align, strike, and copy the glyph from the canvas into the info
CGGI_CreateImageForGlyph(canvas, glyph, info, glyphDescriptor, strike, fallback, isCatalinaOrAbove);
// restore graphics state
CGContextRestoreGState(canvas->context);
CGContextSetTextMatrix(canvas->context, originalTx);
CFRelease(fallback);
#ifdef CGGI_DEBUG
DUMP_GLYPHINFO(info);
#endif
#ifdef CGGI_DEBUG_DUMP
DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
#if 0
PRINT_CGSTATES_INFO(NULL);
#endif
#endif
return info;
}
#pragma mark --- GlyphInfo Filling and Canvas Managment ---
@@ -825,7 +752,6 @@ CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,
const AWTStrike *strike,
const CGGI_RenderingMode *mode,
jlong glyphInfos[],
const UnicodeScalarValue uniChars[],
const CGGlyph glyphs[],
const CFIndex len)
{
@@ -838,13 +764,8 @@ CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,
CFIndex i;
for (i = 0; i < len; i++) {
GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);
if (info != NULL) {
CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode->mainFontDescriptor,
strike, (CTFontRef)strike->fAWTFont->fFont, isMojaveOrAbove);
} else {
info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i], isMojaveOrAbove);
glyphInfos[i] = ptr_to_jlong(info);
}
CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode->mainFontDescriptor,
strike, (CTFontRef)strike->fAWTFont->fFont, isMojaveOrAbove);
#ifdef CGGI_DEBUG
DUMP_GLYPHINFO(info);
#endif
@@ -879,7 +800,7 @@ static NSString *threadLocalLCDCanvasKey =
static inline void
CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
const CGGI_RenderingMode *mode,
const UnicodeScalarValue uniChars[], const CGGlyph glyphs[],
const CGGlyph glyphs[],
const size_t maxWidth, const size_t maxHeight,
const CFIndex len)
{
@@ -889,8 +810,7 @@ CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);
CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
mode, glyphInfos, uniChars,
glyphs, len);
mode, glyphInfos, glyphs, len);
CGGI_FreeCanvas(tmpCanvas);
[tmpCanvas release];
@@ -910,7 +830,7 @@ CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);
CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
glyphInfos, uniChars, glyphs, len);
glyphInfos, glyphs, len);
}
/*
@@ -926,7 +846,7 @@ CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
static inline void
CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
const CGGI_RenderingMode *mode,
const UnicodeScalarValue uniChars[], const CGGlyph glyphs[],
const CGGlyph glyphs[],
CGSize advances[], CGRect bboxes[], const CFIndex len)
{
AWTFont *font = strike->fAWTFont;
@@ -941,12 +861,6 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
CFIndex i;
for (i = 0; i < len; i++)
{
if (uniChars[i] != 0)
{
glyphInfos[i] = 0L;
continue; // will be handled later
}
CGSize advance = advances[i];
CGRect bbox = bboxes[i];
@@ -963,41 +877,29 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
glyphInfos[i] = ptr_to_jlong(glyphInfo);
}
CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars,
CGGI_FillImagesForGlyphs(glyphInfos, strike, mode,
glyphs, maxWidth, maxHeight, len);
}
#pragma mark --- Temporary Buffer Allocations and Initialization ---
/*
* This stage separates the already valid glyph codes from the unicode values
* that need special handling - the rawGlyphCodes array is no longer used
* after this stage.
*/
static void
CGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos,
const AWTStrike *strike,
const CGGI_RenderingMode *mode,
jint rawGlyphCodes[],
UnicodeScalarValue uniChars[], CGGlyph glyphs[],
CGSize advances[], CGRect bboxes[],
const CFIndex len)
CGGI_CreateGlyphs(jlong *glyphInfos,
const AWTStrike *strike,
const CGGI_RenderingMode *mode,
jint rawGlyphCodes[],
CGGlyph glyphs[],
CGSize advances[], CGRect bboxes[],
const CFIndex len)
{
CFIndex i;
for (i = 0; i < len; i++) {
jint code = rawGlyphCodes[i];
if (code < 0) {
glyphs[i] = 0;
uniChars[i] = -code;
} else {
glyphs[i] = code;
uniChars[i] = 0;
}
glyphs[i] = rawGlyphCodes[i];
}
CGGI_CreateGlyphInfos(glyphInfos, strike, mode,
uniChars, glyphs, advances, bboxes, len);
glyphs, advances, bboxes, len);
#ifdef CGGI_DEBUG_HIT_COUNT
static size_t hitCount = 0;
@@ -1023,31 +925,29 @@ CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
CGRect bboxes[len];
CGSize advances[len];
CGGlyph glyphs[len];
UnicodeScalarValue uniChars[len];
CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
rawGlyphCodes, uniChars, glyphs,
advances, bboxes, len);
CGGI_CreateGlyphs(glyphInfos, strike, &mode,
rawGlyphCodes, glyphs,
advances, bboxes, len);
return;
}
// just do one malloc, and carve it up for all the buffers
void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) *
sizeof(CGGlyph) * sizeof(UnicodeScalarValue) * len);
void *buffer = malloc((sizeof(CGRect) + sizeof(CGSize) + sizeof(CGGlyph)) *
len);
if (buffer == NULL) {
[[NSException exceptionWithName:NSMallocException
reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise];
}
CGRect *bboxes = (CGRect *)(buffer);
CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len);
CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len);
UnicodeScalarValue *uniChars = (UnicodeScalarValue *)(glyphs + sizeof(UnicodeScalarValue) * len);
CGSize *advances = (CGSize *)(bboxes + len);
CGGlyph *glyphs = (CGGlyph *)(advances + len);
CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
rawGlyphCodes, uniChars, glyphs,
advances, bboxes, len);
CGGI_CreateGlyphs(glyphInfos, strike, &mode,
rawGlyphCodes, glyphs,
advances, bboxes, len);
free(buffer);
}

View File

@@ -31,37 +31,13 @@
#pragma mark --- CoreText Support ---
#define HI_SURROGATE_START 0xD800
#define HI_SURROGATE_END 0xDBFF
#define LO_SURROGATE_START 0xDC00
#define LO_SURROGATE_END 0xDFFF
/*
* Transform Unicode characters into glyphs.
*
* Fills the "glyphsAsInts" array with the glyph codes for the current font,
* or the negative unicode value if we know the character can be hot-substituted.
*
* This is the heart of "Universal Font Substitution" in Java.
* Fills the "glyphsAsInts" array with the glyph codes for the current font.
*/
void CTS_GetGlyphsAsIntsForCharacters(const AWTFont *font, const UniChar unicodes[], CGGlyph glyphs[], jint glyphsAsInts[], const size_t count);
// Translates a Java glyph code int (might be a negative unicode value) into a CGGlyph/CTFontRef pair
// Returns the substituted font, and places the appropriate glyph into "glyph"
CTFontRef CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(const AWTFont *font, const jint glyphCode, CGGlyph *glyphRef);
// Translates a Unicode into a CGGlyph/CTFontRef pair
// Returns the substituted font, and places the appropriate glyph into "glyphRef"
CTFontRef CTS_CopyCTFallbackFontAndGlyphForUnicode(const AWTFont *font, const UTF16Char *charRef, CGGlyph *glyphRef, int count);
// Transform a single Unicode character code into glyph code.
// Names of the relevant font are also returned, if the substitution is used.
// Non-null components of fontNames array should always be released by the calling code, regardless of the returned value.
CGGlyph CTS_CopyGlyphAndFontNamesForCodePoint(const AWTFont *font, const UnicodeScalarValue codePoint, CFStringRef fontNames[]);
// Breakup a 32 bit unicode value into the component surrogate pairs
void CTS_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]);
// Basic struct that holds everything CoreText is interested in
typedef struct CTS_ProviderStruct {

View File

@@ -88,161 +88,18 @@ ReleaseCTStateDictionary(CFDictionaryRef ctStateDict)
CFRelease(ctStateDict); // GC
}
void GetFontsAndGlyphsForCharacters(CTFontRef font, CTFontRef fallbackBase,
const UniChar unicodes[], CGGlyph glyphs[], jint glyphsAsInts[],
CTFontRef actualFonts[], const size_t count)
{
CTFontGetGlyphsForCharacters(font, unicodes, glyphs, count);
if (!fallbackBase) fallbackBase = font;
size_t i;
for (i = 0; i < count; i++) {
UniChar unicode = unicodes[i];
UniChar nextUnicode = (i+1) < count ? unicodes[i+1] : 0;
bool surrogatePair = unicode >= HI_SURROGATE_START && unicode <= HI_SURROGATE_END
&& nextUnicode >= LO_SURROGATE_START && nextUnicode <= LO_SURROGATE_END;
CGGlyph glyph = glyphs[i];
if (glyph > 0) {
glyphsAsInts[i] = glyph;
if (surrogatePair) i++;
continue;
}
const CTFontRef fallback = JRSFontCreateFallbackFontForCharacters(fallbackBase, &unicodes[i], surrogatePair ? 2 : 1);
if (fallback) {
CTFontGetGlyphsForCharacters(fallback, &unicodes[i], &glyphs[i], surrogatePair ? 2 : 1);
glyph = glyphs[i];
if (actualFonts && glyph > 0) {
actualFonts[i] = fallback;
} else {
CFRelease(fallback);
}
}
if (glyph > 0) {
int codePoint = surrogatePair ? (((int)(unicode - HI_SURROGATE_START)) << 10)
+ nextUnicode - LO_SURROGATE_START + 0x10000 : unicode;
glyphsAsInts[i] = -codePoint; // set the glyph code to the negative unicode value
} else {
glyphsAsInts[i] = 0; // CoreText couldn't find a glyph for this character either
}
if (surrogatePair) i++;
}
}
/*
* Transform Unicode characters into glyphs.
*
* Fills the "glyphsAsInts" array with the glyph codes for the current font,
* or the negative unicode value if we know the character can be hot-substituted.
*
* This is the heart of "Universal Font Substitution" in Java.
* Fills the "glyphsAsInts" array with the glyph codes for the current font.
*/
void CTS_GetGlyphsAsIntsForCharacters
(const AWTFont *font, const UniChar unicodes[], CGGlyph glyphs[], jint glyphsAsInts[], const size_t count)
{
GetFontsAndGlyphsForCharacters((CTFontRef)font->fFont, (CTFontRef)font->fFallbackBase,
unicodes, glyphs, glyphsAsInts, NULL, count);
}
CTFontGetGlyphsForCharacters((CTFontRef)font->fFont, unicodes, glyphs, count);
/*
* Returns glyph code for a given Unicode character.
* Names of the corresponding substituted font are also returned if substitution is performed.
*/
CGGlyph CTS_CopyGlyphAndFontNamesForCodePoint
(const AWTFont *font, const UnicodeScalarValue codePoint, CFStringRef fontNames[])
{
CTFontRef fontRef = (CTFontRef)font->fFont;
CTFontRef fallbackBase = (CTFontRef)font->fFallbackBase;
int count = codePoint >= 0x10000 ? 2 : 1;
UTF16Char unicodes[count];
if (count == 1) {
unicodes[0] = (UTF16Char)codePoint;
} else {
CTS_BreakupUnicodeIntoSurrogatePairs(codePoint, unicodes);
}
CGGlyph glyphs[count];
jint glyphsAsInts[count];
CTFontRef actualFonts[count];
GetFontsAndGlyphsForCharacters(fontRef, fallbackBase, unicodes, glyphs, glyphsAsInts, actualFonts, count);
CGGlyph glyph = glyphs[0];
bool substitutionHappened = glyphsAsInts[0] < 0;
if (glyph > 0 && substitutionHappened) {
CTFontRef actualFont = actualFonts[0];
CFStringRef fontName = CTFontCopyPostScriptName(actualFont);
CFStringRef familyName = CTFontCopyFamilyName(actualFont);
CFRelease(actualFont);
fontNames[0] = fontName;
fontNames[1] = familyName;
if (!fontName || !familyName) glyph = 0;
}
return glyph;
}
/*
* Translates a Unicode into a CGGlyph/CTFontRef pair
* Returns the substituted font, and places the appropriate glyph into "glyphRef"
*/
CTFontRef CTS_CopyCTFallbackFontAndGlyphForUnicode
(const AWTFont *font, const UTF16Char *charRef, CGGlyph *glyphRef, int count) {
CTFontRef primary = (CTFontRef)font->fFont;
CTFontRef fallbackBase = (CTFontRef)font->fFallbackBase;
if (fallbackBase) {
CTFontGetGlyphsForCharacters(primary, charRef, glyphRef, count);
if (glyphRef[0] > 0) {
CFRetain(primary);
return primary;
}
} else {
fallbackBase = primary;
}
CTFontRef fallback = JRSFontCreateFallbackFontForCharacters(fallbackBase, charRef, count);
if (fallback == NULL)
{
// use the original font if we somehow got duped into trying to fallback something we can't
fallback = (CTFontRef)font->fFont;
CFRetain(fallback);
}
CTFontGetGlyphsForCharacters(fallback, charRef, glyphRef, count);
return fallback;
}
/*
* Translates a Java glyph code int (might be a negative unicode value) into a CGGlyph/CTFontRef pair
* Returns the substituted font, and places the appropriate glyph into "glyphRef"
*/
CTFontRef CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode
(const AWTFont *font, const jint glyphCode, CGGlyph *glyphRef)
{
// negative glyph codes are really unicodes, which were placed there by the mapper
// to indicate we should use CoreText to substitute the character
if (glyphCode >= 0)
{
*glyphRef = glyphCode;
CFRetain(font->fFont);
return (CTFontRef)font->fFont;
}
int codePoint = -glyphCode;
if (codePoint >= 0x10000) {
UTF16Char chars[2];
CGGlyph glyphs[2];
CTS_BreakupUnicodeIntoSurrogatePairs(codePoint, chars);
CTFontRef result = CTS_CopyCTFallbackFontAndGlyphForUnicode(font, chars, glyphs, 2);
*glyphRef = glyphs[0];
return result;
} else {
UTF16Char character = codePoint;
return CTS_CopyCTFallbackFontAndGlyphForUnicode(font, &character, glyphRef, 1);
size_t i;
for (i = 0; i < count; i++) {
glyphsAsInts[i] = glyphs[i];
}
}
// Breakup a 32 bit unicode value into the component surrogate pairs
void CTS_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]) {
int value = uniChar - 0x10000;
UTF16Char low_surrogate = (value & 0x3FF) | LO_SURROGATE_START;
UTF16Char high_surrogate = (((int)(value & 0xFFC00)) >> 10) | HI_SURROGATE_START;
charRef[0] = high_surrogate;
charRef[1] = low_surrogate;
}

View File

@@ -67,6 +67,7 @@ import sun.font.FontDesignMetrics;
import sun.font.FontLineMetrics;
import sun.font.FontManager;
import sun.font.FontManagerFactory;
import sun.font.FontSubstitution;
import sun.font.FontUtilities;
import sun.font.GlyphLayout;
import sun.font.StandardGlyphVector;
@@ -266,6 +267,11 @@ public class Font implements java.io.Serializable
return font.getFont2D();
}
@Override
public Font2D getFont2DWithSubstitution(Font font) {
return font.getFont2DWithSubstitution();
}
public void setFont2D(Font font, Font2DHandle handle) {
font.font2DHandle = handle;
}
@@ -537,6 +543,11 @@ public class Font implements java.io.Serializable
return font2DHandle.font2D;
}
private Font2D getFont2DWithSubstitution() {
Font2D font2D = getFont2D();
return font2D instanceof FontSubstitution ? ((FontSubstitution) font2D).getCompositeFont2D() : font2D;
}
/**
* Creates a new {@code Font} from the specified name, style and
* point size.
@@ -2223,7 +2234,7 @@ public class Font implements java.io.Serializable
* @since 1.2
*/
public boolean canDisplay(char c){
return getFont2D().canDisplay(c);
return getFont2DWithSubstitution().canDisplay(c);
}
/**
@@ -2244,7 +2255,7 @@ public class Font implements java.io.Serializable
throw new IllegalArgumentException("invalid code point: " +
Integer.toHexString(codePoint));
}
return getFont2D().canDisplay(codePoint);
return getFont2DWithSubstitution().canDisplay(codePoint);
}
/**
@@ -2265,7 +2276,7 @@ public class Font implements java.io.Serializable
* @since 1.2
*/
public int canDisplayUpTo(String str) {
Font2D font2d = getFont2D();
Font2D font2d = getFont2DWithSubstitution();
int len = str.length();
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
@@ -2303,7 +2314,7 @@ public class Font implements java.io.Serializable
* @since 1.2
*/
public int canDisplayUpTo(char[] text, int start, int limit) {
Font2D font2d = getFont2D();
Font2D font2d = getFont2DWithSubstitution();
for (int i = start; i < limit; i++) {
char c = text[i];
if (font2d.canDisplay(c)) {
@@ -2338,7 +2349,7 @@ public class Font implements java.io.Serializable
* @since 1.2
*/
public int canDisplayUpTo(CharacterIterator iter, int start, int limit) {
Font2D font2d = getFont2D();
Font2D font2d = getFont2DWithSubstitution();
char c = iter.setIndex(start);
for (int i = start; i < limit; i++, c = iter.next()) {
if (font2d.canDisplay(c)) {

View File

@@ -38,7 +38,7 @@ import java.awt.Font;
* But its probably OK to include it so long as only composites include
* fallbacks. If physicals do then it would be really confusing ..
*/
public class CompositeFont extends Font2D {
public final class CompositeFont extends Font2D {
private boolean[] deferredInitialisation;
String[] componentFileNames;

View File

@@ -108,7 +108,7 @@ public class CompositeGlyphMapper extends CharToGlyphMapper {
return mapper;
}
protected int convertToGlyph(int unicode) {
private int convertToGlyph(int unicode) {
for (int slot = 0; slot < font.numSlots; slot++) {
if (!hasExcludes || !font.isExcludedChar(slot, unicode)) {

View File

@@ -335,7 +335,7 @@ public abstract class Font2D {
return getStrike(desc, true);
}
FontStrike getStrike(FontStrikeDesc desc, boolean copy) {
private FontStrike getStrike(FontStrikeDesc desc, boolean copy) {
/* Before looking in the map, see if the descriptor matches the
* last strike returned from this Font2D. This should often be a win
* since its common for the same font, in the same size to be

View File

@@ -43,6 +43,7 @@ public abstract class FontAccess {
}
public abstract Font2D getFont2D(Font f);
public abstract Font2D getFont2DWithSubstitution(Font f);
public abstract void setFont2D(Font f, Font2DHandle h);
public abstract void setCreatedFont(Font f);
public abstract boolean isCreatedFont(Font f);

View File

@@ -356,7 +356,7 @@ public final class FontDesignMetrics extends FontMetrics {
private void initMatrixAndMetrics() {
Font2D font2D = FontUtilities.getFont2D(font);
Font2D font2D = FontUtilities.getFont2DWithSubstitution(font);
fontStrike = font2D.getStrike(font, frc);
StrikeMetrics metrics = fontStrike.getFontMetrics();
this.ascent = metrics.getAscent();

View File

@@ -266,12 +266,12 @@ public class FontFamily {
doSetFont(fontAndStyle.font, fontAndStyle.style);
}
if (italic == null && plain instanceof FontWithDerivedItalic) {
italic = ((FontWithDerivedItalic)plain).createItalic();
italic = ((FontWithDerivedItalic)plain).createItalicVariant();
}
if (bolditalic == null) {
Font2D boldItalicPrototype = bold != null ? bold : plain;
if (boldItalicPrototype instanceof FontWithDerivedItalic) {
bolditalic = ((FontWithDerivedItalic)boldItalicPrototype).createItalic();
bolditalic = ((FontWithDerivedItalic)boldItalicPrototype).createItalicVariant();
}
}
fontSequence.clear();

View File

@@ -168,6 +168,10 @@ public final class FontUtilities {
return FontAccess.getFontAccess().getFont2D(font);
}
public static Font2D getFont2DWithSubstitution(Font font) {
return FontAccess.getFontAccess().getFont2DWithSubstitution(font);
}
/**
* Return true if there any characters which would trigger layout.
* This method considers supplementary characters to be simple,

View File

@@ -1,5 +1,5 @@
package sun.font;
interface FontWithDerivedItalic {
Font2D createItalic();
Font2D createItalicVariant();
}

View File

@@ -410,10 +410,7 @@ public final class GlyphLayout {
int lang = -1; // default for now
Font2D font2D = FontUtilities.getFont2D(font);
if (font2D instanceof FontSubstitution) {
font2D = ((FontSubstitution)font2D).getCompositeFont2D();
}
Font2D font2D = FontUtilities.getFont2DWithSubstitution(font);
_textRecord.init(text, offset, lim, min, max);
int start = offset;

View File

@@ -197,8 +197,7 @@ public class StandardGlyphVector extends GlyphVector {
// how do we know its a base glyph
// for now, it is if the natural advance of the glyph is non-zero
Font2D f2d = FontUtilities.getFont2D(font);
FontStrike strike = f2d.getStrike(font, frc);
FontStrike strike = font2D.getStrike(font, frc);
float[] deltas = { trackPt.x, trackPt.y };
for (int j = 0; j < deltas.length; ++j) {
@@ -1106,10 +1105,7 @@ public class StandardGlyphVector extends GlyphVector {
}
private void initFontData() {
font2D = FontUtilities.getFont2D(font);
if (font2D instanceof FontSubstitution) {
font2D = ((FontSubstitution)font2D).getCompositeFont2D();
}
font2D = FontUtilities.getFont2DWithSubstitution(font);
float s = font.getSize2D();
if (font.isTransformed()) {
ftx = font.getTransform();
@@ -1728,12 +1724,7 @@ public class StandardGlyphVector extends GlyphVector {
aa, fm);
// Get the strike via the handle. Shouldn't matter
// if we've invalidated the font but its an extra precaution.
// do we want the CompFont from CFont here ?
Font2D f2d = sgv.font2D;
if (f2d instanceof FontSubstitution) {
f2d = ((FontSubstitution)f2d).getCompositeFont2D();
}
FontStrike strike = f2d.handle.font2D.getStrike(desc); // !!! getStrike(desc, false)
FontStrike strike = sgv.font2D.handle.font2D.getStrike(desc); // !!! getStrike(desc, false)
return new GlyphStrike(sgv, strike, dx, dy);
}

View File

@@ -670,7 +670,7 @@ public final class SunGraphics2D
info.nonInvertibleTx =
(Math.abs(textAt.getDeterminant()) <= Double.MIN_VALUE);
info.font2D = FontUtilities.getFont2D(font);
info.font2D = FontUtilities.getFont2DWithSubstitution(font);
int fmhint = fractionalMetricsHint;
if (fmhint == SunHints.INTVAL_FRACTIONALMETRICS_DEFAULT) {

View File

@@ -698,7 +698,7 @@ public abstract class PathGraphics extends ProxyGraphics2D {
}
Font font = g.getFont();
Font2D font2D = FontUtilities.getFont2D(font);
Font2D font2D = FontUtilities.getFont2DWithSubstitution(font);
if (font2D.handle.font2D != font2D) {
/* suspicious, may be a bad font. lets bail */
return false;