Skip to content
This repository has been archived by the owner on Jun 24, 2021. It is now read-only.

8207932 : Wrong rendering of variation sequences #126

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 179 additions & 1 deletion modules/javafx.graphics/src/main/java/com/sun/javafx/font/CMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -59,6 +60,11 @@ static CMap initialize(PrismFontFile font) {
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.
Expand All @@ -76,6 +82,9 @@ static CMap initialize(PrismFontFile font) {
zeroStar = true;
encodingID = cmapBuffer.getShort();
zeroStarOffset = cmapBuffer.getInt();
if (encodingID == 5) {
offset_format[0] = zeroStarOffset;
}
}
else if (platformID == 3) {
threeStar = true;
Expand Down Expand Up @@ -133,13 +142,21 @@ static CMap createCMap(Buffer buffer, int offset) {
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));
}
}

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)
Expand Down Expand Up @@ -591,6 +608,167 @@ char getGlyph(int charCode) {

}

// 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;
char[][] glyphID;
/* e.g.
* uniStart[numSelector-1] = U+fe00(=VS1)
* 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,0xfe01,0xe0100}
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();
}

/* nonDefault glyphs table, get Unicode and glyphID */
numMappings = new int[numSelector];
uniStart = new int[numSelector][];
glyphID = new char[numSelector][];
for (int i=0; i<numSelector; i++) {
if (nonDefOff[i] == 0) {
numMappings[i] = 0;
continue;
}
buffer.position(offset+nonDefOff[i]);
numMappings[i] = buffer.getInt();
}

/* Default glyphs table, get Unicode and count */
numRanges = new int[numSelector];
defUniStart = new int[numSelector][];
additionalCnt = new short[numSelector][];
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) {
buffer.position(offset+nonDefOff[i]+4); // +4 = skip numMappings
uniStart[i] = new int[numMappings[i]];
glyphID[i] = new char[numMappings[i]];

/* nonDefault glyphs table, get Unicode and glyphID */
for (int j=0; j<numMappings[i]; j++) {
uniStart[i][j] = ((buffer.getShort() & SHORTMASK)<<8) |
(buffer.get() & BYTEMASK);
glyphID[i][j] = buffer.getChar();
}
}

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);
}
}

char getGlyph(int charCode) {
return getGlyph(charCode, 0);
}

char getGlyph(int charCode, int vs) {
if (vs == 0) return 0;
int c;

for (int v=0; v<numSelector; v++) {
if (varSelector[v] < vs) continue;
if (varSelector[v] > vs) break;

/* non default glyphs table */
if (numMappings[v] > 0) {
if (uniStart[v] == null || glyphID[v] == null) {
try {
initNonDef(v);
} catch (Exception e) {
return 0;
}
}

/* search non default glyphs table */
c = java.util.Arrays.binarySearch(uniStart[v], charCode);
if (c >= 0) {
return glyphID[v][c];
}
}

/* default glyphs table */
if (defCmap == null) break;
if (numRanges[v] > 0) {
if (defUniStart[v] == null || additionalCnt[v] == null) {
try {
initDef(v);
} catch (Exception e) {
return 0;
}
}

/* search default glyphs table */
c = java.util.Arrays.binarySearch(defUniStart[v], charCode);
if (c <= -2) {
c = -c - 2;
if (charCode >= defUniStart[v][c] &&
charCode <= defUniStart[v][c]+additionalCnt[v][c]) {
return defCmap.getGlyph(charCode);
}
} else if (c >= 0) {
return defCmap.getGlyph(charCode);
}
}

break;
}
return 0;
}
}

/* Used to substitute for bad Cmaps. */
static class NullCMapClass extends CMap {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,39 @@

package com.sun.javafx.font;

import com.sun.javafx.text.GlyphLayout;

/*
* NB the versions that take a char as an int are used by the opentype
* layout engine. If that remains in native these methods may not be
* needed in the Java class.
*/
public abstract class CharToGlyphMapper {

public static final int HI_SURROGATE_SHIFT = 10;
public static final int HI_SURROGATE_START = 0xD800;
public static final int HI_SURROGATE_END = 0xDBFF;
public static final int LO_SURROGATE_START = 0xDC00;
public static final int LO_SURROGATE_END = 0xDFFF;
public static final int SURROGATES_START = 0x10000;

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;

public static boolean isVS(int code) {
return (isIVS(code) || isSVS(code));
}

public static boolean isSVS(int code) {
return (code >= SVS_START && code <= SVS_END);
}

public static boolean isIVS(int code) {
return (code >= IVS_START && code <= IVS_END);
}

public boolean canDisplay(char cp) {
int glyph = charToGlyph(cp);
return glyph != missingGlyph;
Expand All @@ -53,35 +67,82 @@ public int getMissingGlyphCode() {
return missingGlyph;
}

public abstract int getGlyphCode(int charCode);
public abstract int getGlyphCode(int charCode, int vs);

public int charToGlyph(char unicode) {
return getGlyphCode(unicode);
public final int charToGlyph(char unicode) {
return getGlyphCode(unicode, (char)0);
}

public int charToGlyph(int unicode) {
return getGlyphCode(unicode);
public final int charToGlyph(int unicode) {
return getGlyphCode(unicode, 0);
}

public int charToGlyph(char unicode, char vs) {
return getGlyphCode(unicode, vs);
}

public int charToGlyph(int unicode, int vs) {
return getGlyphCode(unicode, vs);
}

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

/* implement following patterns
* (A) Normal char (All chars except SurrogatePair, IVS, SVS)
* (B) Surrogate_high + Surrogate_low
*
* (C) CJK + IVS_high + IVS_low
* (D) IVS_high + IVS_low (IVS only, not CJK + IVS)
* (E) Surrogate_high + Surrogate_low + IVS_high + IVS_low
*
* (F) CJK + SVS
* (G) SVS (SVS only, not CJK + SVS)
* (H) Surrogate_high + Surrogate_low + SVS
*/
int prevSurrogate = 0; // store surrogate pair to handle (E)(H)
for (int i=0; i<count; i++) {
int code = unicodes[start + i]; // char is unsigned.
if (code >= HI_SURROGATE_START &&
code <= HI_SURROGATE_END && i + 1 < count) {
char low = unicodes[start + i + 1];

if (low >= LO_SURROGATE_START &&
low <= LO_SURROGATE_END) {
code = ((code - HI_SURROGATE_START) << HI_SURROGATE_SHIFT) +
low - LO_SURROGATE_START + SURROGATES_START;
glyphs[glyphStart + i] = getGlyphCode(code);
i += 1; // Empty glyph slot after surrogate
glyphs[glyphStart + i] = INVISIBLE_GLYPH_ID;
continue;
int st = start + i;
int code = unicodes[st]; // char is unsigned.
boolean isSURROGATE = false;

if (Character.isHighSurrogate(unicodes[st]) &&
i + 1 < count && Character.isLowSurrogate(unicodes[st + 1])) {
code = Character.toCodePoint(unicodes[st], unicodes[st + 1]);
isSURROGATE = true;
}

if (isSURROGATE == false && isSVS(code) == false) {
glyphs[glyphStart + i] = getGlyphCode(code, 0); // (A) ASCII etc
prevSurrogate = 0;
} else if (isSURROGATE && isIVS(code) == false) {
glyphs[glyphStart + i] = getGlyphCode(code, 0); // (B) Surrogate
prevSurrogate = code; // store surrogate pair
} else { // == else if (isIVS || isSVS)
int glSt;
glSt = glyphStart + i;
if (prevSurrogate == 0) {
if (i > 0 && GlyphLayout.isIdeographic(unicodes[st - 1])) {
glyphs[glSt - 1] =
getGlyphCode(unicodes[st - 1], code); // (C) (F) VS
glyphs[glSt] = INVISIBLE_GLYPH_ID;
} else {
glyphs[glSt] = getGlyphCode(code, 0); // (D) (G) VS only
}
} else { // Surrogate + VS
glyphs[glSt - 2] =
getGlyphCode(prevSurrogate, code); // (E) (H)
glyphs[glSt - 1] = INVISIBLE_GLYPH_ID;
glyphs[glSt] = INVISIBLE_GLYPH_ID;
prevSurrogate = 0;
}
}
glyphs[glyphStart + i] = getGlyphCode(code);

if (isSURROGATE) {
i += 1; // Empty glyph slot after surrogate
glyphs[glyphStart + i] = INVISIBLE_GLYPH_ID;
continue;
}
}
}

Expand Down
Loading