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 dirs(int ... dirs)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 pseudoBidiToReal(String src)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 testDirections()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 testTrailingWhitespace()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 testNextToRightOf()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 testNextToLeftOf()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 hexArray(int[] array)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 checkDirections(Layout l, int i, String text, Directions[] expectedDirs, Formatter f)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 expectDirections(String msg, Directions expected, Directions result)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