1 /*
2  * Copyright (c) 1998, 2016, 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 4178145 8144015
27 */
28 
29 /*
30  * Copyright 1998 IBM Corp.  All Rights Reserved.
31  */
32 
33 import java.awt.Color;
34 import java.awt.Font;
35 import java.awt.Frame;
36 import java.awt.Graphics;
37 import java.awt.Graphics2D;
38 import java.awt.GraphicsEnvironment;
39 import java.awt.font.TextAttribute;
40 import java.awt.font.TextLayout;
41 import java.awt.font.TextHitInfo;
42 import java.awt.font.FontRenderContext;
43 import java.util.Hashtable;
44 
45 /**
46  * This test ensures that TextLayout will not place a caret within
47  * an Arabic lam-alef ligature, and will correctly caret through
48  * bidirectional text with numbers.
49  */
50 
51 public class LigatureCaretTest {
52 
main(String[] args)53     public static void main(String[] args) {
54 
55         testBidiWithNumbers();
56         testLamAlef();
57         System.out.println("LigatureCaretTest PASSED");
58     }
59 
60     private static final FontRenderContext frc =
61                                 new FontRenderContext(null, false, false);
62 
getFontForText(String s)63     private static Font getFontForText(String s) {
64         GraphicsEnvironment ge =
65            GraphicsEnvironment.getLocalGraphicsEnvironment();
66         Font[] fonts = ge.getAllFonts();
67 
68         for (Font f : fonts) {
69            if (f.canDisplayUpTo(s) == -1) {
70                return f.deriveFont(Font.PLAIN, 24);
71            }
72         }
73         return null;
74     }
75 
76     /**
77      * Caret through text mixed-direction text and check the results.
78      * If the test fails an Error is thrown.
79      * @exception an Error is thrown if the test fails
80      */
testBidiWithNumbers()81     public static void testBidiWithNumbers() {
82 
83         String bidiWithNumbers = "abc\u05D0\u05D1\u05D2123abc";
84         Font font = getFontForText(bidiWithNumbers);
85         if (font == null) {
86             return;
87         }
88         Hashtable map = new Hashtable();
89         map.put(TextAttribute.FONT, font);
90 
91         // visual order for the text:
92         // abc123<gimel><bet><aleph>abc
93 
94         int[] carets = { 0, 1, 2, 3, 7, 8, 6, 5, 4, 9, 10, 11, 12 };
95         TextLayout layout = new TextLayout(bidiWithNumbers, map, frc);
96 
97         // Caret through TextLayout in both directions and check results.
98         for (int i=0; i < carets.length-1; i++) {
99 
100             TextHitInfo hit = layout.getNextRightHit(carets[i]);
101             if (hit.getInsertionIndex() != carets[i+1]) {
102                 throw new Error("right hit failed within layout");
103             }
104         }
105 
106         if (layout.getNextRightHit(carets[carets.length-1]) != null) {
107             throw new Error("right hit failed at end of layout");
108         }
109 
110         for (int i=carets.length-1; i > 0; i--) {
111 
112             TextHitInfo hit = layout.getNextLeftHit(carets[i]);
113             if (hit.getInsertionIndex() != carets[i-1]) {
114                 throw new Error("left hit failed within layout");
115             }
116         }
117 
118         if (layout.getNextLeftHit(carets[0]) != null) {
119             throw new Error("left hit failed at end of layout");
120         }
121     }
122 
123     /**
124      * Ensure proper careting and hit-testing behavior with
125      * a lam-alef ligature.
126      * If the test fails, an Error is thrown.
127      * @exception an Error is thrown if the test fails
128      */
testLamAlef()129     public static void testLamAlef() {
130 
131         // lam-alef form a mandantory ligature.
132         final String lamAlef = "\u0644\u0627";
133         final String ltrText = "abcd";
134 
135         Font font = getFontForText(lamAlef+ltrText);
136         if (font == null) {
137             return;
138         }
139         Hashtable map = new Hashtable();
140         map.put(TextAttribute.FONT, font);
141 
142         // Create a TextLayout with just a lam-alef sequence.  There
143         // should only be two valid caret positions:  one at
144         // insertion offset 0 and the other at insertion offset 2.
145         TextLayout layout = new TextLayout(lamAlef, map, frc);
146 
147         TextHitInfo hit;
148 
149         hit = layout.getNextLeftHit(0);
150         if (hit.getInsertionIndex() != 2) {
151             throw new Error("Left hit failed.  Hit:" + hit);
152         }
153 
154         hit = layout.getNextRightHit(2);
155         if (hit.getInsertionIndex() != 0) {
156             throw new Error("Right hit failed.  Hit:" + hit);
157         }
158 
159         hit = layout.hitTestChar(layout.getAdvance()/2, 0);
160         if (hit.getInsertionIndex() != 0 && hit.getInsertionIndex() != 2) {
161             throw new Error("Hit-test allowed incorrect caret.  Hit:" + hit);
162         }
163 
164 
165         // Create a TextLayout with some left-to-right text
166         // before the lam-alef sequence.  There should not be
167         // a caret position between the lam and alef.
168         layout = new TextLayout(ltrText+lamAlef, map, frc);
169 
170         final int ltrLen = ltrText.length();
171         final int layoutLen = layout.getCharacterCount();
172 
173         for (int i=0; i < ltrLen; i++) {
174             hit = layout.getNextRightHit(i);
175             if (hit.getInsertionIndex() != i+1) {
176                 throw new Error("Right hit failed in ltr text.");
177             }
178         }
179 
180         hit = layout.getNextRightHit(ltrLen);
181         if (layoutLen != hit.getInsertionIndex()) {
182             throw new Error("Right hit failed at direction boundary.");
183         }
184 
185         hit = layout.getNextLeftHit(layoutLen);
186         if (hit.getInsertionIndex() != ltrLen) {
187             throw new Error("Left hit failed at end of text.");
188         }
189     }
190 }
191