Merge
This commit is contained in:
commit
a6731ea341
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -39,6 +39,7 @@ typedef struct FontManagerNativeIDs {
|
||||
jmethodID getTableBytesMID;
|
||||
jmethodID canDisplayMID;
|
||||
jmethodID f2dCharToGlyphMID;
|
||||
jmethodID f2dCharToVariationGlyphMID;
|
||||
|
||||
/* sun/font/CharToGlyphMapper methods */
|
||||
jmethodID charToGlyphMID;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;"));
|
||||
|
BIN
test/jdk/java/awt/font/TextLayout/TestVS-expect.png
Normal file
BIN
test/jdk/java/awt/font/TextLayout/TestVS-expect.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
92
test/jdk/java/awt/font/TextLayout/TestVS.java
Normal file
92
test/jdk/java/awt/font/TextLayout/TestVS.java
Normal 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);
|
||||
}
|
||||
}
|
65
test/jdk/java/awt/font/TextLayout/VariationSelectorTest.java
Normal file
65
test/jdk/java/awt/font/TextLayout/VariationSelectorTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user