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