JDK-8187100 [PATCH][JavaFX] To make display Variation Selector(IVS/SVS/FVS)
Nakajima Akira
nakajima.akira at nttcom.co.jp
Wed Jun 13 07:40:35 UTC 2018
I happened to create similar patch without knowing the report below.
https://bugs.openjdk.java.net/browse/JDK-8187100
======================================================
Difference with following SWING patch and my JavaFX patch.
http://cr.openjdk.java.net/~srl/8187100/webrev.00/
1. For Acceleration and Memory saving, load only partial glyphs table.
javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
+ if (numMappings[i] > 0 && (uniStart[i] == null ||
glyphID[i] == null)) {
+ try {
+ initNonDef(i);
2. Mongolian support
javafx.graphics/src/main/java/com/sun/javafx/text/ScriptMapper.java
+ else if (code <= 0x18af) { // 1800 - 18AF Mongolian (including FVS)
+ return true;
+ }
3. Fix 2 bugs on Windows (See below bugs)
======================================================
This JavaFX patch fixes following 4 bugs.
1. To make display IVS/SVS
Sample is kami.java and kami2.java
javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
javafx.graphics/src/main/java/com/sun/javafx/font/CharToGlyphMapper.java
javafx.graphics/src/main/java/com/sun/javafx/font/CompositeGlyphMapper.java
javafx.graphics/src/main/java/com/sun/javafx/font/OpenTypeGlyphMapper.java
2. To make dislpay Mongolian and FVS
Sample is mongol.java
javafx.graphics/src/main/java/com/sun/javafx/text/ScriptMapper.java
3. Fix bug to handle minus value glyph_id on Windows
(You run NG.java, then Java aborted)
Sample is NG.java
javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyph.java
javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyphLayout.java
4. Fix bug to display INVISIBLE_GLYPH on Windows (Now display square
box. No need to display.)
javafx.graphics/src/main/java/com/sun/prism/sw/SWGraphics.java
======================================================
I checked this patch on CentOS 7.5 and Windows7 x64.
====================
PATCH
====================
diff -r 284d06bb1364
modules/javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
---
a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
Tue Jun 12 14:40:17 2018 +0530
+++
b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
Wed Jun 13 14:52:07 2018 +0900
@@ -42,12 +42,13 @@
abstract class CMap {
static final char noSuchChar = (char)0xfffd;
+ static final int BYTEMASK = 0x000000ff;
static final int SHORTMASK = 0x0000ffff;
static final int INTMASK = 0xffffffff;
private static final int MAX_CODE_POINTS = 0x10ffff;
- static CMap initialize(PrismFontFile font) {
+ static CMap initialize(PrismFontFile font, int[] offset_format, int
create_cmap) {
CMap cmap = null;
@@ -59,6 +60,11 @@
Buffer cmapBuffer = font.readTable(FontConstants.cmapTag);
short numberSubTables = cmapBuffer.getShort(2);
+ /* create CMap14 */
+ if (create_cmap == 14 && offset_format[0] != 0) {
+ return createCMap(cmapBuffer, offset_format[0]);
+ }
+
/* Locate the offsets of supported 3,* Microsoft platform
encodings,
* and any 0,* Unicode platform encoding. The latter is used by
* all current OS X fonts that don't have a Microsoft cmap.
@@ -76,6 +82,9 @@
zeroStar = true;
encodingID = cmapBuffer.getShort();
zeroStarOffset = cmapBuffer.getInt();
+ if (encodingID == 5) {
+ offset_format[0] = zeroStarOffset;
+ }
}
else if (platformID == 3) {
threeStar = true;
@@ -133,6 +142,7 @@
case 8: return new CMapFormat8(buffer, offset);
case 10: return new CMapFormat10(buffer, offset);
case 12: return new CMapFormat12(buffer, offset);
+ case 14: return new CMapFormat14(buffer, offset);
default: throw new RuntimeException("Cmap format
unimplemented: " +
(int)buffer.getChar(offset));
}
@@ -140,6 +150,13 @@
abstract char getGlyph(int charCode);
+ char getGlyph(int charCode, int vs) {
+ return getGlyph(charCode);
+ }
+
+ void setDefCMap(CMap defCmap) {
+ }
+
/* Format 4 Header is
* ushort format (off=0)
* ushort length (off=2)
@@ -207,6 +224,7 @@
char ctmp = buffer.getChar();
idRangeOffset[i] = (char)((ctmp>>1)&0xffff);
}
+
/* Can calculate the number of glyph IDs by subtracting
* "pos" from the length of the cmap
*/
@@ -591,6 +609,191 @@
}
+ // Format 14: Table for Variation Selector (SVS and IVS)
+ static class CMapFormat14 extends CMap {
+
+ Buffer buffer;
+ int offset;
+
+ int numSelector;
+ int[] varSelector;
+
+ /* default glyphs */
+ int[] defaultOff, numRanges;
+ int[][] defUniStart;
+ short[][] additionalCnt;
+
+ /* non default glyphs */
+ int[] nonDefOff, numMappings;
+ int[][] uniStart, glyphID;
+ /* e.g.
+ * uniStart[numSelector-1] = U+e0100
+ * uniStart[numSelector-1][numMappings-1] = U+795e
+ * glyphID[numSelector-1][numMappings-1] = 12345
+ */
+
+ CMap defCmap;
+ void setDefCMap(CMap cmap) {
+ this.defCmap = cmap;
+ }
+
+ CMapFormat14(Buffer buffer, int offset) {
+ this.buffer = buffer;
+ this.offset = offset;
+
+ buffer.position(offset+6);
+ /* get count of Variation Selector */
+ numSelector = buffer.getInt();
+
+ varSelector = new int[numSelector]; // e.g. {0xfe00,
0xe0100, 0xe0101}
+ defaultOff = new int[numSelector];
+ nonDefOff = new int[numSelector];
+
+ /* get Variation Selector and Table offset */
+ for (int i=0; i<numSelector; i++) {
+ varSelector[i] = ((buffer.getShort() & SHORTMASK)<<8) |
(buffer.get() & BYTEMASK);
+ defaultOff[i] = buffer.getInt();
+ nonDefOff[i] = buffer.getInt();
+ }
+
+ numMappings = new int[numSelector];
+ uniStart = new int[numSelector][];
+ glyphID = new int[numSelector][];
+
+ /* nonDefault glyphs table, get Unicode and glyphID */
+ for (int i=0; i<numSelector; i++) {
+ if (nonDefOff[i] == 0) {
+ numMappings[i] = 0;
+ continue;
+ }
+ buffer.position(offset+nonDefOff[i]);
+ numMappings[i] = buffer.getInt();
+ }
+
+ numRanges = new int[numSelector];
+ defUniStart = new int[numSelector][];
+ additionalCnt = new short[numSelector][];
+
+ /* Default glyphs table, get Unicode and count */
+ for (int i=0; i<numSelector; i++) {
+ if (defaultOff[i] == 0) {
+ numRanges[i] = 0;
+ continue;
+ }
+ buffer.position(offset+defaultOff[i]);
+ numRanges[i] = buffer.getInt();
+ }
+ }
+
+ /* init Non Default Glyphs Table of pointed VS(e.g. fe00,
e0100.) */
+ void initNonDef(int i) {
+ /* nonDefault glyphs table, get Unicode and glyphID */
+ buffer.position(offset+nonDefOff[i]+4); // +4 = skip
numMappings
+ uniStart[i] = new int[numMappings[i]];
+ glyphID[i] = new int[numMappings[i]];
+
+ for (int j=0; j<numMappings[i]; j++) {
+ uniStart[i][j] = ((buffer.getShort() & SHORTMASK)<<8) |
(buffer.get() & BYTEMASK);
+ glyphID[i][j] = buffer.getShort() & SHORTMASK;
+ }
+ }
+
+ void initDef(int i) {
+ buffer.position(offset+defaultOff[i]+4); // +4 = skip numRanges
+ defUniStart[i] = new int[numRanges[i]];
+ additionalCnt[i] = new short[numRanges[i]];
+
+ for (int j=0; j<numRanges[i]; j++) {
+ defUniStart[i][j] = ((buffer.getShort() &
SHORTMASK)<<8) | (buffer.get() & BYTEMASK);
+ additionalCnt[i][j] = (short)(buffer.get() & BYTEMASK);
+ }
+ }
+
+ final int findMapNumber_NonDef(int charCode, int i) {
+ if (numMappings[i] > 0) {
+ int min = 0, max, mid;
+ max = numMappings[i];
+ while (min < max) {
+ mid = (min+max) >> 1;
+ if (charCode < uniStart[i][mid]) {
+ max = mid;
+ } else if (charCode > uniStart[i][mid]) {
+ min = mid + 1;
+ } else {
+ return mid;
+ }
+ }
+ }
+ return -1;
+ }
+
+ final int findRangeNumber_Def(int charCode, int i) {
+ if (numRanges[i] > 0) {
+ int min = 0, max, mid;
+ max = numRanges[i];
+ while (min < max) {
+ mid = (min+max) >> 1;
+ if (charCode < defUniStart[i][mid]) {
+ max = mid;
+ } else if (charCode > defUniStart[i][mid] +
additionalCnt[i][mid]) {
+ min = mid + 1;
+ } else {
+ return mid;
+ }
+ }
+ }
+ return -1;
+ }
+
+ char getGlyph(int charCode) {
+ return getGlyph(charCode, 0);
+ }
+
+ char getGlyph(int charCode, int vs) {
+ if (vs == 0) return 0;
+
+ int j;
+ for (int i=0; i<numSelector; i++) {
+ if (varSelector[i] > vs) break;
+ if (varSelector[i] != vs) continue;
+
+ /* non default glyphs table */
+ if (numMappings[i] > 0 && (uniStart[i] == null ||
glyphID[i] == null)) {
+ try {
+ initNonDef(i);
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+
+ /* search non default glyphs table */
+ j = findMapNumber_NonDef(charCode, i);
+ if (j != -1) {
+ return (char)glyphID[i][j];
+ }
+
+ /* default glyphs table */
+ if (defCmap == null) break; // can't get glyphID by
default glyphs table
+ if (numRanges[i] > 0 && (defUniStart[i] == null ||
additionalCnt[i] == null)) {
+ try {
+ initDef(i);
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+
+ /* search default glyphs table */
+ if (defCmap == null) break;
+ j = findRangeNumber_Def(charCode, i);
+ if (j != -1) {
+ return defCmap.getGlyph(charCode);
+ }
+ }
+
+ return 0;
+ }
+ }
+
/* Used to substitute for bad Cmaps. */
static class NullCMapClass extends CMap {
diff -r 284d06bb1364
modules/javafx.graphics/src/main/java/com/sun/javafx/font/CharToGlyphMapper.java
---
a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CharToGlyphMapper.java
Tue Jun 12 14:40:17 2018 +0530
+++
b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CharToGlyphMapper.java
Wed Jun 13 14:52:07 2018 +0900
@@ -42,8 +42,66 @@
public static final int MISSING_GLYPH = 0;
public static final int INVISIBLE_GLYPH_ID = 0xffff;
+ public static final int SVS_START = 0xFE00; // VS1
+ public static final int SVS_END = 0xFE0F; // VS16
+ public static final int IVS_START = 0xE0100; // VS17
+ public static final int IVS_END = 0xE01EF; // VS256
+ public static final int FVS_START = 0x180B; // FVS1
+ public static final int FVS_END = 0x180D; // FVS3
+
protected int missingGlyph = MISSING_GLYPH;
+ /* http://www.unicode.org/versions/Unicode10.0.0/ch18.pdf */
+ public static boolean isCJK(int code) {
+ if (code >= 0x4E00 && code <= 0x9FFF) // Unified Ideographs
+ return true;
+ if (code >= 0x3400 && code <= 0x4DBF) // Extension A
+ return true;
+ if (code >= 0x20000 && code <= 0x2A6DF) // Extension B
+ return true;
+ if (code >= 0x2A700 && code <= 0x2B73F) // Extension C
+ return true;
+ if (code >= 0x2B740 && code <= 0x2B81F) // Extension D
+ return true;
+ if (code >= 0x2B820 && code <= 0x2CEAF) // Extension E
+ return true;
+ if (code >= 0x2CEB0 && code <= 0x2EBE0) // Extension F
+ return true;
+ if (code >= 0xF900 && code <= 0xFAFF) // Compatibility Ideographs
+ return true;
+ if (code >= 0x2F800 && code <= 0x2FA1F) // Compatibility
Ideographs Supplement
+ return true;
+ return false;
+ }
+
+ public static boolean isVS(int code) {
+ if (isIVS(code))
+ return true;
+ if (isSVS(code))
+ return true;
+// if (isFVS(code))
+// return true;
+ return false;
+ }
+
+ public static boolean isSVS(int code) {
+ if (code >= SVS_START && code <= SVS_END)
+ return true;
+ return false;
+ }
+
+ public static boolean isIVS(int code) {
+ if (code >= IVS_START && code <= IVS_END)
+ return true;
+ return false;
+ }
+
+// public static boolean isFVS(int code) {
+// if (code >= FVS_START && code <= FVS_END)
+// return true;
+// return false;
+// }
+
public boolean canDisplay(char cp) {
int glyph = charToGlyph(cp);
return glyph != missingGlyph;
@@ -53,18 +111,28 @@
return missingGlyph;
}
- public abstract int getGlyphCode(int charCode);
+ public abstract int getGlyphCode(int charCode, int vs);
public int charToGlyph(char unicode) {
- return getGlyphCode(unicode);
+ return getGlyphCode(unicode, (char)0);
+ }
+
+ public int charToGlyph(char unicode, char vs) {
+ return getGlyphCode(unicode, vs);
}
public int charToGlyph(int unicode) {
- return getGlyphCode(unicode);
+ return getGlyphCode(unicode, 0);
+ }
+
+ public int charToGlyph(int unicode, int vs) {
+ return getGlyphCode(unicode, vs);
}
public void charsToGlyphs(int start, int count, char[] unicodes,
int[] glyphs, int glyphStart) {
+
+ int codeWasSurrogate = 0; // store surrogate pair to handle
surrogate pair+VS
for (int i=0; i<count; i++) {
int code = unicodes[start + i]; // char is unsigned.
if (code >= HI_SURROGATE_START &&
@@ -75,13 +143,67 @@
low <= LO_SURROGATE_END) {
code = ((code - HI_SURROGATE_START) <<
HI_SURROGATE_SHIFT) +
low - LO_SURROGATE_START + SURROGATES_START;
- glyphs[glyphStart + i] = getGlyphCode(code);
+
+ if (isIVS(code) && i > 0 &&
+ ((codeWasSurrogate == 0 &&
isCJK((int)unicodes[start + i -1])) ||
+ codeWasSurrogate != 0)) {
+ int glyph;
+ if (codeWasSurrogate == 0) {
+ glyph = getGlyphCode((int)unicodes[start +
i -1], code); // IVS
+ } else {
+ glyph = getGlyphCode(codeWasSurrogate,
code); // surrogate pair+IVS
+ }
+ if (glyph == missingGlyph) {
+ glyphs[glyphStart + i] = missingGlyph;
+ } else {
+ if (codeWasSurrogate == 0) {
+ glyphs[glyphStart + i - 1] = glyph;
+ glyphs[glyphStart + i] =
INVISIBLE_GLYPH_ID;
+ } else {
+ glyphs[glyphStart + i - 2] = glyph;
+ glyphs[glyphStart + i - 1] =
INVISIBLE_GLYPH_ID;
+ glyphs[glyphStart + i] =
INVISIBLE_GLYPH_ID;
+ }
+ }
+ codeWasSurrogate = 0;
+ } else { // surrogate pair
+ glyphs[glyphStart + i] = getGlyphCode(code, 0);
+ if (isIVS(code) == false) {
+ codeWasSurrogate = code; // store surrogate
pair
+ }
+ }
+
i += 1; // Empty glyph slot after surrogate
glyphs[glyphStart + i] = INVISIBLE_GLYPH_ID;
continue;
}
+ } else if (isSVS(code) && i > 0 &&
+ ((codeWasSurrogate == 0 &&
isCJK((int)unicodes[start + i -1])) ||
+ codeWasSurrogate != 0)) {
+ int glyph;
+ if (codeWasSurrogate == 0) {
+ glyph = getGlyphCode((int)unicodes[start + i -1],
code); // SVS
+ } else {
+ glyph = getGlyphCode(codeWasSurrogate, code); //
surrogate pair+SVS
+ }
+ if (glyph == missingGlyph) {
+ glyphs[glyphStart + i] = missingGlyph;
+ } else {
+ if (codeWasSurrogate == 0) {
+ glyphs[glyphStart + i - 1] = glyph;
+ glyphs[glyphStart + i] = INVISIBLE_GLYPH_ID;
+ } else {
+ glyphs[glyphStart + i - 2] = glyph;
+ glyphs[glyphStart + i - 1] = INVISIBLE_GLYPH_ID;
+ glyphs[glyphStart + i] = INVISIBLE_GLYPH_ID;
+ }
+ }
+ codeWasSurrogate = 0;
+ continue;
}
- glyphs[glyphStart + i] = getGlyphCode(code);
+
+ glyphs[glyphStart + i] = getGlyphCode(code, 0);
+ codeWasSurrogate = 0;
}
}
diff -r 284d06bb1364
modules/javafx.graphics/src/main/java/com/sun/javafx/font/CompositeGlyphMapper.java
---
a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CompositeGlyphMapper.java
Tue Jun 12 14:40:17 2018 +0530
+++
b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/CompositeGlyphMapper.java
Wed Jun 13 14:52:07 2018 +0900
@@ -31,6 +31,7 @@
public static final int SLOTMASK = 0xff000000;
public static final int GLYPHMASK = 0x00ffffff;
+ public static final long LONGMASK = 0x00000000ffffffffL;
public static final int NBLOCKS = 216;
public static final int BLOCKSZ = 256;
@@ -54,11 +55,17 @@
* the caching ? So a variety of strategies are possible.
*/
HashMap<Integer, Integer> glyphMap;
+ HashMap<Long, Integer> glyphMapVS; // HashMap for Variation Selector
+
+ public static long shiftVS_for_HashMap(int code) {
+ return (long)code << 32;
+ }
public CompositeGlyphMapper(CompositeFontResource compFont) {
font = compFont;
missingGlyph = 0; // TrueType font standard, avoids lookup.
glyphMap = new HashMap<Integer, Integer>();
+ glyphMapVS = new HashMap<Long, Integer>();
slotMappers = new CharToGlyphMapper[compFont.getNumSlots()];
asciiCacheOK = true;
}
@@ -90,17 +97,35 @@
return ((slot) << 24 | (glyphCode & GLYPHMASK));
}
- private final int convertToGlyph(int unicode) {
+ public static final int compGlyphToSlot(int compGlyphCode) {
+ return (compGlyphCode >> 24);
+ }
+
+ public static final int compGlyphToGlyph(int compGlyphCode) {
+ return (compGlyphCode & GLYPHMASK);
+ }
+
+ private final int convertToGlyph(int unicode, int vs) {
for (int slot = 0; slot < font.getNumSlots(); slot++) {
CharToGlyphMapper mapper = getSlotMapper(slot);
- int glyphCode = mapper.charToGlyph(unicode);
+ int glyphCode = mapper.charToGlyph(unicode, vs);
+
if (glyphCode != mapper.getMissingGlyphCode()) {
glyphCode = compositeGlyphCode(slot, glyphCode);
- glyphMap.put(unicode, glyphCode);
+ if (vs == 0 || CharToGlyphMapper.isVS(vs) == false) {
+ glyphMap.put(unicode, glyphCode);
+ } else {
+ glyphMapVS.put(shiftVS_for_HashMap(vs) | (unicode &
LONGMASK), glyphCode);
+ }
return glyphCode;
}
}
- glyphMap.put(unicode, missingGlyph);
+ if (vs == 0 || CharToGlyphMapper.isVS(vs) == false) {
+ glyphMap.put(unicode, missingGlyph);
+ } else {
+ glyphMapVS.put(shiftVS_for_HashMap(vs) | (unicode &
LONGMASK), missingGlyph);
+ }
+
return missingGlyph;
}
@@ -137,18 +162,22 @@
return charToGlyph[index];
}
- public int getGlyphCode(int charCode) {
+ public int getGlyphCode(int charCode, int vs) {
// If ASCII then array lookup, else use glyphMap
int retVal = getAsciiGlyphCode(charCode);
if (retVal >= 0) {
return retVal;
}
-
- Integer codeInt = glyphMap.get(charCode);
+ Integer codeInt;
+ if (vs == 0 || CharToGlyphMapper.isVS(vs) == false) {
+ codeInt = glyphMap.get(charCode);
+ } else {
+ codeInt = glyphMapVS.get(shiftVS_for_HashMap(vs) |
(charCode & LONGMASK));
+ }
if (codeInt != null) {
return codeInt.intValue();
} else {
- return convertToGlyph(charCode);
+ return convertToGlyph(charCode, vs);
}
}
}
diff -r 284d06bb1364
modules/javafx.graphics/src/main/java/com/sun/javafx/font/OpenTypeGlyphMapper.java
---
a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/OpenTypeGlyphMapper.java
Tue Jun 12 14:40:17 2018 +0530
+++
b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/OpenTypeGlyphMapper.java
Wed Jun 13 14:52:07 2018 +0900
@@ -29,11 +29,14 @@
PrismFontFile font;
CMap cmap;
+ int offset_format[] = {0}; // offset of format14
+ CMap cmap14;
public OpenTypeGlyphMapper(PrismFontFile font) {
this.font = font;
+ offset_format[0] = 0;
try {
- cmap = CMap.initialize(font);
+ cmap = CMap.initialize(font, offset_format, -1);
} catch (Exception e) {
cmap = null;
}
@@ -43,13 +46,38 @@
missingGlyph = 0; /* standard for TrueType fonts */
}
- public int getGlyphCode(int charCode) {
- try {
- return cmap.getGlyph(charCode);
- } catch(Exception e) {
- handleBadCMAP();
- return missingGlyph;
+ public CMap createCMap14() {
+ if (cmap14 == null && offset_format[0] != 0) {
+ try {
+ cmap14 = CMap.initialize(font, offset_format, 14);
+ } catch (Exception e) {
+ cmap14 = null;
+ }
+ if (cmap14 != null) {
+ cmap14.setDefCMap(this.cmap);
+ }
+ offset_format[0] = 0;
}
+ return cmap14;
+ }
+
+ public int getGlyphCode(int charCode, int vs) {
+ if (vs == 0) {
+ try {
+ return cmap.getGlyph(charCode);
+ } catch(Exception e) {
+ handleBadCMAP();
+ return missingGlyph;
+ }
+ } else if (createCMap14() != null) {
+ try {
+ return cmap14.getGlyph(charCode, vs);
+ } catch(Exception e) {
+ handleBadCMAP14();
+ return missingGlyph;
+ }
+ }
+ return missingGlyph;
}
private void handleBadCMAP() {
@@ -57,6 +85,10 @@
cmap = CMap.theNullCmap;
}
+ private void handleBadCMAP14() {
+ cmap14 = CMap.theNullCmap;
+ }
+
/* A pretty good heuristic is that the cmap we are using
* supports 32 bit character codes.
*/
diff -r 284d06bb1364
modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyph.java
---
a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyph.java
Tue Jun 12 14:40:17 2018 +0530
+++
b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyph.java
Wed Jun 13 14:52:07 2018 +0900
@@ -52,6 +52,7 @@
private static D2D1_COLOR_F WHITE = new D2D1_COLOR_F(1f, 1f, 1f, 1f);
private static D2D1_MATRIX_3X2_F D2D2_MATRIX_IDENTITY = new
D2D1_MATRIX_3X2_F(1,0, 0,1, 0,0);
+ static final int intMask = 0x0000ffff;
DWGlyph(DWFontStrike strike, int glyphCode, boolean drawShapes) {
this.strike = strike;
@@ -303,12 +304,12 @@
@Override
public int getGlyphCode() {
- return run.glyphIndices;
+ return ((int)run.glyphIndices & intMask);
}
@Override
public RectBounds getBBox() {
- return strike.getBBox(run.glyphIndices);
+ return strike.getBBox((int)run.glyphIndices & intMask);
}
@Override
@@ -321,7 +322,7 @@
@Override
public Shape getShape() {
- return strike.createGlyphOutline(run.glyphIndices);
+ return strike.createGlyphOutline((int)run.glyphIndices & intMask);
}
@Override
diff -r 284d06bb1364
modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyphLayout.java
---
a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyphLayout.java
Tue Jun 12 14:40:17 2018 +0530
+++
b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/directwrite/DWGlyphLayout.java
Wed Jun 13 14:52:07 2018 +0900
@@ -138,6 +138,7 @@
int i, j;
int[] iglyphs = new int[glyphCount];
int slotMask = slot << 24;
+ final int intMask = 0x0000ffff;
boolean missingGlyph = false;
i = 0; j = rtl ? glyphCount - 1 : 0;
while (i < glyphCount) {
@@ -145,7 +146,7 @@
missingGlyph = true;
if (composite) break;
}
- iglyphs[i] = glyphs[j] | slotMask;
+ iglyphs[i] = ((int)glyphs[j] & intMask) | slotMask;
i++;
j+=step;
}
diff -r 284d06bb1364
modules/javafx.graphics/src/main/java/com/sun/javafx/text/ScriptMapper.java
---
a/modules/javafx.graphics/src/main/java/com/sun/javafx/text/ScriptMapper.java
Tue Jun 12 14:40:17 2018 +0530
+++
b/modules/javafx.graphics/src/main/java/com/sun/javafx/text/ScriptMapper.java
Wed Jun 13 14:52:07 2018 +0900
@@ -154,6 +154,9 @@
else if (code <= 0x17ff) { // 1780 - 17FF Khmer
return true;
}
+ else if (code <= 0x18af) { // 1800 - 18AF Mongolian (including FVS)
+ return true;
+ }
else if (code < 0x200c) {
return false;
}
diff -r 284d06bb1364
modules/javafx.graphics/src/main/java/com/sun/prism/sw/SWGraphics.java
---
a/modules/javafx.graphics/src/main/java/com/sun/prism/sw/SWGraphics.java
Tue Jun 12 14:40:17 2018 +0530
+++
b/modules/javafx.graphics/src/main/java/com/sun/prism/sw/SWGraphics.java
Wed Jun 13 14:52:07 2018 +0900
@@ -62,6 +62,8 @@
import com.sun.prism.paint.ImagePattern;
import com.sun.prism.paint.Paint;
+import com.sun.javafx.font.CharToGlyphMapper;
+
final class SWGraphics implements ReadbackGraphics {
private static final BasicStroke DEFAULT_STROKE =
@@ -630,6 +632,9 @@
final Glyph g = strike.getGlyph(gl.getGlyphCode(idx));
if (drawAsMasks) {
+ if (g.getGlyphCode() == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
+ return;
+ }
final Point2D pt = new Point2D((float)(x + tx.getMxt() +
gl.getPosX(idx)),
(float)(y + tx.getMyt() +
gl.getPosY(idx)));
int subPixel = strike.getQuantizedPosition(pt);
====================
Sample (kami.java)
====================
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class kami extends Application {
@Override
public void start(Stage stage) throws Exception {
// You need to install ipamjm font
String family = "IPAmjMincho"; // for Linux
// String family = "IPAmj明朝"; // for Windows
Text text[] = new Text[3];
text[0] = new Text("\u795E+VS1 --> \u795E\uFE00\n"); // FVS
text[1] = new Text("\u795E+VS20 --> \u795E\uDB40\uDD03\n"); // IVS
text[2] = new Text("\uD87A\uDF79+VS17 -->
\uD87A\uDF79\uDB40\uDD01\n"); // Surrogate Pair+IVS
for (int i=0; i<3; i++) {
text[i].setFill(Color.BLACK);
text[i].setFont(Font.font(family, 48));
}
TextFlow textFlow = new TextFlow(text[0], text[1], text[2]);
textFlow.setLayoutX(60);
textFlow.setLayoutY(60);
Group group = new Group(textFlow);
Scene scene = new Scene(group, 450, 250, Color.WHITE);
stage.setScene(scene);
stage.show();
}
}
====================
Sample (kami2.java)
====================
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
public class kami2 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
// You need to install ipamjm font
String family = "IPAmjMincho"; // for Linux
// String family = "IPAmj明朝"; // for Windows
Scene scene = new Scene(new Group(), 650, 300);
String str[] = new String[3];
str[0] = new String("\u795E+VS1 --> \u795E\uFE00\n"); // FVS
str[1] = new String("\u795E+VS20 --> \u795E\uDB40\uDD03\n"); // IVS
str[2] = new String("\uD87A\uDF79+VS17 -->
\uD87A\uDF79\uDB40\uDD01\n"); // Surrogate Pair+IVS
String str_for_area = new String("");
for (int i=0; i<3; i++) {
str_for_area += str[i].toString();
}
TextArea area = new TextArea(str_for_area);
area.setFont(Font.font(family, 48));
area.setPrefWidth(600);
area.setPrefHeight(250);
area.setPrefColumnCount(3);
GridPane grid = new GridPane();
grid.setVgap(4);
grid.setHgap(10);
grid.setPadding(new Insets(5, 5, 5, 5));
grid.add(area, 1, 0);
Group root = (Group) scene.getRoot();
root.getChildren().add(grid);
stage.setScene(scene);
stage.show();
}
}
====================
Sample (mongol.java)
====================
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class mongol extends Application {
@Override
public void start(Stage stage) throws Exception {
// http://www.mongolfont.com/en/font/mnglartotf.html
String family = "Mongolian Art";
double size = 48;
// Correct mongolian form
// http://www.unicode.org/versions/Unicode10.0.0/ch13.pdf#G27803
Text text[] = new Text[9];
text[0] = new Text("ᠤ ᠷ ᠲ ᠤ --> ᠤᠷᠲᠤ (urtu)\n");
text[1] = new Text("ᠣ ᠷ ᠳ ᠤ --> ᠣᠷᠳᠤ (ordu)\n");
text[2] = new Text("ᠡ ᠨ ᠳ ᠡ --> ᠡᠨᠳᠡ (ende)\n");
text[3] = new Text("ᠠ ᠳ ᠠ --> ᠠᠳᠠ (ada)\n");
text[4] = new Text("ᠠ ᠪ ᠤ --> ᠠᠪᠤ (abu)\n");
text[5] = new Text("ᠣ ᠳ ᠣ --> ᠣᠳᠣ (odo)\n");
text[6] = new Text("ᠡ ᠨ ᠡ --> ᠡᠨᠡ (ene)\n");
text[7] = new Text("ᠭ ᠠ --> ᠭᠠ (gal)\n");
text[8] = new Text("ᠭ᠋ ᠠ --> ᠭ᠋ᠠ (gal+U+180B)\n");
for (int i=0; i<9; i++) {
text[i].setFill(Color.BLACK);
text[i].setFont(Font.font(family, size));
}
TextFlow textFlow = new TextFlow(text[0], text[1], text[2],
text[3], text[4], text[5], text[6], text[7], text[8]);
textFlow.setLayoutX(60);
textFlow.setLayoutY(60);
Group group = new Group(textFlow);
Scene scene = new Scene(group, 600, 650, Color.WHITE);
stage.setScene(scene);
stage.show();
}
}
====================
Sample (NG.java)
====================
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
// Run on Windows, then Java abort because of bug.
public class NG extends Application {
@Override
public void start(Stage stage) throws Exception {
String family = "Arial Unicode MS";
double size = 36;
TextFlow textFlow = new TextFlow();
textFlow.setLayoutX(60);
textFlow.setLayoutY(60);
// Unicode(GlyphID)
Text text = new Text("힣฿"); // U+2F9D4(49496) + U+0E3F(1262)
/* Inside JavaFX, 49496(Uint16) is handled as -16040(short).
* By ScriptMapper.isComplexCharCode(), U+0E3F is handled as complex.
* When in condition with minus glyphID value and complex
* , JavaFX is forcibly terminated.
* (java.lang.ArrayIndexOutOfBoundsException)
*/
text.setFill(Color.GREEN);
text.setFont(Font.font(family, size));
textFlow.getChildren().addAll(text);
Group group = new Group(textFlow);
Scene scene = new Scene(group, 1200, 300, Color.WHITE);
stage.setScene(scene);
stage.show();
}
}
--------------------------------------
Name: Akira Nakajima
E-Mail: nakajima.akira at nttcom.co.jp
--------------------------------------
More information about the openjfx-dev
mailing list