JDK-8187100 [PATCH][JavaFX] To make display Variation Selector(IVS/SVS/FVS)
Phil Race
philip.race at oracle.com
Wed Jun 13 18:17:39 UTC 2018
Hi,
As I said in response to your email on the 2D list, you need to sign the OCA
before we can consider any of this.
-phil.
On 06/13/2018 12:40 AM, Nakajima Akira wrote:
> I happened to create similar patch without knowing the report below.
> https://bugs.openjdk.java.net/browse/JDK-8187100
>
> ======================================================
> Difference with following SWING patch and my JavaFX patch.
> http://cr.openjdk.java.net/~srl/8187100/webrev.00/
>
> 1. For Acceleration and Memory saving, load only partial glyphs table.
> javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
>
> + if (numMappings[i] > 0 && (uniStart[i] == null ||
> glyphID[i] == null)) {
> + try {
> + initNonDef(i);
>
>
> 2. Mongolian support
> javafx.graphics/src/main/java/com/sun/javafx/text/ScriptMapper.java
>
> + else if (code <= 0x18af) { // 1800 - 18AF Mongolian
> (including FVS)
> + return true;
> + }
>
>
> 3. Fix 2 bugs on Windows (See below bugs)
>
>
> ======================================================
> This JavaFX patch fixes following 4 bugs.
>
> 1. To make display IVS/SVS
> Sample is kami.java and kami2.java
>
> javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
> javafx.graphics/src/main/java/com/sun/javafx/font/CharToGlyphMapper.java
> javafx.graphics/src/main/java/com/sun/javafx/font/CompositeGlyphMapper.java
>
> javafx.graphics/src/main/java/com/sun/javafx/font/OpenTypeGlyphMapper.java
>
>
>
>
> 2. To make dislpay Mongolian and FVS
> Sample is mongol.java
>
> javafx.graphics/src/main/java/com/sun/javafx/text/ScriptMapper.java
>
>
>
> 3. Fix bug to handle minus value glyph_id on Windows
> (You run NG.java, then Java aborted)
> Sample is NG.java
>
> javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyph.java
>
> javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyphLayout.java
>
>
>
>
> 4. Fix bug to display INVISIBLE_GLYPH on Windows (Now display square
> box. No need to display.)
>
> javafx.graphics/src/main/java/com/sun/prism/sw/SWGraphics.java
>
>
> ======================================================
> I checked this patch on CentOS 7.5 and Windows7 x64.
>
>
> ====================
> PATCH
> ====================
> diff -r 284d06bb1364
> modules/javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
> ---
> a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
> Tue Jun 12 14:40:17 2018 +0530
> +++
> b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
> Wed Jun 13 14:52:07 2018 +0900
> @@ -42,12 +42,13 @@
> abstract class CMap {
>
> static final char noSuchChar = (char)0xfffd;
> + static final int BYTEMASK = 0x000000ff;
> static final int SHORTMASK = 0x0000ffff;
> static final int INTMASK = 0xffffffff;
>
> private static final int MAX_CODE_POINTS = 0x10ffff;
>
> - static CMap initialize(PrismFontFile font) {
> + static CMap initialize(PrismFontFile font, int[] offset_format,
> int create_cmap) {
>
> CMap cmap = null;
>
> @@ -59,6 +60,11 @@
> Buffer cmapBuffer = font.readTable(FontConstants.cmapTag);
> short numberSubTables = cmapBuffer.getShort(2);
>
> + /* create CMap14 */
> + if (create_cmap == 14 && offset_format[0] != 0) {
> + return createCMap(cmapBuffer, offset_format[0]);
> + }
> +
> /* Locate the offsets of supported 3,* Microsoft platform
> encodings,
> * and any 0,* Unicode platform encoding. The latter is used by
> * all current OS X fonts that don't have a Microsoft cmap.
> @@ -76,6 +82,9 @@
> zeroStar = true;
> encodingID = cmapBuffer.getShort();
> zeroStarOffset = cmapBuffer.getInt();
> + if (encodingID == 5) {
> + offset_format[0] = zeroStarOffset;
> + }
> }
> else if (platformID == 3) {
> threeStar = true;
> @@ -133,6 +142,7 @@
> case 8: return new CMapFormat8(buffer, offset);
> case 10: return new CMapFormat10(buffer, offset);
> case 12: return new CMapFormat12(buffer, offset);
> + case 14: return new CMapFormat14(buffer, offset);
> default: throw new RuntimeException("Cmap format
> unimplemented: " +
> (int)buffer.getChar(offset));
> }
> @@ -140,6 +150,13 @@
>
> abstract char getGlyph(int charCode);
>
> + char getGlyph(int charCode, int vs) {
> + return getGlyph(charCode);
> + }
> +
> + void setDefCMap(CMap defCmap) {
> + }
> +
> /* Format 4 Header is
> * ushort format (off=0)
> * ushort length (off=2)
> @@ -207,6 +224,7 @@
> char ctmp = buffer.getChar();
> idRangeOffset[i] = (char)((ctmp>>1)&0xffff);
> }
> +
> /* Can calculate the number of glyph IDs by subtracting
> * "pos" from the length of the cmap
> */
> @@ -591,6 +609,191 @@
>
> }
>
> + // Format 14: Table for Variation Selector (SVS and IVS)
> + static class CMapFormat14 extends CMap {
> +
> + Buffer buffer;
> + int offset;
> +
> + int numSelector;
> + int[] varSelector;
> +
> + /* default glyphs */
> + int[] defaultOff, numRanges;
> + int[][] defUniStart;
> + short[][] additionalCnt;
> +
> + /* non default glyphs */
> + int[] nonDefOff, numMappings;
> + int[][] uniStart, glyphID;
> + /* e.g.
> + * uniStart[numSelector-1] = U+e0100
> + * uniStart[numSelector-1][numMappings-1] = U+795e
> + * glyphID[numSelector-1][numMappings-1] = 12345
> + */
> +
> + CMap defCmap;
> + void setDefCMap(CMap cmap) {
> + this.defCmap = cmap;
> + }
> +
> + CMapFormat14(Buffer buffer, int offset) {
> + this.buffer = buffer;
> + this.offset = offset;
> +
> + buffer.position(offset+6);
> + /* get count of Variation Selector */
> + numSelector = buffer.getInt();
> +
> + varSelector = new int[numSelector]; // e.g. {0xfe00,
> 0xe0100, 0xe0101}
> + defaultOff = new int[numSelector];
> + nonDefOff = new int[numSelector];
> +
> + /* get Variation Selector and Table offset */
> + for (int i=0; i<numSelector; i++) {
> + varSelector[i] = ((buffer.getShort() & SHORTMASK)<<8)
> | (buffer.get() & BYTEMASK);
> + defaultOff[i] = buffer.getInt();
> + nonDefOff[i] = buffer.getInt();
> + }
> +
> + numMappings = new int[numSelector];
> + uniStart = new int[numSelector][];
> + glyphID = new int[numSelector][];
> +
> + /* nonDefault glyphs table, get Unicode and glyphID */
> + for (int i=0; i<numSelector; i++) {
> + if (nonDefOff[i] == 0) {
> + numMappings[i] = 0;
> + continue;
> + }
> + buffer.position(offset+nonDefOff[i]);
> + numMappings[i] = buffer.getInt();
> + }
> +
> + numRanges = new int[numSelector];
> + defUniStart = new int[numSelector][];
> + additionalCnt = new short[numSelector][];
> +
> + /* Default glyphs table, get Unicode and count */
> + for (int i=0; i<numSelector; i++) {
> + if (defaultOff[i] == 0) {
> + numRanges[i] = 0;
> + continue;
> + }
> + buffer.position(offset+defaultOff[i]);
> + numRanges[i] = buffer.getInt();
> + }
> + }
> +
> + /* init Non Default Glyphs Table of pointed VS(e.g. fe00,
> e0100.) */
> + void initNonDef(int i) {
> + /* nonDefault glyphs table, get Unicode and glyphID */
> + buffer.position(offset+nonDefOff[i]+4); // +4 = skip
> numMappings
> + uniStart[i] = new int[numMappings[i]];
> + glyphID[i] = new int[numMappings[i]];
> +
> + for (int j=0; j<numMappings[i]; j++) {
> + uniStart[i][j] = ((buffer.getShort() & SHORTMASK)<<8)
> | (buffer.get() & BYTEMASK);
> + glyphID[i][j] = buffer.getShort() & SHORTMASK;
> + }
> + }
> +
> + void initDef(int i) {
> + buffer.position(offset+defaultOff[i]+4); // +4 = skip
> numRanges
> + defUniStart[i] = new int[numRanges[i]];
> + additionalCnt[i] = new short[numRanges[i]];
> +
> + for (int j=0; j<numRanges[i]; j++) {
> + defUniStart[i][j] = ((buffer.getShort() &
> SHORTMASK)<<8) | (buffer.get() & BYTEMASK);
> + additionalCnt[i][j] = (short)(buffer.get() & BYTEMASK);
> + }
> + }
> +
> + final int findMapNumber_NonDef(int charCode, int i) {
> + if (numMappings[i] > 0) {
> + int min = 0, max, mid;
> + max = numMappings[i];
> + while (min < max) {
> + mid = (min+max) >> 1;
> + if (charCode < uniStart[i][mid]) {
> + max = mid;
> + } else if (charCode > uniStart[i][mid]) {
> + min = mid + 1;
> + } else {
> + return mid;
> + }
> + }
> + }
> + return -1;
> + }
> +
> + final int findRangeNumber_Def(int charCode, int i) {
> + if (numRanges[i] > 0) {
> + int min = 0, max, mid;
> + max = numRanges[i];
> + while (min < max) {
> + mid = (min+max) >> 1;
> + if (charCode < defUniStart[i][mid]) {
> + max = mid;
> + } else if (charCode > defUniStart[i][mid] +
> additionalCnt[i][mid]) {
> + min = mid + 1;
> + } else {
> + return mid;
> + }
> + }
> + }
> + return -1;
> + }
> +
> + char getGlyph(int charCode) {
> + return getGlyph(charCode, 0);
> + }
> +
> + char getGlyph(int charCode, int vs) {
> + if (vs == 0) return 0;
> +
> + int j;
> + for (int i=0; i<numSelector; i++) {
> + if (varSelector[i] > vs) break;
> + if (varSelector[i] != vs) continue;
> +
> + /* non default glyphs table */
> + if (numMappings[i] > 0 && (uniStart[i] == null ||
> glyphID[i] == null)) {
> + try {
> + initNonDef(i);
> + } catch (Exception e) {
> + return 0;
> + }
> + }
> +
> + /* search non default glyphs table */
> + j = findMapNumber_NonDef(charCode, i);
> + if (j != -1) {
> + return (char)glyphID[i][j];
> + }
> +
> + /* default glyphs table */
> + if (defCmap == null) break; // can't get glyphID by
> default glyphs table
> + if (numRanges[i] > 0 && (defUniStart[i] == null ||
> additionalCnt[i] == null)) {
> + try {
> + initDef(i);
> + } catch (Exception e) {
> + return 0;
> + }
> + }
> +
> + /* search default glyphs table */
> + if (defCmap == null) break;
> + j = findRangeNumber_Def(charCode, i);
> + if (j != -1) {
> + return defCmap.getGlyph(charCode);
> + }
> + }
> +
> + return 0;
> + }
> + }
> +
> /* Used to substitute for bad Cmaps. */
> static class NullCMapClass extends CMap {
>
> diff -r 284d06bb1364
> modules/javafx.graphics/src/main/java/com/sun/javafx/font/CharToGlyphMapper.java
> ---
> a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CharToGlyphMapper.java
> Tue Jun 12 14:40:17 2018 +0530
> +++
> b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CharToGlyphMapper.java
> Wed Jun 13 14:52:07 2018 +0900
> @@ -42,8 +42,66 @@
> public static final int MISSING_GLYPH = 0;
> public static final int INVISIBLE_GLYPH_ID = 0xffff;
>
> + public static final int SVS_START = 0xFE00; // VS1
> + public static final int SVS_END = 0xFE0F; // VS16
> + public static final int IVS_START = 0xE0100; // VS17
> + public static final int IVS_END = 0xE01EF; // VS256
> + public static final int FVS_START = 0x180B; // FVS1
> + public static final int FVS_END = 0x180D; // FVS3
> +
> protected int missingGlyph = MISSING_GLYPH;
>
> + /* http://www.unicode.org/versions/Unicode10.0.0/ch18.pdf */
> + public static boolean isCJK(int code) {
> + if (code >= 0x4E00 && code <= 0x9FFF) // Unified Ideographs
> + return true;
> + if (code >= 0x3400 && code <= 0x4DBF) // Extension A
> + return true;
> + if (code >= 0x20000 && code <= 0x2A6DF) // Extension B
> + return true;
> + if (code >= 0x2A700 && code <= 0x2B73F) // Extension C
> + return true;
> + if (code >= 0x2B740 && code <= 0x2B81F) // Extension D
> + return true;
> + if (code >= 0x2B820 && code <= 0x2CEAF) // Extension E
> + return true;
> + if (code >= 0x2CEB0 && code <= 0x2EBE0) // Extension F
> + return true;
> + if (code >= 0xF900 && code <= 0xFAFF) // Compatibility
> Ideographs
> + return true;
> + if (code >= 0x2F800 && code <= 0x2FA1F) // Compatibility
> Ideographs Supplement
> + return true;
> + return false;
> + }
> +
> + public static boolean isVS(int code) {
> + if (isIVS(code))
> + return true;
> + if (isSVS(code))
> + return true;
> +// if (isFVS(code))
> +// return true;
> + return false;
> + }
> +
> + public static boolean isSVS(int code) {
> + if (code >= SVS_START && code <= SVS_END)
> + return true;
> + return false;
> + }
> +
> + public static boolean isIVS(int code) {
> + if (code >= IVS_START && code <= IVS_END)
> + return true;
> + return false;
> + }
> +
> +// public static boolean isFVS(int code) {
> +// if (code >= FVS_START && code <= FVS_END)
> +// return true;
> +// return false;
> +// }
> +
> public boolean canDisplay(char cp) {
> int glyph = charToGlyph(cp);
> return glyph != missingGlyph;
> @@ -53,18 +111,28 @@
> return missingGlyph;
> }
>
> - public abstract int getGlyphCode(int charCode);
> + public abstract int getGlyphCode(int charCode, int vs);
>
> public int charToGlyph(char unicode) {
> - return getGlyphCode(unicode);
> + return getGlyphCode(unicode, (char)0);
> + }
> +
> + public int charToGlyph(char unicode, char vs) {
> + return getGlyphCode(unicode, vs);
> }
>
> public int charToGlyph(int unicode) {
> - return getGlyphCode(unicode);
> + return getGlyphCode(unicode, 0);
> + }
> +
> + public int charToGlyph(int unicode, int vs) {
> + return getGlyphCode(unicode, vs);
> }
>
> public void charsToGlyphs(int start, int count, char[] unicodes,
> int[] glyphs, int glyphStart) {
> +
> + int codeWasSurrogate = 0; // store surrogate pair to handle
> surrogate pair+VS
> for (int i=0; i<count; i++) {
> int code = unicodes[start + i]; // char is unsigned.
> if (code >= HI_SURROGATE_START &&
> @@ -75,13 +143,67 @@
> low <= LO_SURROGATE_END) {
> code = ((code - HI_SURROGATE_START) <<
> HI_SURROGATE_SHIFT) +
> low - LO_SURROGATE_START + SURROGATES_START;
> - glyphs[glyphStart + i] = getGlyphCode(code);
> +
> + if (isIVS(code) && i > 0 &&
> + ((codeWasSurrogate == 0 &&
> isCJK((int)unicodes[start + i -1])) ||
> + codeWasSurrogate != 0)) {
> + int glyph;
> + if (codeWasSurrogate == 0) {
> + glyph = getGlyphCode((int)unicodes[start
> + i -1], code); // IVS
> + } else {
> + glyph = getGlyphCode(codeWasSurrogate,
> code); // surrogate pair+IVS
> + }
> + if (glyph == missingGlyph) {
> + glyphs[glyphStart + i] = missingGlyph;
> + } else {
> + if (codeWasSurrogate == 0) {
> + glyphs[glyphStart + i - 1] = glyph;
> + glyphs[glyphStart + i] =
> INVISIBLE_GLYPH_ID;
> + } else {
> + glyphs[glyphStart + i - 2] = glyph;
> + glyphs[glyphStart + i - 1] =
> INVISIBLE_GLYPH_ID;
> + glyphs[glyphStart + i] =
> INVISIBLE_GLYPH_ID;
> + }
> + }
> + codeWasSurrogate = 0;
> + } else { // surrogate pair
> + glyphs[glyphStart + i] = getGlyphCode(code, 0);
> + if (isIVS(code) == false) {
> + codeWasSurrogate = code; // store
> surrogate pair
> + }
> + }
> +
> i += 1; // Empty glyph slot after surrogate
> glyphs[glyphStart + i] = INVISIBLE_GLYPH_ID;
> continue;
> }
> + } else if (isSVS(code) && i > 0 &&
> + ((codeWasSurrogate == 0 &&
> isCJK((int)unicodes[start + i -1])) ||
> + codeWasSurrogate != 0)) {
> + int glyph;
> + if (codeWasSurrogate == 0) {
> + glyph = getGlyphCode((int)unicodes[start + i -1],
> code); // SVS
> + } else {
> + glyph = getGlyphCode(codeWasSurrogate, code); //
> surrogate pair+SVS
> + }
> + if (glyph == missingGlyph) {
> + glyphs[glyphStart + i] = missingGlyph;
> + } else {
> + if (codeWasSurrogate == 0) {
> + glyphs[glyphStart + i - 1] = glyph;
> + glyphs[glyphStart + i] = INVISIBLE_GLYPH_ID;
> + } else {
> + glyphs[glyphStart + i - 2] = glyph;
> + glyphs[glyphStart + i - 1] = INVISIBLE_GLYPH_ID;
> + glyphs[glyphStart + i] = INVISIBLE_GLYPH_ID;
> + }
> + }
> + codeWasSurrogate = 0;
> + continue;
> }
> - glyphs[glyphStart + i] = getGlyphCode(code);
> +
> + glyphs[glyphStart + i] = getGlyphCode(code, 0);
> + codeWasSurrogate = 0;
> }
> }
>
> diff -r 284d06bb1364
> modules/javafx.graphics/src/main/java/com/sun/javafx/font/CompositeGlyphMapper.java
> ---
> a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CompositeGlyphMapper.java
> Tue Jun 12 14:40:17 2018 +0530
> +++
> b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CompositeGlyphMapper.java
> Wed Jun 13 14:52:07 2018 +0900
> @@ -31,6 +31,7 @@
>
> public static final int SLOTMASK = 0xff000000;
> public static final int GLYPHMASK = 0x00ffffff;
> + public static final long LONGMASK = 0x00000000ffffffffL;
>
> public static final int NBLOCKS = 216;
> public static final int BLOCKSZ = 256;
> @@ -54,11 +55,17 @@
> * the caching ? So a variety of strategies are possible.
> */
> HashMap<Integer, Integer> glyphMap;
> + HashMap<Long, Integer> glyphMapVS; // HashMap for Variation Selector
> +
> + public static long shiftVS_for_HashMap(int code) {
> + return (long)code << 32;
> + }
>
> public CompositeGlyphMapper(CompositeFontResource compFont) {
> font = compFont;
> missingGlyph = 0; // TrueType font standard, avoids lookup.
> glyphMap = new HashMap<Integer, Integer>();
> + glyphMapVS = new HashMap<Long, Integer>();
> slotMappers = new CharToGlyphMapper[compFont.getNumSlots()];
> asciiCacheOK = true;
> }
> @@ -90,17 +97,35 @@
> return ((slot) << 24 | (glyphCode & GLYPHMASK));
> }
>
> - private final int convertToGlyph(int unicode) {
> + public static final int compGlyphToSlot(int compGlyphCode) {
> + return (compGlyphCode >> 24);
> + }
> +
> + public static final int compGlyphToGlyph(int compGlyphCode) {
> + return (compGlyphCode & GLYPHMASK);
> + }
> +
> + private final int convertToGlyph(int unicode, int vs) {
> for (int slot = 0; slot < font.getNumSlots(); slot++) {
> CharToGlyphMapper mapper = getSlotMapper(slot);
> - int glyphCode = mapper.charToGlyph(unicode);
> + int glyphCode = mapper.charToGlyph(unicode, vs);
> +
> if (glyphCode != mapper.getMissingGlyphCode()) {
> glyphCode = compositeGlyphCode(slot, glyphCode);
> - glyphMap.put(unicode, glyphCode);
> + if (vs == 0 || CharToGlyphMapper.isVS(vs) == false) {
> + glyphMap.put(unicode, glyphCode);
> + } else {
> + glyphMapVS.put(shiftVS_for_HashMap(vs) | (unicode
> & LONGMASK), glyphCode);
> + }
> return glyphCode;
> }
> }
> - glyphMap.put(unicode, missingGlyph);
> + if (vs == 0 || CharToGlyphMapper.isVS(vs) == false) {
> + glyphMap.put(unicode, missingGlyph);
> + } else {
> + glyphMapVS.put(shiftVS_for_HashMap(vs) | (unicode &
> LONGMASK), missingGlyph);
> + }
> +
> return missingGlyph;
> }
>
> @@ -137,18 +162,22 @@
> return charToGlyph[index];
> }
>
> - public int getGlyphCode(int charCode) {
> + public int getGlyphCode(int charCode, int vs) {
> // If ASCII then array lookup, else use glyphMap
> int retVal = getAsciiGlyphCode(charCode);
> if (retVal >= 0) {
> return retVal;
> }
> -
> - Integer codeInt = glyphMap.get(charCode);
> + Integer codeInt;
> + if (vs == 0 || CharToGlyphMapper.isVS(vs) == false) {
> + codeInt = glyphMap.get(charCode);
> + } else {
> + codeInt = glyphMapVS.get(shiftVS_for_HashMap(vs) |
> (charCode & LONGMASK));
> + }
> if (codeInt != null) {
> return codeInt.intValue();
> } else {
> - return convertToGlyph(charCode);
> + return convertToGlyph(charCode, vs);
> }
> }
> }
> diff -r 284d06bb1364
> modules/javafx.graphics/src/main/java/com/sun/javafx/font/OpenTypeGlyphMapper.java
> ---
> a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/OpenTypeGlyphMapper.java
> Tue Jun 12 14:40:17 2018 +0530
> +++
> b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/OpenTypeGlyphMapper.java
> Wed Jun 13 14:52:07 2018 +0900
> @@ -29,11 +29,14 @@
>
> PrismFontFile font;
> CMap cmap;
> + int offset_format[] = {0}; // offset of format14
> + CMap cmap14;
>
> public OpenTypeGlyphMapper(PrismFontFile font) {
> this.font = font;
> + offset_format[0] = 0;
> try {
> - cmap = CMap.initialize(font);
> + cmap = CMap.initialize(font, offset_format, -1);
> } catch (Exception e) {
> cmap = null;
> }
> @@ -43,13 +46,38 @@
> missingGlyph = 0; /* standard for TrueType fonts */
> }
>
> - public int getGlyphCode(int charCode) {
> - try {
> - return cmap.getGlyph(charCode);
> - } catch(Exception e) {
> - handleBadCMAP();
> - return missingGlyph;
> + public CMap createCMap14() {
> + if (cmap14 == null && offset_format[0] != 0) {
> + try {
> + cmap14 = CMap.initialize(font, offset_format, 14);
> + } catch (Exception e) {
> + cmap14 = null;
> + }
> + if (cmap14 != null) {
> + cmap14.setDefCMap(this.cmap);
> + }
> + offset_format[0] = 0;
> }
> + return cmap14;
> + }
> +
> + public int getGlyphCode(int charCode, int vs) {
> + if (vs == 0) {
> + try {
> + return cmap.getGlyph(charCode);
> + } catch(Exception e) {
> + handleBadCMAP();
> + return missingGlyph;
> + }
> + } else if (createCMap14() != null) {
> + try {
> + return cmap14.getGlyph(charCode, vs);
> + } catch(Exception e) {
> + handleBadCMAP14();
> + return missingGlyph;
> + }
> + }
> + return missingGlyph;
> }
>
> private void handleBadCMAP() {
> @@ -57,6 +85,10 @@
> cmap = CMap.theNullCmap;
> }
>
> + private void handleBadCMAP14() {
> + cmap14 = CMap.theNullCmap;
> + }
> +
> /* A pretty good heuristic is that the cmap we are using
> * supports 32 bit character codes.
> */
> diff -r 284d06bb1364
> modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyph.java
> ---
> a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyph.java
> Tue Jun 12 14:40:17 2018 +0530
> +++
> b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyph.java
> Wed Jun 13 14:52:07 2018 +0900
> @@ -52,6 +52,7 @@
> private static D2D1_COLOR_F WHITE = new D2D1_COLOR_F(1f, 1f, 1f,
> 1f);
> private static D2D1_MATRIX_3X2_F D2D2_MATRIX_IDENTITY = new
> D2D1_MATRIX_3X2_F(1,0, 0,1, 0,0);
>
> + static final int intMask = 0x0000ffff;
>
> DWGlyph(DWFontStrike strike, int glyphCode, boolean drawShapes) {
> this.strike = strike;
> @@ -303,12 +304,12 @@
>
> @Override
> public int getGlyphCode() {
> - return run.glyphIndices;
> + return ((int)run.glyphIndices & intMask);
> }
>
> @Override
> public RectBounds getBBox() {
> - return strike.getBBox(run.glyphIndices);
> + return strike.getBBox((int)run.glyphIndices & intMask);
> }
>
> @Override
> @@ -321,7 +322,7 @@
>
> @Override
> public Shape getShape() {
> - return strike.createGlyphOutline(run.glyphIndices);
> + return strike.createGlyphOutline((int)run.glyphIndices &
> intMask);
> }
>
> @Override
> diff -r 284d06bb1364
> modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyphLayout.java
> ---
> a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyphLayout.java
> Tue Jun 12 14:40:17 2018 +0530
> +++
> b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyphLayout.java
> Wed Jun 13 14:52:07 2018 +0900
> @@ -138,6 +138,7 @@
> int i, j;
> int[] iglyphs = new int[glyphCount];
> int slotMask = slot << 24;
> + final int intMask = 0x0000ffff;
> boolean missingGlyph = false;
> i = 0; j = rtl ? glyphCount - 1 : 0;
> while (i < glyphCount) {
> @@ -145,7 +146,7 @@
> missingGlyph = true;
> if (composite) break;
> }
> - iglyphs[i] = glyphs[j] | slotMask;
> + iglyphs[i] = ((int)glyphs[j] & intMask) | slotMask;
> i++;
> j+=step;
> }
> diff -r 284d06bb1364
> modules/javafx.graphics/src/main/java/com/sun/javafx/text/ScriptMapper.java
> ---
> a/modules/javafx.graphics/src/main/java/com/sun/javafx/text/ScriptMapper.java
> Tue Jun 12 14:40:17 2018 +0530
> +++
> b/modules/javafx.graphics/src/main/java/com/sun/javafx/text/ScriptMapper.java
> Wed Jun 13 14:52:07 2018 +0900
> @@ -154,6 +154,9 @@
> else if (code <= 0x17ff) { // 1780 - 17FF Khmer
> return true;
> }
> + else if (code <= 0x18af) { // 1800 - 18AF Mongolian
> (including FVS)
> + return true;
> + }
> else if (code < 0x200c) {
> return false;
> }
> diff -r 284d06bb1364
> modules/javafx.graphics/src/main/java/com/sun/prism/sw/SWGraphics.java
> ---
> a/modules/javafx.graphics/src/main/java/com/sun/prism/sw/SWGraphics.java
> Tue Jun 12 14:40:17 2018 +0530
> +++
> b/modules/javafx.graphics/src/main/java/com/sun/prism/sw/SWGraphics.java
> Wed Jun 13 14:52:07 2018 +0900
> @@ -62,6 +62,8 @@
> import com.sun.prism.paint.ImagePattern;
> import com.sun.prism.paint.Paint;
>
> +import com.sun.javafx.font.CharToGlyphMapper;
> +
> final class SWGraphics implements ReadbackGraphics {
>
> private static final BasicStroke DEFAULT_STROKE =
> @@ -630,6 +632,9 @@
>
> final Glyph g = strike.getGlyph(gl.getGlyphCode(idx));
> if (drawAsMasks) {
> + if (g.getGlyphCode() ==
> CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
> + return;
> + }
> final Point2D pt = new Point2D((float)(x + tx.getMxt() +
> gl.getPosX(idx)),
> (float)(y + tx.getMyt() +
> gl.getPosY(idx)));
> int subPixel = strike.getQuantizedPosition(pt);
>
>
>
>
>
>
> ====================
> Sample (kami.java)
> ====================
> import javafx.application.Application;
> import static javafx.application.Application.launch;
> import javafx.scene.Group;
> import javafx.scene.Scene;
> import javafx.scene.paint.Color;
> import javafx.scene.text.Font;
> import javafx.scene.text.Text;
> import javafx.scene.text.TextFlow;
> import javafx.stage.Stage;
>
> public class kami extends Application {
>
> @Override
> public void start(Stage stage) throws Exception {
>
> // You need to install ipamjm font
> String family = "IPAmjMincho"; // for Linux
> // String family = "IPAmj明朝"; // for Windows
>
> Text text[] = new Text[3];
> text[0] = new Text("\u795E+VS1 --> \u795E\uFE00\n"); // FVS
> text[1] = new Text("\u795E+VS20 --> \u795E\uDB40\uDD03\n"); // IVS
> text[2] = new Text("\uD87A\uDF79+VS17 -->
> \uD87A\uDF79\uDB40\uDD01\n"); // Surrogate Pair+IVS
>
> for (int i=0; i<3; i++) {
> text[i].setFill(Color.BLACK);
> text[i].setFont(Font.font(family, 48));
> }
> TextFlow textFlow = new TextFlow(text[0], text[1], text[2]);
> textFlow.setLayoutX(60);
> textFlow.setLayoutY(60);
>
> Group group = new Group(textFlow);
> Scene scene = new Scene(group, 450, 250, Color.WHITE);
> stage.setScene(scene);
> stage.show();
> }
> }
>
>
>
>
>
> ====================
> Sample (kami2.java)
> ====================
> import javafx.application.Application;
> import javafx.geometry.Insets;
> import javafx.scene.Group;
> import javafx.scene.Scene;
> import javafx.scene.control.Label;
> import javafx.scene.control.TextArea;
> import javafx.scene.layout.GridPane;
> import javafx.stage.Stage;
> import javafx.scene.text.Text;
> import javafx.scene.text.Font;
>
> public class kami2 extends Application {
> public static void main(String[] args) {
> launch(args);
> }
> @Override
> public void start(Stage stage) {
> // You need to install ipamjm font
> String family = "IPAmjMincho"; // for Linux
> // String family = "IPAmj明朝"; // for Windows
>
> Scene scene = new Scene(new Group(), 650, 300);
>
> String str[] = new String[3];
> str[0] = new String("\u795E+VS1 --> \u795E\uFE00\n"); // FVS
> str[1] = new String("\u795E+VS20 --> \u795E\uDB40\uDD03\n"); // IVS
> str[2] = new String("\uD87A\uDF79+VS17 -->
> \uD87A\uDF79\uDB40\uDD01\n"); // Surrogate Pair+IVS
>
> String str_for_area = new String("");
> for (int i=0; i<3; i++) {
> str_for_area += str[i].toString();
> }
>
> TextArea area = new TextArea(str_for_area);
> area.setFont(Font.font(family, 48));
> area.setPrefWidth(600);
> area.setPrefHeight(250);
> area.setPrefColumnCount(3);
>
> GridPane grid = new GridPane();
> grid.setVgap(4);
> grid.setHgap(10);
> grid.setPadding(new Insets(5, 5, 5, 5));
> grid.add(area, 1, 0);
>
> Group root = (Group) scene.getRoot();
> root.getChildren().add(grid);
> stage.setScene(scene);
> stage.show();
> }
> }
>
>
>
> ====================
> Sample (mongol.java)
> ====================
> import javafx.application.Application;
> import static javafx.application.Application.launch;
> import javafx.scene.Group;
> import javafx.scene.Scene;
> import javafx.scene.paint.Color;
> import javafx.scene.text.Font;
> import javafx.scene.text.Text;
> import javafx.scene.text.TextFlow;
> import javafx.stage.Stage;
>
> public class mongol extends Application {
>
> @Override
> public void start(Stage stage) throws Exception {
>
> // http://www.mongolfont.com/en/font/mnglartotf.html
> String family = "Mongolian Art";
> double size = 48;
>
> // Correct mongolian form
> // http://www.unicode.org/versions/Unicode10.0.0/ch13.pdf#G27803
> Text text[] = new Text[9];
> text[0] = new Text("ᠤ ᠷ ᠲ ᠤ --> ᠤᠷᠲᠤ (urtu)\n");
> text[1] = new Text("ᠣ ᠷ ᠳ ᠤ --> ᠣᠷᠳᠤ (ordu)\n");
> text[2] = new Text("ᠡ ᠨ ᠳ ᠡ --> ᠡᠨᠳᠡ (ende)\n");
> text[3] = new Text("ᠠ ᠳ ᠠ --> ᠠᠳᠠ (ada)\n");
> text[4] = new Text("ᠠ ᠪ ᠤ --> ᠠᠪᠤ (abu)\n");
> text[5] = new Text("ᠣ ᠳ ᠣ --> ᠣᠳᠣ (odo)\n");
> text[6] = new Text("ᠡ ᠨ ᠡ --> ᠡᠨᠡ (ene)\n");
> text[7] = new Text("ᠭ ᠠ --> ᠭᠠ (gal)\n");
> text[8] = new Text("ᠭ᠋ ᠠ --> ᠭ᠋ᠠ (gal+U+180B)\n");
>
> for (int i=0; i<9; i++) {
> text[i].setFill(Color.BLACK);
> text[i].setFont(Font.font(family, size));
> }
> TextFlow textFlow = new TextFlow(text[0], text[1], text[2],
> text[3], text[4], text[5], text[6], text[7], text[8]);
> textFlow.setLayoutX(60);
> textFlow.setLayoutY(60);
>
> Group group = new Group(textFlow);
> Scene scene = new Scene(group, 600, 650, Color.WHITE);
> stage.setScene(scene);
> stage.show();
> }
> }
>
>
>
>
> ====================
> Sample (NG.java)
> ====================
> import javafx.application.Application;
> import static javafx.application.Application.launch;
> import javafx.scene.Group;
> import javafx.scene.Scene;
> import javafx.scene.paint.Color;
> import javafx.scene.text.Font;
> import javafx.scene.text.Text;
> import javafx.scene.text.TextFlow;
> import javafx.stage.Stage;
>
> // Run on Windows, then Java abort because of bug.
> public class NG extends Application {
> @Override
> public void start(Stage stage) throws Exception {
>
> String family = "Arial Unicode MS";
> double size = 36;
>
> TextFlow textFlow = new TextFlow();
> textFlow.setLayoutX(60);
> textFlow.setLayoutY(60);
>
> // Unicode(GlyphID)
> Text text = new Text("힣฿"); // U+2F9D4(49496) + U+0E3F(1262)
> /* Inside JavaFX, 49496(Uint16) is handled as -16040(short).
> * By ScriptMapper.isComplexCharCode(), U+0E3F is handled as complex.
> * When in condition with minus glyphID value and complex
> * , JavaFX is forcibly terminated.
> * (java.lang.ArrayIndexOutOfBoundsException)
> */
>
> text.setFill(Color.GREEN);
> text.setFont(Font.font(family, size));
> textFlow.getChildren().addAll(text);
>
> Group group = new Group(textFlow);
> Scene scene = new Scene(group, 1200, 300, Color.WHITE);
> stage.setScene(scene);
> stage.show();
> }
> }
>
>
>
>
> --------------------------------------
> Name: Akira Nakajima
> E-Mail: nakajima.akira at nttcom.co.jp
> --------------------------------------
>
More information about the openjfx-dev
mailing list