[OpenJDK 2D-Dev] JDK-8187100 [PATCH][SWING] To make display Variation Selector(IVS/SVS/FVS)

Nakajima Akira nakajima.akira at nttcom.co.jp
Wed Jun 13 07:53:40 UTC 2018


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-dev at 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.akira at nttcom.co.jp
--------------------------------------


More information about the 2d-dev mailing list