1 /*
2  * Copyright (C) 2018 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.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 
21 import android.text.Layout;
22 import android.text.Layout.Alignment;
23 import android.text.SpannableString;
24 import android.text.Spanned;
25 import android.text.StaticLayout;
26 import android.text.TextDirectionHeuristic;
27 import android.text.TextDirectionHeuristics;
28 import android.text.TextPaint;
29 import android.text.style.LeadingMarginSpan;
30 
31 import androidx.test.filters.SmallTest;
32 
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.junit.runners.Parameterized;
36 
37 import java.util.ArrayList;
38 import java.util.Collection;
39 
40 @SmallTest
41 @RunWith(Parameterized.class)
42 public final class StaticLayoutGetLineLeftRightTest {
43 
44     // Here we should have a string without any space in case layout omit the
45     // space, which will cause troubles for us to verify the length  of each line.
46     // And in this test, we assume TEST_STR is LTR. It's initialized in setUp()
47     static final String TEST_STR;
48 
49     static {
50         String test_str = "";
51         for (int i = 0; i < 100; ++i) {
52             test_str += "a";
53         }
54         TEST_STR = test_str;
55     }
56     // Because Alignment.LEFT and Alignment.RIGHT are hidden,
57     // following integers are used to represent alignment.
58     static final int ALIGN_LEFT = 0;
59     static final int ALIGN_RIGHT = 1;
60     static final int ALIGN_CENTER = 2;
61 
62     final CharSequence mText;
63     final TextPaint mPaint;
64     final int mWidth;
65     final Layout mLayout;
66 
67     final int mLeadingMargin;
68     final Alignment mAlign;
69     final TextDirectionHeuristic mDir;
70     final int mExpectedAlign;
71 
72     @Parameterized.Parameters(name = "leadingMargin {0} align {1} dir {2}")
cases()73     public static Collection<Object[]> cases() {
74         final Alignment[] aligns = new Alignment[]{Alignment.ALIGN_NORMAL, Alignment.ALIGN_OPPOSITE,
75                 Alignment.ALIGN_CENTER, null};
76         // Use String to generate a meaningful test name,
77         final TextDirectionHeuristic[] dirs = new TextDirectionHeuristic[]
78                 {TextDirectionHeuristics.LTR, TextDirectionHeuristics.RTL};
79         final int[] leadingMargins = new int[] {0, 17, 23, 37};
80 
81         final ArrayList<Object[]> params = new ArrayList<>();
82         for (int leadingMargin: leadingMargins) {
83             for (Alignment align: aligns) {
84                 for (TextDirectionHeuristic dir: dirs) {
85                     final String dirName =
86                             dir == TextDirectionHeuristics.LTR ? "DIR_LTR" : "DIR_RTL";
87                     params.add(new Object[] {leadingMargin, align, dirName, dir});
88                 }
89             }
90         }
91         return params;
92     }
93 
StaticLayoutGetLineLeftRightTest(int leadingMargin, Alignment align, String dirName, TextDirectionHeuristic dir)94     public StaticLayoutGetLineLeftRightTest(int leadingMargin, Alignment align,
95             String dirName, TextDirectionHeuristic dir) {
96         mLeadingMargin = leadingMargin;
97         mAlign = align;
98         mDir = dir;
99 
100         if (mLeadingMargin > 0) {
101             final SpannableString ss = new SpannableString(TEST_STR);
102             ss.setSpan(new LeadingMarginSpan.Standard(mLeadingMargin),
103                     0, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
104             mText = ss;
105         } else {
106             mText = TEST_STR;
107         }
108 
109         mPaint = new TextPaint();
110         // Set TextSize to 32 in case the default is too small.
111         mPaint.setTextSize(32);
112         // Try to make 10 lines.
113         mWidth = mLeadingMargin + (int) mPaint.measureText(mText, 0, mText.length()) / 10;
114         mLayout = StaticLayout.Builder.obtain(mText, 0, mText.length(), mPaint, mWidth)
115                 .setAlignment(mAlign)
116                 .setTextDirection(mDir)
117                 .build();
118 
119         if (mAlign == null) {
120             mExpectedAlign = ALIGN_CENTER;
121             return;
122         }
123         switch (mAlign) {
124             case ALIGN_NORMAL:
125                 if (dir == TextDirectionHeuristics.RTL) {
126                     mExpectedAlign = ALIGN_RIGHT;
127                 } else {
128                     mExpectedAlign = ALIGN_LEFT;
129                 }
130                 break;
131             case ALIGN_OPPOSITE:
132                 if (dir == TextDirectionHeuristics.RTL) {
133                     mExpectedAlign = ALIGN_LEFT;
134                 } else {
135                     mExpectedAlign = ALIGN_RIGHT;
136                 }
137                 break;
138             default: /* mAlign == Alignment.ALIGN_CENTER */
139                 mExpectedAlign = ALIGN_CENTER;
140         }
141     }
142 
143     @Test
testGetLineLeft()144     public void testGetLineLeft() {
145         final int lineCount = mLayout.getLineCount();
146         for (int line = 0; line < lineCount; ++line) {
147             final int start = mLayout.getLineStart(line);
148             final int end = mLayout.getLineEnd(line);
149             final float textWidth = mPaint.measureText(mText, start, end);
150             final float expectedLeft;
151             if (mExpectedAlign == ALIGN_LEFT) {
152                 expectedLeft = 0.0f;
153             } else if (mExpectedAlign == ALIGN_RIGHT) {
154                 expectedLeft = mWidth - textWidth - mLeadingMargin;
155             } else {
156                 if (mDir == TextDirectionHeuristics.RTL) {
157                     expectedLeft = (float) Math.floor((mWidth - mLeadingMargin - textWidth) / 2);
158                 } else {
159                     expectedLeft = (float) Math.floor(mLeadingMargin
160                             + (mWidth - mLeadingMargin - textWidth) / 2);
161                 }
162             }
163             assertEquals(expectedLeft, mLayout.getLineLeft(line), 0.0f);
164         }
165     }
166 
167     @Test
testGetLineRight()168     public void testGetLineRight() {
169         final int lineCount = mLayout.getLineCount();
170         for (int line = 0; line < lineCount; ++line) {
171             final int start = mLayout.getLineStart(line);
172             final int end = mLayout.getLineEnd(line);
173             final float textWidth = mPaint.measureText(mText, start, end);
174             final float expectedRight;
175             if (mExpectedAlign == ALIGN_LEFT) {
176                 expectedRight = mLeadingMargin + textWidth;
177             } else if (mExpectedAlign == ALIGN_RIGHT) {
178                 expectedRight = mWidth;
179             } else {
180                 if (mDir == TextDirectionHeuristics.RTL) {
181                     expectedRight = (float) Math.ceil(mWidth - mLeadingMargin
182                             - (mWidth - mLeadingMargin - textWidth) / 2);
183                 } else {
184                     expectedRight = (float) Math.ceil(mWidth
185                             - (mWidth - mLeadingMargin - textWidth) / 2);
186                 }
187             }
188             assertEquals(expectedRight, mLayout.getLineRight(line), 0.0f);
189         }
190     }
191 }
192