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 }