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 }