1 /* 2 * Copyright (c) 2012, 2013, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package test.com.sun.javafx.text; 27 28 import javafx.scene.text.Font; 29 30 import com.sun.javafx.font.PGFont; 31 import com.sun.javafx.geom.RectBounds; 32 import com.sun.javafx.scene.text.GlyphList; 33 import com.sun.javafx.scene.text.TextSpan; 34 import com.sun.javafx.scene.text.TextLine; 35 import com.sun.javafx.text.PrismTextLayout; 36 import com.sun.javafx.text.PrismTextLayout; 37 38 import org.junit.Ignore; 39 import org.junit.Test; 40 41 import sun.font.CharToGlyphMapper; 42 import static org.junit.Assert.*; 43 44 public class TextLayoutTest { 45 private String J = "\u3041"; //Japanese not complex 46 private String D = "\u0907"; //Devanagari complex 47 private String T = "\u0E34"; //Devanagari complex 48 49 class TestSpan implements TextSpan { 50 String text; 51 Object font; 52 TestSpan(Object text, Object font) { 53 this.text = (String)text; 54 this.font = font; 55 } 56 @Override public String getText() { 57 return text; 58 } 59 @Override public Object getFont() { 60 return font; 61 } 62 @Override public RectBounds getBounds() { 63 return null; 64 } 65 } 66 67 public TextLayoutTest() { 68 } 69 70 private void setContent(PrismTextLayout layout, Object... content) { 71 int count = content.length / 2; 72 TextSpan[] spans = new TextSpan[count]; 73 int i = 0; 74 while (i < content.length) { 75 spans[i>>1] = new TestSpan(content[i++], content[i++]); 76 } 77 layout.setContent(spans); 78 } 79 80 private void verifyLayout(PrismTextLayout layout, int lineCount, int runCount, int... glyphCount) { 81 TextLine[] lines = layout.getLines(); 82 assertEquals("lineCount", lineCount, lines.length); 83 GlyphList[] runs = layout.getRuns(); 84 assertEquals("runCount", runCount, runs.length); 85 assertEquals("runCount", runCount, glyphCount.length); 86 for (int i = 0; i < runs.length; i++) { 87 assertEquals("run " +i, glyphCount[i], runs[i].getGlyphCount()); 88 } 89 } 90 91 private void verifyComplex(PrismTextLayout layout, boolean... complex) { 92 GlyphList[] runs = layout.getRuns(); 93 for (int i = 0; i < runs.length; i++) { 94 assertEquals("run " +i, complex[i], runs[i].isComplex()); 95 } 96 } 97 98 @SuppressWarnings("deprecation") 99 @Ignore("RT-31357") 100 @Test public void buildRuns() { 101 102 PrismTextLayout layout = new PrismTextLayout(); 103 PGFont font = (PGFont)Font.font("Monaco", 12).impl_getNativeFont(); 104 PGFont font2 = (PGFont)Font.font("Tahoma", 12).impl_getNativeFont(); 105 106 /* simple case */ 107 layout.setContent("hello", font); 108 verifyLayout(layout, 1, 1, 5); 109 110 /* simple case, two workd*/ 111 layout.setContent("hello world", font); 112 verifyLayout(layout, 1, 1, 11); 113 114 /* empty string */ 115 layout.setContent("", font); 116 verifyLayout(layout, 1, 1, 0); 117 118 /* line break */ 119 layout.setContent("\n", font); //first line has the line break (glyphCount=0), 120 verifyLayout(layout, 2, 2, 0,0); 121 layout.setContent("\r", font); 122 verifyLayout(layout, 2, 2, 0,0); 123 layout.setContent("\r\n", font); 124 verifyLayout(layout, 2, 2, 0,0); 125 layout.setContent("a\nb", font); 126 verifyLayout(layout, 2, 3, 1, 0, 1); 127 layout.setContent("\n\n\r\r\n", font); 128 verifyLayout(layout, 5, 5, 0,0,0,0,0); 129 130 /* tabs */ 131 layout.setContent("\t", font); 132 verifyLayout(layout, 1, 1, 0); 133 layout.setContent("\t\t", font); 134 verifyLayout(layout, 1, 2, 0,0); 135 layout.setContent("a\tb", font); 136 verifyLayout(layout, 1, 3, 1,0,1); 137 138 /* complex */ 139 layout.setContent("aa"+J+J, font); 140 verifyLayout(layout, 1, 1, 4);// no complex (english to japanese) 141 verifyComplex(layout, false); 142 143 144 layout.setContent(D, font); 145 verifyLayout(layout, 1, 1, 1);// complex (english to devanagari) 146 verifyComplex(layout, true); 147 148 layout.setContent("aa"+D+D, font); 149 verifyLayout(layout, 1, 2, 2,2);// complex (english to devanagari) 150 verifyComplex(layout, false, true); 151 152 layout.setContent(D+D+"aa", font); 153 verifyLayout(layout, 1, 2, 2,2);// complex (devanagari to english) 154 verifyComplex(layout, true, false); 155 156 layout.setContent("aa"+D+D+J+J, font); 157 verifyLayout(layout, 1, 3, 2,2,2);// complex (english to devanagari to japanese) 158 verifyComplex(layout, false, true, false); 159 160 /*Tahoma has Thai but no Hindi, font slot break expected*/ 161 layout.setContent(D+D+T+T, font2); 162 verifyLayout(layout, 1, 2, 2,2);// complex (devanagari to thai) 163 verifyComplex(layout, true, true); 164 165 layout.setContent(T+T+D+D+T+T, font2); 166 verifyLayout(layout, 1, 3, 2,2,2); 167 verifyComplex(layout, true, true, true); 168 169 layout.setContent(T+T+D+D+"aa", font2); 170 verifyLayout(layout, 1, 3, 2,2,2); 171 verifyComplex(layout, true, true, false); 172 173 layout.setContent(T+T+"aa"+T+T, font2); 174 verifyLayout(layout, 1, 3, 2,2,2); 175 verifyComplex(layout, true, false, true); 176 177 layout.setContent("aa"+D+D+T+T, font2); 178 verifyLayout(layout, 1, 3, 2,2,2); 179 verifyComplex(layout, false, true, true); 180 181 /* Rich Text test */ 182 183 setContent(layout, "hello ", font, "world", font); 184 verifyLayout(layout, 1, 2, 6,5); 185 verifyComplex(layout, false, false); 186 187 setContent(layout, "aaa", font, J+J+J, font); 188 verifyLayout(layout, 1, 2, 3,3); 189 verifyComplex(layout, false, false); 190 191 setContent(layout, "aaa", font, D+D+D, font); 192 verifyLayout(layout, 1, 2, 3,3); 193 verifyComplex(layout, false, true); 194 195 /* can't merge \r\n in different spans*/ 196 setContent(layout, "aa\r", font, "\nbb", font); 197 verifyLayout(layout, 3, 4, 2,0,0,2); 198 verifyComplex(layout, false, false, false, false); 199 200 setContent(layout, "aa\r\n", font, "bb", font); 201 verifyLayout(layout, 2, 3, 2,0,2); 202 verifyComplex(layout, false, false, false); 203 204 /* can't merge surrogate pairs in different spans*/ 205 setContent(layout, "\uD840\uDC0B", font, "\uD840\uDC89\uD840\uDCA2", font); 206 verifyLayout(layout, 1, 2, 2, 4); 207 GlyphList[] runs = layout.getRuns(); 208 assertTrue(runs[0].getGlyphCode(0) != CharToGlyphMapper.INVISIBLE_GLYPH_ID); 209 assertTrue(runs[0].getGlyphCode(1) == CharToGlyphMapper.INVISIBLE_GLYPH_ID); 210 assertTrue(runs[1].getGlyphCode(0) != CharToGlyphMapper.INVISIBLE_GLYPH_ID); 211 assertTrue(runs[1].getGlyphCode(1) == CharToGlyphMapper.INVISIBLE_GLYPH_ID); 212 assertTrue(runs[1].getGlyphCode(2) != CharToGlyphMapper.INVISIBLE_GLYPH_ID); 213 assertTrue(runs[1].getGlyphCode(3) == CharToGlyphMapper.INVISIBLE_GLYPH_ID); 214 215 /* Split surrogate pair*/ 216 setContent(layout, "\uD840\uDC0B\uD840", font, "\uDC89\uD840\uDCA2", font); 217 verifyLayout(layout, 1, 2, 3, 3); 218 runs = layout.getRuns(); 219 assertTrue(runs[0].getGlyphCode(0) != CharToGlyphMapper.INVISIBLE_GLYPH_ID); 220 assertTrue(runs[0].getGlyphCode(1) == CharToGlyphMapper.INVISIBLE_GLYPH_ID); 221 assertTrue(runs[0].getGlyphCode(2) != CharToGlyphMapper.INVISIBLE_GLYPH_ID);//broken pair, results in missing glyph 222 assertTrue(runs[1].getGlyphCode(0) != CharToGlyphMapper.INVISIBLE_GLYPH_ID);//broken pair, results in missing glyph 223 assertTrue(runs[1].getGlyphCode(1) != CharToGlyphMapper.INVISIBLE_GLYPH_ID); 224 assertTrue(runs[1].getGlyphCode(2) == CharToGlyphMapper.INVISIBLE_GLYPH_ID); 225 226 } 227 228 }