1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 /* @test
  26    @bug 7017058 8191130 8195836
  27    @summary Test handling of ZWJ by layout.
  28  */
  29 
  30 /*
  31  * A forced mapping of ZWJ (u+200D) to a special invisible glyph ID
  32  * was breaking many uses of ZWJ to form ligatures in fonts supporting
  33  * Indic scripts (Malayalam, Bengali, Sinhala at least) and also Emoji.
  34  * Without knowing the exact properties of a font under test, and also
  35  * how a layout engine maps chars to glyphs, it is difficult to write
  36  * a complete robust automated test.
  37  * So whilst it tries to show rendering for any fonts that claims to
  38  * support the target alphabet, it will fail only when specific known
  39  * fonts fail.
  40  * The test automatically passes or fails only if these fonts do
  41  * not ligature away the ZWJ.
  42  * Besides this the test renders the specific text from these fonts
  43  * and any others that claim to fully support the text, so it can be
  44  * manually examined if so desired.
  45  */
  46 
  47 import java.awt.Font;
  48 import java.awt.Graphics2D;
  49 import java.awt.font.FontRenderContext;
  50 import java.awt.font.GlyphVector;
  51 import java.awt.geom.Point2D;
  52 import java.awt.image.BufferedImage;
  53 import java.util.Locale;
  54 
  55 public class ZWJLigatureTest {
  56 
  57    // These are fonts and scripts on Windows that should support
  58    // the behaviours enough to make reliable tests";
  59 
  60    static final String malayalamName = "Malayalam";
  61    static final String malayalamFont = "Kartika";
  62    static final String malayalamText = "\u0D2C\u0D3E\u0D32\u0D28\u0D4D\u200D";
  63 
  64    static final String bengaliName = "Bengali";
  65    static final String bengaliFont = "Vrinda";
  66    static final String bengaliText =
  67        "\u09CE \u09A4\u09CD\u200D " +
  68        "\u09A4\u09BE\u09CE \u09A4\u09BE\u09A4\u09CD\u200D";
  69 
  70    static final String sinhalaName = "Sinhala";
  71    static final String sinhalaFont = "Iskoola Pota";
  72    static final String sinhalaText =
  73        "\u0DC1\u0DCA\u200D\u0DBB\u0DD3" +
  74        "\u0D9A\u0DCA\u200D\u0DBB\u0DD2" +
  75        "\u0D9A\u0DCA\u200D\u0DBB\u0DD3" +
  76        "\u0DA7\u0DCA\u200D\u0DBB\u0DDA" +
  77        "\u0DB6\u0DCA\u200D\u0DBB\u0DD0" +
  78        "\u0D9B\u0DCA\u200D\u0DBA\u0DCF";
  79 
  80 
  81    static String[] scripts = { malayalamName, bengaliName, sinhalaName };
  82    static String[] fontNames = { malayalamFont, bengaliFont, sinhalaFont };
  83    static String[] text = { malayalamText, bengaliText, sinhalaText };
  84 
  85 
  86    static void doTest() {
  87        boolean testFailed = false;
  88 
  89        BufferedImage bi = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
  90        Graphics2D g2d = (Graphics2D)bi.getGraphics();
  91        FontRenderContext frc = g2d.getFontRenderContext();
  92        for (int f=0; f < fontNames.length; f++) {
  93            Font font = new Font(fontNames[f], Font.PLAIN, 30);
  94            String family = font.getFamily(Locale.ENGLISH).toLowerCase();
  95            if (!fontNames[f].toLowerCase().equals(family)) {
  96                System.out.println(fontNames[f] + " not found, skipping.");
  97                continue;
  98            } else {
  99                System.out.println("Testing " + fontNames[f] +
 100                                   " for " + scripts[f]);
 101            }
 102            char[] chs = text[f].toCharArray();
 103            GlyphVector gv = font.layoutGlyphVector(frc, chs, 0, chs.length, 0);
 104            for (int g=0; g<gv.getNumGlyphs(); g++) {
 105                int glyph = gv.getGlyphCode(g);
 106                int charIdx = gv.getGlyphCharIndex(g);
 107                int codePoint = text[f].codePointAt(charIdx);
 108                Point2D pos = gv.getGlyphPosition(g);
 109 
 110                if (codePoint == 0x200D) {
 111                   testFailed = true;
 112                   System.out.println("FAIL: GOT ZWJ\n");
 113                }
 114                System.out.println("["+g+"]: gid="+Integer.toHexString(glyph)
 115                    +", charIdx="+Integer.toHexString(charIdx)
 116                    +", codePoint="+Integer.toHexString(codePoint)
 117                    +", pos=["+pos.getX()+","+pos.getY()+"]");
 118                }
 119            }
 120            if (testFailed) {
 121                throw new RuntimeException("TEST FAILED");
 122            }
 123     }
 124 
 125     public static void main(String[] args) {
 126         doTest();
 127     }
 128 }