• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of 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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.text;
18 
19 import static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.graphics.Canvas;
25 import android.graphics.Paint;
26 import android.graphics.Typeface;
27 import android.platform.test.annotations.Presubmit;
28 import android.text.Layout.TabStops;
29 import android.text.style.AbsoluteSizeSpan;
30 import android.text.style.ReplacementSpan;
31 import android.text.style.TabStopSpan;
32 
33 import androidx.test.filters.SmallTest;
34 import androidx.test.filters.Suppress;
35 import androidx.test.platform.app.InstrumentationRegistry;
36 import androidx.test.runner.AndroidJUnit4;
37 
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 
41 import java.util.Arrays;
42 
43 @Presubmit
44 @SmallTest
45 @RunWith(AndroidJUnit4.class)
46 public class TextLineTest {
stretchesToFullWidth(CharSequence line)47     private boolean stretchesToFullWidth(CharSequence line) {
48         final TextPaint paint = new TextPaint();
49         final TextLine tl = TextLine.obtain();
50         tl.set(paint, line, 0, line.length(), Layout.DIR_LEFT_TO_RIGHT,
51                 Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */,
52                 0, 0 /* no ellipsis */, false /* useFallbackLinespace */);
53         final float originalWidth = tl.metrics(null, null, false, null);
54         final float expandedWidth = 2 * originalWidth;
55 
56         tl.justify(Layout.JUSTIFICATION_MODE_INTER_WORD, expandedWidth);
57         final float newWidth = tl.metrics(null, null, false, null);
58         TextLine.recycle(tl);
59         return Math.abs(newWidth - expandedWidth) < 0.5;
60     }
61 
62     @Test
testJustify_spaces()63     public void testJustify_spaces() {
64         // There are no spaces to stretch.
65         assertFalse(stretchesToFullWidth("text"));
66 
67         assertTrue(stretchesToFullWidth("one space"));
68         assertTrue(stretchesToFullWidth("exactly two spaces"));
69         assertTrue(stretchesToFullWidth("up to three spaces"));
70     }
71 
72     // NBSP should also stretch when it's not used as a base for a combining mark. This doesn't work
73     // yet (b/68204709).
74     @Suppress
disabledTestJustify_NBSP()75     public void disabledTestJustify_NBSP() {
76         final char nbsp = '\u00A0';
77         assertTrue(stretchesToFullWidth("non-breaking" + nbsp + "space"));
78         assertTrue(stretchesToFullWidth("mix" + nbsp + "and match"));
79 
80         final char combining_acute = '\u0301';
81         assertFalse(stretchesToFullWidth("combining" + nbsp + combining_acute + "acute"));
82     }
83 
84     // The test font has following coverage and width.
85     // U+0020: 10em
86     // U+002E (.): 10em
87     // U+0043 (C): 100em
88     // U+0049 (I): 1em
89     // U+004C (L): 50em
90     // U+0056 (V): 5em
91     // U+0058 (X): 10em
92     // U+005F (_): 0em
93     // U+05D0    : 1em  // HEBREW LETTER ALEF
94     // U+05D1    : 5em  // HEBREW LETTER BET
95     // U+FFFD (invalid surrogate will be replaced to this): 7em
96     // U+10331 (\uD800\uDF31): 10em
97     private static final Typeface TYPEFACE = Typeface.createFromAsset(
98             InstrumentationRegistry.getInstrumentation().getTargetContext().getAssets(),
99             "fonts/StaticLayoutLineBreakingTestFont.ttf");
100 
101     // The test font has following coverage and width.
102     // U+0020: 1em
103     // U+0049 (I): 1em
104     // U+0066 (f): 0.5em
105     // U+0069 (i): 0.5em
106     // ligature fi: 2em
107     // U+10331 (\uD800\uDF31): 10em
108     private static final Typeface TYPEFACE_LIGATURE = Typeface.createFromAsset(
109             InstrumentationRegistry.getInstrumentation().getTargetContext().getAssets(),
110             "fonts/ligature.ttf");
111 
getTextLine(CharSequence str, TextPaint paint, TabStops tabStops)112     private TextLine getTextLine(CharSequence str, TextPaint paint, TabStops tabStops) {
113         Layout layout =
114                 StaticLayout.Builder.obtain(str, 0, str.length(), paint, Integer.MAX_VALUE)
115                     .build();
116         TextLine tl = TextLine.obtain();
117         tl.set(paint, str, 0, str.length(),
118                 TextDirectionHeuristics.FIRSTSTRONG_LTR.isRtl(str, 0, str.length()) ? -1 : 1,
119                 layout.getLineDirections(0), tabStops != null, tabStops,
120                 0, 0 /* no ellipsis */, false /* useFallbackLineSpacing */);
121         return tl;
122     }
123 
getTextLine(CharSequence str, TextPaint paint)124     private TextLine getTextLine(CharSequence str, TextPaint paint) {
125         return getTextLine(str, paint, null);
126     }
127 
assertMeasurements(final TextLine tl, final int length, boolean trailing, final float[] expected)128     private void assertMeasurements(final TextLine tl, final int length, boolean trailing,
129             final float[] expected) {
130         for (int offset = 0; offset <= length; ++offset) {
131             assertEquals(expected[offset], tl.measure(offset, trailing, null, null, null), 0.0f);
132         }
133 
134         final boolean[] trailings = new boolean[length + 1];
135         Arrays.fill(trailings, trailing);
136         final float[] allMeasurements = tl.measureAllOffsets(trailings, null);
137         assertArrayEquals(expected, allMeasurements, 0.0f);
138     }
139 
140     @Test
testMeasure_LTR()141     public void testMeasure_LTR() {
142         final TextPaint paint = new TextPaint();
143         paint.setTypeface(TYPEFACE);
144         paint.setTextSize(10.0f);  // make 1em = 10px
145 
146         TextLine tl = getTextLine("IIIIIV", paint);
147         assertMeasurements(tl, 6, false,
148                 new float[]{0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 100.0f});
149         assertMeasurements(tl, 6, true,
150                 new float[]{0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 100.0f});
151     }
152 
153     @Test
testMeasure_RTL()154     public void testMeasure_RTL() {
155         final TextPaint paint = new TextPaint();
156         paint.setTypeface(TYPEFACE);
157         paint.setTextSize(10.0f);  // make 1em = 10px
158 
159         TextLine tl = getTextLine("\u05D0\u05D0\u05D0\u05D0\u05D0\u05D1", paint);
160         assertMeasurements(tl, 6, false,
161                 new float[]{0.0f, -10.0f, -20.0f, -30.0f, -40.0f, -50.0f, -100.0f});
162         assertMeasurements(tl, 6, true,
163                 new float[]{0.0f, -10.0f, -20.0f, -30.0f, -40.0f, -50.0f, -100.0f});
164     }
165 
166     @Test
testMeasure_BiDi()167     public void testMeasure_BiDi() {
168         final TextPaint paint = new TextPaint();
169         paint.setTypeface(TYPEFACE);
170         paint.setTextSize(10.0f);  // make 1em = 10px
171 
172         TextLine tl = getTextLine("II\u05D0\u05D0II", paint);
173         assertMeasurements(tl, 6, false,
174                 new float[]{0.0f, 10.0f, 40.0f, 30.0f, 40.0f, 50.0f, 60.0f});
175         assertMeasurements(tl, 6, true,
176                 new float[]{0.0f, 10.0f, 20.0f, 30.0f, 20.0f, 50.0f, 60.0f});
177     }
178 
179     private static final String LRI = "\u2066";  // LEFT-TO-RIGHT ISOLATE
180     private static final String RLI = "\u2067";  // RIGHT-TO-LEFT ISOLATE
181     private static final String PDI = "\u2069";  // POP DIRECTIONAL ISOLATE
182 
183     @Test
testMeasure_BiDi2()184     public void testMeasure_BiDi2() {
185         final TextPaint paint = new TextPaint();
186         paint.setTypeface(TYPEFACE);
187         paint.setTextSize(10.0f);  // make 1em = 10px
188 
189         TextLine tl = getTextLine("I" + RLI + "I\u05D0\u05D0" + PDI + "I", paint);
190         assertMeasurements(tl, 7, false,
191                 new float[]{0.0f, 10.0f, 30.0f, 30.0f, 20.0f, 40.0f, 40.0f, 50.0f});
192         assertMeasurements(tl, 7, true,
193                 new float[]{0.0f, 10.0f, 10.0f, 40.0f, 20.0f, 10.0f, 40.0f, 50.0f});
194     }
195 
196     @Test
testMeasure_BiDi3()197     public void testMeasure_BiDi3() {
198         final TextPaint paint = new TextPaint();
199         paint.setTypeface(TYPEFACE);
200         paint.setTextSize(10.0f);  // make 1em = 10px
201 
202         TextLine tl = getTextLine("\u05D0" + LRI + "\u05D0II" + PDI + "\u05D0", paint);
203         assertMeasurements(tl, 7, false,
204                 new float[]{0.0f, -10.0f, -30.0f, -30.0f, -20.0f, -40.0f, -40.0f, -50.0f});
205         assertMeasurements(tl, 7, true,
206                 new float[]{0.0f, -10.0f, -10.0f, -40.0f, -20.0f, -10.0f, -40.0f, -50.0f});
207     }
208 
209     @Test
testMeasure_Tab_LTR()210     public void testMeasure_Tab_LTR() {
211         final Object[] spans = { new TabStopSpan.Standard(100) };
212         final TabStops stops = new TabStops(100, spans);
213         final TextPaint paint = new TextPaint();
214         paint.setTypeface(TYPEFACE);
215         paint.setTextSize(10.0f);  // make 1em = 10px
216 
217         TextLine tl = getTextLine("II\tII", paint, stops);
218         assertMeasurements(tl, 5, false,
219                 new float[]{0.0f, 10.0f, 20.0f, 100.0f, 110.0f, 120.0f});
220         assertMeasurements(tl, 5, true,
221                 new float[]{0.0f, 10.0f, 20.0f, 100.0f, 110.0f, 120.0f});
222     }
223 
224     @Test
testMeasure_Tab_RTL()225     public void testMeasure_Tab_RTL() {
226         final Object[] spans = { new TabStopSpan.Standard(100) };
227         final TabStops stops = new TabStops(100, spans);
228         final TextPaint paint = new TextPaint();
229         paint.setTypeface(TYPEFACE);
230         paint.setTextSize(10.0f);  // make 1em = 10px
231 
232         TextLine tl = getTextLine("\u05D0\u05D0\t\u05D0\u05D0", paint, stops);
233         assertMeasurements(tl, 5, false,
234                 new float[]{0.0f, -10.0f, -20.0f, -100.0f, -110.0f, -120.0f});
235         assertMeasurements(tl, 5, true,
236                 new float[]{0.0f, -10.0f, -20.0f, -100.0f, -110.0f, -120.0f});
237     }
238 
239     @Test
testMeasure_Tab_BiDi()240     public void testMeasure_Tab_BiDi() {
241         final Object[] spans = { new TabStopSpan.Standard(100) };
242         final TabStops stops = new TabStops(100, spans);
243         final TextPaint paint = new TextPaint();
244         paint.setTypeface(TYPEFACE);
245         paint.setTextSize(10.0f);  // make 1em = 10px
246 
247         TextLine tl = getTextLine("I\u05D0\tI\u05D0", paint, stops);
248         assertMeasurements(tl, 5, false,
249                 new float[]{0.0f, 20.0f, 20.0f, 100.0f, 120.0f, 120.0f});
250         assertMeasurements(tl, 5, true,
251                 new float[]{0.0f, 10.0f, 10.0f, 100.0f, 110.0f, 110.0f});
252     }
253 
254     @Test
testMeasure_Tab_BiDi2()255     public void testMeasure_Tab_BiDi2() {
256         final Object[] spans = { new TabStopSpan.Standard(100) };
257         final TabStops stops = new TabStops(100, spans);
258         final TextPaint paint = new TextPaint();
259         paint.setTypeface(TYPEFACE);
260         paint.setTextSize(10.0f);  // make 1em = 10px
261 
262         TextLine tl = getTextLine("\u05D0I\t\u05D0I", paint, stops);
263         assertMeasurements(tl, 5, false,
264                 new float[]{0.0f, -20.0f, -20.0f, -100.0f, -120.0f, -120.0f});
265         assertMeasurements(tl, 5, true,
266                 new float[]{0.0f, -10.0f, -10.0f, -100.0f, -110.0f, -110.0f});
267     }
268 
269     @Test
testMeasure_wordSpacing()270     public void testMeasure_wordSpacing() {
271         final TextPaint paint = new TextPaint();
272         paint.setTypeface(TYPEFACE);
273         paint.setTextSize(10.0f);  // make 1em = 10px
274         paint.setWordSpacing(10.0f);
275 
276         TextLine tl = getTextLine("I I", paint);
277         assertMeasurements(tl, 3, false,
278                 new float[]{0.0f, 10.0f, 120.0f, 130.0f});
279         assertMeasurements(tl, 3, true,
280                 new float[]{0.0f, 10.0f, 120.0f, 130.0f});
281     }
282 
283     @Test
testMeasure_surrogate()284     public void testMeasure_surrogate() {
285         final TextPaint paint = new TextPaint();
286         paint.setTypeface(TYPEFACE);
287         paint.setTextSize(10.0f);  // make 1em = 10px
288 
289         TextLine tl = getTextLine("I\uD800\uDF31I", paint);
290         assertMeasurements(tl, 4, false,
291                 new float[]{0.0f, 10.0f, 110.0f, 110.0f, 120.0f});
292         assertMeasurements(tl, 4, true,
293                 new float[]{0.0f, 10.0f, 110.0f, 110.0f, 120.0f});
294     }
295 
296     @Test
testMeasure_ligature()297     public void testMeasure_ligature() {
298         final TextPaint paint = new TextPaint();
299         paint.setTypeface(TYPEFACE_LIGATURE);
300         paint.setTextSize(10.0f);  // make 1em = 10px
301 
302         TextLine tl = getTextLine("IfiI", paint);
303         assertMeasurements(tl, 4, false,
304                 new float[]{0.0f, 10.0f, 20.0f, 30.0f, 40.0f});
305         assertMeasurements(tl, 4, true,
306                 new float[]{0.0f, 10.0f, 20.0f, 30.0f, 40.0f});
307     }
308 
309     @Test
testHandleRun_ellipsizedReplacementSpan_isSkipped()310     public void testHandleRun_ellipsizedReplacementSpan_isSkipped() {
311         final Spannable text = new SpannableStringBuilder("This is a... text");
312 
313         // Setup a replacement span that the measurement should not interact with.
314         final TestReplacementSpan span = new TestReplacementSpan();
315         text.setSpan(span, 9, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
316 
317         final TextLine tl = TextLine.obtain();
318         tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
319                 false /* hasTabs */, null /* tabStops */, 9, 12,
320                 false /* useFallbackLineSpacing */);
321         tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null);
322 
323         assertFalse(span.mIsUsed);
324     }
325 
326     @Test
testHandleRun_notEllipsizedReplacementSpan_isNotSkipped()327     public void testHandleRun_notEllipsizedReplacementSpan_isNotSkipped() {
328         final Spannable text = new SpannableStringBuilder("This is a... text");
329 
330         // Setup a replacement span that the measurement should not interact with.
331         final TestReplacementSpan span = new TestReplacementSpan();
332         text.setSpan(span, 1, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
333 
334         final TextLine tl = TextLine.obtain();
335         tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
336                 false /* hasTabs */, null /* tabStops */, 9, 12,
337                 false /* useFallbackLineSpacing */);
338         tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null);
339 
340         assertTrue(span.mIsUsed);
341     }
342 
343     @Test
testHandleRun_halfEllipsizedReplacementSpan_isNotSkipped()344     public void testHandleRun_halfEllipsizedReplacementSpan_isNotSkipped() {
345         final Spannable text = new SpannableStringBuilder("This is a... text");
346 
347         // Setup a replacement span that the measurement should not interact with.
348         final TestReplacementSpan span = new TestReplacementSpan();
349         text.setSpan(span, 7, 11, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
350 
351         final TextLine tl = TextLine.obtain();
352         tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
353                 false /* hasTabs */, null /* tabStops */, 9, 12,
354                 false /* useFallbackLineSpacing */);
355         tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null);
356         assertTrue(span.mIsUsed);
357     }
358 
359     @Test
testMeasureAllBounds_LTR()360     public void testMeasureAllBounds_LTR() {
361         final TextPaint paint = new TextPaint();
362         paint.setTypeface(TYPEFACE);
363         paint.setTextSize(10.0f);  // make 1em = 10px
364 
365         TextLine tl = getTextLine("IIIIIV", paint);
366         float[] bounds = new float[12];
367         float[] advances = new float[6];
368         tl.measureAllBounds(bounds, advances);
369         assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 20.0f, 20.0f, 30.0f, 30.0f, 40.0f,
370                 40.0f, 50.0f, 50.0f, 100.0f}, bounds, 0.0f);
371         assertArrayEquals(new float[] {10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 50.0f}, advances, 0.0f);
372     }
373 
374     @Test
testMeasureAllBounds_LTR_StyledText()375     public void testMeasureAllBounds_LTR_StyledText() {
376         final TextPaint paint = new TextPaint();
377         paint.setTypeface(TYPEFACE);
378         paint.setTextSize(10.0f);  // make 1em = 10px
379         SpannableString text = new SpannableString("IIIIIV");
380         text.setSpan(new AbsoluteSizeSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
381 
382         TextLine tl = getTextLine(text, paint);
383         float[] bounds = new float[12];
384         float[] advances = new float[6];
385         tl.measureAllBounds(bounds, advances);
386         assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 15.0f, 15.0f, 20.0f, 20.0f, 30.0f,
387                 30.0f, 40.0f, 40.0f, 90.0f}, bounds, 0.0f);
388         assertArrayEquals(new float[] {10.0f, 5.0f, 5.0f, 10.0f, 10.0f, 50.0f}, advances, 0.0f);
389     }
390 
391     @Test
testMeasureAllBounds_RTL()392     public void testMeasureAllBounds_RTL() {
393         final TextPaint paint = new TextPaint();
394         paint.setTypeface(TYPEFACE);
395         paint.setTextSize(10.0f);  // make 1em = 10px
396 
397         TextLine tl = getTextLine("\u05D0\u05D0\u05D0\u05D0\u05D0\u05D1", paint);
398         float[] bounds = new float[12];
399         float[] advances = new float[6];
400         tl.measureAllBounds(bounds, advances);
401         assertArrayEquals(new float[] {-10.0f, 0.0f, -20.0f, -10.0f, -30.0f, -20.0f, -40.0f, -30.0f,
402                 -50.0f, -40.0f, -100.0f, -50.0f}, bounds, 0.0f);
403         assertArrayEquals(new float[] {10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 50.0f}, advances, 0.0f);
404     }
405 
406 
407     @Test
testMeasureAllBounds_RTL_StyledText()408     public void testMeasureAllBounds_RTL_StyledText() {
409         final TextPaint paint = new TextPaint();
410         paint.setTypeface(TYPEFACE);
411         paint.setTextSize(10.0f);  // make 1em = 10px
412         SpannableString text = new SpannableString("\u05D0\u05D0\u05D0\u05D0\u05D0\u05D1");
413         text.setSpan(new AbsoluteSizeSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
414 
415         TextLine tl = getTextLine(text, paint);
416         float[] bounds = new float[12];
417         float[] advances = new float[6];
418         tl.measureAllBounds(bounds, advances);
419         assertArrayEquals(new float[] {-10.0f, 0.0f, -15.0f, -10.0f, -20.0f, -15.0f,
420                 -30.0f, -20.0f, -40.0f, -30.0f, -90.0f, -40.0f}, bounds, 0.0f);
421         assertArrayEquals(new float[] {10.0f, 5.0f, 5.0f, 10.0f, 10.0f, 50.0f}, advances, 0.0f);
422     }
423 
424     @Test
testMeasureAllBounds_BiDi()425     public void testMeasureAllBounds_BiDi() {
426         final TextPaint paint = new TextPaint();
427         paint.setTypeface(TYPEFACE);
428         paint.setTextSize(10.0f);  // make 1em = 10px
429 
430         TextLine tl = getTextLine("II\u05D0\u05D0II", paint);
431         float[] bounds = new float[12];
432         float[] advances = new float[6];
433         tl.measureAllBounds(bounds, advances);
434         assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 20.0f, 30.0f, 40.0f, 20.0f, 30.0f,
435                 40.0f, 50.0f, 50.0f, 60.0f}, bounds, 0.0f);
436         assertArrayEquals(new float[] {10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f}, advances, 0.0f);
437     }
438 
439     @Test
testMeasureAllBounds_BiDi2()440     public void testMeasureAllBounds_BiDi2() {
441         final TextPaint paint = new TextPaint();
442         paint.setTypeface(TYPEFACE);
443         paint.setTextSize(10.0f);  // make 1em = 10px
444 
445         TextLine tl = getTextLine("I" + RLI + "I\u05D0\u05D0" + PDI + "I", paint);
446         float[] bounds = new float[14];
447         float[] advances = new float[7];
448         tl.measureAllBounds(bounds, advances);
449         assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 10.0f, 30.0f, 40.0f, 20.0f, 30.0f,
450                 10.0f, 20.0f, 40.0f, 40.0f, 40.0f, 50.0f}, bounds, 0.0f);
451         assertArrayEquals(new float[] {10.0f, 0.0f, 10.0f, 10.0f, 10.0f, 0.0f, 10.0f}, advances,
452                 0.0f);
453     }
454 
455     @Test
testMeasureAllBounds_BiDi3()456     public void testMeasureAllBounds_BiDi3() {
457         final TextPaint paint = new TextPaint();
458         paint.setTypeface(TYPEFACE);
459         paint.setTextSize(10.0f);  // make 1em = 10px
460 
461         TextLine tl = getTextLine("\u05D0" + LRI + "\u05D0II" + PDI + "\u05D0", paint);
462         float[] bounds = new float[14];
463         float[] advances = new float[7];
464         tl.measureAllBounds(bounds, advances);
465         assertArrayEquals(new float[] {-10.0f, 0.0f, -10.0f, -10.0f, -40.0f, -30.0f,
466                 -30.0f, -20.0f, -20.0f, -10.0f, -40.0f, -40.0f, -50.0f, -40.0f}, bounds, 0.0f);
467         assertArrayEquals(new float[] {10.0f, 0.0f, 10.0f, 10.0f, 10.0f, 0.0f, 10.0f}, advances,
468                 0.0f);
469     }
470 
471     @Test
testMeasureAllBounds_styled_BiDi()472     public void testMeasureAllBounds_styled_BiDi() {
473         final TextPaint paint = new TextPaint();
474         paint.setTypeface(TYPEFACE);
475         paint.setTextSize(10.0f);  // make 1em = 10px
476 
477         SpannableString text = new SpannableString("II\u05D0\u05D0II");
478         text.setSpan(new AbsoluteSizeSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
479 
480         TextLine tl = getTextLine(text, paint);
481         float[] bounds = new float[12];
482         float[] advances = new float[6];
483         tl.measureAllBounds(bounds, advances);
484         assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 15.0f, 25.0f, 30.0f,
485                 15.0f, 25.0f, 30.0f, 40.0f, 40.0f, 50.0f}, bounds, 0.0f);
486         assertArrayEquals(new float[] {10.0f, 5.0f, 5.0f, 10.0f, 10.0f, 10.0f}, advances, 0.0f);
487     }
488 
489     @Test
testMeasureAllBounds_Tab_LTR()490     public void testMeasureAllBounds_Tab_LTR() {
491         final Object[] spans = { new TabStopSpan.Standard(100) };
492         final TabStops stops = new TabStops(100, spans);
493         final TextPaint paint = new TextPaint();
494         paint.setTypeface(TYPEFACE);
495         paint.setTextSize(10.0f);  // make 1em = 10px
496 
497         TextLine tl = getTextLine("II\tII", paint, stops);
498         float[] bounds = new float[10];
499         float[] advances = new float[5];
500         tl.measureAllBounds(bounds, advances);
501         assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 20.0f, 20.0f, 100.0f, 100.0f, 110.0f,
502                 110.0f, 120.0f}, bounds, 0.0f);
503         assertArrayEquals(new float[] {10.0f, 10.0f, 80.0f, 10.0f, 10.0f}, advances, 0.0f);
504     }
505 
506     @Test
testMeasureAllBounds_Tab_RTL()507     public void testMeasureAllBounds_Tab_RTL() {
508         final Object[] spans = { new TabStopSpan.Standard(100) };
509         final TabStops stops = new TabStops(100, spans);
510         final TextPaint paint = new TextPaint();
511         paint.setTypeface(TYPEFACE);
512         paint.setTextSize(10.0f);  // make 1em = 10px
513 
514         TextLine tl = getTextLine("\u05D0\u05D0\t\u05D0\u05D0", paint, stops);
515         float[] bounds = new float[10];
516         float[] advances = new float[5];
517         tl.measureAllBounds(bounds, advances);
518         assertArrayEquals(new float[] {-10.0f, 0.0f, -20.0f, -10.0f, -100.0f, -20.0f,
519                 -110.0f, -100.0f, -120.0f, -110.0f}, bounds, 0.0f);
520         assertArrayEquals(new float[] {10.0f, 10.0f, 80.0f, 10.0f, 10.0f}, advances, 0.0f);
521     }
522 
523     @Test
testMeasureAllBounds_Tab_BiDi()524     public void testMeasureAllBounds_Tab_BiDi() {
525         final Object[] spans = { new TabStopSpan.Standard(100) };
526         final TabStops stops = new TabStops(100, spans);
527         final TextPaint paint = new TextPaint();
528         paint.setTypeface(TYPEFACE);
529         paint.setTextSize(10.0f);  // make 1em = 10px
530 
531         TextLine tl = getTextLine("I\u05D0\tI\u05D0", paint, stops);
532         float[] bounds = new float[10];
533         float[] advances = new float[5];
534         tl.measureAllBounds(bounds, advances);
535         assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 20.0f, 20.0f, 100.0f,
536                 100.0f, 110.0f, 110.0f, 120.0f}, bounds, 0.0f);
537         assertArrayEquals(new float[] {10.0f, 10.0f, 80.0f, 10.0f, 10.0f}, advances, 0.0f);
538     }
539 
540     @Test
testMeasureAllBounds_Tab_BiDi2()541     public void testMeasureAllBounds_Tab_BiDi2() {
542         final Object[] spans = { new TabStopSpan.Standard(100) };
543         final TabStops stops = new TabStops(100, spans);
544         final TextPaint paint = new TextPaint();
545         paint.setTypeface(TYPEFACE);
546         paint.setTextSize(10.0f);  // make 1em = 10px
547 
548         TextLine tl = getTextLine("\u05D0I\t\u05D0I", paint, stops);
549         float[] bounds = new float[10];
550         float[] advances = new float[5];
551         tl.measureAllBounds(bounds, advances);
552         assertArrayEquals(new float[] {-10.0f, 0.0f, -20.0f, -10.0f, -100.0f, -20.0f,
553                 -110.0f, -100.0f, -120.0f, -110.0f}, bounds, 0.0f);
554         assertArrayEquals(new float[] {10.0f, 10.0f, 80.0f, 10.0f, 10.0f}, advances, 0.0f);
555     }
556 
557     @Test
testMeasureAllBounds_replacement_LTR()558     public void testMeasureAllBounds_replacement_LTR() {
559         final TextPaint paint = new TextPaint();
560         paint.setTypeface(TYPEFACE);
561         paint.setTextSize(10.0f);  // make 1em = 10px
562 
563         SpannableString text = new SpannableString("IIIII");
564         text.setSpan(new TestReplacementSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
565 
566         TextLine tl = getTextLine(text, paint);
567         float[] bounds = new float[10];
568         float[] advances = new float[5];
569         tl.measureAllBounds(bounds, advances);
570         assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 15.0f, 15.0f, 15.0f,
571                 15.0f, 25.0f, 25.0f, 35.0f}, bounds, 0.0f);
572         assertArrayEquals(new float[] {10.0f, 5.0f, 0.0f, 10.0f, 10.0f}, advances, 0.0f);
573     }
574 
575     @Test
testMeasureAllBounds_replacement_RTL()576     public void testMeasureAllBounds_replacement_RTL() {
577         final TextPaint paint = new TextPaint();
578         paint.setTypeface(TYPEFACE);
579         paint.setTextSize(10.0f);  // make 1em = 10px
580 
581         SpannableString text = new SpannableString("\u05D0\u05D0\u05D0\u05D0\u05D0");
582         text.setSpan(new TestReplacementSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
583 
584         TextLine tl = getTextLine(text, paint);
585         float[] bounds = new float[10];
586         float[] advances = new float[5];
587         tl.measureAllBounds(bounds, advances);
588         assertArrayEquals(new float[] {-10.0f, 0.0f, -15.0f, -10.0f, -15.0f, -15.0f,
589                 -25.0f, -15.0f, -35.0f, -25.0f}, bounds, 0.0f);
590         assertArrayEquals(new float[] {10.0f, 5.0f, 0.0f, 10.0f, 10.0f}, advances, 0.0f);
591     }
592 
593     @Test
testMeasureAllBounds_replacement_BiDi()594     public void testMeasureAllBounds_replacement_BiDi() {
595         final TextPaint paint = new TextPaint();
596         paint.setTypeface(TYPEFACE);
597         paint.setTextSize(10.0f);  // make 1em = 10px
598 
599         SpannableString text = new SpannableString("II\u05D0\u05D0II");
600         text.setSpan(new TestReplacementSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
601 
602         TextLine tl = getTextLine(text, paint);
603         float[] bounds = new float[12];
604         float[] advances = new float[6];
605         tl.measureAllBounds(bounds, advances);
606         assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 15.0f, 15.0f, 15.0f,
607                 15.0f, 25.0f, 25.0f, 35.0f, 35.0f, 45.0f}, bounds, 0.0f);
608         assertArrayEquals(new float[] {10.0f, 5.0f, 0.0f, 10.0f, 10.0f, 10.0f}, advances, 0.0f);
609     }
610 
611     private static class TestReplacementSpan extends ReplacementSpan {
612         boolean mIsUsed;
613         private final int mWidth;
614 
TestReplacementSpan()615         TestReplacementSpan() {
616             mWidth = 0;
617         }
618 
TestReplacementSpan(int width)619         TestReplacementSpan(int width) {
620             mWidth = width;
621         }
622 
623         @Override
getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm)624         public int getSize(Paint paint, CharSequence text, int start, int end,
625                 Paint.FontMetricsInt fm) {
626             mIsUsed = true;
627             return mWidth;
628         }
629 
630         @Override
draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint)631         public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top,
632                 int y,
633                 int bottom, Paint paint) {
634             mIsUsed = true;
635         }
636     }
637 }
638