Hello Phil.

Thanks for your reply and suggestion.

> http://www.oracle.com/technetwork/community/oca-486395.html

Signed OCA is listed as
NTT Comware Corporation - OpenJDK


--------------------------------------
Company: NTT Comware Corporation
Name: Akira Nakajima
E-Mail: nakajima.ak...@nttcom.co.jp
--------------------------------------


On 2018/06/14 3:14, Phil Race wrote:
Hi Akira,

It seems that maybe we should be looking at what you propose and
comparing it
to see if one or the other approach is better and if one missed
something the other spotted.
I'd like to ask Steven and Toshio to take the lead on that.

However for any of your patch to be included it is imperative that you
FIRST
have a signed OCA accepted. Please see
http://www.oracle.com/technetwork/community/oca-486395.html
where your name is not present ...

-phil.

On 06/13/2018 12:53 AM, Nakajima Akira wrote:
I happened to create similar patch(for SWING and JavaFX) without
knowing the report below.
https://bugs.openjdk.java.net/browse/JDK-8187100

I do not know much about circumstances, because this is my first post
about Java forum.
Please discard this if unnecessary.


======================================================
Difference with following patch.
http://cr.openjdk.java.net/~srl/8187100/webrev.00/

1.  For Acceleration and Memory saving, load only partial format14
glyphs table.
java.desktop/share/classes/sun/font/CMap.java

+                if (numMappings[i] > 0 && (uniStart[i] == null ||
glyphID[i] == null)) {
+                    try {
+                        initNonDef(i);



2.  Support Mongolian and FVS  (I checked on Linux and Windows)
java.desktop/share/classes/sun/font/FontUtilities.java

+        else if (code <= 0x18af) { // 1800 - 18AF Mongolian
(including FVS)
+            return true;
+        }


3.  Not implementing following

>> 3) Swing text component's DEL and BS key operations change




======================================================
This SWING patch fixes following 2 bugs.

1. To make display IVS/SVS (JDK-8187100)
Sample is kami.java and kami2.java.

java.desktop/share/classes/sun/font/CMap.java
java.desktop/share/classes/sun/font/CharToGlyphMapper.java
java.desktop/share/classes/sun/font/Font2D.java
java.desktop/share/classes/sun/font/TrueTypeGlyphMapper.java
java.desktop/share/native/libfontmanager/sunFont.c
java.desktop/share/native/libfontmanager/hb-jdk-font.cc


2. To make dislpay Mongolian and FVS
Sample is mongol.java.

java.desktop/share/classes/sun/font/FontUtilities.java



======================================================
I checked this patch on CentOS 7.5 and Windows7 x64.


I created same patch for JavaFX
 and posted to openjfx-...@openjdk.java.net.


====================
PATCH
====================
diff -r e1b3def12624 src/java.desktop/share/classes/sun/font/CMap.java
--- a/src/java.desktop/share/classes/sun/font/CMap.java    Wed Jun 13
06:35:04 2018 +0200
+++ b/src/java.desktop/share/classes/sun/font/CMap.java    Wed Jun 13
14:14:08 2018 +0900
@@ -129,6 +129,7 @@
     static final short MSUnicodeSurrogateEncoding = 10;

     static final char noSuchChar = (char)0xfffd;
+    static final int BYTEMASK  = 0x000000ff;
     static final int SHORTMASK = 0x0000ffff;
     static final int INTMASK   = 0xffffffff;

@@ -141,7 +142,7 @@
      */
     char[] xlat;

-    static CMap initialize(TrueTypeFont font) {
+    static CMap initialize(TrueTypeFont font, int[] offset_format,
int create_cmap) {

         CMap cmap = null;

@@ -150,8 +151,15 @@
         int three0=0, three1=0, three2=0, three3=0, three4=0, three5=0,
             three6=0, three10=0;
         boolean threeStar = false;
+        boolean zeroStar = false;

         ByteBuffer cmapBuffer =
font.getTableBuffer(TrueTypeFont.cmapTag);
+
+        /* create CMap14 */
+        if (create_cmap == 14 && offset_format[0] != 0) {
+            return createCMap(cmapBuffer, offset_format[0], null);
+        }
+
         int cmapTableOffset = font.getTableSize(TrueTypeFont.cmapTag);
         short numberSubTables = cmapBuffer.getShort(2);

@@ -159,7 +167,7 @@
         for (int i=0; i<numberSubTables; i++) {
             cmapBuffer.position(i * 8 + 4);
             platformID = cmapBuffer.getShort();
-            if (platformID == 3) {
+            if (platformID == 3) {  // MS
                 threeStar = true;
                 encodingID = cmapBuffer.getShort();
                 offset     = cmapBuffer.getInt();
@@ -173,6 +181,13 @@
                 case 6:  three6  = offset; break; // Johab
                 case 10: three10 = offset; break; // MS Unicode
surrogates
                 }
+            } else if (platformID == 0) {  // APPLE_UNICODE
+                zeroStar = true;
+                encodingID = cmapBuffer.getShort();
+                offset     = cmapBuffer.getInt();
+                if (encodingID == 5) {
+                    offset_format[0] = offset;
+                }
             }
         }

@@ -419,6 +434,7 @@
         case 8:  return new CMapFormat8(buffer, offset, xlat);
         case 10: return new CMapFormat10(buffer, offset, xlat);
         case 12: return new CMapFormat12(buffer, offset, xlat);
+        case 14: return new CMapFormat14(buffer, offset);
         default: throw new RuntimeException("Cmap format
unimplemented: " +
(int)buffer.getChar(offset));
         }
@@ -435,6 +451,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)
@@ -1031,6 +1054,191 @@

     }

+    // Format 14: (Table for variation selector)
+    static class CMapFormat14 extends CMap {
+
+        ByteBuffer 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(ByteBuffer 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 e1b3def12624
src/java.desktop/share/classes/sun/font/CharToGlyphMapper.java
--- a/src/java.desktop/share/classes/sun/font/CharToGlyphMapper.java
Wed Jun 13 06:35:04 2018 +0200
+++ b/src/java.desktop/share/classes/sun/font/CharToGlyphMapper.java
Wed Jun 13 14:14:08 2018 +0900
@@ -43,6 +43,64 @@

     protected int missingGlyph = CharToGlyphMapper.UNINITIALIZED_GLYPH;

+    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
+
+    /* 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 int getMissingGlyphCode() {
         return missingGlyph;
     }
@@ -62,18 +120,44 @@
     }

     public int charToGlyph(char unicode) {
-        char[] chars = new char[1];
+        return charToGlyph(unicode, 0);
+    }
+
+    public int charToGlyph(char unicode, char vs) {
+        char[] chars;
+        int count;
+        if (vs == 0) {
+            chars = new char[1];
+            count = 1;
+        } else {
+            chars = new char[2];
+            chars[1] = vs;
+            count = 2;
+        }
         int[] glyphs = new int[1];
         chars[0] = unicode;
-        charsToGlyphs(1, chars, glyphs);
+        charsToGlyphs(count, chars, glyphs);
         return glyphs[0];
     }

     public int charToGlyph(int unicode) {
-        int[] chars = new int[1];
+        return charToGlyph(unicode, 0);
+    }
+
+    public int charToGlyph(int unicode, int vs) {
+        int[] chars;
+        int count;
+        if (vs == 0) {
+            chars = new int[1];
+            count = 1;
+        } else {
+            chars = new int[2];
+            chars[1] = vs;
+            count = 2;
+        }
         int [] glyphs = new int[1];
         chars[0] = unicode;
-        charsToGlyphs(1, chars, glyphs);
+        charsToGlyphs(count, chars, glyphs);
         return glyphs[0];
     }

diff -r e1b3def12624 src/java.desktop/share/classes/sun/font/Font2D.java
--- a/src/java.desktop/share/classes/sun/font/Font2D.java    Wed Jun
13 06:35:04 2018 +0200
+++ b/src/java.desktop/share/classes/sun/font/Font2D.java    Wed Jun
13 14:14:08 2018 +0900
@@ -524,6 +524,10 @@
         return getMapper().charToGlyph(wchar);
     }

+    public int charToGlyph(int wchar, int vs) {
+        return getMapper().charToGlyph(wchar, vs);
+    }
+
     public int getMissingGlyphCode() {
         return getMapper().getMissingGlyphCode();
     }
diff -r e1b3def12624
src/java.desktop/share/classes/sun/font/FontUtilities.java
--- a/src/java.desktop/share/classes/sun/font/FontUtilities.java Wed
Jun 13 06:35:04 2018 +0200
+++ b/src/java.desktop/share/classes/sun/font/FontUtilities.java Wed
Jun 13 14:14:08 2018 +0900
@@ -299,6 +299,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 e1b3def12624
src/java.desktop/share/classes/sun/font/TrueTypeGlyphMapper.java
--- a/src/java.desktop/share/classes/sun/font/TrueTypeGlyphMapper.java
Wed Jun 13 06:35:04 2018 +0200
+++ b/src/java.desktop/share/classes/sun/font/TrueTypeGlyphMapper.java
Wed Jun 13 14:14:08 2018 +0900
@@ -43,11 +43,14 @@
     TrueTypeFont font;
     CMap cmap;
     int numGlyphs;
+    int offset_format[] = {0}; // offset of format14
+    CMap cmap14;

     public TrueTypeGlyphMapper(TrueTypeFont 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;
         }
@@ -68,29 +71,63 @@
         }
     }

+    public CMap createCMap14() {
+        if (cmap14 == null && offset_format[0] != 0) {
+            try {
+                cmap14 = CMap.initialize(font, offset_format, 14);
+                if (cmap14 != null) {
+                    cmap14.setDefCMap(this.cmap);
+                    ByteBuffer buffer =
font.getTableBuffer(TrueTypeFont.maxpTag);
+                    if (buffer != null && buffer.capacity() >= 6) {
+                        numGlyphs = buffer.getChar(4); // offset 4
bytes in MAXP table.
+                    }
+                }
+            } catch (Exception e) {
+                cmap14 = null;
+            }
+            offset_format[0] = 0;
+        }
+        return cmap14;
+    }
+
     public int getNumGlyphs() {
         return numGlyphs;
     }

+    private char getGlyphFromCMAP(int charCode, int vs) {
+        char glyphCode = (char)missingGlyph;
+        if (vs == 0) {
+            try {
+                glyphCode = cmap.getGlyph(charCode);
+            } catch(Exception e) {
+                handleBadCMAP();
+                return (char) missingGlyph;
+            }
+        } else if (createCMap14() != null) {
+            try {
+                glyphCode = cmap14.getGlyph(charCode, vs);
+            } catch(Exception e) {
+                handleBadCMAP14();
+                return (char) missingGlyph;
+            }
+        }
+
+        if (glyphCode < numGlyphs ||
+            glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) {
+            return glyphCode;
+        } else {
+            if (FontUtilities.isLogging()) {
+                FontUtilities.getLogger().warning
+                    (font + " out of range glyph id=" +
+                     Integer.toHexString((int)glyphCode) +
+                     " for char " + Integer.toHexString(charCode));
+            }
+            return (char)missingGlyph;
+        }
+    }
+
     private char getGlyphFromCMAP(int charCode) {
-        try {
-            char glyphCode = cmap.getGlyph(charCode);
-            if (glyphCode < numGlyphs ||
-                glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) {
-                return glyphCode;
-            } else {
-                if (FontUtilities.isLogging()) {
-                    FontUtilities.getLogger().warning
-                        (font + " out of range glyph id=" +
-                         Integer.toHexString((int)glyphCode) +
-                         " for char " + Integer.toHexString(charCode));
-                }
-                return (char)missingGlyph;
-            }
-        } catch(Exception e) {
-            handleBadCMAP();
-            return (char) missingGlyph;
-        }
+        return getGlyphFromCMAP(charCode, 0);
     }

     private void handleBadCMAP() {
@@ -106,6 +143,15 @@
         cmap = CMap.theNullCmap;
     }

+    private void handleBadCMAP14() {
+        if (FontUtilities.isLogging()) {
+            FontUtilities.getLogger().severe("Null Cmap for " + font +
+                                      "substituting for this font");
+        }
+        SunFontManager.getInstance().deRegisterBadFont(font);
+        cmap14 = CMap.theNullCmap;
+    }
+
     private char remapJAChar(char unicode) {
         return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode;
     }
@@ -114,22 +160,22 @@
         return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode;
     }

-    public int charToGlyph(char unicode) {
+    public int charToGlyph(char unicode, char vs) {
         if (needsJAremapping) {
             unicode = remapJAChar(unicode);
         }
-        int glyph = getGlyphFromCMAP(unicode);
+        int glyph = getGlyphFromCMAP(unicode, vs);
         if (font.checkUseNatives() && glyph <
font.glyphToCharMap.length) {
             font.glyphToCharMap[glyph] = unicode;
         }
         return glyph;
     }

-    public int charToGlyph(int unicode) {
+    public int charToGlyph(int unicode, int vs) {
         if (needsJAremapping) {
             unicode = remapJAIntChar(unicode);
         }
-        int glyph = getGlyphFromCMAP(unicode);
+        int glyph = getGlyphFromCMAP(unicode, vs);
         if (font.checkUseNatives() && glyph <
font.glyphToCharMap.length) {
             font.glyphToCharMap[glyph] = (char)unicode;
         }
@@ -152,6 +198,8 @@

     public void charsToGlyphs(int count, char[] unicodes, int[]
glyphs) {

+        int codeWasSurrogate = 0; // store surrogate pair to handle
surrogate pair+VS
+        boolean isSurrogate = false; // set true except IVS
         for (int i=0; i<count; i++) {
             int code;
             if (needsJAremapping) {
@@ -169,13 +217,74 @@
                     code = (code - HI_SURROGATE_START) *
                         0x400 + low - LO_SURROGATE_START + 0x10000;

-                    glyphs[i] = getGlyphFromCMAP(code);
+                    if (isIVS(code) && i != 0 &&
+                        ((codeWasSurrogate == 0 &&
isCJK((int)unicodes[i -1])) ||
+                          codeWasSurrogate != 0)) {
+                        int glyph;
+                        if (codeWasSurrogate == 0) {
+                            glyph = getGlyphFromCMAP((int)unicodes[i
-1], code); // IVS
+                        } else {
+                            glyph =
getGlyphFromCMAP(codeWasSurrogate, code); // surrogate pair+IVS
+                        }
+                        if (glyph == missingGlyph) {
+                            glyphs[i] = missingGlyph;
+                        } else {
+                            if (codeWasSurrogate == 0) {
+                                glyphs[i - 1] = glyph;
+                                glyphs[i] = INVISIBLE_GLYPH_ID;
+                            } else {
+                                glyphs[i - 2] = glyph;
+                                glyphs[i - 1] = INVISIBLE_GLYPH_ID;
+                                glyphs[i] = INVISIBLE_GLYPH_ID;
+                            }
+                        }
+                    } else { // surrogate pair, or notCJK+IVS
+                        glyphs[i] = getGlyphFromCMAP(code);
+                        if (isIVS(code) == false) {
+                            isSurrogate = true;
+                        }
+                    }
                     i += 1; // Empty glyph slot after surrogate
                     glyphs[i] = INVISIBLE_GLYPH_ID;
+
+                    if (isSurrogate == false) {
+                        codeWasSurrogate = 0;
+                    } else {
+                        codeWasSurrogate = code;
+                        isSurrogate = false;
+                    }
                     continue;
                 }
+            } else {
+                if (isSVS(code) && i != 0 &&
+                    ((codeWasSurrogate == 0 && isCJK((int)unicodes[i
-1])) ||
+                      codeWasSurrogate != 0)) {
+                    int glyph;
+                    if (codeWasSurrogate == 0) {
+                        glyph = getGlyphFromCMAP((int)unicodes[i -1],
code);
+                    } else {
+                        glyph = getGlyphFromCMAP(codeWasSurrogate,
code);
+                    }
+                    if (glyph == missingGlyph) {
+                        glyphs[i] = missingGlyph;
+                    } else {
+                        if (codeWasSurrogate == 0) {
+                            glyphs[i - 1] = glyph;
+                            glyphs[i] = INVISIBLE_GLYPH_ID;
+                        } else {
+                            glyphs[i - 2] = glyph;
+                            glyphs[i - 1] = INVISIBLE_GLYPH_ID;
+                            glyphs[i] = INVISIBLE_GLYPH_ID;
+                        }
+                    }
+                } else {
+                    glyphs[i] = getGlyphFromCMAP(code);
+                }
             }
-            glyphs[i] = getGlyphFromCMAP(code);
+
+            if (isSurrogate == false) {
+                codeWasSurrogate = 0;
+            }

             if (font.checkUseNatives() &&
                 glyphs[i] < font.glyphToCharMap.length) {
@@ -192,6 +301,8 @@
      */
     public boolean charsToGlyphsNS(int count, char[] unicodes, int[]
glyphs) {

+        int codeWasSurrogate = 0; // store surrogate pair to handle
surrogate pair+VS
+        boolean isSurrogate = false; // set true except IVS
         for (int i=0; i<count; i++) {
             int code;
             if (needsJAremapping) {
@@ -208,11 +319,71 @@
                     low <= LO_SURROGATE_END) {
                     code = (code - HI_SURROGATE_START) *
                         0x400 + low - LO_SURROGATE_START + 0x10000;
+                    if (isIVS(code) && i != 0 &&
+                        ((codeWasSurrogate == 0 &&
isCJK((int)unicodes[i -1])) ||
+                          codeWasSurrogate != 0)) {
+                        int glyph;
+                        if (codeWasSurrogate == 0) {
+                            glyph = getGlyphFromCMAP((int)unicodes[i
-1], code); // IVS
+                        } else {
+                            glyph =
getGlyphFromCMAP(codeWasSurrogate, code); // surrogate pair+IVS
+                        }
+                        if (glyph == missingGlyph) {
+                            glyphs[i] = missingGlyph;
+                        } else {
+                            if (codeWasSurrogate == 0) {
+                                glyphs[i - 1] = glyph;
+                                glyphs[i] = INVISIBLE_GLYPH_ID;
+                            } else {
+                                glyphs[i - 2] = glyph;
+                                glyphs[i - 1] = INVISIBLE_GLYPH_ID;
+                                glyphs[i] = INVISIBLE_GLYPH_ID;
+                            }
+                        }
+                    } else { // surrogate pair, or notCJK+IVS
+                        glyphs[i] = getGlyphFromCMAP(code);
+                        if (isIVS(code) == false) {
+                            isSurrogate = true;
+                        }
+                    }
                     glyphs[i + 1] = INVISIBLE_GLYPH_ID;
+                } else { // not surrogate pair
+                    glyphs[i] = getGlyphFromCMAP(code);
+                }
+            } else { // not surrogate pair
+                if (isSVS(code) && i != 0 &&
+                    ((codeWasSurrogate == 0 && isCJK((int)unicodes[i
-1])) ||
+                      codeWasSurrogate != 0)) {
+                    int glyph;
+                    if (codeWasSurrogate == 0) {
+                        glyph = getGlyphFromCMAP((int)unicodes[i -1],
code);
+                    } else {
+                        glyph = getGlyphFromCMAP(codeWasSurrogate,
code);
+                    }
+                    if (glyph == missingGlyph) {
+                        glyphs[i] = missingGlyph;
+                    } else {
+                        if (codeWasSurrogate == 0) {
+                            glyphs[i - 1] = glyph;
+                            glyphs[i] = INVISIBLE_GLYPH_ID;
+                        } else {
+                            glyphs[i - 2] = glyph;
+                            glyphs[i - 1] = INVISIBLE_GLYPH_ID;
+                            glyphs[i] = INVISIBLE_GLYPH_ID;
+                        }
+                    }
+                } else {
+                    glyphs[i] = getGlyphFromCMAP(code);
                 }
             }

-            glyphs[i] = getGlyphFromCMAP(code);
+            if (isSurrogate == false) {
+                codeWasSurrogate = 0;
+            } else {
+                codeWasSurrogate = code;
+                isSurrogate = false;
+            }
+
             if (font.checkUseNatives() &&
                 glyphs[i] < font.glyphToCharMap.length) {
                 font.glyphToCharMap[glyphs[i]] = (char)code;
diff -r e1b3def12624
src/java.desktop/share/native/libfontmanager/hb-jdk-font.cc
--- a/src/java.desktop/share/native/libfontmanager/hb-jdk-font.cc Wed
Jun 13 06:35:04 2018 +0200
+++ b/src/java.desktop/share/native/libfontmanager/hb-jdk-font.cc Wed
Jun 13 14:14:08 2018 +0900
@@ -48,10 +48,9 @@
     JDKFontInfo *jdkFontInfo = (JDKFontInfo*)font_data;
     JNIEnv* env = jdkFontInfo->env;
     jobject font2D = jdkFontInfo->font2D;
-    hb_codepoint_t u = (variation_selector==0) ? unicode :
variation_selector;

     *glyph = (hb_codepoint_t)
-          env->CallIntMethod(font2D, sunFontIDs.f2dCharToGlyphMID, u);
+          env->CallIntMethod(font2D, sunFontIDs.f2dCharToGlyphMID,
unicode, variation_selector);
     if ((int)*glyph < 0) {
         *glyph = 0;
     }
diff -r e1b3def12624
src/java.desktop/share/native/libfontmanager/sunFont.c
--- a/src/java.desktop/share/native/libfontmanager/sunFont.c Wed Jun
13 06:35:04 2018 +0200
+++ b/src/java.desktop/share/native/libfontmanager/sunFont.c Wed Jun
13 14:14:08 2018 +0900
@@ -143,7 +143,7 @@

      CHECK_NULL(tmpClass = (*env)->FindClass(env, "sun/font/Font2D"));
      CHECK_NULL(sunFontIDs.f2dCharToGlyphMID =
-         (*env)->GetMethodID(env, tmpClass, "charToGlyph", "(I)I"));
+         (*env)->GetMethodID(env, tmpClass, "charToGlyph", "(II)I"));
      CHECK_NULL(sunFontIDs.getMapperMID =
          (*env)->GetMethodID(env, tmpClass, "getMapper",
                              "()Lsun/font/CharToGlyphMapper;"));
@@ -154,7 +154,7 @@

      CHECK_NULL(tmpClass = (*env)->FindClass(env,
"sun/font/CharToGlyphMapper"));
      CHECK_NULL(sunFontIDs.charToGlyphMID =
-        (*env)->GetMethodID(env, tmpClass, "charToGlyph", "(I)I"));
+        (*env)->GetMethodID(env, tmpClass, "charToGlyph", "(II)I"));

      CHECK_NULL(tmpClass = (*env)->FindClass(env,
"sun/font/PhysicalStrike"));
      CHECK_NULL(sunFontIDs.getGlyphMetricsMID =



====================
Sample (kami.java)
====================
import javax.swing.*;
import java.awt.Font;
import java.awt.BorderLayout;

public class kami extends JFrame {

  public static void main(String[] args) {
    kami frame = new kami();

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds(10, 10, 450, 250);
    frame.setVisible(true);
  }

  kami() {
    // You need to install ipamjm font
    String family = "IPAmjMincho"; // for Linux
//    String family = "IPAmj明朝"; // for Windows

    JLabel label[] = new JLabel[3];
    label[0] = new JLabel("\u795E+VS1 --> \u795E\uFE00\n"); // SVS
    label[1] = new JLabel("\u795E+VS20 --> \u795E\uDB40\uDD03\n"); // IVS
    label[2] = new JLabel("\uD87A\uDF79+VS17 -->
\uD87A\uDF79\uDB40\uDD01\n"); // surrogate pair+IVS

    JPanel p = new JPanel();
    for (int i=0; i<3; i++) {
      label[i].setFont(new Font(family, Font.PLAIN, 48));
      p.add(label[i]);
    }
    getContentPane().add(p, BorderLayout.CENTER);
  }
}


====================
Sample (kami2.java)
====================
import javax.swing.*;
import java.awt.Font;
import java.awt.BorderLayout;

public class kami2 extends JFrame {

  public static void main(String[] args) {
    kami2 frame = new kami2();

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds(10, 10, 450, 250);
    frame.setVisible(true);
  }

  kami2() {
    // You need to install ipamjm font
    String family = "IPAmjMincho"; // for Linux
//    String family = "IPAmj明朝"; // for Windows

    String str[] = new String[3];
    str[0] = new String("\u795E+VS1 --> \u795E\uFE00\n");
    str[1] = new String("\u795E+VS20 --> \u795E\uDB40\uDD03\n");
    str[2] = new String("\uD87A\uDF79+VS17 -->
\uD87A\uDF79\uDB40\uDD01\n");

    String str_for_area = new String("");
    for (int i=0; i<3; i++) {
      str_for_area += str[i].toString();
    }

    JTextArea area = new JTextArea(str_for_area, 3, 10);
    area.setFont(new Font(family, 0, 48));

    JPanel p = new JPanel();
    p.add(area);
    getContentPane().add(p, BorderLayout.CENTER);
  }
}


====================
Sample (mongol.java)
====================
import javax.swing.*;
import java.awt.Font;
import java.awt.BorderLayout;

public class mongol extends JFrame{

  public static void main(String[] args) {
    mongol frame = new mongol();

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds(10, 10, 600, 650);
    frame.setVisible(true);
  }

  mongol() {
    // http://www.mongolfont.com/en/font/mnglartotf.html
    String family = "Mongolian Art";
    JLabel label[] = new JLabel[9];

    // Correct mongolian form
    // http://www.unicode.org/versions/Unicode10.0.0/ch13.pdf#G27803
    label[0] = new JLabel("ᠤ ᠷ ᠲ ᠤ   --> ᠤᠷᠲᠤ (urtu)\n");
    label[1] = new JLabel("ᠣ ᠷ ᠳ ᠤ   --> ᠣᠷᠳᠤ (ordu)\n");
    label[2] = new JLabel("ᠡ ᠨ ᠳ ᠡ   --> ᠡᠨᠳᠡ (ende)\n");
    label[3] = new JLabel("ᠠ ᠳ ᠠ     --> ᠠᠳᠠ  (ada)\n");
    label[4] = new JLabel("ᠠ ᠪ ᠤ   --> ᠠᠪᠤ (abu)\n");
    label[5] = new JLabel("ᠣ ᠳ ᠣ   --> ᠣᠳᠣ (odo)\n");
    label[6] = new JLabel("ᠡ ᠨ ᠡ   --> ᠡᠨᠡ (ene)\n");
    label[7] = new JLabel("ᠭ ᠠ  --> ᠭᠠ (gal)\n");
    label[8] = new JLabel("ᠭ᠋ ᠠ  --> ᠭ᠋ᠠ (gal+U+180B)\n");

    JPanel p = new JPanel();
    for (int i=0; i<9; i++) {
      label[i].setFont(new Font(family, Font.PLAIN, 48));
      p.add(label[i]);
    }
    getContentPane().add(p, BorderLayout.CENTER);
  }
}



--------------------------------------
Name: Akira Nakajima
E-Mail: nakajima.ak...@nttcom.co.jp
--------------------------------------

Reply via email to