<html><body><p><font size="2">Hi</font><br><br><font size="2">That's very encourage us that the similar requirement exist.</font><br><br><font size="2">As the starting point of discussion, I'd like to share the results of analysis.</font><br><font size="2">(This is also Steven's request.)</font><br><br><font size="2">1) Heap usage</font><br><font size="2">Akira-san's code had smaller foot print.</font><br><font size="2">(Mesured by Font2Demo, IPAmj font, and RHEL7)</font><br><font size="2">Akira-san's<br>(w/o VS) 7454464 bytes, (w/ VS) 7773472 bytes<br>Ours<br>(w/o VS) 7593096 bytes, (w/ VS) 7631088 bytes</font><br><br><font size="2">2) Mongolian support</font><br><font size="2">This works for not only FVS, but also other Mongolian glyphs.</font><br><font size="2">This is separated by our patch.</font><br><br><font size="2">3) Swing key operations</font><br><font size="2">I agreed that this is another layer's issue, and dropped from my patch, too.</font><br><br><font size="2">4) Our code's advantages</font><br><font size="2">- Composite (logical) fonts support<br>- Obeyed Unicode standard more strictly<br>    - Base character definition<br>    - Behavior when no VS glyph is available</font><br><br><font size="2">I sent my latest update to Steven, which was revised based on Phil's greate comments.</font><br><font size="2"><br>Best regards,<br>Toshio Nakamura, IBM Japan<br></font><br><img width="16" height="16" src="cid:2__=8FBB0823DFC9221F8f9e8a93df938690918c8FB@" border="0" alt="Inactive hide details for Nakajima Akira ---2018/06/14 08:41:17---From: Nakajima Akira <nakajima.akira@nttcom.co.jp> To: Phil R"><font size="2" color="#424282">Nakajima Akira ---2018/06/14 08:41:17---From: Nakajima Akira <nakajima.akira@nttcom.co.jp> To: Phil Race <philip.race@oracle.com>, <2d-dev@openjdk.java.net>, Toshio 5</font><br><br><font size="2" color="#5F5F5F">From:        </font><font size="2">Nakajima Akira <nakajima.akira@nttcom.co.jp></font><br><font size="2" color="#5F5F5F">To:        </font><font size="2">Phil Race <philip.race@oracle.com>, <2d-dev@openjdk.java.net>, Toshio 5 Nakamura <TOSHIONA@jp.ibm.com>, "Steven R. Loomis" <srl@icu-project.org></font><br><font size="2" color="#5F5F5F">Date:        </font><font size="2">2018/06/14 08:41</font><br><font size="2" color="#5F5F5F">Subject:        </font><font size="2">Re: [OpenJDK 2D-Dev] JDK-8187100 [PATCH][SWING] To make display Variation Selector(IVS/SVS/FVS)</font><br><hr width="100%" size="2" align="left" noshade style="color:#8091A5; "><br><br><br><tt><font size="2">Hello Phil.<br><br>Thanks for your reply and suggestion.<br><br> > </font></tt><tt><font size="2"><a href="http://www.oracle.com/technetwork/community/oca-486395.html">http://www.oracle.com/technetwork/community/oca-486395.html</a></font></tt><tt><font size="2"><br><br>Signed OCA is listed as<br>NTT Comware Corporation - OpenJDK<br><br><br>--------------------------------------<br>Company: NTT Comware Corporation<br>Name: Akira Nakajima<br>E-Mail: nakajima.akira@nttcom.co.jp<br>--------------------------------------<br><br><br>On 2018/06/14 3:14, Phil Race wrote:<br>> Hi Akira,<br>><br>> It seems that maybe we should be looking at what you propose and<br>> comparing it<br>> to see if one or the other approach is better and if one missed<br>> something the other spotted.<br>> I'd like to ask Steven and Toshio to take the lead on that.<br>><br>> However for any of your patch to be included it is imperative that you<br>> FIRST<br>> have a signed OCA accepted. Please see<br>> </font></tt><tt><font size="2"><a href="http://www.oracle.com/technetwork/community/oca-486395.html">http://www.oracle.com/technetwork/community/oca-486395.html</a></font></tt><tt><font size="2"><br>> where your name is not present ...<br>><br>> -phil.<br>><br>> On 06/13/2018 12:53 AM, Nakajima Akira wrote:<br>>> I happened to create similar patch(for SWING and JavaFX) without<br>>> knowing the report below.<br>>> </font></tt><tt><font size="2"><a href="https://bugs.openjdk.java.net/browse/JDK-8187100">https://bugs.openjdk.java.net/browse/JDK-8187100</a></font></tt><tt><font size="2"><br>>><br>>> I do not know much about circumstances, because this is my first post<br>>> about Java forum.<br>>> Please discard this if unnecessary.<br>>><br>>><br>>> ======================================================<br>>> Difference with following patch.<br>>> </font></tt><tt><font size="2"><a href="http://cr.openjdk.java.net/~srl/8187100/webrev.00/">http://cr.openjdk.java.net/~srl/8187100/webrev.00/</a></font></tt><tt><font size="2"><br>>><br>>> 1.  For Acceleration and Memory saving, load only partial format14<br>>> glyphs table.<br>>> java.desktop/share/classes/sun/font/CMap.java<br>>><br>>> +                if (numMappings[i] > 0 && (uniStart[i] == null ||<br>>> glyphID[i] == null)) {<br>>> +                    try {<br>>> +                        initNonDef(i);<br>>><br>>><br>>><br>>> 2.  Support Mongolian and FVS  (I checked on Linux and Windows)<br>>> java.desktop/share/classes/sun/font/FontUtilities.java<br>>><br>>> +        else if (code <= 0x18af) { // 1800 - 18AF Mongolian<br>>> (including FVS)<br>>> +            return true;<br>>> +        }<br>>><br>>><br>>> 3.  Not implementing following<br>>><br>>> >> 3) Swing text component's DEL and BS key operations change<br>>><br>>><br>>><br>>><br>>> ======================================================<br>>> This SWING patch fixes following 2 bugs.<br>>><br>>> 1. To make display IVS/SVS (JDK-8187100)<br>>> Sample is kami.java and kami2.java.<br>>><br>>> java.desktop/share/classes/sun/font/CMap.java<br>>> java.desktop/share/classes/sun/font/CharToGlyphMapper.java<br>>> java.desktop/share/classes/sun/font/Font2D.java<br>>> java.desktop/share/classes/sun/font/TrueTypeGlyphMapper.java<br>>> java.desktop/share/native/libfontmanager/sunFont.c<br>>> java.desktop/share/native/libfontmanager/hb-jdk-font.cc<br>>><br>>><br>>> 2. To make dislpay Mongolian and FVS<br>>> Sample is mongol.java.<br>>><br>>> java.desktop/share/classes/sun/font/FontUtilities.java<br>>><br>>><br>>><br>>> ======================================================<br>>> I checked this patch on CentOS 7.5 and Windows7 x64.<br>>><br>>><br>>> I created same patch for JavaFX<br>>>  and posted to openjfx-dev@openjdk.java.net.<br>>><br>>><br>>> ====================<br>>> PATCH<br>>> ====================<br>>> diff -r e1b3def12624 src/java.desktop/share/classes/sun/font/CMap.java<br>>> --- a/src/java.desktop/share/classes/sun/font/CMap.java    Wed Jun 13<br>>> 06:35:04 2018 +0200<br>>> +++ b/src/java.desktop/share/classes/sun/font/CMap.java    Wed Jun 13<br>>> 14:14:08 2018 +0900<br>>> @@ -129,6 +129,7 @@<br>>>      static final short MSUnicodeSurrogateEncoding = 10;<br>>><br>>>      static final char noSuchChar = (char)0xfffd;<br>>> +    static final int BYTEMASK  = 0x000000ff;<br>>>      static final int SHORTMASK = 0x0000ffff;<br>>>      static final int INTMASK   = 0xffffffff;<br>>><br>>> @@ -141,7 +142,7 @@<br>>>       */<br>>>      char[] xlat;<br>>><br>>> -    static CMap initialize(TrueTypeFont font) {<br>>> +    static CMap initialize(TrueTypeFont font, int[] offset_format,<br>>> int create_cmap) {<br>>><br>>>          CMap cmap = null;<br>>><br>>> @@ -150,8 +151,15 @@<br>>>          int three0=0, three1=0, three2=0, three3=0, three4=0, three5=0,<br>>>              three6=0, three10=0;<br>>>          boolean threeStar = false;<br>>> +        boolean zeroStar = false;<br>>><br>>>          ByteBuffer cmapBuffer =<br>>> font.getTableBuffer(TrueTypeFont.cmapTag);<br>>> +<br>>> +        /* create CMap14 */<br>>> +        if (create_cmap == 14 && offset_format[0] != 0) {<br>>> +            return createCMap(cmapBuffer, offset_format[0], null);<br>>> +        }<br>>> +<br>>>          int cmapTableOffset = font.getTableSize(TrueTypeFont.cmapTag);<br>>>          short numberSubTables = cmapBuffer.getShort(2);<br>>><br>>> @@ -159,7 +167,7 @@<br>>>          for (int i=0; i<numberSubTables; i++) {<br>>>              cmapBuffer.position(i * 8 + 4);<br>>>              platformID = cmapBuffer.getShort();<br>>> -            if (platformID == 3) {<br>>> +            if (platformID == 3) {  // MS<br>>>                  threeStar = true;<br>>>                  encodingID = cmapBuffer.getShort();<br>>>                  offset     = cmapBuffer.getInt();<br>>> @@ -173,6 +181,13 @@<br>>>                  case 6:  three6  = offset; break; // Johab<br>>>                  case 10: three10 = offset; break; // MS Unicode<br>>> surrogates<br>>>                  }<br>>> +            } else if (platformID == 0) {  // APPLE_UNICODE<br>>> +                zeroStar = true;<br>>> +                encodingID = cmapBuffer.getShort();<br>>> +                offset     = cmapBuffer.getInt();<br>>> +                if (encodingID == 5) {<br>>> +                    offset_format[0] = offset;<br>>> +                }<br>>>              }<br>>>          }<br>>><br>>> @@ -419,6 +434,7 @@<br>>>          case 8:  return new CMapFormat8(buffer, offset, xlat);<br>>>          case 10: return new CMapFormat10(buffer, offset, xlat);<br>>>          case 12: return new CMapFormat12(buffer, offset, xlat);<br>>> +        case 14: return new CMapFormat14(buffer, offset);<br>>>          default: throw new RuntimeException("Cmap format<br>>> unimplemented: " +<br>>> (int)buffer.getChar(offset));<br>>>          }<br>>> @@ -435,6 +451,13 @@<br>>>  */<br>>>      abstract char getGlyph(int charCode);<br>>><br>>> +    char getGlyph(int charCode, int vs) {<br>>> +        return getGlyph(charCode);<br>>> +    }<br>>> +<br>>> +    void setDefCMap(CMap defCmap) {<br>>> +    };<br>>> +<br>>>      /* Format 4 Header is<br>>>       * ushort format (off=0)<br>>>       * ushort length (off=2)<br>>> @@ -1031,6 +1054,191 @@<br>>><br>>>      }<br>>><br>>> +    // Format 14: (Table for variation selector)<br>>> +    static class CMapFormat14 extends CMap {<br>>> +<br>>> +        ByteBuffer buffer;<br>>> +        int offset;<br>>> +        int numSelector;<br>>> +        int[] varSelector;<br>>> +<br>>> +        /* default glyphs */<br>>> +        int[] defaultOff, numRanges;<br>>> +        int[][] defUniStart;<br>>> +        short[][] additionalCnt;<br>>> +<br>>> +        /* non default glyphs */<br>>> +        int[] nonDefOff, numMappings;<br>>> +        int[][] uniStart, glyphID;<br>>> +        /* e.g.<br>>> +         *  uniStart[numSelector-1] = U+e0100<br>>> +         *  uniStart[numSelector-1][numMappings-1] = U+795e<br>>> +         *  glyphID[numSelector-1][numMappings-1] = 12345<br>>> +         */<br>>> +<br>>> +        CMap defCmap;<br>>> +        void setDefCMap(CMap cmap) {<br>>> +            this.defCmap = cmap;<br>>> +        }<br>>> +<br>>> +        CMapFormat14(ByteBuffer buffer, int offset) {<br>>> +            this.buffer = buffer;<br>>> +            this.offset = offset;<br>>> +<br>>> +            buffer.position(offset+6);<br>>> +            /* get count of Variation Selector */<br>>> +            numSelector = buffer.getInt();<br>>> +<br>>> +            varSelector = new int[numSelector]; // e.g. {0xfe00,<br>>> 0xe0100, 0xe0101}<br>>> +            defaultOff = new int[numSelector];<br>>> +            nonDefOff = new int[numSelector];<br>>> +<br>>> +            /* get Variation Selector and Table offset */<br>>> +            for (int i=0; i<numSelector; i++) {<br>>> +                varSelector[i] = ((buffer.getShort() & SHORTMASK)<<8)<br>>> | (buffer.get() & BYTEMASK);<br>>> +                defaultOff[i] = buffer.getInt();<br>>> +                nonDefOff[i] = buffer.getInt();<br>>> +            }<br>>> +<br>>> +            numMappings = new int[numSelector];<br>>> +            uniStart = new int[numSelector][];<br>>> +            glyphID = new int[numSelector][];<br>>> +<br>>> +            /* nonDefault glyphs table, get Unicode and glyphID */<br>>> +            for (int i=0; i<numSelector; i++) {<br>>> +                if (nonDefOff[i] == 0) {<br>>> +                    numMappings[i] = 0;<br>>> +                    continue;<br>>> +                }<br>>> +                buffer.position(offset+nonDefOff[i]);<br>>> +                numMappings[i] = buffer.getInt();<br>>> +            }<br>>> +<br>>> +            numRanges = new int[numSelector];<br>>> +            defUniStart = new int[numSelector][];<br>>> +            additionalCnt = new short[numSelector][];<br>>> +<br>>> +            /* Default glyphs table, get Unicode and count */<br>>> +            for (int i=0; i<numSelector; i++) {<br>>> +                if (defaultOff[i] == 0) {<br>>> +                    numRanges[i] = 0;<br>>> +                    continue;<br>>> +                }<br>>> +                buffer.position(offset+defaultOff[i]);<br>>> +                numRanges[i] = buffer.getInt();<br>>> +            }<br>>> +        }<br>>> +<br>>> +        /* init Non Default Glyphs Table of pointed VS(e.g. fe00,<br>>> e0100.) */<br>>> +        void initNonDef(int i) {<br>>> +            /* nonDefault glyphs table, get Unicode and glyphID */<br>>> +            buffer.position(offset+nonDefOff[i]+4); // +4 = skip<br>>> numMappings<br>>> +            uniStart[i] = new int[numMappings[i]];<br>>> +            glyphID[i] = new int[numMappings[i]];<br>>> +<br>>> +            for (int j=0; j<numMappings[i]; j++) {<br>>> +                uniStart[i][j] = ((buffer.getShort() & SHORTMASK)<<8)<br>>> | (buffer.get() & BYTEMASK);<br>>> +                glyphID[i][j] = buffer.getShort() & SHORTMASK;<br>>> +            }<br>>> +        }<br>>> +<br>>> +        void initDef(int i) {<br>>> +            buffer.position(offset+defaultOff[i]+4); // +4 = skip<br>>> numRanges<br>>> +            defUniStart[i] = new int[numRanges[i]];<br>>> +            additionalCnt[i] = new short[numRanges[i]];<br>>> +<br>>> +            for (int j=0; j<numRanges[i]; j++) {<br>>> +                defUniStart[i][j] = ((buffer.getShort() &<br>>> SHORTMASK)<<8) | (buffer.get() & BYTEMASK);<br>>> +                additionalCnt[i][j] = (short)(buffer.get() & BYTEMASK);<br>>> +            }<br>>> +        }<br>>> +<br>>> +        final int findMapNumber_NonDef(int charCode, int i) {<br>>> +            if (numMappings[i] > 0) {<br>>> +                int min = 0, max, mid;<br>>> +                max = numMappings[i];<br>>> +                while (min < max) {<br>>> +                    mid = (min+max) >> 1;<br>>> +                    if (charCode < uniStart[i][mid]) {<br>>> +                        max = mid;<br>>> +                    } else if (charCode > uniStart[i][mid]) {<br>>> +                        min = mid + 1;<br>>> +                    } else {<br>>> +                        return mid;<br>>> +                    }<br>>> +                }<br>>> +            }<br>>> +            return -1;<br>>> +        }<br>>> +<br>>> +        final int findRangeNumber_Def(int charCode, int i) {<br>>> +            if (numRanges[i] > 0) {<br>>> +                int min = 0, max, mid;<br>>> +                max = numRanges[i];<br>>> +                while (min < max) {<br>>> +                    mid = (min+max) >> 1;<br>>> +                    if (charCode < defUniStart[i][mid]) {<br>>> +                        max = mid;<br>>> +                    } else if (charCode > defUniStart[i][mid] +<br>>> additionalCnt[i][mid]) {<br>>> +                        min = mid + 1;<br>>> +                    } else {<br>>> +                        return mid;<br>>> +                    }<br>>> +                }<br>>> +            }<br>>> +            return -1;<br>>> +        }<br>>> +<br>>> +        char getGlyph(int charCode) {<br>>> +            return getGlyph(charCode, 0);<br>>> +        }<br>>> +<br>>> +        char getGlyph(int charCode, int vs) {<br>>> +            if (vs == 0) return 0;<br>>> +<br>>> +            int j;<br>>> +            for (int i=0; i<numSelector; i++) {<br>>> +                if (varSelector[i] > vs) break;<br>>> +                if (varSelector[i] != vs) continue;<br>>> +<br>>> +                /* non default glyphs table */<br>>> +                if (numMappings[i] > 0 && (uniStart[i] == null ||<br>>> glyphID[i] == null)) {<br>>> +                    try {<br>>> +                        initNonDef(i);<br>>> +                    } catch (Exception e) {<br>>> +                        return 0;<br>>> +                    }<br>>> +                }<br>>> +<br>>> +                /* search non default glyphs table */<br>>> +                j = findMapNumber_NonDef(charCode, i);<br>>> +                if (j != -1) {<br>>> +                    return (char)glyphID[i][j];<br>>> +                }<br>>> +<br>>> +                /* default glyphs table */<br>>> +                if (defCmap == null) break; // can't get glyphID by<br>>> default glyphs table<br>>> +                if (numRanges[i] > 0 && (defUniStart[i] == null ||<br>>> additionalCnt[i] == null)) {<br>>> +                    try {<br>>> +                        initDef(i);<br>>> +                    } catch (Exception e) {<br>>> +                        return 0;<br>>> +                    }<br>>> +                }<br>>> +<br>>> +                /* search default glyphs table */<br>>> +                if (defCmap == null) break;<br>>> +                    j = findRangeNumber_Def(charCode, i);<br>>> +                if (j != -1) {<br>>> +                    return defCmap.getGlyph(charCode);<br>>> +                }<br>>> +            }<br>>> +<br>>> +            return 0;<br>>> +        }<br>>> +<br>>> +    }<br>>> +<br>>>      /* Used to substitute for bad Cmaps. */<br>>>      static class NullCMapClass extends CMap {<br>>><br>>> diff -r e1b3def12624<br>>> src/java.desktop/share/classes/sun/font/CharToGlyphMapper.java<br>>> --- a/src/java.desktop/share/classes/sun/font/CharToGlyphMapper.java<br>>> Wed Jun 13 06:35:04 2018 +0200<br>>> +++ b/src/java.desktop/share/classes/sun/font/CharToGlyphMapper.java<br>>> Wed Jun 13 14:14:08 2018 +0900<br>>> @@ -43,6 +43,64 @@<br>>><br>>>      protected int missingGlyph = CharToGlyphMapper.UNINITIALIZED_GLYPH;<br>>><br>>> +    public static final int SVS_START = 0xFE00;  // VS1<br>>> +    public static final int SVS_END = 0xFE0F;    // VS16<br>>> +    public static final int IVS_START = 0xE0100; // VS17<br>>> +    public static final int IVS_END = 0xE01EF;   // VS256<br>>> +    public static final int FVS_START = 0x180B;  // FVS1<br>>> +    public static final int FVS_END = 0x180D;    // FVS3<br>>> +<br>>> +    /* </font></tt><tt><font size="2"><a href="http://www.unicode.org/versions/Unicode10.0.0/ch18.pdf">http://www.unicode.org/versions/Unicode10.0.0/ch18.pdf</a></font></tt><tt><font size="2"> */<br>>> +    public static boolean isCJK(int code) {<br>>> +        if (code >= 0x4E00 && code <= 0x9FFF) // Unified Ideographs<br>>> +            return true;<br>>> +        if (code >= 0x3400 && code <= 0x4DBF) // Extension A<br>>> +            return true;<br>>> +        if (code >= 0x20000 && code <= 0x2A6DF) // Extension B<br>>> +            return true;<br>>> +        if (code >= 0x2A700 && code <= 0x2B73F) // Extension C<br>>> +            return true;<br>>> +        if (code >= 0x2B740 && code <= 0x2B81F) // Extension D<br>>> +            return true;<br>>> +        if (code >= 0x2B820 && code <= 0x2CEAF) // Extension E<br>>> +            return true;<br>>> +        if (code >= 0x2CEB0 && code <= 0x2EBE0) // Extension F<br>>> +            return true;<br>>> +        if (code >= 0xF900 && code <= 0xFAFF) // Compatibility<br>>> Ideographs<br>>> +            return true;<br>>> +        if (code >= 0x2F800 && code <= 0x2FA1F) // Compatibility<br>>> Ideographs Supplement<br>>> +            return true;<br>>> +        return false;<br>>> +    }<br>>> +<br>>> +    public static boolean isVS(int code) {<br>>> +        if (isIVS(code))<br>>> +            return true;<br>>> +        if (isSVS(code))<br>>> +            return true;<br>>> +//        if (isFVS(code))<br>>> +//            return true;<br>>> +        return false;<br>>> +    }<br>>> +<br>>> +    public static boolean isSVS(int code) {<br>>> +        if (code >= SVS_START && code <= SVS_END)<br>>> +            return true;<br>>> +        return false;<br>>> +    }<br>>> +<br>>> +    public static boolean isIVS(int code) {<br>>> +        if (code >= IVS_START && code <= IVS_END)<br>>> +            return true;<br>>> +        return false;<br>>> +    }<br>>> +<br>>> +//    public static boolean isFVS(int code) {<br>>> +//        if (code >= FVS_START && code <= FVS_END)<br>>> +//            return true;<br>>> +//        return false;<br>>> +//    }<br>>> +<br>>>      public int getMissingGlyphCode() {<br>>>          return missingGlyph;<br>>>      }<br>>> @@ -62,18 +120,44 @@<br>>>      }<br>>><br>>>      public int charToGlyph(char unicode) {<br>>> -        char[] chars = new char[1];<br>>> +        return charToGlyph(unicode, 0);<br>>> +    }<br>>> +<br>>> +    public int charToGlyph(char unicode, char vs) {<br>>> +        char[] chars;<br>>> +        int count;<br>>> +        if (vs == 0) {<br>>> +            chars = new char[1];<br>>> +            count = 1;<br>>> +        } else {<br>>> +            chars = new char[2];<br>>> +            chars[1] = vs;<br>>> +            count = 2;<br>>> +        }<br>>>          int[] glyphs = new int[1];<br>>>          chars[0] = unicode;<br>>> -        charsToGlyphs(1, chars, glyphs);<br>>> +        charsToGlyphs(count, chars, glyphs);<br>>>          return glyphs[0];<br>>>      }<br>>><br>>>      public int charToGlyph(int unicode) {<br>>> -        int[] chars = new int[1];<br>>> +        return charToGlyph(unicode, 0);<br>>> +    }<br>>> +<br>>> +    public int charToGlyph(int unicode, int vs) {<br>>> +        int[] chars;<br>>> +        int count;<br>>> +        if (vs == 0) {<br>>> +            chars = new int[1];<br>>> +            count = 1;<br>>> +        } else {<br>>> +            chars = new int[2];<br>>> +            chars[1] = vs;<br>>> +            count = 2;<br>>> +        }<br>>>          int [] glyphs = new int[1];<br>>>          chars[0] = unicode;<br>>> -        charsToGlyphs(1, chars, glyphs);<br>>> +        charsToGlyphs(count, chars, glyphs);<br>>>          return glyphs[0];<br>>>      }<br>>><br>>> diff -r e1b3def12624 src/java.desktop/share/classes/sun/font/Font2D.java<br>>> --- a/src/java.desktop/share/classes/sun/font/Font2D.java    Wed Jun<br>>> 13 06:35:04 2018 +0200<br>>> +++ b/src/java.desktop/share/classes/sun/font/Font2D.java    Wed Jun<br>>> 13 14:14:08 2018 +0900<br>>> @@ -524,6 +524,10 @@<br>>>          return getMapper().charToGlyph(wchar);<br>>>      }<br>>><br>>> +    public int charToGlyph(int wchar, int vs) {<br>>> +        return getMapper().charToGlyph(wchar, vs);<br>>> +    }<br>>> +<br>>>      public int getMissingGlyphCode() {<br>>>          return getMapper().getMissingGlyphCode();<br>>>      }<br>>> diff -r e1b3def12624<br>>> src/java.desktop/share/classes/sun/font/FontUtilities.java<br>>> --- a/src/java.desktop/share/classes/sun/font/FontUtilities.java Wed<br>>> Jun 13 06:35:04 2018 +0200<br>>> +++ b/src/java.desktop/share/classes/sun/font/FontUtilities.java Wed<br>>> Jun 13 14:14:08 2018 +0900<br>>> @@ -299,6 +299,9 @@<br>>>          else if (code <= 0x17ff) { // 1780 - 17FF Khmer<br>>>              return true;<br>>>          }<br>>> +        else if (code <= 0x18af) { // 1800 - 18AF Mongolian<br>>> (including FVS)<br>>> +            return true;<br>>> +        }<br>>>          else if (code < 0x200c) {<br>>>              return false;<br>>>          }<br>>> diff -r e1b3def12624<br>>> src/java.desktop/share/classes/sun/font/TrueTypeGlyphMapper.java<br>>> --- a/src/java.desktop/share/classes/sun/font/TrueTypeGlyphMapper.java<br>>> Wed Jun 13 06:35:04 2018 +0200<br>>> +++ b/src/java.desktop/share/classes/sun/font/TrueTypeGlyphMapper.java<br>>> Wed Jun 13 14:14:08 2018 +0900<br>>> @@ -43,11 +43,14 @@<br>>>      TrueTypeFont font;<br>>>      CMap cmap;<br>>>      int numGlyphs;<br>>> +    int offset_format[] = {0}; // offset of format14<br>>> +    CMap cmap14;<br>>><br>>>      public TrueTypeGlyphMapper(TrueTypeFont font) {<br>>>          this.font = font;<br>>> +        offset_format[0] = 0;<br>>>          try {<br>>> -            cmap = CMap.initialize(font);<br>>> +            cmap = CMap.initialize(font, offset_format, -1);<br>>>          } catch (Exception e) {<br>>>              cmap = null;<br>>>          }<br>>> @@ -68,29 +71,63 @@<br>>>          }<br>>>      }<br>>><br>>> +    public CMap createCMap14() {<br>>> +        if (cmap14 == null && offset_format[0] != 0) {<br>>> +            try {<br>>> +                cmap14 = CMap.initialize(font, offset_format, 14);<br>>> +                if (cmap14 != null) {<br>>> +                    cmap14.setDefCMap(this.cmap);<br>>> +                    ByteBuffer buffer =<br>>> font.getTableBuffer(TrueTypeFont.maxpTag);<br>>> +                    if (buffer != null && buffer.capacity() >= 6) {<br>>> +                        numGlyphs = buffer.getChar(4); // offset 4<br>>> bytes in MAXP table.<br>>> +                    }<br>>> +                }<br>>> +            } catch (Exception e) {<br>>> +                cmap14 = null;<br>>> +            }<br>>> +            offset_format[0] = 0;<br>>> +        }<br>>> +        return cmap14;<br>>> +    }<br>>> +<br>>>      public int getNumGlyphs() {</font></tt><br><tt><font size="2">>>          return numGlyphs;<br>>>      }<br>>><br>>> +    private char getGlyphFromCMAP(int charCode, int vs) {<br>>> +        char glyphCode = (char)missingGlyph;<br>>> +        if (vs == 0) {<br>>> +            try {<br>>> +                glyphCode = cmap.getGlyph(charCode);<br>>> +            } catch(Exception e) {<br>>> +                handleBadCMAP();<br>>> +                return (char) missingGlyph;<br>>> +            }<br>>> +        } else if (createCMap14() != null) {<br>>> +            try {<br>>> +                glyphCode = cmap14.getGlyph(charCode, vs);<br>>> +            } catch(Exception e) {<br>>> +                handleBadCMAP14();<br>>> +                return (char) missingGlyph;<br>>> +            }<br>>> +        }<br>>> +<br>>> +        if (glyphCode < numGlyphs ||<br>>> +            glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) {<br>>> +            return glyphCode;<br>>> +        } else {<br>>> +            if (FontUtilities.isLogging()) {<br>>> +                FontUtilities.getLogger().warning<br>>> +                    (font + " out of range glyph id=" +<br>>> +                     Integer.toHexString((int)glyphCode) +<br>>> +                     " for char " + Integer.toHexString(charCode));<br>>> +            }<br>>> +            return (char)missingGlyph;<br>>> +        }<br>>> +    }<br>>> +<br>>>      private char getGlyphFromCMAP(int charCode) {<br>>> -        try {<br>>> -            char glyphCode = cmap.getGlyph(charCode);<br>>> -            if (glyphCode < numGlyphs ||<br>>> -                glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) {<br>>> -                return glyphCode;<br>>> -            } else {<br>>> -                if (FontUtilities.isLogging()) {<br>>> -                    FontUtilities.getLogger().warning<br>>> -                        (font + " out of range glyph id=" +<br>>> -                         Integer.toHexString((int)glyphCode) +<br>>> -                         " for char " + Integer.toHexString(charCode));<br>>> -                }<br>>> -                return (char)missingGlyph;<br>>> -            }<br>>> -        } catch(Exception e) {<br>>> -            handleBadCMAP();<br>>> -            return (char) missingGlyph;<br>>> -        }<br>>> +        return getGlyphFromCMAP(charCode, 0);<br>>>      }<br>>><br>>>      private void handleBadCMAP() {<br>>> @@ -106,6 +143,15 @@<br>>>          cmap = CMap.theNullCmap;<br>>>      }<br>>><br>>> +    private void handleBadCMAP14() {<br>>> +        if (FontUtilities.isLogging()) {<br>>> +            FontUtilities.getLogger().severe("Null Cmap for " + font +<br>>> +                                      "substituting for this font");<br>>> +        }<br>>> +        SunFontManager.getInstance().deRegisterBadFont(font);<br>>> +        cmap14 = CMap.theNullCmap;<br>>> +    }<br>>> +<br>>>      private char remapJAChar(char unicode) {<br>>>          return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode;<br>>>      }<br>>> @@ -114,22 +160,22 @@<br>>>          return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode;<br>>>      }<br>>><br>>> -    public int charToGlyph(char unicode) {<br>>> +    public int charToGlyph(char unicode, char vs) {<br>>>          if (needsJAremapping) {<br>>>              unicode = remapJAChar(unicode);<br>>>          }<br>>> -        int glyph = getGlyphFromCMAP(unicode);<br>>> +        int glyph = getGlyphFromCMAP(unicode, vs);<br>>>          if (font.checkUseNatives() && glyph <<br>>> font.glyphToCharMap.length) {<br>>>              font.glyphToCharMap[glyph] = unicode;<br>>>          }<br>>>          return glyph;<br>>>      }<br>>><br>>> -    public int charToGlyph(int unicode) {<br>>> +    public int charToGlyph(int unicode, int vs) {<br>>>          if (needsJAremapping) {<br>>>              unicode = remapJAIntChar(unicode);<br>>>          }<br>>> -        int glyph = getGlyphFromCMAP(unicode);<br>>> +        int glyph = getGlyphFromCMAP(unicode, vs);<br>>>          if (font.checkUseNatives() && glyph <<br>>> font.glyphToCharMap.length) {<br>>>              font.glyphToCharMap[glyph] = (char)unicode;<br>>>          }<br>>> @@ -152,6 +198,8 @@<br>>><br>>>      public void charsToGlyphs(int count, char[] unicodes, int[]<br>>> glyphs) {<br>>><br>>> +        int codeWasSurrogate = 0; // store surrogate pair to handle<br>>> surrogate pair+VS<br>>> +        boolean isSurrogate = false; // set true except IVS<br>>>          for (int i=0; i<count; i++) {<br>>>              int code;<br>>>              if (needsJAremapping) {<br>>> @@ -169,13 +217,74 @@<br>>>                      code = (code - HI_SURROGATE_START) *<br>>>                          0x400 + low - LO_SURROGATE_START + 0x10000;<br>>><br>>> -                    glyphs[i] = getGlyphFromCMAP(code);<br>>> +                    if (isIVS(code) && i != 0 &&<br>>> +                        ((codeWasSurrogate == 0 &&<br>>> isCJK((int)unicodes[i -1])) ||<br>>> +                          codeWasSurrogate != 0)) {<br>>> +                        int glyph;<br>>> +                        if (codeWasSurrogate == 0) {<br>>> +                            glyph = getGlyphFromCMAP((int)unicodes[i<br>>> -1], code); // IVS<br>>> +                        } else {<br>>> +                            glyph =<br>>> getGlyphFromCMAP(codeWasSurrogate, code); // surrogate pair+IVS<br>>> +                        }<br>>> +                        if (glyph == missingGlyph) {<br>>> +                            glyphs[i] = missingGlyph;<br>>> +                        } else {<br>>> +                            if (codeWasSurrogate == 0) {<br>>> +                                glyphs[i - 1] = glyph;<br>>> +                                glyphs[i] = INVISIBLE_GLYPH_ID;<br>>> +                            } else {<br>>> +                                glyphs[i - 2] = glyph;<br>>> +                                glyphs[i - 1] = INVISIBLE_GLYPH_ID;<br>>> +                                glyphs[i] = INVISIBLE_GLYPH_ID;<br>>> +                            }<br>>> +                        }<br>>> +                    } else { // surrogate pair, or notCJK+IVS<br>>> +                        glyphs[i] = getGlyphFromCMAP(code);<br>>> +                        if (isIVS(code) == false) {<br>>> +                            isSurrogate = true;<br>>> +                        }<br>>> +                    }<br>>>                      i += 1; // Empty glyph slot after surrogate<br>>>                      glyphs[i] = INVISIBLE_GLYPH_ID;<br>>> +<br>>> +                    if (isSurrogate == false) {<br>>> +                        codeWasSurrogate = 0;<br>>> +                    } else {<br>>> +                        codeWasSurrogate = code;<br>>> +                        isSurrogate = false;<br>>> +                    }<br>>>                      continue;<br>>>                  }<br>>> +            } else {<br>>> +                if (isSVS(code) && i != 0 &&<br>>> +                    ((codeWasSurrogate == 0 && isCJK((int)unicodes[i<br>>> -1])) ||<br>>> +                      codeWasSurrogate != 0)) {<br>>> +                    int glyph;<br>>> +                    if (codeWasSurrogate == 0) {<br>>> +                        glyph = getGlyphFromCMAP((int)unicodes[i -1],<br>>> code);<br>>> +                    } else {<br>>> +                        glyph = getGlyphFromCMAP(codeWasSurrogate,<br>>> code);<br>>> +                    }<br>>> +                    if (glyph == missingGlyph) {<br>>> +                        glyphs[i] = missingGlyph;<br>>> +                    } else {<br>>> +                        if (codeWasSurrogate == 0) {<br>>> +                            glyphs[i - 1] = glyph;<br>>> +                            glyphs[i] = INVISIBLE_GLYPH_ID;<br>>> +                        } else {<br>>> +                            glyphs[i - 2] = glyph;<br>>> +                            glyphs[i - 1] = INVISIBLE_GLYPH_ID;<br>>> +                            glyphs[i] = INVISIBLE_GLYPH_ID;<br>>> +                        }<br>>> +                    }<br>>> +                } else {<br>>> +                    glyphs[i] = getGlyphFromCMAP(code);<br>>> +                }<br>>>              }<br>>> -            glyphs[i] = getGlyphFromCMAP(code);<br>>> +<br>>> +            if (isSurrogate == false) {<br>>> +                codeWasSurrogate = 0;<br>>> +            }<br>>><br>>>              if (font.checkUseNatives() &&<br>>>                  glyphs[i] < font.glyphToCharMap.length) {<br>>> @@ -192,6 +301,8 @@<br>>>       */<br>>>      public boolean charsToGlyphsNS(int count, char[] unicodes, int[]<br>>> glyphs) {<br>>><br>>> +        int codeWasSurrogate = 0; // store surrogate pair to handle<br>>> surrogate pair+VS<br>>> +        boolean isSurrogate = false; // set true except IVS<br>>>          for (int i=0; i<count; i++) {<br>>>              int code;<br>>>              if (needsJAremapping) {<br>>> @@ -208,11 +319,71 @@<br>>>                      low <= LO_SURROGATE_END) {<br>>>                      code = (code - HI_SURROGATE_START) *<br>>>                          0x400 + low - LO_SURROGATE_START + 0x10000;<br>>> +                    if (isIVS(code) && i != 0 &&<br>>> +                        ((codeWasSurrogate == 0 &&<br>>> isCJK((int)unicodes[i -1])) ||<br>>> +                          codeWasSurrogate != 0)) {<br>>> +                        int glyph;<br>>> +                        if (codeWasSurrogate == 0) {<br>>> +                            glyph = getGlyphFromCMAP((int)unicodes[i<br>>> -1], code); // IVS<br>>> +                        } else {<br>>> +                            glyph =<br>>> getGlyphFromCMAP(codeWasSurrogate, code); // surrogate pair+IVS<br>>> +                        }<br>>> +                        if (glyph == missingGlyph) {<br>>> +                            glyphs[i] = missingGlyph;<br>>> +                        } else {<br>>> +                            if (codeWasSurrogate == 0) {<br>>> +                                glyphs[i - 1] = glyph;<br>>> +                                glyphs[i] = INVISIBLE_GLYPH_ID;<br>>> +                            } else {<br>>> +                                glyphs[i - 2] = glyph;<br>>> +                                glyphs[i - 1] = INVISIBLE_GLYPH_ID;<br>>> +                                glyphs[i] = INVISIBLE_GLYPH_ID;<br>>> +                            }<br>>> +                        }<br>>> +                    } else { // surrogate pair, or notCJK+IVS<br>>> +                        glyphs[i] = getGlyphFromCMAP(code);<br>>> +                        if (isIVS(code) == false) {<br>>> +                            isSurrogate = true;<br>>> +                        }<br>>> +                    }<br>>>                      glyphs[i + 1] = INVISIBLE_GLYPH_ID;<br>>> +                } else { // not surrogate pair<br>>> +                    glyphs[i] = getGlyphFromCMAP(code);<br>>> +                }<br>>> +            } else { // not surrogate pair<br>>> +                if (isSVS(code) && i != 0 &&<br>>> +                    ((codeWasSurrogate == 0 && isCJK((int)unicodes[i<br>>> -1])) ||<br>>> +                      codeWasSurrogate != 0)) {<br>>> +                    int glyph;<br>>> +                    if (codeWasSurrogate == 0) {<br>>> +                        glyph = getGlyphFromCMAP((int)unicodes[i -1],<br>>> code);<br>>> +                    } else {<br>>> +                        glyph = getGlyphFromCMAP(codeWasSurrogate,<br>>> code);<br>>> +                    }<br>>> +                    if (glyph == missingGlyph) {<br>>> +                        glyphs[i] = missingGlyph;<br>>> +                    } else {<br>>> +                        if (codeWasSurrogate == 0) {<br>>> +                            glyphs[i - 1] = glyph;<br>>> +                            glyphs[i] = INVISIBLE_GLYPH_ID;<br>>> +                        } else {<br>>> +                            glyphs[i - 2] = glyph;<br>>> +                            glyphs[i - 1] = INVISIBLE_GLYPH_ID;<br>>> +                            glyphs[i] = INVISIBLE_GLYPH_ID;<br>>> +                        }<br>>> +                    }<br>>> +                } else {<br>>> +                    glyphs[i] = getGlyphFromCMAP(code);<br>>>                  }<br>>>              }<br>>><br>>> -            glyphs[i] = getGlyphFromCMAP(code);<br>>> +            if (isSurrogate == false) {<br>>> +                codeWasSurrogate = 0;<br>>> +            } else {<br>>> +                codeWasSurrogate = code;<br>>> +                isSurrogate = false;<br>>> +            }<br>>> +<br>>>              if (font.checkUseNatives() &&<br>>>                  glyphs[i] < font.glyphToCharMap.length) {<br>>>                  font.glyphToCharMap[glyphs[i]] = (char)code;<br>>> diff -r e1b3def12624<br>>> src/java.desktop/share/native/libfontmanager/hb-jdk-font.cc<br>>> --- a/src/java.desktop/share/native/libfontmanager/hb-jdk-font.cc Wed<br>>> Jun 13 06:35:04 2018 +0200<br>>> +++ b/src/java.desktop/share/native/libfontmanager/hb-jdk-font.cc Wed<br>>> Jun 13 14:14:08 2018 +0900<br>>> @@ -48,10 +48,9 @@<br>>>      JDKFontInfo *jdkFontInfo = (JDKFontInfo*)font_data;<br>>>      JNIEnv* env = jdkFontInfo->env;<br>>>      jobject font2D = jdkFontInfo->font2D;<br>>> -    hb_codepoint_t u = (variation_selector==0) ? unicode :<br>>> variation_selector;<br>>><br>>>      *glyph = (hb_codepoint_t)<br>>> -          env->CallIntMethod(font2D, sunFontIDs.f2dCharToGlyphMID, u);<br>>> +          env->CallIntMethod(font2D, sunFontIDs.f2dCharToGlyphMID,<br>>> unicode, variation_selector);<br>>>      if ((int)*glyph < 0) {<br>>>          *glyph = 0;<br>>>      }<br>>> diff -r e1b3def12624<br>>> src/java.desktop/share/native/libfontmanager/sunFont.c<br>>> --- a/src/java.desktop/share/native/libfontmanager/sunFont.c Wed Jun<br>>> 13 06:35:04 2018 +0200<br>>> +++ b/src/java.desktop/share/native/libfontmanager/sunFont.c Wed Jun<br>>> 13 14:14:08 2018 +0900<br>>> @@ -143,7 +143,7 @@<br>>><br>>>       CHECK_NULL(tmpClass = (*env)->FindClass(env, "sun/font/Font2D"));<br>>>       CHECK_NULL(sunFontIDs.f2dCharToGlyphMID =<br>>> -         (*env)->GetMethodID(env, tmpClass, "charToGlyph", "(I)I"));<br>>> +         (*env)->GetMethodID(env, tmpClass, "charToGlyph", "(II)I"));<br>>>       CHECK_NULL(sunFontIDs.getMapperMID =<br>>>           (*env)->GetMethodID(env, tmpClass, "getMapper",<br>>>                               "()Lsun/font/CharToGlyphMapper;"));<br>>> @@ -154,7 +154,7 @@<br>>><br>>>       CHECK_NULL(tmpClass = (*env)->FindClass(env,<br>>> "sun/font/CharToGlyphMapper"));<br>>>       CHECK_NULL(sunFontIDs.charToGlyphMID =<br>>> -        (*env)->GetMethodID(env, tmpClass, "charToGlyph", "(I)I"));<br>>> +        (*env)->GetMethodID(env, tmpClass, "charToGlyph", "(II)I"));<br>>><br>>>       CHECK_NULL(tmpClass = (*env)->FindClass(env,<br>>> "sun/font/PhysicalStrike"));<br>>>       CHECK_NULL(sunFontIDs.getGlyphMetricsMID =<br>>><br>>><br>>><br>>> ====================<br>>> Sample (kami.java)<br>>> ====================<br>>> import javax.swing.*;<br>>> import java.awt.Font;<br>>> import java.awt.BorderLayout;<br>>><br>>> public class kami extends JFrame {<br>>><br>>>   public static void main(String[] args) {<br>>>     kami frame = new kami();<br>>><br>>>     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br>>>     frame.setBounds(10, 10, 450, 250);<br>>>     frame.setVisible(true);<br>>>   }<br>>><br>>>   kami() {<br>>>     // You need to install ipamjm font<br>>>     String family = "IPAmjMincho"; // for Linux<br>>> //    String family = "IPAmj明朝"; // for Windows<br>>><br>>>     JLabel label[] = new JLabel[3];<br>>>     label[0] = new JLabel("\u795E+VS1 --> \u795E\uFE00\n"); // SVS<br>>>     label[1] = new JLabel("\u795E+VS20 --> \u795E\uDB40\uDD03\n"); // IVS<br>>>     label[2] = new JLabel("\uD87A\uDF79+VS17 --><br>>> \uD87A\uDF79\uDB40\uDD01\n"); // surrogate pair+IVS<br>>><br>>>     JPanel p = new JPanel();<br>>>     for (int i=0; i<3; i++) {<br>>>       label[i].setFont(new Font(family, Font.PLAIN, 48));<br>>>       p.add(label[i]);<br>>>     }<br>>>     getContentPane().add(p, BorderLayout.CENTER);<br>>>   }<br>>> }<br>>><br>>><br>>> ====================<br>>> Sample (kami2.java)<br>>> ====================<br>>> import javax.swing.*;<br>>> import java.awt.Font;<br>>> import java.awt.BorderLayout;<br>>><br>>> public class kami2 extends JFrame {<br>>><br>>>   public static void main(String[] args) {<br>>>     kami2 frame = new kami2();<br>>><br>>>     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br>>>     frame.setBounds(10, 10, 450, 250);<br>>>     frame.setVisible(true);<br>>>   }<br>>><br>>>   kami2() {<br>>>     // You need to install ipamjm font<br>>>     String family = "IPAmjMincho"; // for Linux<br>>> //    String family = "IPAmj明朝"; // for Windows<br>>><br>>>     String str[] = new String[3];<br>>>     str[0] = new String("\u795E+VS1 --> \u795E\uFE00\n");<br>>>     str[1] = new String("\u795E+VS20 --> \u795E\uDB40\uDD03\n");<br>>>     str[2] = new String("\uD87A\uDF79+VS17 --><br>>> \uD87A\uDF79\uDB40\uDD01\n");<br>>><br>>>     String str_for_area = new String("");<br>>>     for (int i=0; i<3; i++) {<br>>>       str_for_area += str[i].toString();<br>>>     }<br>>><br>>>     JTextArea area = new JTextArea(str_for_area, 3, 10);<br>>>     area.setFont(new Font(family, 0, 48));<br>>><br>>>     JPanel p = new JPanel();<br>>>     p.add(area);<br>>>     getContentPane().add(p, BorderLayout.CENTER);<br>>>   }<br>>> }<br>>><br>>><br>>> ====================<br>>> Sample (mongol.java)<br>>> ====================<br>>> import javax.swing.*;<br>>> import java.awt.Font;<br>>> import java.awt.BorderLayout;<br>>><br>>> public class mongol extends JFrame{<br>>><br>>>   public static void main(String[] args) {<br>>>     mongol frame = new mongol();<br>>><br>>>     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br>>>     frame.setBounds(10, 10, 600, 650);<br>>>     frame.setVisible(true);<br>>>   }<br>>><br>>>   mongol() {<br>>>     // </font></tt><tt><font size="2"><a href="http://www.mongolfont.com/en/font/mnglartotf.html">http://www.mongolfont.com/en/font/mnglartotf.html</a></font></tt><tt><font size="2"><br>>>     String family = "Mongolian Art";<br>>>     JLabel label[] = new JLabel[9];<br>>><br>>>     // Correct mongolian form<br>>>     // </font></tt><tt><font size="2"><a href="http://www.unicode.org/versions/Unicode10.0.0/ch13.pdf#G27803">http://www.unicode.org/versions/Unicode10.0.0/ch13.pdf#G27803</a></font></tt><tt><font size="2"><br>>>     label[0] = new JLabel("ᠤ ᠷ ᠲ ᠤ   --> ᠤᠷᠲᠤ (urtu)\n");<br>>>     label[1] = new JLabel("ᠣ ᠷ ᠳ ᠤ   --> ᠣᠷᠳᠤ (ordu)\n");<br>>>     label[2] = new JLabel("ᠡ ᠨ ᠳ ᠡ   --> ᠡᠨᠳᠡ (ende)\n");<br>>>     label[3] = new JLabel("ᠠ ᠳ ᠠ     --> ᠠᠳᠠ  (ada)\n");<br>>>     label[4] = new JLabel("ᠠ ᠪ ᠤ   --> ᠠᠪᠤ (abu)\n");<br>>>     label[5] = new JLabel("ᠣ ᠳ ᠣ   --> ᠣᠳᠣ (odo)\n");<br>>>     label[6] = new JLabel("ᠡ ᠨ ᠡ   --> ᠡᠨᠡ (ene)\n");<br>>>     label[7] = new JLabel("ᠭ ᠠ  --> ᠭᠠ (gal)\n");<br>>>     label[8] = new JLabel("ᠭ᠋ ᠠ  --> ᠭ᠋ᠠ (gal+U+180B)\n");<br>>><br>>>     JPanel p = new JPanel();<br>>>     for (int i=0; i<9; i++) {<br>>>       label[i].setFont(new Font(family, Font.PLAIN, 48));<br>>>       p.add(label[i]);<br>>>     }<br>>>     getContentPane().add(p, BorderLayout.CENTER);<br>>>   }<br>>> }<br>>><br>>><br>>><br>>> --------------------------------------<br>>> Name: Akira Nakajima<br>>> E-Mail: nakajima.akira@nttcom.co.jp<br>>> --------------------------------------<br><br></font></tt><br><br><BR>
</body></html>