7017058: Malayalam glyph substitution is failing for Malayalam with Windows Kartika font.
8191130: Sinhala text rendering problem with C+VIRAMA+ZWJ+RA/YA+V
8195836: opentype:Bengali: "Khanda Ta" shaping issue with U+09A4 TA, U+09CD virama, U+200D ZWJ
Reviewed-by: serb, psadhukhan
--- a/src/java.desktop/share/classes/sun/font/CMap.java Thu Sep 13 10:54:11 2018 -0700
+++ b/src/java.desktop/share/classes/sun/font/CMap.java Thu Sep 13 11:31:59 2018 -0700
@@ -566,6 +566,7 @@
char getGlyph(int charCode) {
+ final int origCharCode = charCode;
int index = 0;
char glyphCode = 0;
@@ -637,8 +638,8 @@
}
}
}
- if (glyphCode != 0) {
- //System.err.println("cc="+Integer.toHexString((int)charCode) + " gc="+(int)glyphCode);
+ if (glyphCode == 0) {
+ glyphCode = getFormatCharGlyph(origCharCode);
}
return glyphCode;
}
@@ -804,6 +805,7 @@
}
char getGlyph(int charCode) {
+ final int origCharCode = charCode;
int controlGlyph = getControlCodeGlyph(charCode, true);
if (controlGlyph >= 0) {
return (char)controlGlyph;
@@ -859,7 +861,7 @@
return glyphCode;
}
}
- return 0;
+ return getFormatCharGlyph(origCharCode);
}
}
@@ -883,6 +885,7 @@
}
char getGlyph(int charCode) {
+ final int origCharCode = charCode;
int controlGlyph = getControlCodeGlyph(charCode, true);
if (controlGlyph >= 0) {
return (char)controlGlyph;
@@ -894,7 +897,7 @@
charCode -= firstCode;
if (charCode < 0 || charCode >= entryCount) {
- return 0;
+ return getFormatCharGlyph(origCharCode);
} else {
return glyphIdArray[charCode];
}
@@ -1032,6 +1035,7 @@
}
char getGlyph(int charCode) {
+ final int origCharCode = charCode;
int controlGlyph = getControlCodeGlyph(charCode, false);
if (controlGlyph >= 0) {
return (char)controlGlyph;
@@ -1057,7 +1061,7 @@
(startGlyphID[range] + (charCode - startCharCode[range]));
}
- return 0;
+ return getFormatCharGlyph(origCharCode);
}
}
@@ -1079,16 +1083,21 @@
case 0x000a:
case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID;
}
- } else if (charCode >= 0x200c) {
+ } else if (noSurrogates && charCode >= 0xFFFF) {
+ return 0;
+ }
+ return -1;
+ }
+
+ final char getFormatCharGlyph(int charCode) {
+ if (charCode >= 0x200c) {
if ((charCode <= 0x200f) ||
(charCode >= 0x2028 && charCode <= 0x202e) ||
(charCode >= 0x206a && charCode <= 0x206f)) {
- return CharToGlyphMapper.INVISIBLE_GLYPH_ID;
- } else if (noSurrogates && charCode >= 0xFFFF) {
- return 0;
+ return (char)CharToGlyphMapper.INVISIBLE_GLYPH_ID;
}
}
- return -1;
+ return 0;
}
static class UVS {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/awt/font/GlyphVector/ZWJLigatureTest.java Thu Sep 13 11:31:59 2018 -0700
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * 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
+ @bug 7017058 8191130 8195836
+ @summary Test handling of ZWJ by layout.
+ */
+
+/*
+ * A forced mapping of ZWJ (u+200D) to a special invisible glyph ID
+ * was breaking many uses of ZWJ to form ligatures in fonts supporting
+ * Indic scripts (Malayalam, Bengali, Sinhala at least) and also Emoji.
+ * Without knowing the exact properties of a font under test, and also
+ * how a layout engine maps chars to glyphs, it is difficult to write
+ * a complete robust automated test.
+ * So whilst it tries to show rendering for any fonts that claims to
+ * support the target alphabet, it will fail only when specific known
+ * fonts fail.
+ * The test automatically passes or fails only if these fonts do
+ * not ligature away the ZWJ.
+ * Besides this the test renders the specific text from these fonts
+ * and any others that claim to fully support the text, so it can be
+ * manually examined if so desired.
+ */
+
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.geom.Point2D;
+import java.awt.image.BufferedImage;
+import java.util.Locale;
+
+public class ZWJLigatureTest {
+
+ // These are fonts and scripts on Windows that should support
+ // the behaviours enough to make reliable tests";
+
+ static final String malayalamName = "Malayalam";
+ static final String malayalamFont = "Kartika";
+ static final String malayalamText = "\u0D2C\u0D3E\u0D32\u0D28\u0D4D\u200D";
+
+ static final String bengaliName = "Bengali";
+ static final String bengaliFont = "Vrinda";
+ static final String bengaliText =
+ "\u09CE \u09A4\u09CD\u200D " +
+ "\u09A4\u09BE\u09CE \u09A4\u09BE\u09A4\u09CD\u200D";
+
+ static final String sinhalaName = "Sinhala";
+ static final String sinhalaFont = "Iskoola Pota";
+ static final String sinhalaText =
+ "\u0DC1\u0DCA\u200D\u0DBB\u0DD3" +
+ "\u0D9A\u0DCA\u200D\u0DBB\u0DD2" +
+ "\u0D9A\u0DCA\u200D\u0DBB\u0DD3" +
+ "\u0DA7\u0DCA\u200D\u0DBB\u0DDA" +
+ "\u0DB6\u0DCA\u200D\u0DBB\u0DD0" +
+ "\u0D9B\u0DCA\u200D\u0DBA\u0DCF";
+
+
+ static String[] scripts = { malayalamName, bengaliName, sinhalaName };
+ static String[] fontNames = { malayalamFont, bengaliFont, sinhalaFont };
+ static String[] text = { malayalamText, bengaliText, sinhalaText };
+
+
+ static void doTest() {
+ boolean testFailed = false;
+
+ BufferedImage bi = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
+ Graphics2D g2d = (Graphics2D)bi.getGraphics();
+ FontRenderContext frc = g2d.getFontRenderContext();
+ for (int f=0; f < fontNames.length; f++) {
+ Font font = new Font(fontNames[f], Font.PLAIN, 30);
+ String family = font.getFamily(Locale.ENGLISH).toLowerCase();
+ if (!fontNames[f].toLowerCase().equals(family)) {
+ System.out.println(fontNames[f] + " not found, skipping.");
+ continue;
+ } else {
+ System.out.println("Testing " + fontNames[f] +
+ " for " + scripts[f]);
+ }
+ char[] chs = text[f].toCharArray();
+ GlyphVector gv = font.layoutGlyphVector(frc, chs, 0, chs.length, 0);
+ for (int g=0; g<gv.getNumGlyphs(); g++) {
+ int glyph = gv.getGlyphCode(g);
+ int charIdx = gv.getGlyphCharIndex(g);
+ int codePoint = text[f].codePointAt(charIdx);
+ Point2D pos = gv.getGlyphPosition(g);
+
+ if (codePoint == 0x200D) {
+ testFailed = true;
+ System.out.println("FAIL: GOT ZWJ\n");
+ }
+ System.out.println("["+g+"]: gid="+Integer.toHexString(glyph)
+ +", charIdx="+Integer.toHexString(charIdx)
+ +", codePoint="+Integer.toHexString(codePoint)
+ +", pos=["+pos.getX()+","+pos.getY()+"]");
+ }
+ }
+ if (testFailed) {
+ throw new RuntimeException("TEST FAILED");
+ }
+ }
+
+ public static void main(String[] args) {
+ doTest();
+ }
+}