Home | History | Annotate | Download | only in text
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package android.text;
     18 
     19 import android.graphics.Paint.FontMetricsInt;
     20 import android.test.suitebuilder.annotation.SmallTest;
     21 import android.text.Layout.Alignment;
     22 import static android.text.Layout.Alignment.*;
     23 import android.util.Log;
     24 
     25 import junit.framework.TestCase;
     26 
     27 /**
     28  * Tests StaticLayout vertical metrics behavior.
     29  *
     30  * Requires disabling access checks in the vm since this calls package-private
     31  * APIs.
     32  *
     33  * @Suppress
     34  */
     35 public class StaticLayoutTest extends TestCase {
     36 
     37     /**
     38      * Basic test showing expected behavior and relationship between font
     39      * metrics and line metrics.
     40      */
     41     //@SmallTest
     42     public void testGetters1() {
     43         LayoutBuilder b = builder();
     44         FontMetricsInt fmi = b.paint.getFontMetricsInt();
     45 
     46         // check default paint
     47         Log.i("TG1:paint", fmi.toString());
     48 
     49         Layout l = b.build();
     50         assertVertMetrics(l, 0, 0,
     51                 fmi.ascent, fmi.descent);
     52 
     53         // other quick metrics
     54         assertEquals(0, l.getLineStart(0));
     55         assertEquals(Layout.DIR_LEFT_TO_RIGHT, l.getParagraphDirection(0));
     56         assertEquals(false, l.getLineContainsTab(0));
     57         assertEquals(Layout.DIRS_ALL_LEFT_TO_RIGHT, l.getLineDirections(0));
     58         assertEquals(0, l.getEllipsisCount(0));
     59         assertEquals(0, l.getEllipsisStart(0));
     60         assertEquals(b.width, l.getEllipsizedWidth());
     61     }
     62 
     63     /**
     64      * Basic test showing effect of includePad = true with 1 line.
     65      * Top and bottom padding are affected, as is the line descent and height.
     66      */
     67     //@SmallTest
     68     public void testGetters2() {
     69         LayoutBuilder b = builder()
     70             .setIncludePad(true);
     71         FontMetricsInt fmi = b.paint.getFontMetricsInt();
     72 
     73         Layout l = b.build();
     74         assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
     75                 fmi.top, fmi.bottom);
     76     }
     77 
     78     /**
     79      * Basic test showing effect of includePad = true wrapping to 2 lines.
     80      * Ascent of top line and descent of bottom line are affected.
     81      */
     82     //@SmallTest
     83     public void testGetters3() {
     84         LayoutBuilder b = builder()
     85             .setIncludePad(true)
     86             .setWidth(50);
     87         FontMetricsInt fmi = b.paint.getFontMetricsInt();
     88 
     89         Layout l =  b.build();
     90         assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
     91             fmi.top, fmi.descent,
     92             fmi.ascent, fmi.bottom);
     93     }
     94 
     95     /**
     96      * Basic test showing effect of includePad = true wrapping to 3 lines.
     97      * First line ascent is top, bottom line descent is bottom.
     98      */
     99     //@SmallTest
    100     public void testGetters4() {
    101         LayoutBuilder b = builder()
    102             .setText("This is a longer test")
    103             .setIncludePad(true)
    104             .setWidth(50);
    105         FontMetricsInt fmi = b.paint.getFontMetricsInt();
    106 
    107         Layout l = b.build();
    108         assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
    109                 fmi.top, fmi.descent,
    110                 fmi.ascent, fmi.descent,
    111                 fmi.ascent, fmi.bottom);
    112     }
    113 
    114     /**
    115      * Basic test showing effect of includePad = true wrapping to 3 lines and
    116      * large text. See effect of leading. Currently, we don't expect there to
    117      * even be non-zero leading.
    118      */
    119     //@SmallTest
    120     public void testGetters5() {
    121         LayoutBuilder b = builder()
    122             .setText("This is a longer test")
    123             .setIncludePad(true)
    124             .setWidth(150);
    125         b.paint.setTextSize(36);
    126         FontMetricsInt fmi = b.paint.getFontMetricsInt();
    127 
    128         if (fmi.leading == 0) { // nothing to test
    129             Log.i("TG5", "leading is 0, skipping test");
    130             return;
    131         }
    132 
    133         // So far, leading is not used, so this is the same as TG4.  If we start
    134         // using leading, this will fail.
    135         Layout l = b.build();
    136         assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
    137                 fmi.top, fmi.descent,
    138                 fmi.ascent, fmi.descent,
    139                 fmi.ascent, fmi.bottom);
    140     }
    141 
    142     /**
    143      * Basic test showing effect of includePad = true, spacingAdd = 2, wrapping
    144      * to 3 lines.
    145      */
    146     //@SmallTest
    147     public void testGetters6() {
    148         int spacingAdd = 2; // int so expressions return int
    149         LayoutBuilder b = builder()
    150             .setText("This is a longer test")
    151             .setIncludePad(true)
    152             .setWidth(50)
    153             .setSpacingAdd(spacingAdd);
    154         FontMetricsInt fmi = b.paint.getFontMetricsInt();
    155 
    156         Layout l = b.build();
    157         assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
    158                 fmi.top, fmi.descent + spacingAdd,
    159                 fmi.ascent, fmi.descent + spacingAdd,
    160                 fmi.ascent, fmi.bottom + spacingAdd);
    161     }
    162 
    163     /**
    164      * Basic test showing effect of includePad = true, spacingAdd = 2,
    165      * spacingMult = 1.5, wrapping to 3 lines.
    166      */
    167     //@SmallTest
    168     public void testGetters7() {
    169         LayoutBuilder b = builder()
    170             .setText("This is a longer test")
    171             .setIncludePad(true)
    172             .setWidth(50)
    173             .setSpacingAdd(2)
    174             .setSpacingMult(1.5f);
    175         FontMetricsInt fmi = b.paint.getFontMetricsInt();
    176         Scaler s = new Scaler(b.spacingMult, b.spacingAdd);
    177 
    178         Layout l = b.build();
    179         assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
    180                 fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top),
    181                 fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent),
    182                 fmi.ascent, fmi.bottom + s.scale(fmi.bottom - fmi.ascent));
    183     }
    184 
    185     /**
    186      * Basic test showing effect of includePad = true, spacingAdd = 0,
    187      * spacingMult = 0.8 when wrapping to 3 lines.
    188      */
    189     //@SmallTest
    190     public void testGetters8() {
    191         LayoutBuilder b = builder()
    192             .setText("This is a longer test")
    193             .setIncludePad(true)
    194             .setWidth(50)
    195             .setSpacingAdd(2)
    196             .setSpacingMult(.8f);
    197         FontMetricsInt fmi = b.paint.getFontMetricsInt();
    198         Scaler s = new Scaler(b.spacingMult, b.spacingAdd);
    199 
    200         Layout l = b.build();
    201         assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
    202                 fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top),
    203                 fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent),
    204                 fmi.ascent, fmi.bottom + s.scale(fmi.bottom - fmi.ascent));
    205     }
    206 
    207     // ----- test utility classes and methods -----
    208 
    209     // Models the effect of the scale and add parameters.  I think the current
    210     // implementation misbehaves.
    211     private static class Scaler {
    212         private final float sMult;
    213         private final float sAdd;
    214 
    215         Scaler(float sMult, float sAdd) {
    216             this.sMult = sMult - 1;
    217             this.sAdd = sAdd;
    218         }
    219 
    220         public int scale(float height) {
    221             int altVal = (int)(height * sMult + sAdd + 0.5);
    222             int rndVal = Math.round(height * sMult + sAdd);
    223             if (altVal != rndVal) {
    224                 Log.i("Scale", "expected scale: " + rndVal +
    225                         " != returned scale: " + altVal);
    226             }
    227             return rndVal;
    228         }
    229     }
    230 
    231     /* package */ static LayoutBuilder builder() {
    232         return new LayoutBuilder();
    233     }
    234 
    235     /* package */ static class LayoutBuilder {
    236         String text = "This is a test";
    237         TextPaint paint = new TextPaint(); // default
    238         int width = 100;
    239         Alignment align = ALIGN_NORMAL;
    240         float spacingMult = 1;
    241         float spacingAdd = 0;
    242         boolean includePad = false;
    243 
    244         LayoutBuilder setText(String text) {
    245             this.text = text;
    246             return this;
    247         }
    248 
    249         LayoutBuilder setPaint(TextPaint paint) {
    250             this.paint = paint;
    251             return this;
    252         }
    253 
    254         LayoutBuilder setWidth(int width) {
    255             this.width = width;
    256             return this;
    257         }
    258 
    259         LayoutBuilder setAlignment(Alignment align) {
    260             this.align = align;
    261             return this;
    262         }
    263 
    264         LayoutBuilder setSpacingMult(float spacingMult) {
    265             this.spacingMult = spacingMult;
    266             return this;
    267         }
    268 
    269         LayoutBuilder setSpacingAdd(float spacingAdd) {
    270             this.spacingAdd = spacingAdd;
    271             return this;
    272         }
    273 
    274         LayoutBuilder setIncludePad(boolean includePad) {
    275             this.includePad = includePad;
    276             return this;
    277         }
    278 
    279        Layout build() {
    280             return  new StaticLayout(text, paint, width, align, spacingMult,
    281                 spacingAdd, includePad);
    282         }
    283     }
    284 
    285     private void assertVertMetrics(Layout l, int topPad, int botPad, int... values) {
    286         assertTopBotPadding(l, topPad, botPad);
    287         assertLinesMetrics(l, values);
    288     }
    289 
    290     private void assertLinesMetrics(Layout l, int... values) {
    291         // sanity check
    292         if ((values.length & 0x1) != 0) {
    293             throw new IllegalArgumentException(String.valueOf(values.length));
    294         }
    295 
    296         int lines = values.length >> 1;
    297         assertEquals(lines, l.getLineCount());
    298 
    299         int t = 0;
    300         for (int i = 0, n = 0; i < lines; ++i, n += 2) {
    301             int a = values[n];
    302             int d = values[n+1];
    303             int h = -a + d;
    304             assertLineMetrics(l, i, t, a, d, h);
    305             t += h;
    306         }
    307 
    308         assertEquals(t, l.getHeight());
    309     }
    310 
    311     private void assertLineMetrics(Layout l, int line,
    312             int top, int ascent, int descent, int height) {
    313         String info = "line " + line;
    314         assertEquals(info, top, l.getLineTop(line));
    315         assertEquals(info, ascent, l.getLineAscent(line));
    316         assertEquals(info, descent, l.getLineDescent(line));
    317         assertEquals(info, height, l.getLineBottom(line) - top);
    318     }
    319 
    320     private void assertTopBotPadding(Layout l, int topPad, int botPad) {
    321         assertEquals(topPad, l.getTopPadding());
    322         assertEquals(botPad, l.getBottomPadding());
    323     }
    324 }
    325