1 /*
2  * Copyright (c) 2004, 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 4328745 5090704 8166111 8176510 8202767
27    @summary exercise getLayoutFlags, getGlyphCharIndex, getGlyphCharIndices
28  */
29 
30 import java.awt.Font;
31 import static java.awt.Font.*;
32 import java.awt.GraphicsEnvironment;
33 import java.awt.font.FontRenderContext;
34 import java.awt.font.GlyphVector;
35 import static java.awt.font.GlyphVector.*;
36 import java.awt.geom.AffineTransform;
37 import java.awt.geom.Point2D;
38 
39 public class TestLayoutFlags {
40 
main(String[] args)41     static public void main(String[] args) {
42         new TestLayoutFlags().runTest();
43     }
44 
runTest()45     void runTest() {
46 
47         Font font = findFont("abcde");
48         if (font == null) {
49            return; // this system is no use for this test.
50         }
51 
52         String latin1 = "This is a latin1 string"; // none
53         String hebrew = "\u05d0\u05d1\u05d2\u05d3"; // rtl
54         String arabic = "\u0646\u0644\u0622\u0646"; // rtl + mc/g
55         String hindi = "\u0939\u093f\u0923\u094d\u0921\u0940"; // ltr + reorder
56         String tamil = "\u0b9c\u0bcb"; // ltr + mg/c + split
57 
58         FontRenderContext frc = new FontRenderContext(null, true, true);
59 
60         // get glyph char indices needs to initializes layoutFlags before
61         // use (5090704)
62         {
63           GlyphVector gv = font.createGlyphVector(frc, "abcde");
64           int ix = gv.getGlyphCharIndex(0);
65           if (ix != 0) {
66             throw new Error("glyph 0 incorrectly mapped to char " + ix);
67           }
68           int[] ixs = gv.getGlyphCharIndices(0, gv.getNumGlyphs(), null);
69           for (int i = 0; i < ixs.length; ++i) {
70             if (ixs[i] != i) {
71               throw new Error("glyph " + i + " incorrectly mapped to char " + ixs[i]);
72             }
73           }
74         }
75 
76         GlyphVector latinGV = makeGlyphVector("latin", latin1, false, frc);
77         GlyphVector hebrewGV = makeGlyphVector("hebrew", hebrew, true, frc);
78         GlyphVector arabicGV = makeGlyphVector("arabic", arabic, true, frc);
79         GlyphVector hindiGV = makeGlyphVector("devanagari", hindi, false, frc);
80         GlyphVector tamilGV = makeGlyphVector("tamil", tamil, false, frc);
81 
82         GlyphVector latinPos = font.createGlyphVector(frc, latin1);
83         Point2D pt = latinPos.getGlyphPosition(0);
84         pt.setLocation(pt.getX(), pt.getY() + 1.0);
85         latinPos.setGlyphPosition(0, pt);
86 
87         GlyphVector latinTrans = font.createGlyphVector(frc, latin1);
88         latinTrans.setGlyphTransform(0, AffineTransform.getRotateInstance(.15));
89 
90         test("latin", latinGV, true, 0);
91         test("hebrew", hebrewGV, true,  FLAG_RUN_RTL);
92         test("arabic", arabicGV, true, FLAG_COMPLEX_GLYPHS | FLAG_RUN_RTL);
93         test("hindi", hindiGV, true, FLAG_COMPLEX_GLYPHS);
94         test("tamil", tamilGV, true, FLAG_COMPLEX_GLYPHS);
95         test("pos", latinPos, true, 0);
96         test("trans", latinTrans, false, 0);
97     }
98 
isLogicalFont(Font f)99     static boolean isLogicalFont(Font f) {
100         String family = f.getFamily().toLowerCase();
101         switch (family) {
102           case "dialog":
103           case "dialoginput":
104           case "serif":
105           case "sansserif":
106           case "monospaced": return true;
107           default: return false;
108         }
109     }
110 
111     Font[] allFonts = null;
112 
findFont(String text)113     Font findFont(String text) {
114         if (allFonts == null) {
115             allFonts =
116                 GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
117         }
118         for (Font f : allFonts) {
119             if (isLogicalFont(f)) {
120                 continue;
121             }
122             if (f.canDisplayUpTo(text) == -1) {
123                  return f.deriveFont(Font.PLAIN, 24);
124             }
125         }
126         return null;
127     }
128 
makeGlyphVector(String script, String text, boolean rtl, FontRenderContext frc)129     GlyphVector makeGlyphVector(String script, String text,
130                                 boolean rtl, FontRenderContext frc) {
131 
132         Font font = findFont(text);
133         if (font == null) {
134             System.out.println("No font found for script " + script);
135             return null;
136         } else {
137             System.out.println("Using " + font.getFontName() +
138                                " for script " + script);
139         }
140         int flags = rtl ? LAYOUT_RIGHT_TO_LEFT : LAYOUT_LEFT_TO_RIGHT;
141         return font.layoutGlyphVector(frc, text.toCharArray(),
142                                       0, text.length(), flags);
143     }
144 
test(String name, GlyphVector gv, boolean layout, int allowedFlags)145     void test(String name, GlyphVector gv, boolean layout, int allowedFlags) {
146 
147         if (gv == null) {
148             return;
149         }
150         int iv = (layout) ? FLAG_HAS_POSITION_ADJUSTMENTS : 0;
151 
152         int computedFlags = computeFlags(gv, iv) & gv.FLAG_MASK;
153         int actualFlags = gv.getLayoutFlags() & gv.FLAG_MASK;
154 
155         System.out.println("\n*** " + name + " ***");
156         System.out.println(" test flags");
157         System.out.print("computed ");
158         printFlags(computedFlags);
159         System.out.print("  actual ");
160         printFlags(actualFlags);
161         System.out.print("allowed layout ");
162         printFlags(allowedFlags);
163 
164         if (computedFlags != actualFlags) {
165             // Depending on the font, if layout is run for a RTL script
166             // you might get that flag set, or COMPLEX_GLYPHS instead.
167             boolean error = false;
168             int COMPLEX_RTL = FLAG_COMPLEX_GLYPHS | FLAG_RUN_RTL;
169             if (allowedFlags == 0) {
170                 error = (allowedFlags & COMPLEX_RTL) != 0;
171             }
172             if (allowedFlags == FLAG_RUN_RTL) {
173                error = (actualFlags & FLAG_COMPLEX_GLYPHS) != 0;
174             }
175             if (allowedFlags == FLAG_COMPLEX_GLYPHS) {
176                error = (actualFlags & FLAG_RUN_RTL) != 0;
177             }
178             if (allowedFlags == COMPLEX_RTL) {
179                error = (actualFlags & COMPLEX_RTL) == 0;
180             }
181             if (error) {
182                 throw new Error("layout flags in test: " + name +
183                      " expected: " + Integer.toHexString(computedFlags) +
184                      " but got: " + Integer.toHexString(actualFlags));
185             }
186         }
187     }
188 
printFlags(int flags)189     static public void printFlags(int flags) {
190         System.out.print("flags:");
191         if ((flags & FLAG_HAS_POSITION_ADJUSTMENTS) != 0) {
192             System.out.print(" pos");
193         }
194         if ((flags & FLAG_HAS_TRANSFORMS) != 0) {
195             System.out.print(" trans");
196         }
197         if ((flags & FLAG_RUN_RTL) != 0) {
198             System.out.print(" rtl");
199         }
200         if ((flags & FLAG_COMPLEX_GLYPHS) != 0) {
201             System.out.print(" complex");
202         }
203         if ((flags & FLAG_MASK) == 0) {
204             System.out.print(" none");
205         }
206         System.out.println();
207     }
208 
computeFlags(GlyphVector gv, int initValue)209     int computeFlags(GlyphVector gv, int initValue) {
210         validateCharIndexMethods(gv);
211 
212         int result = initValue;
213         if (glyphsAreRTL(gv)) {
214             result |= FLAG_RUN_RTL;
215         }
216         if (hasComplexGlyphs(gv)) {
217             result |= FLAG_COMPLEX_GLYPHS;
218         }
219 
220         if (gv.getFont().isTransformed()) {
221             result |= FLAG_HAS_TRANSFORMS;
222         }
223         return result;
224     }
225 
226     /**
227      * throw an exception if getGlyphCharIndices returns a different result than
228      * you get from iterating through getGlyphCharIndex one at a time.
229      */
validateCharIndexMethods(GlyphVector gv)230     void validateCharIndexMethods(GlyphVector gv) {
231         int[] indices = gv.getGlyphCharIndices(0, gv.getNumGlyphs(), null);
232         for (int i = 0; i < gv.getNumGlyphs(); ++i) {
233             if (gv.getGlyphCharIndex(i) != indices[i]) {
234                 throw new Error("glyph index mismatch at " + i);
235             }
236         }
237     }
238 
239     /**
240      * Return true if the glyph indices are pure ltr
241      */
glyphsAreLTR(GlyphVector gv)242     boolean glyphsAreLTR(GlyphVector gv) {
243         int[] indices = gv.getGlyphCharIndices(0, gv.getNumGlyphs(), null);
244         for (int i = 0; i < indices.length; ++i) {
245             if (indices[i] != i) {
246                 return false;
247             }
248         }
249         return true;
250     }
251 
252     /**
253      * Return true if the glyph indices are pure rtl
254      */
glyphsAreRTL(GlyphVector gv)255     boolean glyphsAreRTL(GlyphVector gv) {
256         int[] indices = gv.getGlyphCharIndices(0, gv.getNumGlyphs(), null);
257         for (int i = 0; i < indices.length; ++i) {
258             if (indices[i] != indices.length - i - 1) {
259                 return false;
260             }
261         }
262         return true;
263     }
264 
265     /**
266      * Return true if there is a local reordering (the run is not ltr or rtl).
267      * !!! We can't have mixed bidi runs in the glyphs.
268      */
hasComplexGlyphs(GlyphVector gv)269     boolean hasComplexGlyphs(GlyphVector gv) {
270         return !glyphsAreLTR(gv) && !glyphsAreRTL(gv);
271     }
272 }
273