This commit is contained in:
Phil Race 2018-06-25 14:56:07 -07:00
commit a6731ea341
11 changed files with 346 additions and 6 deletions

View File

@ -140,6 +140,7 @@ abstract class CMap {
* Using this saves running character coverters repeatedly.
*/
char[] xlat;
UVS uvs = null;
static CMap initialize(TrueTypeFont font) {
@ -149,6 +150,7 @@ abstract class CMap {
int three0=0, three1=0, three2=0, three3=0, three4=0, three5=0,
three6=0, three10=0;
int zero5 = 0; // for Unicode Variation Sequences
boolean threeStar = false;
ByteBuffer cmapBuffer = font.getTableBuffer(TrueTypeFont.cmapTag);
@ -173,6 +175,12 @@ abstract class CMap {
case 6: three6 = offset; break; // Johab
case 10: three10 = offset; break; // MS Unicode surrogates
}
} else if (platformID == 0) {
encodingID = cmapBuffer.getShort();
offset = cmapBuffer.getInt();
if (encodingID == 5) {
zero5 = offset;
}
}
}
@ -262,6 +270,10 @@ abstract class CMap {
*/
cmap = createCMap(cmapBuffer, cmapBuffer.getInt(8), null);
}
// For Unicode Variation Sequences
if (cmap != null && zero5 != 0) {
cmap.createUVS(cmapBuffer, zero5);
}
return cmap;
}
@ -424,6 +436,25 @@ abstract class CMap {
}
}
private void createUVS(ByteBuffer buffer, int offset) {
int subtableFormat = buffer.getChar(offset);
if (subtableFormat == 14) {
long subtableLength = buffer.getInt(offset + 2) & INTMASK;
if (offset + subtableLength > buffer.capacity()) {
if (FontUtilities.isLogging()) {
FontUtilities.getLogger()
.warning("Cmap UVS subtable overflows buffer.");
}
}
try {
this.uvs = new UVS(buffer, offset);
} catch (Throwable t) {
t.printStackTrace();
}
}
return;
}
/*
final char charVal(byte[] cmap, int index) {
return (char)(((0xff & cmap[index]) << 8)+(0xff & cmap[index+1]));
@ -1059,4 +1090,87 @@ abstract class CMap {
}
return -1;
}
static class UVS {
int numSelectors;
int[] selector;
//for Non-Default UVS Table
int[] numUVSMapping;
int[][] unicodeValue;
char[][] glyphID;
UVS(ByteBuffer buffer, int offset) {
numSelectors = buffer.getInt(offset+6);
selector = new int[numSelectors];
numUVSMapping = new int[numSelectors];
unicodeValue = new int[numSelectors][];
glyphID = new char[numSelectors][];
for (int i = 0; i < numSelectors; i++) {
buffer.position(offset + 10 + i * 11);
selector[i] = (buffer.get() & 0xff) << 16; //UINT24
selector[i] += (buffer.get() & 0xff) << 8;
selector[i] += buffer.get() & 0xff;
//skip Default UVS Table
//for Non-Default UVS Table
int tableOffset = buffer.getInt(offset + 10 + i * 11 + 7);
if (tableOffset == 0) {
numUVSMapping[i] = 0;
} else if (tableOffset > 0) {
buffer.position(offset+tableOffset);
numUVSMapping[i] = buffer.getInt() & INTMASK;
unicodeValue[i] = new int[numUVSMapping[i]];
glyphID[i] = new char[numUVSMapping[i]];
for (int j = 0; j < numUVSMapping[i]; j++) {
int temp = (buffer.get() & 0xff) << 16; //UINT24
temp += (buffer.get() & 0xff) << 8;
temp += buffer.get() & 0xff;
unicodeValue[i][j] = temp;
glyphID[i][j] = buffer.getChar();
}
}
}
}
static final int VS_NOGLYPH = 0;
private int getGlyph(int charCode, int variationSelector) {
int targetSelector = -1;
for (int i = 0; i < numSelectors; i++) {
if (selector[i] == variationSelector) {
targetSelector = i;
break;
}
}
if (targetSelector == -1) {
return VS_NOGLYPH;
}
if (numUVSMapping[targetSelector] > 0) {
int index = java.util.Arrays.binarySearch(
unicodeValue[targetSelector], charCode);
if (index >= 0) {
return glyphID[targetSelector][index];
}
}
return VS_NOGLYPH;
}
}
char getVariationGlyph(int charCode, int variationSelector) {
char glyph = 0;
if (uvs == null) {
glyph = getGlyph(charCode);
} else {
int result = uvs.getGlyph(charCode, variationSelector);
if (result > 0) {
glyph = (char)(result & 0xFFFF);
} else {
glyph = getGlyph(charCode);
}
}
return glyph;
}
}

View File

@ -36,6 +36,10 @@ public abstract class CharToGlyphMapper {
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 VS_START = 0xFE00;
public static final int VS_END = 0xFE0F;
public static final int VSS_START = 0xE0100;
public static final int VSS_END = 0xE01FF;
public static final int UNINITIALIZED_GLYPH = -1;
public static final int INVISIBLE_GLYPH_ID = 0xffff;
@ -77,6 +81,11 @@ public abstract class CharToGlyphMapper {
return glyphs[0];
}
public int charToVariationGlyph(int unicode, int variationSelector) {
// Override this if variation selector is supported.
return charToGlyph(unicode);
}
public abstract int getNumGlyphs();
public abstract void charsToGlyphs(int count,
@ -88,4 +97,9 @@ public abstract class CharToGlyphMapper {
public abstract void charsToGlyphs(int count,
int[] unicodes, int[] glyphs);
public static boolean isVariationSelector(int charCode) {
return ((charCode >= VSS_START && charCode <= VSS_END) ||
(charCode >= VS_START && charCode <= VS_END));
}
}

View File

@ -214,7 +214,8 @@ public class CompositeGlyphMapper extends CharToGlyphMapper {
if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {
continue;
}
else if (FontUtilities.isComplexCharCode(code)) {
else if (FontUtilities.isComplexCharCode(code) ||
CharToGlyphMapper.isVariationSelector(code)) {
return true;
}
else if (code >= 0x10000) {

View File

@ -524,6 +524,10 @@ public abstract class Font2D {
return getMapper().charToGlyph(wchar);
}
public int charToVariationGlyph(int wchar, int variationSelector) {
return getMapper().charToVariationGlyph(wchar, variationSelector);
}
public int getMissingGlyphCode() {
return getMapper().getMissingGlyphCode();
}

View File

@ -93,6 +93,32 @@ public class TrueTypeGlyphMapper extends CharToGlyphMapper {
}
}
private char getGlyphFromCMAP(int charCode, int variationSelector) {
if (variationSelector == 0) {
return getGlyphFromCMAP(charCode);
}
try {
char glyphCode = cmap.getVariationGlyph(charCode,
variationSelector);
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) +
" for vs " + Integer.toHexString(variationSelector));
}
return (char)missingGlyph;
}
} catch (Exception e) {
handleBadCMAP();
return (char) missingGlyph;
}
}
private void handleBadCMAP() {
if (FontUtilities.isLogging()) {
FontUtilities.getLogger().severe("Null Cmap for " + font +
@ -136,6 +162,18 @@ public class TrueTypeGlyphMapper extends CharToGlyphMapper {
return glyph;
}
@Override
public int charToVariationGlyph(int unicode, int variationSelector) {
if (needsJAremapping) {
unicode = remapJAIntChar(unicode);
}
int glyph = getGlyphFromCMAP(unicode, variationSelector);
if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) {
font.glyphToCharMap[glyph] = (char)unicode;
}
return glyph;
}
public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) {
for (int i=0;i<count;i++) {
if (needsJAremapping) {
@ -221,7 +259,8 @@ public class TrueTypeGlyphMapper extends CharToGlyphMapper {
if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {
continue;
}
else if (FontUtilities.isComplexCharCode(code)) {
else if (FontUtilities.isComplexCharCode(code) ||
CharToGlyphMapper.isVariationSelector(code)) {
return true;
}
else if (code >= 0x10000) {

View File

@ -39,6 +39,7 @@ typedef struct FontManagerNativeIDs {
jmethodID getTableBytesMID;
jmethodID canDisplayMID;
jmethodID f2dCharToGlyphMID;
jmethodID f2dCharToVariationGlyphMID;
/* sun/font/CharToGlyphMapper methods */
jmethodID charToGlyphMID;

View File

@ -48,10 +48,18 @@ hb_jdk_get_glyph (hb_font_t *font HB_UNUSED,
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);
if (variation_selector == 0) {
*glyph = (hb_codepoint_t)env->CallIntMethod(
font2D, sunFontIDs.f2dCharToGlyphMID, unicode);
} else {
*glyph = (hb_codepoint_t)env->CallIntMethod(
font2D, sunFontIDs.f2dCharToVariationGlyphMID,
unicode, variation_selector);
}
if (env->ExceptionOccurred())
{
env->ExceptionClear();
}
if ((int)*glyph < 0) {
*glyph = 0;
}

View File

@ -144,6 +144,8 @@ static void initFontIDs(JNIEnv *env) {
CHECK_NULL(tmpClass = (*env)->FindClass(env, "sun/font/Font2D"));
CHECK_NULL(sunFontIDs.f2dCharToGlyphMID =
(*env)->GetMethodID(env, tmpClass, "charToGlyph", "(I)I"));
CHECK_NULL(sunFontIDs.f2dCharToVariationGlyphMID =
(*env)->GetMethodID(env, tmpClass, "charToVariationGlyph", "(II)I"));
CHECK_NULL(sunFontIDs.getMapperMID =
(*env)->GetMethodID(env, tmpClass, "getMapper",
"()Lsun/font/CharToGlyphMapper;"));

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,92 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/* @test
* @summary Verify Variation Selector matches an expected image
* @bug 8187100
* @ignore Requires a special font installed.
*/
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.ImageIcon;
import java.awt.Font;
import java.awt.Color;
public class TestVS {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestVS().run();
}
});
}
private void run() {
Font ourFont = null;
final String fontName = "ipaexm.ttf";
// download from https://ipafont.ipa.go.jp/node26#en
// and place in {user.home}/fonts/
try {
ourFont = Font.createFont(Font.TRUETYPE_FONT,
new java.io.File(new java.io.File(
System.getProperty("user.home"),
"fonts"), fontName));
ourFont = ourFont.deriveFont((float)48.0);
final String actualFontName = ourFont.getFontName();
if (!actualFontName.equals("IPAexMincho")) {
System.err.println("*** Warning: missing font IPAexMincho.");
System.err.println("*** Using font: " + actualFontName);
}
} catch(Throwable t) {
t.printStackTrace();
System.err.println("Fail: " + t);
return;
}
JFrame frame = new JFrame(System.getProperty("java.version"));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
final JTextArea label = new JTextArea("empty");
label.setSize(400, 300);
label.setBorder(new LineBorder(Color.black));
label.setFont(ourFont);
final String str = "\u845b\udb40\udd00\u845b\udb40\udd01\n";
label.setText(str);
panel.add(label);
panel.add(new JLabel(ourFont.getFamily()));
// Show the expected result.
panel.add(new JLabel(new ImageIcon("TestVS-expect.png")));
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}

View File

@ -0,0 +1,65 @@
/* @test
* @summary Verify two identical 'a's are rendered
* @bug 8187100
* @ignore Requires a special font installed.
*/
import javax.swing.JFrame;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
public class VariationSelectorTest {
// A font supporting Unicode variation selectors is required
// At least DejaVu 2.20 from 2007
private static final Font FONT = new Font("DejaVu Sans", Font.PLAIN, 12);
public static void main(String[] args) {
final String fontName = FONT.getFontName();
if (!fontName.equals("DejaVuSans")) {
System.err.println("*** Warning: Font DejaVuSans not installed.");
System.err.println("*** Using font: " + fontName);
}
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.add(new MyComponent());
frame.setSize(200, 200);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
});
}
private static class MyComponent extends JComponent {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
FontRenderContext frc = g2d.getFontRenderContext();
String text = "a";
GlyphVector gv = FONT.layoutGlyphVector(
frc, text.toCharArray(), 0, text.length(),
Font.LAYOUT_LEFT_TO_RIGHT);
System.out.println("'a'=" + gv.getNumGlyphs());
g2d.drawString("=" + gv.getNumGlyphs() + " ('a')", 100, 50);
g2d.drawGlyphVector(gv, 80, 50);
String text2 = "a\ufe00";
GlyphVector gv2 = FONT.layoutGlyphVector(
frc, text2.toCharArray(), 0, text2.length(),
Font.LAYOUT_LEFT_TO_RIGHT);
g2d.drawGlyphVector(gv2, 80, 100);
System.out.println("'a'+VS=" + gv2.getNumGlyphs());
g2d.drawString("=" + gv2.getNumGlyphs() + " ('a'+VS)", 100, 100);
if ((gv.getNumGlyphs() == 1) && (gv2.getNumGlyphs() == 1)) {
System.out.println("PASS");
g2d.drawString("PASS", 10, 15);
} else {
System.err.println("FAIL");
g2d.drawString("FAIL", 10, 15);
}
}
}
}