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