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.text.Layout.Directions;
     20 import android.text.StaticLayoutTest.LayoutBuilder;
     21 
     22 import java.util.Arrays;
     23 import java.util.Formatter;
     24 
     25 import junit.framework.TestCase;
     26 
     27 public class StaticLayoutDirectionsTest extends TestCase {
     28     private static final char ALEF = '\u05d0';
     29 
     30     private static Directions dirs(int ... dirs) {
     31         return new Directions(dirs);
     32     }
     33 
     34     // constants from Layout that are package-protected
     35     private static final int RUN_LENGTH_MASK = 0x03ffffff;
     36     private static final int RUN_LEVEL_SHIFT = 26;
     37     private static final int RUN_LEVEL_MASK = 0x3f;
     38     private static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT;
     39 
     40     private static final Directions DIRS_ALL_LEFT_TO_RIGHT =
     41         new Directions(new int[] { 0, RUN_LENGTH_MASK });
     42     private static final Directions DIRS_ALL_RIGHT_TO_LEFT =
     43         new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG });
     44 
     45     private static final int LVL1_1 = 1 | (1 << RUN_LEVEL_SHIFT);
     46     private static final int LVL2_1 = 1 | (2 << RUN_LEVEL_SHIFT);
     47     private static final int LVL2_2 = 2 | (2 << RUN_LEVEL_SHIFT);
     48 
     49     private static String[] texts = {
     50         "",
     51         " ",
     52         "a",
     53         "a1",
     54         "aA",
     55         "a1b",
     56         "a1A",
     57         "aA1",
     58         "aAb",
     59         "aA1B",
     60         "aA1B2",
     61 
     62         // rtl
     63         "A",
     64         "A1",
     65         "Aa",
     66         "A1B",
     67         "A1a",
     68         "Aa1",
     69         "AaB"
     70     };
     71 
     72     // Expected directions are an array of start/length+level pairs,
     73     // in visual order from the leading margin.
     74     private static Directions[] expected = {
     75         DIRS_ALL_LEFT_TO_RIGHT,
     76         DIRS_ALL_LEFT_TO_RIGHT,
     77         DIRS_ALL_LEFT_TO_RIGHT,
     78         DIRS_ALL_LEFT_TO_RIGHT,
     79         dirs(0, 1, 1, LVL1_1),
     80         DIRS_ALL_LEFT_TO_RIGHT,
     81         dirs(0, 2, 2, LVL1_1),
     82         dirs(0, 1, 2, LVL2_1, 1, LVL1_1),
     83         dirs(0, 1, 1, LVL1_1, 2, 1),
     84         dirs(0, 1, 3, LVL1_1, 2, LVL2_1, 1, LVL1_1),
     85         dirs(0, 1, 4, LVL2_1, 3, LVL1_1, 2, LVL2_1, 1, LVL1_1),
     86 
     87         // rtl
     88         DIRS_ALL_RIGHT_TO_LEFT,
     89         dirs(0, LVL1_1, 1, LVL2_1),
     90         dirs(0, LVL1_1, 1, LVL2_1),
     91         dirs(0, LVL1_1, 1, LVL2_1, 2, LVL1_1),
     92         dirs(0, LVL1_1, 1, LVL2_2),
     93         dirs(0, LVL1_1, 1, LVL2_2),
     94         dirs(0, LVL1_1, 1, LVL2_1, 2, LVL1_1),
     95     };
     96 
     97     private static String pseudoBidiToReal(String src) {
     98         char[] chars = src.toCharArray();
     99         for (int j = 0; j < chars.length; ++j) {
    100             char c = chars[j];
    101             if (c >= 'A' && c <= 'D') {
    102                 chars[j] = (char)(ALEF + c - 'A');
    103             }
    104         }
    105 
    106         return new String(chars, 0, chars.length);
    107     }
    108 
    109     // @SmallTest
    110     public void testDirections() {
    111         StringBuilder buf = new StringBuilder("\n");
    112         Formatter f = new Formatter(buf);
    113 
    114         LayoutBuilder b = StaticLayoutTest.builder();
    115         for (int i = 0; i < texts.length; ++i) {
    116             b.setText(pseudoBidiToReal(texts[i]));
    117             checkDirections(b.build(), i, b.text, expected, f);
    118         }
    119         if (buf.length() > 1) {
    120             fail(buf.toString());
    121         }
    122     }
    123 
    124     // @SmallTest
    125     public void testTrailingWhitespace() {
    126         LayoutBuilder b = StaticLayoutTest.builder();
    127         b.setText(pseudoBidiToReal("Ab   c"));
    128         float width = b.paint.measureText(b.text, 0, 5);  // exclude 'c'
    129         b.setWidth(Math.round(width));
    130         Layout l = b.build();
    131         if (l.getLineCount() != 2) {
    132             throw new RuntimeException("expected 2 lines, got: " + l.getLineCount());
    133         }
    134         Directions result = l.getLineDirections(0);
    135         Directions expected = dirs(0, LVL1_1, 1, LVL2_1, 2, 3 | (1 << Layout.RUN_LEVEL_SHIFT));
    136         expectDirections("split line", expected, result);
    137     }
    138 
    139     public void testNextToRightOf() {
    140         LayoutBuilder b = StaticLayoutTest.builder();
    141         b.setText(pseudoBidiToReal("aA1B2"));
    142         // visual a2B1A positions 04321
    143         // 0: |a2B1A, strong is sol, after -> 0
    144         // 1: a|2B1A, strong is a, after ->, 1
    145         // 2: a2|B1A, strong is B, after -> 4
    146         // 3: a2B|1A, strong is B, before -> 3
    147         // 4: a2B1|A, strong is A, after -> 2
    148         // 5: a2B1A|, strong is eol, before -> 5
    149         int[] expected = { 0, 1, 4, 3, 2, 5 };
    150         Layout l = b.build();
    151         int n = 0;
    152         for (int i = 1; i < expected.length; ++i) {
    153             int t = l.getOffsetToRightOf(n);
    154             if (t != expected[i]) {
    155                 fail("offset[" + i + "] to right of: " + n + " expected: " +
    156                         expected[i] + " got: " + t);
    157             }
    158             n = t;
    159         }
    160     }
    161 
    162     public void testNextToLeftOf() {
    163         LayoutBuilder b = StaticLayoutTest.builder();
    164         b.setText(pseudoBidiToReal("aA1B2"));
    165         int[] expected = { 0, 1, 4, 3, 2, 5 };
    166         Layout l = b.build();
    167         int n = 5;
    168         for (int i = expected.length - 1; --i >= 0;) {
    169             int t = l.getOffsetToLeftOf(n);
    170             if (t != expected[i]) {
    171                 fail("offset[" + i + "] to left of: " + n + " expected: " +
    172                         expected[i] + " got: " + t);
    173             }
    174             n = t;
    175         }
    176     }
    177 
    178     // utility, not really a test
    179     /*
    180     public void testMeasureText1() {
    181         LayoutBuilder b = StaticLayoutTest.builder();
    182         String text = "ABC"; // "abAB"
    183         b.setText(pseudoBidiToReal(text));
    184         Layout l = b.build();
    185         Directions directions = l.getLineDirections(0);
    186 
    187         TextPaint workPaint = new TextPaint();
    188 
    189         int dir = -1; // LEFT_TO_RIGHT
    190         boolean trailing = true;
    191         boolean alt = true;
    192         do {
    193             dir = -dir;
    194             do {
    195                 trailing = !trailing;
    196                 for (int offset = 0, end = b.text.length(); offset <= end; ++offset) {
    197                     float width = Layout.measureText(b.paint,
    198                             workPaint,
    199                             b.text,
    200                             0, offset, end,
    201                             dir, directions,
    202                             trailing, false,
    203                             null);
    204                     Log.i("BIDI", "dir: " + dir + " trail: " + trailing +
    205                             " offset: " + offset + " width: " + width);
    206                 }
    207             } while (!trailing);
    208         } while (dir > 0);
    209     }
    210     */
    211 
    212     // utility for displaying arrays in hex
    213     private static String hexArray(int[] array) {
    214         StringBuilder sb = new StringBuilder();
    215         sb.append('{');
    216         for (int i : array) {
    217             if (sb.length() > 1) {
    218                 sb.append(", ");
    219             }
    220             sb.append(Integer.toHexString(i));
    221         }
    222         sb.append('}');
    223         return sb.toString();
    224     }
    225 
    226     private void checkDirections(Layout l, int i, String text,
    227             Directions[] expectedDirs, Formatter f) {
    228         Directions expected = expectedDirs[i];
    229         Directions result = l.getLineDirections(0);
    230         if (!Arrays.equals(expected.mDirections, result.mDirections)) {
    231             f.format("%n[%2d] '%s', %s != %s", i, text,
    232                     hexArray(expected.mDirections),
    233                     hexArray(result.mDirections));
    234         }
    235     }
    236 
    237     private void expectDirections(String msg, Directions expected, Directions result) {
    238         if (!Arrays.equals(expected.mDirections, result.mDirections)) {
    239             fail("expected: " + hexArray(expected.mDirections) +
    240                     " got: " + hexArray(result.mDirections));
    241         }
    242     }
    243 }
    244