--- old/src/java.desktop/macosx/classes/sun/font/CFont.java 2015-11-13 14:23:38.000000000 -0800 +++ new/src/java.desktop/macosx/classes/sun/font/CFont.java 2015-11-13 14:23:38.000000000 -0800 @@ -31,12 +31,13 @@ import java.awt.geom.GeneralPath;; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.util.ArrayList; // Right now this class is final to avoid a problem with native code. // For some reason the JNI IsInstanceOf was not working correctly // so we are checking the class specifically. If we subclass this // we need to modify the native code in CFontWrapper.m -public final class CFont extends PhysicalFont { +public final class CFont extends PhysicalFont implements FontSubstitution { /* CFontStrike doesn't call these methods so they are unimplemented. * They are here to meet the requirements of PhysicalFont, needed @@ -76,6 +77,20 @@ throw new InternalError("Not implemented"); } + @Override + protected long getLayoutTableCache() { + return getLayoutTableCacheNative(getNativeFontPtr()); + } + + @Override + protected byte[] getTableBytes(int tag) { + return getTableBytesNative(getNativeFontPtr(), tag); + } + + private native synchronized long getLayoutTableCacheNative(long nativeFontPtr); + + private native byte[] getTableBytesNative(long nativeFontPtr, int tag); + private static native long createNativeFont(final String nativeFontName, final int style); private static native void disposeNativeFont(final long nativeFontPtr); @@ -179,10 +194,51 @@ protected synchronized long getNativeFontPtr() { if (nativeFontPtr == 0L) { nativeFontPtr = createNativeFont(nativeFontName, style); -} + } return nativeFontPtr; } + static native void getCascadeList(long nativeFontPtr, ArrayList listOfString); + + private CompositeFont createCompositeFont() { + ArrayList listOfString = new ArrayList(); + getCascadeList(nativeFontPtr, listOfString); + + FontManager fm = FontManagerFactory.getInstance(); + int numFonts = 1 + listOfString.size(); + PhysicalFont[] fonts = new PhysicalFont[numFonts]; + fonts[0] = this; + int idx = 1; + for (String s : listOfString) { + if (s.equals(".AppleSymbolsFB")) { + // Don't know why we get the weird name above .. replace. + s = "AppleSymbols"; + } + Font2D f2d = fm.findFont2D(s, Font.PLAIN, FontManager.NO_FALLBACK); + 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); + } + CompositeFont compFont = new CompositeFont(fonts); + compFont.mapper = new CCompositeGlyphMapper(compFont); + return compFont; + } + + private CompositeFont compFont; + + public CompositeFont getCompositeFont2D() { + if (compFont == null) { + compFont = createCompositeFont(); + } + return compFont; + } + protected synchronized void finalize() { if (nativeFontPtr != 0) { disposeNativeFont(nativeFontPtr); --- old/src/java.desktop/macosx/classes/sun/font/CStrike.java 2015-11-13 14:23:39.000000000 -0800 +++ new/src/java.desktop/macosx/classes/sun/font/CStrike.java 2015-11-13 14:23:39.000000000 -0800 @@ -31,7 +31,7 @@ import sun.awt.SunHints; -public final class CStrike extends FontStrike { +public final class CStrike extends PhysicalStrike { // Creates the native strike private static native long createNativeStrikePtr(long nativeFontPtr, --- old/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h 2015-11-13 14:23:39.000000000 -0800 +++ new/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h 2015-11-13 14:23:39.000000000 -0800 @@ -26,6 +26,8 @@ #import #import +#import "fontscalerdefs.h" + #define MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE 256 @interface AWTFont : NSObject { @@ -33,6 +35,7 @@ NSFont *fFont; CGFontRef fNativeCGFont; BOOL fIsFakeItalic; + TTLayoutTableCache* layoutTableCache; } + (AWTFont *) awtFontForName:(NSString *)name --- old/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m 2015-11-13 14:23:40.000000000 -0800 +++ new/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m 2015-11-13 14:23:39.000000000 -0800 @@ -42,10 +42,33 @@ if (self) { fFont = [font retain]; fNativeCGFont = CTFontCopyGraphicsFont((CTFontRef)font, NULL); + layoutTableCache = NULL; } return self; } +static TTLayoutTableCache* newCFontLayoutTableCache() { + TTLayoutTableCache* ltc = calloc(1, sizeof(TTLayoutTableCache)); + if (ltc) { + int i; + for(i=0;ientries[i].len = -1; + } + } + return ltc; +} + +static void freeCFontLayoutTableCache(TTLayoutTableCache* ltc) { + if (ltc) { + int i; + for(i=0;ientries[i].ptr) free (ltc->entries[i].ptr); + } + if (ltc->kernPairs) free(ltc->kernPairs); + free(ltc); + } +} + - (void) dealloc { [fFont release]; fFont = nil; @@ -53,6 +76,10 @@ if (fNativeCGFont) { CGFontRelease(fNativeCGFont); fNativeCGFont = NULL; + if (layoutTableCache != NULL) { + freeCFontLayoutTableCache(layoutTableCache); + layoutTableCache = NULL; + } } [super dealloc]; @@ -63,6 +90,10 @@ CGFontRelease(fNativeCGFont); fNativeCGFont = NULL; } + if (layoutTableCache != NULL) { + freeCFontLayoutTableCache(layoutTableCache); + layoutTableCache = NULL; + } [super finalize]; } @@ -345,6 +376,95 @@ /* * Class: sun_font_CFont + * Method: getPlatformFontPtrNative + * Signature: (JI)[B + */ +JNIEXPORT jlong JNICALL +Java_sun_font_CFont_getCGFontPtrNative + (JNIEnv *env, jclass clazz, + jlong awtFontPtr) +{ + AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr); + return (jlong)(awtFont->fNativeCGFont); +} + +/* + * Class: sun_font_CFont + * Method: getLayoutTableCacheNative + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL +Java_sun_font_CFont_getLayoutTableCacheNative + (JNIEnv *env, jclass clazz, + jlong awtFontPtr) +{ + AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr); + if (awtFont->layoutTableCache == NULL) { + awtFont->layoutTableCache = newCFontLayoutTableCache(); + } + return (jlong)(awtFont->layoutTableCache); +} + +/* + * Class: sun_font_CFont + * Method: getTableBytesNative + * Signature: (JI)[B + */ +JNIEXPORT jbyteArray JNICALL +Java_sun_font_CFont_getTableBytesNative + (JNIEnv *env, jclass clazz, + jlong awtFontPtr, jint jtag) +{ + jbyteArray jbytes = NULL; +JNF_COCOA_ENTER(env); + + CTFontTableTag tag = (CTFontTableTag)jtag; + int i, found = 0; + AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr); + NSFont* nsFont = awtFont->fFont; + CTFontRef ctfont = (CTFontRef)nsFont; + CFArrayRef tagsArray = + CTFontCopyAvailableTables(ctfont, kCTFontTableOptionNoOptions); + CFIndex numTags = CFArrayGetCount(tagsArray); + for (i=0; iNewByteArray(env, (jsize)tableLength); + if (jbytes == NULL) { + return NULL; + } + (*env)->SetByteArrayRegion(env, jbytes, 0, + (jsize)tableLength, + (jbyte*)tableBytes); + CFRelease(table); + +JNF_COCOA_EXIT(env); + + return jbytes; +} + +/* + * Class: sun_font_CFont * Method: initNativeFont * Signature: (Ljava/lang/String;I)J */ @@ -460,3 +580,42 @@ { } #endif + +/* + * Class: sun_awt_FontDescriptor + * Method: initIDs + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_sun_font_CFont_getCascadeList + (JNIEnv *env, jclass cls, jlong awtFontPtr, jobject arrayListOfString) +{ + 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; + CTFontRef font = (CTFontRef)nsFont; + CFStringRef base = CTFontCopyFullName(font); + CFArrayRef codes = CFLocaleCopyISOLanguageCodes(); + +#ifdef DEBUG + NSLog(@"BaseFont is : %@", (NSString*)base); +#endif + CFArrayRef fds = CTFontCopyDefaultCascadeListForLanguages(font, codes); + CFIndex cnt = CFArrayGetCount(fds); + for (i=0; iCallBooleanMethod(env, arrayListOfString, addMID, jFontName); + (*env)->DeleteLocalRef(env, jFontName); + } +} --- old/src/java.desktop/share/classes/sun/font/CompositeFont.java 2015-11-13 14:23:40.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/font/CompositeFont.java 2015-11-13 14:23:40.000000000 -0800 @@ -149,6 +149,25 @@ } } + /* + * Build a composite from a set of individual slot fonts. + */ + CompositeFont(PhysicalFont[] slotFonts) { + + isStdComposite = false; + handle = new Font2DHandle(this); + fullName = slotFonts[0].fullName; + familyName = slotFonts[0].familyName; + style = slotFonts[0].style; + + numMetricsSlots = 1; /* Only the physical Font */ + numSlots = slotFonts.length; + + components = new PhysicalFont[numSlots]; + System.arraycopy(slotFonts, 0, components, 0, numSlots); + deferredInitialisation = new boolean[numSlots]; // all false. + } + /* This method is currently intended to be called only from * FontManager.getCompositeFontUIResource(Font) * It creates a new CompositeFont with the contents of the Physical --- old/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java 2015-11-13 14:23:41.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java 2015-11-13 14:23:41.000000000 -0800 @@ -42,7 +42,7 @@ * this appears to cause problems. */ -public final class CompositeGlyphMapper extends CharToGlyphMapper { +public class CompositeGlyphMapper extends CharToGlyphMapper { public static final int SLOTMASK = 0xff000000; public static final int GLYPHMASK = 0x00ffffff; --- old/src/java.desktop/share/classes/sun/font/Font2D.java 2015-11-13 14:23:41.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/font/Font2D.java 2015-11-13 14:23:41.000000000 -0800 @@ -461,10 +461,17 @@ * to check the font class before attempting to run, rather than needing * to promote this method up from TrueTypeFont */ - byte[] getTableBytes(int tag) { + protected byte[] getTableBytes(int tag) { return null; } + /* implemented for fonts backed by an sfnt that has + * OpenType or AAT layout tables. + */ + protected long getLayoutTableCache() { + return 0L; + } + /* for layout code */ protected long getUnitsPerEm() { return 2048; --- old/src/java.desktop/share/classes/sun/font/GlyphLayout.java 2015-11-13 14:23:42.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/font/GlyphLayout.java 2015-11-13 14:23:42.000000000 -0800 @@ -408,6 +408,9 @@ int lang = -1; // default for now Font2D font2D = FontUtilities.getFont2D(font); + if (font2D instanceof FontSubstitution) { + font2D = ((FontSubstitution)font2D).getCompositeFont2D(); + } _textRecord.init(text, offset, lim, min, max); int start = offset; --- old/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java 2015-11-13 14:23:42.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java 2015-11-13 14:23:42.000000000 -0800 @@ -1124,6 +1124,9 @@ private void initFontData() { font2D = FontUtilities.getFont2D(font); + if (font2D instanceof FontSubstitution) { + font2D = ((FontSubstitution)font2D).getCompositeFont2D(); + } float s = font.getSize2D(); if (font.isTransformed()) { ftx = font.getTransform(); @@ -1742,7 +1745,12 @@ aa, fm); // Get the strike via the handle. Shouldn't matter // if we've invalidated the font but its an extra precaution. - FontStrike strike = sgv.font2D.handle.font2D.getStrike(desc); // !!! getStrike(desc, false) + // 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) return new GlyphStrike(sgv, strike, dx, dy); } --- old/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java 2015-11-13 14:23:43.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/font/SunLayoutEngine.java 2015-11-13 14:23:43.000000000 -0800 @@ -155,10 +155,7 @@ Point2D.Float pt, GVData data) { Font2D font = key.font(); FontStrike strike = font.getStrike(desc); - long layoutTables = 0; - if (font instanceof TrueTypeFont) { - layoutTables = ((TrueTypeFont) font).getLayoutTableCache(); - } + long layoutTables = font.getLayoutTableCache(); nativeLayout(font, strike, mat, gmask, baseIndex, tr.text, tr.start, tr.limit, tr.min, tr.max, key.script(), key.lang(), typo_flags, pt, data, --- old/src/java.desktop/share/classes/sun/font/TrueTypeFont.java 2015-11-13 14:23:43.000000000 -0800 +++ new/src/java.desktop/share/classes/sun/font/TrueTypeFont.java 2015-11-13 14:23:43.000000000 -0800 @@ -874,8 +874,8 @@ } } - /* NB: is it better to move declaration to Font2D? */ - long getLayoutTableCache() { + @Override + protected long getLayoutTableCache() { try { return getScaler().getLayoutTableCache(); } catch(FontScalerException fe) { @@ -884,7 +884,7 @@ } @Override - byte[] getTableBytes(int tag) { + protected byte[] getTableBytes(int tag) { ByteBuffer buffer = getTableBuffer(tag); if (buffer == null) { return null; --- /dev/null 2015-11-13 14:23:44.000000000 -0800 +++ new/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java 2015-11-13 14:23:44.000000000 -0800 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2015, 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; + +public final class CCompositeGlyphMapper extends CompositeGlyphMapper { + + private CompositeFont font; + private CharToGlyphMapper slotMappers[]; + + public CCompositeGlyphMapper(CompositeFont compFont) { + super(compFont); + font = compFont; + slotMappers = new CharToGlyphMapper[font.numSlots]; + missingGlyph = 0; + } + + private CharToGlyphMapper getSlotMapper(int slot) { + CharToGlyphMapper mapper = slotMappers[slot]; + if (mapper == null) { + mapper = font.getSlotFont(slot).getMapper(); + slotMappers[slot] = mapper; + } + return mapper; + } + + public boolean canDisplay(char ch) { + int glyph = charToGlyph(ch); + return glyph != missingGlyph; + } + + private int convertToGlyph(int unicode) { + for (int slot = 0; slot < font.numSlots; slot++) { + CharToGlyphMapper mapper = getSlotMapper(slot); + int glyphCode = mapper.charToGlyph(unicode); + // The CFont Mappers will return a negative code + // for fonts that will fill the glyph from fallbacks + // - cascading font in OSX-speak. But we need to be + // know here that only the codes > 0 are really present. + if (glyphCode > 0) { + glyphCode = compositeGlyphCode(slot, glyphCode); + return glyphCode; + } + } + return missingGlyph; + } + + public int getNumGlyphs() { + int numGlyphs = 0; + for (int slot=0; slot<1 /*font.numSlots*/; slot++) { + CharToGlyphMapper mapper = slotMappers[slot]; + if (mapper == null) { + mapper = font.getSlotFont(slot).getMapper(); + slotMappers[slot] = mapper; + } + numGlyphs += mapper.getNumGlyphs(); + } + return numGlyphs; + } + + public int charToGlyph(int unicode) { + return convertToGlyph(unicode); + } + + public int charToGlyph(char unicode) { + return convertToGlyph(unicode); + } + + public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { + + for (int i=0; i= HI_SURROGATE_START && + code <= HI_SURROGATE_END && i < count - 1) { + char low = unicodes[i + 1]; + + if (low >= LO_SURROGATE_START && + low <= LO_SURROGATE_END) { + code = (code - HI_SURROGATE_START) * + 0x400 + low - LO_SURROGATE_START + 0x10000; + glyphs[i + 1] = INVISIBLE_GLYPH_ID; + } + } + + glyphs[i] = convertToGlyph(code); + + if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { + continue; + } + else if (FontUtilities.isComplexCharCode(code)) { + return true; + } + else if (code >= 0x10000) { + i += 1; // Empty glyph slot after surrogate + continue; + } + } + + return false; + } + + public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { + for (int i=0; i= HI_SURROGATE_START && + code <= HI_SURROGATE_END && i < count - 1) { + char low = unicodes[i + 1]; + + if (low >= LO_SURROGATE_START && + low <= LO_SURROGATE_END) { + code = (code - HI_SURROGATE_START) * + 0x400 + low - LO_SURROGATE_START + 0x10000; + + glyphs[i] = convertToGlyph(code); + i += 1; // Empty glyph slot after surrogate + glyphs[i] = INVISIBLE_GLYPH_ID; + continue; + } + } + + glyphs[i] = convertToGlyph(code); + } + } + + public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { + for (int i=0; i attributes = new HashMap<>(); + attributes.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON); + Font ligFont = noLigFont.deriveFont(attributes); + bi2Graphics.setFont(ligFont); + bi2Graphics.drawString(ligStr, 10, 40); + + boolean same = true; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + int c1 = bi1.getRGB(x, y); + int c2 = bi2.getRGB(x, y); + same &= (c1 == c2); + } + if (!same) { + break; + } + } + if (same) { + throw new RuntimeException("Images do not differ - no ligature"); + } + } +}