1 /*
2  * Copyright (c) 1999, 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  *
26  * See TestJustification.html for main test.
27  */
28 
29 import java.applet.*;
30 import java.awt.*;
31 import java.awt.event.*;
32 import java.awt.font.*;
33 import java.awt.geom.*;
34 import java.text.*;
35 
36 public class TestJustification extends Applet {
37   JustificationPanel panel;
38 
init()39   public void init() {
40     setLayout(new BorderLayout());
41     panel = new JustificationPanel("Bitstream Cyberbit");
42     add("Center", panel);
43   }
44 
destroy()45   public void destroy() {
46     remove(panel);
47   }
48 
49   // calls system.exit, not for use in tests.
main(String args[])50   public static void main(String args[]) {
51     TestJustification justificationTest = new TestJustification();
52     justificationTest.init();
53     justificationTest.start();
54 
55     Frame f = new Frame("Test Justification");
56     f.addWindowListener(new WindowAdapter() {
57       public void windowClosing(WindowEvent e) {
58         System.exit(0);
59       }
60     });
61 
62     f.add("Center", justificationTest);
63     f.setSize(500, 500);
64     f.show();
65   }
66 
getAppletInfo()67   public String getAppletInfo() {
68     return "Test TextLayout.getJustifiedLayout()";
69   }
70 
71   static class JustificationPanel extends Panel {
72     TextLayout[] layouts;
73     String fontname;
74     float height;
75     float oldfsize;
76 
77     AttributedCharacterIterator lineText;
78     TextLayout[] lines;
79     int linecount;
80     float oldwidth;
81 
JustificationPanel(String fontname)82     JustificationPanel(String fontname) {
83       this.fontname = fontname;
84     }
85 
86     private static final String[] texts = {
87       "This is an english Highlighting demo.", "Highlighting",
88       "This is an arabic \u0627\u0628\u062a\u062c \u062e\u0644\u0627\u062e demo.", "arabic \u0627\u0628\u062a\u062c",
89       "This is a hebrew \u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5 demo.", "hebrew \u05d0\u05d1\u05d2",
90       "This is a cjk \u4e00\u4e01\u4e02\uac00\uac01\uc4fa\uf900\uf901\uf902 demo.", "cjk",
91       "NoSpaceCJK:\u4e00\u4e01\u4e02and\uac00\uac01\uc4faand\uf900\uf901\uf902", "No",
92       "NoSpaceRoman", "Space"
93     };
94 
paint(Graphics g)95     public void paint(Graphics g) {
96       Graphics2D g2d = (Graphics2D)g;
97 
98       Dimension d = getSize();
99       Insets insets = getInsets();
100 
101       float w = d.width - insets.left - insets.right;
102       float h = d.height - insets.top - insets.bottom;
103       int fsize = (int)w/25;
104 
105       FontRenderContext frc = g2d.getFontRenderContext();
106 
107       if (layouts == null || fsize != oldfsize) {
108         oldfsize = fsize;
109 
110         Font f0 = new Font(fontname, Font.PLAIN, fsize);
111         Font f1 = new Font(fontname, Font.ITALIC, (int)(fsize * 1.5));
112 
113         if (layouts == null) {
114           layouts = new TextLayout[texts.length / 2];
115         }
116 
117         height = 0;
118         for (int i = 0; i < layouts.length; ++i) {
119           String text = texts[i*2];
120           String target = texts[i*2+1];
121 
122           AttributedString astr = new AttributedString(text);
123           astr.addAttribute(TextAttribute.FONT, f0, 0, text.length());
124 
125           int start = text.indexOf(target);
126           int limit = start + target.length();
127           astr.addAttribute(TextAttribute.FONT, f1, start, limit);
128 
129           TextLayout layout = new TextLayout(astr.getIterator(), frc);
130 
131           layout = layout.getJustifiedLayout(w - 20);
132 
133           layouts[i] = layout;
134 
135           height += layout.getAscent() + layout.getDescent() + layout.getLeading();
136         }
137       }
138 
139       g2d.setColor(Color.white);
140       g2d.fill(new Rectangle.Float(insets.left, insets.top, w, h));
141 
142       float basey = 20;
143 
144       for (int i = 0; i < layouts.length; ++i) {
145         TextLayout layout = layouts[i];
146 
147         float la = layout.getAscent();
148         float ld = layout.getDescent();
149         float ll = layout.getLeading();
150         float lw = layout.getAdvance();
151         float lh = la + ld + ll;
152         float lx = (w - lw) / 2f;
153         float ly = basey + layout.getAscent();
154 
155         g2d.setColor(Color.black);
156         g2d.translate(insets.left + lx, insets.top + ly);
157 
158         Rectangle2D bounds = new Rectangle2D.Float(0, -la, lw, lh);
159         g2d.draw(bounds);
160 
161         layout.draw(g2d, 0, 0);
162 
163         g2d.setColor(Color.red);
164         for (int j = 0, e = layout.getCharacterCount(); j <= e; ++j) {
165           Shape[] carets = layout.getCaretShapes(j, bounds);
166           g2d.draw(carets[0]);
167         }
168 
169         g2d.translate(-insets.left - lx, -insets.top - ly);
170         basey += layout.getAscent() + layout.getDescent() + layout.getLeading();
171       }
172 
173       // add LineBreakMeasurer-generated layouts
174 
175       if (lineText == null) {
176         String text = "This is a long line of text that should be broken across multiple "
177           + "lines and then justified to fit the break width.  This test should pass if "
178           + "these lines are justified to the same width, and fail otherwise.  It should "
179           + "also format the hebrew (\u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5) and arabic "
180           + "(\u0627\u0628\u062a\u062c \u062e\u0644\u0627\u062e) and CJK "
181           + "(\u4e00\u4e01\u4e02\uac00\uac01\uc4fa\u67b1\u67b2\u67b3\u67b4\u67b5\u67b6\u67b7"
182           + "\u67b8\u67b9) text correctly.";
183 
184         Float regular = new Float(16.0);
185         Float big = new Float(24.0);
186         AttributedString astr = new AttributedString(text);
187         astr.addAttribute(TextAttribute.SIZE, regular, 0, text.length());
188         astr.addAttribute(TextAttribute.FAMILY, fontname, 0, text.length());
189 
190         int ix = text.indexOf("broken");
191         astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6);
192         ix = text.indexOf("hebrew");
193         astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6);
194         ix = text.indexOf("arabic");
195         astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6);
196         ix = text.indexOf("CJK");
197         astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 3);
198 
199         lineText = astr.getIterator();
200       }
201 
202       float width = w - 20;
203       if (lines == null || width != oldwidth) {
204         oldwidth = width;
205 
206         lines = new TextLayout[10];
207         linecount = 0;
208 
209         LineBreakMeasurer measurer = new LineBreakMeasurer(lineText, frc);
210 
211         for (;;) {
212           TextLayout layout = measurer.nextLayout(width);
213           if (layout == null) {
214             break;
215           }
216 
217           // justify all but last line
218           if (linecount > 0) {
219             lines[linecount - 1] = lines[linecount - 1].getJustifiedLayout(width);
220           }
221 
222           if (linecount == lines.length) {
223             TextLayout[] nlines = new TextLayout[lines.length * 2];
224             System.arraycopy(lines, 0, nlines, 0, lines.length);
225             lines = nlines;
226           }
227 
228           lines[linecount++] = layout;
229         }
230       }
231 
232       float basex = insets.left + 10;
233       basey += 10;
234       g2d.setColor(Color.black);
235 
236       for (int i = 0; i < linecount; ++i) {
237         TextLayout layout = lines[i];
238 
239         basey += layout.getAscent();
240         float adv = layout.getAdvance();
241         float dx = layout.isLeftToRight() ? 0 : width - adv;
242 
243         layout.draw(g2d, basex + dx, basey);
244 
245         basey += layout.getDescent() + layout.getLeading();
246       }
247     }
248   }
249 }
250