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