[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