1 /* 2 * Copyright (c) 2012, 2015, 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.FontHelper; 36 import com.sun.javafx.text.PrismTextLayout; 37 import com.sun.javafx.text.PrismTextLayout; 38 39 import org.junit.Ignore; 40 import org.junit.Test; 41 42 import sun.font.CharToGlyphMapper; 43 import static org.junit.Assert.*; 44 45 public class TextLayoutTest { 46 private String J = "\u3041"; //Japanese not complex 47 private String D = "\u0907"; //Devanagari complex 48 private String T = "\u0E34"; //Devanagari complex 49 50 class TestSpan implements TextSpan { 51 String text; 52 Object font; 53 TestSpan(Object text, Object font) { 54 this.text = (String)text; 55 this.font = font; 56 } 57 @Override public String getText() { 58 return text; 59 } 60 @Override public Object getFont() { 61 return font; 62 } 63 @Override public RectBounds getBounds() { 64 return null; 65 } 66 } 67 68 public TextLayoutTest() { 69 } 70 71 private void setContent(PrismTextLayout layout, Object... content) { 72 int count = content.length / 2; 73 TextSpan[] spans = new TextSpan[count]; 74 int i = 0; 75 while (i < content.length) { 76 spans[i>>1] = new TestSpan(content[i++], content[i++]); 77 } 78 layout.setContent(spans); 79 } 80 81 private void verifyLayout(PrismTextLayout layout, int lineCount, int runCount, int... glyphCount) { 82 TextLine[] lines = layout.getLines(); 83 assertEquals("lineCount", lineCount, lines.length); 84 GlyphList[] runs = layout.getRuns(); 85 assertEquals("runCount", runCount, runs.length); 86 assertEquals("runCount", runCount, glyphCount.length); 87 for (int i = 0; i < runs.length; i++) { 88 assertEquals("run " +i, glyphCount[i], runs[i].getGlyphCount()); 89 } 90 } 91 92 private void verifyComplex(PrismTextLayout layout, boolean... complex) { 93 GlyphList[] runs = layout.getRuns(); 94 for (int i = 0; i < runs.length; i++) { 95 assertEquals("run " +i, complex[i], runs[i].isComplex()); 96 } 97 } 98 99 @SuppressWarnings("deprecation") 100 @Ignore("RT-31357") 101 @Test public void buildRuns() { 102 103 PrismTextLayout layout = new PrismTextLayout(); 104 PGFont font = (PGFont) FontHelper.getNativeFont(Font.font("Monaco", 12)); 105 PGFont font2 = (PGFont) FontHelper.getNativeFont(Font.font("Tahoma", 12)); 106 107 /* simple case */ 108 layout.setContent("hello", font); 109 verifyLayout(layout, 1, 1, 5); 110 111 /* simple case, two workd*/ 112 layout.setContent("hello world", font); 113 verifyLayout(layout, 1, 1, 11); 114 115 /* empty string */ 116 layout.setContent("", font); 117 verifyLayout(layout, 1, 1, 0); 118 119 /* line break */ 120 layout.setContent("\n", font); //first line has the line break (glyphCount=0), 121 verifyLayout(layout, 2, 2, 0,0); 122 layout.setContent("\r", font); 123 verifyLayout(layout, 2, 2, 0,0); 124 layout.setContent("\r\n", font); 125 verifyLayout(layout, 2, 2, 0,0); 126 layout.setContent("a\nb", font); 127 verifyLayout(layout, 2, 3, 1, 0, 1); 128 layout.setContent("\n\n\r\r\n", font); 129 verifyLayout(layout, 5, 5, 0,0,0,0,0); 130 131 /* tabs */ 132 layout.setContent("\t", font); 133 verifyLayout(layout, 1, 1, 0); 134 layout.setContent("\t\t", font); 135 verifyLayout(layout, 1, 2, 0,0); 136 layout.setContent("a\tb", font); 137 verifyLayout(layout, 1, 3, 1,0,1); 138 139 /* complex */ 140 layout.setContent("aa"+J+J, font); 141 verifyLayout(layout, 1, 1, 4);// no complex (english to japanese) 142 verifyComplex(layout, false); 143 144 145 layout.setContent(D, font); 146 verifyLayout(layout, 1, 1, 1);// complex (english to devanagari) 147 verifyComplex(layout, true); 148 149 layout.setContent("aa"+D+D, font); 150 verifyLayout(layout, 1, 2, 2,2);// complex (english to devanagari) 151 verifyComplex(layout, false, true); 152 153 layout.setContent(D+D+"aa", font); 154 verifyLayout(layout, 1, 2, 2,2);// complex (devanagari to english) 155 verifyComplex(layout, true, false); 156 157 layout.setContent("aa"+D+D+J+J, font); 158 verifyLayout(layout, 1, 3, 2,2,2);// complex (english to devanagari to japanese) 159 verifyComplex(layout, false, true, false); 160 161 /*Tahoma has Thai but no Hindi, font slot break expected*/ 162 layout.setContent(D+D+T+T, font2); 163 verifyLayout(layout, 1, 2, 2,2);// complex (devanagari to thai) 164 verifyComplex(layout, true, true); 165 166 layout.setContent(T+T+D+D+T+T, font2); 167 verifyLayout(layout, 1, 3, 2,2,2); 168 verifyComplex(layout, true, true, true); 169 170 layout.setContent(T+T+D+D+"aa", font2); 171 verifyLayout(layout, 1, 3, 2,2,2); 172 verifyComplex(layout, true, true, false); 173 174 layout.setContent(T+T+"aa"+T+T, font2); 175 verifyLayout(layout, 1, 3, 2,2,2); 176 verifyComplex(layout, true, false, true); 177 178 layout.setContent("aa"+D+D+T+T, font2); 179 verifyLayout(layout, 1, 3, 2,2,2); 180 verifyComplex(layout, false, true, true); 181 182 /* Rich Text test */ 183 184 setContent(layout, "hello ", font, "world", font); 185 verifyLayout(layout, 1, 2, 6,5); 186 verifyComplex(layout, false, false); 187 188 setContent(layout, "aaa", font, J+J+J, font); 189 verifyLayout(layout, 1, 2, 3,3); 190 verifyComplex(layout, false, false); 191 192 setContent(layout, "aaa", font, D+D+D, font); 193 verifyLayout(layout, 1, 2, 3,3); 194 verifyComplex(layout, false, true); 195 196 /* can't merge \r\n in different spans*/ 197 setContent(layout, "aa\r", font, "\nbb", font); 198 verifyLayout(layout, 3, 4, 2,0,0,2); 199 verifyComplex(layout, false, false, false, false); 200 201 setContent(layout, "aa\r\n", font, "bb", font); 202 verifyLayout(layout, 2, 3, 2,0,2); 203 verifyComplex(layout, false, false, false); 204 205 /* can't merge surrogate pairs in different spans*/ 206 setContent(layout, "\uD840\uDC0B", font, "\uD840\uDC89\uD840\uDCA2", font); 207 verifyLayout(layout, 1, 2, 2, 4); 208 GlyphList[] runs = layout.getRuns(); 209 assertTrue(runs[0].getGlyphCode(0) != CharToGlyphMapper.INVISIBLE_GLYPH_ID); 210 assertTrue(runs[0].getGlyphCode(1) == CharToGlyphMapper.INVISIBLE_GLYPH_ID); 211 assertTrue(runs[1].getGlyphCode(0) != CharToGlyphMapper.INVISIBLE_GLYPH_ID); 212 assertTrue(runs[1].getGlyphCode(1) == CharToGlyphMapper.INVISIBLE_GLYPH_ID); 213 assertTrue(runs[1].getGlyphCode(2) != CharToGlyphMapper.INVISIBLE_GLYPH_ID); 214 assertTrue(runs[1].getGlyphCode(3) == CharToGlyphMapper.INVISIBLE_GLYPH_ID); 215 216 /* Split surrogate pair*/ 217 setContent(layout, "\uD840\uDC0B\uD840", font, "\uDC89\uD840\uDCA2", font); 218 verifyLayout(layout, 1, 2, 3, 3); 219 runs = layout.getRuns(); 220 assertTrue(runs[0].getGlyphCode(0) != CharToGlyphMapper.INVISIBLE_GLYPH_ID); 221 assertTrue(runs[0].getGlyphCode(1) == CharToGlyphMapper.INVISIBLE_GLYPH_ID); 222 assertTrue(runs[0].getGlyphCode(2) != CharToGlyphMapper.INVISIBLE_GLYPH_ID);//broken pair, results in missing glyph 223 assertTrue(runs[1].getGlyphCode(0) != CharToGlyphMapper.INVISIBLE_GLYPH_ID);//broken pair, results in missing glyph 224 assertTrue(runs[1].getGlyphCode(1) != CharToGlyphMapper.INVISIBLE_GLYPH_ID); 225 assertTrue(runs[1].getGlyphCode(2) == CharToGlyphMapper.INVISIBLE_GLYPH_ID); 226 227 } 228 229 }