--- old/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java 2018-06-18 09:58:54.000000000 -0700 +++ new/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java 2018-06-18 09:58:54.000000000 -0700 @@ -133,6 +133,26 @@ return missingGlyph; } + private int convertToGlyph(int unicode, int variationSelector) { + if (variationSelector == 0) { + return convertToGlyph(unicode); + } + for (int slot = 0; slot < font.numSlots; slot++) { + if (!hasExcludes || !font.isExcludedChar(slot, unicode)) { + CharToGlyphMapper mapper = getSlotMapper(slot); + if (mapper instanceof TrueTypeGlyphMapper) { + int glyphCode = ((TrueTypeGlyphMapper)mapper). + getGlyphOfVS(unicode, variationSelector); + if (glyphCode != mapper.getMissingGlyphCode()) { + glyphCode = compositeGlyphCode(slot, glyphCode); + return glyphCode; + } + } + } + } + return convertToGlyph(unicode); //retry without Variation Selector + } + public int getNumGlyphs() { int numGlyphs = 0; /* The number of glyphs in a composite is affected by @@ -205,6 +225,9 @@ glyphs[i + 1] = INVISIBLE_GLYPH_ID; } } + if (isVariationSelector(code)) { + return charsToGlyphsNSVS(count, unicodes, glyphs); + } int gc = glyphs[i] = getCachedGlyphCode(code); if (gc == UNINITIALIZED_GLYPH) { @@ -226,6 +249,69 @@ return false; } + private boolean charsToGlyphsNSVS(int count, char[] unicodes, + int[] glyphs) { + + for (int i = 0; i < count; i++) { + int code = unicodes[i]; // char is unsigned. + int step = 1; + int variationSelector = 0; + + if (code >= HI_SURROGATE_START && + code <= HI_SURROGATE_END && i < count - 1) { + char low = unicodes[i + 1]; + + if (low >= LO_SURROGATE_START && + low <= LO_SURROGATE_END) { + code = (code - HI_SURROGATE_START) * + 0x400 + low - LO_SURROGATE_START + 0x10000; + glyphs[i + 1] = INVISIBLE_GLYPH_ID; + step = 2; + } + } + + if (i < count - step && + isVariationSelectorBMP(unicodes[i+step]) && + isVSBaseChar(code)) { + variationSelector = unicodes[i+step]; + glyphs[i] = convertToGlyph(code, variationSelector); + glyphs[i+step] = INVISIBLE_GLYPH_ID; + i += 1; + } else if (i < count - step -1 && + isVariationSelectorExt(unicodes[i+step], + unicodes[i+step+1]) && + isVSBaseChar(code)) { + variationSelector = (unicodes[i+step] + - HI_SURROGATE_START) * 0x400 + + unicodes[i+step+1] - LO_SURROGATE_START + + 0x10000; + glyphs[i] = convertToGlyph(code, variationSelector); + glyphs[i+step] = INVISIBLE_GLYPH_ID; + glyphs[i+step+1] = INVISIBLE_GLYPH_ID; + i += 2; + } + if (variationSelector == 0) { + int gc = glyphs[i] = getCachedGlyphCode(code); + if (gc == UNINITIALIZED_GLYPH) { + glyphs[i] = convertToGlyph(code); + } + } + + if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { + continue; + } + else if (FontUtilities.isComplexCharCode(code)) { + return true; + } + else if (code >= 0x10000) { + i += 1; // Empty glyph slot after surrogate + continue; + } + } + + return false; + } + /* The conversion is not very efficient - looping as it does, converting * one char at a time. However the cache should fill very rapidly. */ @@ -241,6 +327,10 @@ low <= LO_SURROGATE_END) { code = (code - HI_SURROGATE_START) * 0x400 + low - LO_SURROGATE_START + 0x10000; + if (isVariationSelector(code)) { + charsToGlyphsVS(count, unicodes, glyphs); + return; + } int gc = glyphs[i] = getCachedGlyphCode(code); if (gc == UNINITIALIZED_GLYPH) { @@ -250,6 +340,9 @@ glyphs[i] = INVISIBLE_GLYPH_ID; continue; } + } else if (isVariationSelectorBMP(unicodes[i])) { + charsToGlyphsVS(count, unicodes, glyphs); + return; } int gc = glyphs[i] = getCachedGlyphCode(code); @@ -259,9 +352,65 @@ } } + private void charsToGlyphsVS(int count, char[] unicodes, int[] glyphs) { + for (int i = 0; i < count; i++) { + int code = unicodes[i]; // char is unsigned. + int variationSelector = 0; + int step = 1; + + if (code >= HI_SURROGATE_START && + code <= HI_SURROGATE_END && i < count - 1) { + char low = unicodes[i + 1]; + + if (low >= LO_SURROGATE_START && + low <= LO_SURROGATE_END) { + code = (code - HI_SURROGATE_START) * + 0x400 + low - LO_SURROGATE_START + 0x10000; + + glyphs[i+1] = INVISIBLE_GLYPH_ID; + step = 2; + } + } + + if (i < count - step && + isVariationSelectorBMP(unicodes[i+step]) && + isVSBaseChar(code)) { + variationSelector = unicodes[i+step]; + glyphs[i] = convertToGlyph(code, variationSelector); + glyphs[i+step] = INVISIBLE_GLYPH_ID; + i += 1; + } else if (i < count - step -1 && + isVariationSelectorExt(unicodes[i+step], + unicodes[i+step+1]) && + isVSBaseChar(code)) { + variationSelector = (unicodes[i+step] + - HI_SURROGATE_START) * 0x400 + + unicodes[i+step+1] - LO_SURROGATE_START + + 0x10000; + glyphs[i] = convertToGlyph(code, variationSelector); + glyphs[i+step] = INVISIBLE_GLYPH_ID; + glyphs[i+step+1] = INVISIBLE_GLYPH_ID; + i += 2; + } + if (variationSelector == 0) { + int gc = glyphs[i] = getCachedGlyphCode(code); + if (gc == UNINITIALIZED_GLYPH) { + glyphs[i] = convertToGlyph(code); + } + } + if (code >= 0x10000) { + i++; + } + } + } + public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { for (int i=0; i