7017058: Malayalam glyph substitution is failing for Malayalam with Windows Kartika font.
authorprr
Thu, 13 Sep 2018 11:31:59 -0700
changeset 51923 16a0f33a5052
parent 51922 16c6d8d35fd7
child 51924 cfbfa216f3c0
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
src/java.desktop/share/classes/sun/font/CMap.java
test/jdk/java/awt/font/GlyphVector/ZWJLigatureTest.java
--- 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();
+    }
+}