blob: a826a97ff8894dc9d3970fce80f53b8e9ec49f80 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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
17package android.text;
18
Doug Felt4e0c5e52010-03-15 16:56:02 -070019import com.android.internal.util.ArrayUtils;
20
The Android Open Source Project10592532009-03-18 17:39:46 -070021import android.graphics.Bitmap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.graphics.Paint;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.text.style.LeadingMarginSpan;
Gilles Debunne66111472010-11-19 11:04:37 -080024import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.text.style.LineHeightSpan;
26import android.text.style.MetricAffectingSpan;
Doug Feltc982f602010-05-25 11:51:40 -070027import android.text.style.TabStopSpan;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028
29/**
30 * StaticLayout is a Layout for text that will not be edited after it
31 * is laid out. Use {@link DynamicLayout} for text that may change.
32 * <p>This is used by widgets to control text layout. You should not need
33 * to use this class directly unless you are implementing your own widget
34 * or custom display object, or would be tempted to call
Doug Felt4e0c5e52010-03-15 16:56:02 -070035 * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int,
36 * float, float, android.graphics.Paint)
37 * Canvas.drawText()} directly.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038 */
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -080039public class StaticLayout extends Layout {
40
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 public StaticLayout(CharSequence source, TextPaint paint,
42 int width,
43 Alignment align, float spacingmult, float spacingadd,
44 boolean includepad) {
45 this(source, 0, source.length(), paint, width, align,
46 spacingmult, spacingadd, includepad);
47 }
48
49 public StaticLayout(CharSequence source, int bufstart, int bufend,
50 TextPaint paint, int outerwidth,
51 Alignment align,
52 float spacingmult, float spacingadd,
53 boolean includepad) {
54 this(source, bufstart, bufend, paint, outerwidth, align,
55 spacingmult, spacingadd, includepad, null, 0);
56 }
57
58 public StaticLayout(CharSequence source, int bufstart, int bufend,
59 TextPaint paint, int outerwidth,
60 Alignment align,
61 float spacingmult, float spacingadd,
62 boolean includepad,
63 TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
64 super((ellipsize == null)
Doug Felt4e0c5e52010-03-15 16:56:02 -070065 ? source
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 : (source instanceof Spanned)
67 ? new SpannedEllipsizer(source)
68 : new Ellipsizer(source),
69 paint, outerwidth, align, spacingmult, spacingadd);
70
71 /*
72 * This is annoying, but we can't refer to the layout until
73 * superclass construction is finished, and the superclass
74 * constructor wants the reference to the display text.
Doug Felt4e0c5e52010-03-15 16:56:02 -070075 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 * This will break if the superclass constructor ever actually
77 * cares about the content instead of just holding the reference.
78 */
79 if (ellipsize != null) {
80 Ellipsizer e = (Ellipsizer) getText();
81
82 e.mLayout = this;
83 e.mWidth = ellipsizedWidth;
84 e.mMethod = ellipsize;
85 mEllipsizedWidth = ellipsizedWidth;
86
87 mColumns = COLUMNS_ELLIPSIZE;
88 } else {
89 mColumns = COLUMNS_NORMAL;
90 mEllipsizedWidth = outerwidth;
91 }
92
93 mLines = new int[ArrayUtils.idealIntArraySize(2 * mColumns)];
94 mLineDirections = new Directions[
95 ArrayUtils.idealIntArraySize(2 * mColumns)];
96
Doug Felte8e45f22010-03-29 14:58:40 -070097 mMeasured = MeasuredText.obtain();
98
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 generate(source, bufstart, bufend, paint, outerwidth, align,
100 spacingmult, spacingadd, includepad, includepad,
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800101 ellipsizedWidth, ellipsize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
Doug Felte8e45f22010-03-29 14:58:40 -0700103 mMeasured = MeasuredText.recycle(mMeasured);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 mFontMetricsInt = null;
105 }
106
107 /* package */ StaticLayout(boolean ellipsize) {
108 super(null, null, 0, null, 0, 0);
109
110 mColumns = COLUMNS_ELLIPSIZE;
111 mLines = new int[ArrayUtils.idealIntArraySize(2 * mColumns)];
112 mLineDirections = new Directions[
113 ArrayUtils.idealIntArraySize(2 * mColumns)];
Doug Felte8e45f22010-03-29 14:58:40 -0700114 mMeasured = MeasuredText.obtain();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 }
116
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800117 /* package */ void generate(CharSequence source, int bufStart, int bufEnd,
118 TextPaint paint, int outerWidth,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 Alignment align,
120 float spacingmult, float spacingadd,
121 boolean includepad, boolean trackpad,
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800122 float ellipsizedWidth, TextUtils.TruncateAt ellipsize) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 mLineCount = 0;
124
125 int v = 0;
126 boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
127
128 Paint.FontMetricsInt fm = mFontMetricsInt;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800129 int[] chooseHtv = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130
Doug Felte8e45f22010-03-29 14:58:40 -0700131 MeasuredText measured = mMeasured;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 Spanned spanned = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 if (source instanceof Spanned)
135 spanned = (Spanned) source;
136
137 int DEFAULT_DIR = DIR_LEFT_TO_RIGHT; // XXX
138
Doug Felte8e45f22010-03-29 14:58:40 -0700139 int paraEnd;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800140 for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
141 paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
Doug Felte8e45f22010-03-29 14:58:40 -0700142 if (paraEnd < 0)
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800143 paraEnd = bufEnd;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 else
Doug Felte8e45f22010-03-29 14:58:40 -0700145 paraEnd++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146
Doug Feltc982f602010-05-25 11:51:40 -0700147 int firstWidthLineLimit = mLineCount + 1;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800148 int firstWidth = outerWidth;
149 int restWidth = outerWidth;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800151 LineHeightSpan[] chooseHt = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152
153 if (spanned != null) {
Eric Fischer74d31ef2010-08-05 15:29:36 -0700154 LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
Doug Felte8e45f22010-03-29 14:58:40 -0700155 LeadingMarginSpan.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 for (int i = 0; i < sp.length; i++) {
Mark Wagner7b5676e2009-10-16 11:44:23 -0700157 LeadingMarginSpan lms = sp[i];
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800158 firstWidth -= sp[i].getLeadingMargin(true);
159 restWidth -= sp[i].getLeadingMargin(false);
Doug Feltc982f602010-05-25 11:51:40 -0700160
161 // LeadingMarginSpan2 is odd. The count affects all
162 // leading margin spans, not just this particular one,
163 // and start from the top of the span, not the top of the
164 // paragraph.
165 if (lms instanceof LeadingMarginSpan2) {
166 LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
167 int lmsFirstLine = getLineForOffset(spanned.getSpanStart(lms2));
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800168 firstWidthLineLimit = lmsFirstLine + lms2.getLeadingMarginLineCount();
Mark Wagner7b5676e2009-10-16 11:44:23 -0700169 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 }
171
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800172 chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800174 if (chooseHt.length != 0) {
175 if (chooseHtv == null ||
176 chooseHtv.length < chooseHt.length) {
177 chooseHtv = new int[ArrayUtils.idealIntArraySize(
178 chooseHt.length)];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 }
180
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800181 for (int i = 0; i < chooseHt.length; i++) {
182 int o = spanned.getSpanStart(chooseHt[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183
Doug Felte8e45f22010-03-29 14:58:40 -0700184 if (o < paraStart) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 // starts in this layout, before the
186 // current paragraph
187
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800188 chooseHtv[i] = getLineTop(getLineForOffset(o));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 } else {
190 // starts in this paragraph
191
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800192 chooseHtv[i] = v;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 }
194 }
195 }
196 }
197
Doug Felte8e45f22010-03-29 14:58:40 -0700198 measured.setPara(source, paraStart, paraEnd, DIR_REQUEST_DEFAULT_LTR);
199 char[] chs = measured.mChars;
200 float[] widths = measured.mWidths;
201 byte[] chdirs = measured.mLevels;
202 int dir = measured.mDir;
203 boolean easy = measured.mEasy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800205 int width = firstWidth;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206
207 float w = 0;
Doug Felte8e45f22010-03-29 14:58:40 -0700208 int here = paraStart;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209
Doug Felte8e45f22010-03-29 14:58:40 -0700210 int ok = paraStart;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800211 float okWidth = w;
212 int okAscent = 0, okDescent = 0, okTop = 0, okBottom = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213
Doug Felte8e45f22010-03-29 14:58:40 -0700214 int fit = paraStart;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800215 float fitWidth = w;
216 int fitAscent = 0, fitDescent = 0, fitTop = 0, fitBottom = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217
Doug Feltc982f602010-05-25 11:51:40 -0700218 boolean hasTabOrEmoji = false;
219 boolean hasTab = false;
220 TabStops tabStops = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221
Doug Felt23241882010-06-02 14:41:06 -0700222 for (int spanStart = paraStart, spanEnd = spanStart, nextSpanStart;
223 spanStart < paraEnd; spanStart = nextSpanStart) {
Doug Felte8e45f22010-03-29 14:58:40 -0700224
Doug Felt23241882010-06-02 14:41:06 -0700225 if (spanStart == spanEnd) {
226 if (spanned == null)
227 spanEnd = paraEnd;
228 else
229 spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
230 MetricAffectingSpan.class);
231
232 int spanLen = spanEnd - spanStart;
233 if (spanned == null) {
234 measured.addStyleRun(paint, spanLen, fm);
235 } else {
236 MetricAffectingSpan[] spans =
237 spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
238 measured.addStyleRun(paint, spans, spanLen, fm);
239 }
240 }
241
242 nextSpanStart = spanEnd;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800244 int fmTop = fm.top;
245 int fmBottom = fm.bottom;
246 int fmAscent = fm.ascent;
247 int fmDescent = fm.descent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248
Doug Felte8e45f22010-03-29 14:58:40 -0700249 for (int j = spanStart; j < spanEnd; j++) {
250 char c = chs[j - paraStart];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800252 if (c == CHAR_NEW_LINE) {
Gilles Debunne66111472010-11-19 11:04:37 -0800253 // intentionally left empty
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800254 } else if (c == CHAR_TAB) {
Doug Feltc982f602010-05-25 11:51:40 -0700255 if (hasTab == false) {
256 hasTab = true;
257 hasTabOrEmoji = true;
Kenny Root24ca4542010-06-22 23:46:35 -0700258 if (spanned != null) {
259 // First tab this para, check for tabstops
Eric Fischer74d31ef2010-08-05 15:29:36 -0700260 TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
Kenny Root24ca4542010-06-22 23:46:35 -0700261 paraEnd, TabStopSpan.class);
262 if (spans.length > 0) {
263 tabStops = new TabStops(TAB_INCREMENT, spans);
264 }
Doug Feltc982f602010-05-25 11:51:40 -0700265 }
266 }
267 if (tabStops != null) {
268 w = tabStops.nextTab(w);
269 } else {
270 w = TabStops.nextDefaultStop(w, TAB_INCREMENT);
271 }
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800272 } else if (c >= CHAR_FIRST_HIGH_SURROGATE && c <= CHAR_LAST_LOW_SURROGATE
273 && j + 1 < spanEnd) {
Doug Felte8e45f22010-03-29 14:58:40 -0700274 int emoji = Character.codePointAt(chs, j - paraStart);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275
The Android Open Source Project10592532009-03-18 17:39:46 -0700276 if (emoji >= MIN_EMOJI && emoji <= MAX_EMOJI) {
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800277 Bitmap bm = EMOJI_FACTORY.getBitmapFromAndroidPua(emoji);
The Android Open Source Project10592532009-03-18 17:39:46 -0700278
279 if (bm != null) {
Eric Fischer423f0e42009-03-27 18:04:12 -0700280 Paint whichPaint;
281
282 if (spanned == null) {
283 whichPaint = paint;
284 } else {
285 whichPaint = mWorkPaint;
286 }
287
Doug Felt4e0c5e52010-03-15 16:56:02 -0700288 float wid = bm.getWidth() *
Eric Fischer423f0e42009-03-27 18:04:12 -0700289 -whichPaint.ascent() /
290 bm.getHeight();
291
292 w += wid;
Doug Feltc982f602010-05-25 11:51:40 -0700293 hasTabOrEmoji = true;
The Android Open Source Project10592532009-03-18 17:39:46 -0700294 j++;
295 } else {
Doug Felte8e45f22010-03-29 14:58:40 -0700296 w += widths[j - paraStart];
The Android Open Source Project10592532009-03-18 17:39:46 -0700297 }
298 } else {
Doug Felte8e45f22010-03-29 14:58:40 -0700299 w += widths[j - paraStart];
The Android Open Source Project10592532009-03-18 17:39:46 -0700300 }
301 } else {
Doug Felte8e45f22010-03-29 14:58:40 -0700302 w += widths[j - paraStart];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 }
304
305 // Log.e("text", "was " + before + " now " + w + " after " + c + " within " + width);
306
307 if (w <= width) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800308 fitWidth = w;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 fit = j + 1;
310
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800311 if (fmTop < fitTop)
312 fitTop = fmTop;
313 if (fmAscent < fitAscent)
314 fitAscent = fmAscent;
315 if (fmDescent > fitDescent)
316 fitDescent = fmDescent;
317 if (fmBottom > fitBottom)
318 fitBottom = fmBottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319
320 /*
321 * From the Unicode Line Breaking Algorithm:
322 * (at least approximately)
Doug Felt4e0c5e52010-03-15 16:56:02 -0700323 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 * .,:; are class IS: breakpoints
325 * except when adjacent to digits
326 * / is class SY: a breakpoint
327 * except when followed by a digit.
328 * - is class HY: a breakpoint
329 * except when followed by a digit.
330 *
Eric Fischer549d7242009-03-31 14:19:47 -0700331 * Ideographs are class ID: breakpoints when adjacent,
332 * except for NS (non-starters), which can be broken
333 * after but not before.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 */
335
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800336 if (c == CHAR_SPACE || c == CHAR_TAB ||
337 ((c == CHAR_DOT || c == CHAR_COMMA ||
338 c == CHAR_COLON || c == CHAR_SEMICOLON) &&
Doug Felte8e45f22010-03-29 14:58:40 -0700339 (j - 1 < here || !Character.isDigit(chs[j - 1 - paraStart])) &&
340 (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) ||
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800341 ((c == CHAR_SLASH || c == CHAR_HYPHEN) &&
Doug Felte8e45f22010-03-29 14:58:40 -0700342 (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) ||
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800343 (c >= CHAR_FIRST_CJK && isIdeographic(c, true) &&
Doug Felte8e45f22010-03-29 14:58:40 -0700344 j + 1 < spanEnd && isIdeographic(chs[j + 1 - paraStart], false))) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800345 okWidth = w;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 ok = j + 1;
347
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800348 if (fitTop < okTop)
349 okTop = fitTop;
350 if (fitAscent < okAscent)
351 okAscent = fitAscent;
352 if (fitDescent > okDescent)
353 okDescent = fitDescent;
354 if (fitBottom > okBottom)
355 okBottom = fitBottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 }
Gilles Debunne4cf435d2011-01-04 15:35:29 -0800357 } else {
Gilles Debunned434d232011-01-04 17:15:14 -0800358 if (ok != here) {
359 // Log.e("text", "output ok " + here + " to " +ok);
Gilles Debunne32ea4ff2010-12-21 11:28:34 -0800360
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800361 while (ok < spanEnd && chs[ok - paraStart] == CHAR_SPACE) {
Gilles Debunned434d232011-01-04 17:15:14 -0800362 ok++;
363 }
364
365 v = out(source,
366 here, ok,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800367 okAscent, okDescent, okTop, okBottom,
Gilles Debunned434d232011-01-04 17:15:14 -0800368 v,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800369 spacingmult, spacingadd, chooseHt,
370 chooseHtv, fm, hasTabOrEmoji,
Gilles Debunned434d232011-01-04 17:15:14 -0800371 needMultiply, paraStart, chdirs, dir, easy,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800372 ok == bufEnd, includepad, trackpad,
Gilles Debunnef3fa0cd2011-02-03 14:17:05 -0800373 chs, widths, paraStart,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800374 ellipsize, ellipsizedWidth, okWidth,
Gilles Debunned434d232011-01-04 17:15:14 -0800375 paint);
376
377 here = ok;
378 } else if (fit != here) {
379 // Log.e("text", "output fit " + here + " to " +fit);
380 v = out(source,
381 here, fit,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800382 fitAscent, fitDescent,
383 fitTop, fitBottom,
Gilles Debunned434d232011-01-04 17:15:14 -0800384 v,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800385 spacingmult, spacingadd, chooseHt,
386 chooseHtv, fm, hasTabOrEmoji,
Gilles Debunned434d232011-01-04 17:15:14 -0800387 needMultiply, paraStart, chdirs, dir, easy,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800388 fit == bufEnd, includepad, trackpad,
Gilles Debunnef3fa0cd2011-02-03 14:17:05 -0800389 chs, widths, paraStart,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800390 ellipsize, ellipsizedWidth, fitWidth,
Gilles Debunned434d232011-01-04 17:15:14 -0800391 paint);
392
393 here = fit;
394 } else {
395 // Log.e("text", "output one " + here + " to " +(here + 1));
396 // XXX not sure why the existing fm wasn't ok.
397 // measureText(paint, mWorkPaint,
398 // source, here, here + 1, fm, tab,
399 // null);
400
401 v = out(source,
402 here, here+1,
403 fm.ascent, fm.descent,
404 fm.top, fm.bottom,
405 v,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800406 spacingmult, spacingadd, chooseHt,
407 chooseHtv, fm, hasTabOrEmoji,
Gilles Debunned434d232011-01-04 17:15:14 -0800408 needMultiply, paraStart, chdirs, dir, easy,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800409 here + 1 == bufEnd, includepad,
Gilles Debunned434d232011-01-04 17:15:14 -0800410 trackpad,
Gilles Debunnef3fa0cd2011-02-03 14:17:05 -0800411 chs, widths, paraStart,
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800412 ellipsize, ellipsizedWidth,
Gilles Debunned434d232011-01-04 17:15:14 -0800413 widths[here - paraStart], paint);
414
415 here = here + 1;
416 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417
Doug Felte8e45f22010-03-29 14:58:40 -0700418 if (here < spanStart) {
Doug Felt23241882010-06-02 14:41:06 -0700419 // didn't output all the text for this span
420 // we've measured the raw widths, though, so
421 // just reset the start point
422 j = nextSpanStart = here;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 } else {
424 j = here - 1; // continue looping
425 }
426
427 ok = fit = here;
428 w = 0;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800429 fitAscent = fitDescent = fitTop = fitBottom = 0;
430 okAscent = okDescent = okTop = okBottom = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431
Doug Feltc982f602010-05-25 11:51:40 -0700432 if (--firstWidthLineLimit <= 0) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800433 width = restWidth;
Mark Wagner7b5676e2009-10-16 11:44:23 -0700434 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 }
436 }
437 }
438
Doug Felte8e45f22010-03-29 14:58:40 -0700439 if (paraEnd != here) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800440 if ((fitTop | fitBottom | fitDescent | fitAscent) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 paint.getFontMetricsInt(fm);
442
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800443 fitTop = fm.top;
444 fitBottom = fm.bottom;
445 fitAscent = fm.ascent;
446 fitDescent = fm.descent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 }
448
449 // Log.e("text", "output rest " + here + " to " + end);
450
451 v = out(source,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800452 here, paraEnd, fitAscent, fitDescent,
453 fitTop, fitBottom,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 v,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800455 spacingmult, spacingadd, chooseHt,
456 chooseHtv, fm, hasTabOrEmoji,
Doug Felte8e45f22010-03-29 14:58:40 -0700457 needMultiply, paraStart, chdirs, dir, easy,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800458 paraEnd == bufEnd, includepad, trackpad,
Gilles Debunnef3fa0cd2011-02-03 14:17:05 -0800459 chs, widths, paraStart,
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800460 ellipsize, ellipsizedWidth, w, paint);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 }
462
Doug Felte8e45f22010-03-29 14:58:40 -0700463 paraStart = paraEnd;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800465 if (paraEnd == bufEnd)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 break;
467 }
468
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800469 if (bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) {
470 // Log.e("text", "output last " + bufEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471
472 paint.getFontMetricsInt(fm);
473
474 v = out(source,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800475 bufEnd, bufEnd, fm.ascent, fm.descent,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 fm.top, fm.bottom,
477 v,
478 spacingmult, spacingadd, null,
479 null, fm, false,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800480 needMultiply, bufEnd, null, DEFAULT_DIR, true,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 true, includepad, trackpad,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800482 null, null, bufStart,
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800483 ellipsize, ellipsizedWidth, 0, paint);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 }
485 }
486
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 /**
488 * Returns true if the specified character is one of those specified
489 * as being Ideographic (class ID) by the Unicode Line Breaking Algorithm
490 * (http://www.unicode.org/unicode/reports/tr14/), and is therefore OK
491 * to break between a pair of.
Eric Fischer549d7242009-03-31 14:19:47 -0700492 *
493 * @param includeNonStarters also return true for category NS
494 * (non-starters), which can be broken
495 * after but not before.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 */
Eric Fischer549d7242009-03-31 14:19:47 -0700497 private static final boolean isIdeographic(char c, boolean includeNonStarters) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 if (c >= '\u2E80' && c <= '\u2FFF') {
499 return true; // CJK, KANGXI RADICALS, DESCRIPTION SYMBOLS
500 }
501 if (c == '\u3000') {
502 return true; // IDEOGRAPHIC SPACE
503 }
504 if (c >= '\u3040' && c <= '\u309F') {
Eric Fischer549d7242009-03-31 14:19:47 -0700505 if (!includeNonStarters) {
506 switch (c) {
507 case '\u3041': // # HIRAGANA LETTER SMALL A
508 case '\u3043': // # HIRAGANA LETTER SMALL I
509 case '\u3045': // # HIRAGANA LETTER SMALL U
510 case '\u3047': // # HIRAGANA LETTER SMALL E
511 case '\u3049': // # HIRAGANA LETTER SMALL O
512 case '\u3063': // # HIRAGANA LETTER SMALL TU
513 case '\u3083': // # HIRAGANA LETTER SMALL YA
514 case '\u3085': // # HIRAGANA LETTER SMALL YU
515 case '\u3087': // # HIRAGANA LETTER SMALL YO
516 case '\u308E': // # HIRAGANA LETTER SMALL WA
517 case '\u3095': // # HIRAGANA LETTER SMALL KA
518 case '\u3096': // # HIRAGANA LETTER SMALL KE
519 case '\u309B': // # KATAKANA-HIRAGANA VOICED SOUND MARK
520 case '\u309C': // # KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
521 case '\u309D': // # HIRAGANA ITERATION MARK
522 case '\u309E': // # HIRAGANA VOICED ITERATION MARK
523 return false;
524 }
525 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 return true; // Hiragana (except small characters)
527 }
528 if (c >= '\u30A0' && c <= '\u30FF') {
Eric Fischer549d7242009-03-31 14:19:47 -0700529 if (!includeNonStarters) {
530 switch (c) {
531 case '\u30A0': // # KATAKANA-HIRAGANA DOUBLE HYPHEN
532 case '\u30A1': // # KATAKANA LETTER SMALL A
533 case '\u30A3': // # KATAKANA LETTER SMALL I
534 case '\u30A5': // # KATAKANA LETTER SMALL U
535 case '\u30A7': // # KATAKANA LETTER SMALL E
536 case '\u30A9': // # KATAKANA LETTER SMALL O
537 case '\u30C3': // # KATAKANA LETTER SMALL TU
538 case '\u30E3': // # KATAKANA LETTER SMALL YA
539 case '\u30E5': // # KATAKANA LETTER SMALL YU
540 case '\u30E7': // # KATAKANA LETTER SMALL YO
541 case '\u30EE': // # KATAKANA LETTER SMALL WA
542 case '\u30F5': // # KATAKANA LETTER SMALL KA
543 case '\u30F6': // # KATAKANA LETTER SMALL KE
544 case '\u30FB': // # KATAKANA MIDDLE DOT
545 case '\u30FC': // # KATAKANA-HIRAGANA PROLONGED SOUND MARK
546 case '\u30FD': // # KATAKANA ITERATION MARK
547 case '\u30FE': // # KATAKANA VOICED ITERATION MARK
548 return false;
549 }
550 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 return true; // Katakana (except small characters)
552 }
553 if (c >= '\u3400' && c <= '\u4DB5') {
554 return true; // CJK UNIFIED IDEOGRAPHS EXTENSION A
555 }
556 if (c >= '\u4E00' && c <= '\u9FBB') {
557 return true; // CJK UNIFIED IDEOGRAPHS
558 }
559 if (c >= '\uF900' && c <= '\uFAD9') {
560 return true; // CJK COMPATIBILITY IDEOGRAPHS
561 }
562 if (c >= '\uA000' && c <= '\uA48F') {
563 return true; // YI SYLLABLES
564 }
565 if (c >= '\uA490' && c <= '\uA4CF') {
566 return true; // YI RADICALS
567 }
568 if (c >= '\uFE62' && c <= '\uFE66') {
569 return true; // SMALL PLUS SIGN to SMALL EQUALS SIGN
570 }
571 if (c >= '\uFF10' && c <= '\uFF19') {
572 return true; // WIDE DIGITS
573 }
574
575 return false;
576 }
577
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 private int out(CharSequence text, int start, int end,
579 int above, int below, int top, int bottom, int v,
580 float spacingmult, float spacingadd,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800581 LineHeightSpan[] chooseHt, int[] chooseHtv,
Doug Feltc982f602010-05-25 11:51:40 -0700582 Paint.FontMetricsInt fm, boolean hasTabOrEmoji,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 boolean needMultiply, int pstart, byte[] chdirs,
584 int dir, boolean easy, boolean last,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800585 boolean includePad, boolean trackPad,
586 char[] chs, float[] widths, int widthStart,
587 TextUtils.TruncateAt ellipsize, float ellipsisWidth,
588 float textWidth, TextPaint paint) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 int j = mLineCount;
590 int off = j * mColumns;
591 int want = off + mColumns + TOP;
592 int[] lines = mLines;
593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 if (want >= lines.length) {
595 int nlen = ArrayUtils.idealIntArraySize(want + 1);
596 int[] grow = new int[nlen];
597 System.arraycopy(lines, 0, grow, 0, lines.length);
598 mLines = grow;
599 lines = grow;
600
601 Directions[] grow2 = new Directions[nlen];
602 System.arraycopy(mLineDirections, 0, grow2, 0,
603 mLineDirections.length);
604 mLineDirections = grow2;
605 }
606
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800607 if (chooseHt != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 fm.ascent = above;
609 fm.descent = below;
610 fm.top = top;
611 fm.bottom = bottom;
612
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800613 for (int i = 0; i < chooseHt.length; i++) {
614 if (chooseHt[i] instanceof LineHeightSpan.WithDensity) {
615 ((LineHeightSpan.WithDensity) chooseHt[i]).
616 chooseHeight(text, start, end, chooseHtv[i], v, fm, paint);
Eric Fischera9f1dd02009-08-12 15:00:10 -0700617
618 } else {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800619 chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm);
Eric Fischera9f1dd02009-08-12 15:00:10 -0700620 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 }
622
623 above = fm.ascent;
624 below = fm.descent;
625 top = fm.top;
626 bottom = fm.bottom;
627 }
628
629 if (j == 0) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800630 if (trackPad) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 mTopPadding = top - above;
632 }
633
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800634 if (includePad) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 above = top;
636 }
637 }
638 if (last) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800639 if (trackPad) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 mBottomPadding = bottom - below;
641 }
642
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800643 if (includePad) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 below = bottom;
645 }
646 }
647
648 int extra;
649
650 if (needMultiply) {
Doug Felt10657582010-02-22 11:19:01 -0800651 double ex = (below - above) * (spacingmult - 1) + spacingadd;
652 if (ex >= 0) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800653 extra = (int)(ex + EXTRA_ROUNDING);
Doug Felt10657582010-02-22 11:19:01 -0800654 } else {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800655 extra = -(int)(-ex + EXTRA_ROUNDING);
Doug Felt10657582010-02-22 11:19:01 -0800656 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 } else {
658 extra = 0;
659 }
660
661 lines[off + START] = start;
662 lines[off + TOP] = v;
663 lines[off + DESCENT] = below + extra;
664
665 v += (below - above) + extra;
666 lines[off + mColumns + START] = end;
667 lines[off + mColumns + TOP] = v;
668
Doug Feltc982f602010-05-25 11:51:40 -0700669 if (hasTabOrEmoji)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 lines[off + TAB] |= TAB_MASK;
671
Doug Felt9f7a4442010-03-01 12:45:56 -0800672 lines[off + DIR] |= dir << DIR_SHIFT;
673 Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
674 // easy means all chars < the first RTL, so no emoji, no nothing
Doug Felt4e0c5e52010-03-15 16:56:02 -0700675 // XXX a run with no text or all spaces is easy but might be an empty
Doug Felt9f7a4442010-03-01 12:45:56 -0800676 // RTL paragraph. Make sure easy is false if this is the case.
677 if (easy) {
678 mLineDirections[j] = linedirs;
679 } else {
Gilles Debunnef3fa0cd2011-02-03 14:17:05 -0800680 mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
681 start - widthStart, end - start);
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800682 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800684 // If ellipsize is in marquee mode, do not apply ellipsis on the first line
685 if (ellipsize != null && (ellipsize != TextUtils.TruncateAt.MARQUEE || j != 0)) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800686 calculateEllipsis(start, end, widths, widthStart,
687 ellipsisWidth, ellipsize, j,
688 textWidth, paint);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 }
690
691 mLineCount++;
692 return v;
693 }
694
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800695 private void calculateEllipsis(int lineStart, int lineEnd,
696 float[] widths, int widthStart,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 float avail, TextUtils.TruncateAt where,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800698 int line, float textWidth, TextPaint paint) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800700 if (textWidth <= avail) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 // Everything fits!
702 mLines[mColumns * line + ELLIPSIS_START] = 0;
703 mLines[mColumns * line + ELLIPSIS_COUNT] = 0;
704 return;
705 }
706
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800707 float ellipsisWidth = paint.measureText(HORIZONTAL_ELLIPSIS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 int ellipsisStart, ellipsisCount;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800709 int len = lineEnd - lineStart;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710
711 if (where == TextUtils.TruncateAt.START) {
712 float sum = 0;
713 int i;
714
715 for (i = len; i >= 0; i--) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800716 float w = widths[i - 1 + lineStart - widthStart];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800718 if (w + sum + ellipsisWidth > avail) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 break;
720 }
721
722 sum += w;
723 }
724
725 ellipsisStart = 0;
726 ellipsisCount = i;
727 } else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE) {
728 float sum = 0;
729 int i;
730
731 for (i = 0; i < len; i++) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800732 float w = widths[i + lineStart - widthStart];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800734 if (w + sum + ellipsisWidth > avail) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 break;
736 }
737
738 sum += w;
739 }
740
741 ellipsisStart = i;
742 ellipsisCount = len - i;
743 } else /* where = TextUtils.TruncateAt.MIDDLE */ {
744 float lsum = 0, rsum = 0;
745 int left = 0, right = len;
746
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800747 float ravail = (avail - ellipsisWidth) / 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800748 for (right = len; right >= 0; right--) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800749 float w = widths[right - 1 + lineStart - widthStart];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750
751 if (w + rsum > ravail) {
752 break;
753 }
754
755 rsum += w;
756 }
757
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800758 float lavail = avail - ellipsisWidth - rsum;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 for (left = 0; left < right; left++) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800760 float w = widths[left + lineStart - widthStart];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761
762 if (w + lsum > lavail) {
763 break;
764 }
765
766 lsum += w;
767 }
768
769 ellipsisStart = left;
770 ellipsisCount = right - left;
771 }
772
773 mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart;
774 mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
775 }
776
Doug Felte8e45f22010-03-29 14:58:40 -0700777 // Override the base class so we can directly access our members,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 // rather than relying on member functions.
779 // The logic mirrors that of Layout.getLineForVertical
780 // FIXME: It may be faster to do a linear search for layouts without many lines.
Gilles Debunne66111472010-11-19 11:04:37 -0800781 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 public int getLineForVertical(int vertical) {
783 int high = mLineCount;
784 int low = -1;
785 int guess;
786 int[] lines = mLines;
787 while (high - low > 1) {
788 guess = (high + low) >> 1;
789 if (lines[mColumns * guess + TOP] > vertical){
790 high = guess;
791 } else {
792 low = guess;
793 }
794 }
795 if (low < 0) {
796 return 0;
797 } else {
798 return low;
799 }
800 }
801
Gilles Debunne66111472010-11-19 11:04:37 -0800802 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 public int getLineCount() {
804 return mLineCount;
805 }
806
Gilles Debunne66111472010-11-19 11:04:37 -0800807 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 public int getLineTop(int line) {
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800809 int top = mLines[mColumns * line + TOP];
810 if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount &&
811 line != mLineCount) {
812 top += getBottomPadding();
813 }
814 return top;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 }
816
Gilles Debunne66111472010-11-19 11:04:37 -0800817 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 public int getLineDescent(int line) {
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800819 int descent = mLines[mColumns * line + DESCENT];
Gilles Debunnef3fa0cd2011-02-03 14:17:05 -0800820 if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount - 1 && // -1 intended
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800821 line != mLineCount) {
822 descent += getBottomPadding();
823 }
824 return descent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 }
826
Gilles Debunne66111472010-11-19 11:04:37 -0800827 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 public int getLineStart(int line) {
829 return mLines[mColumns * line + START] & START_MASK;
830 }
831
Gilles Debunne66111472010-11-19 11:04:37 -0800832 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 public int getParagraphDirection(int line) {
834 return mLines[mColumns * line + DIR] >> DIR_SHIFT;
835 }
836
Gilles Debunne66111472010-11-19 11:04:37 -0800837 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 public boolean getLineContainsTab(int line) {
839 return (mLines[mColumns * line + TAB] & TAB_MASK) != 0;
840 }
841
Gilles Debunne66111472010-11-19 11:04:37 -0800842 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 public final Directions getLineDirections(int line) {
844 return mLineDirections[line];
845 }
846
Gilles Debunne66111472010-11-19 11:04:37 -0800847 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 public int getTopPadding() {
849 return mTopPadding;
850 }
851
Gilles Debunne66111472010-11-19 11:04:37 -0800852 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 public int getBottomPadding() {
854 return mBottomPadding;
855 }
856
857 @Override
858 public int getEllipsisCount(int line) {
859 if (mColumns < COLUMNS_ELLIPSIZE) {
860 return 0;
861 }
862
863 return mLines[mColumns * line + ELLIPSIS_COUNT];
864 }
865
866 @Override
867 public int getEllipsisStart(int line) {
868 if (mColumns < COLUMNS_ELLIPSIZE) {
869 return 0;
870 }
871
872 return mLines[mColumns * line + ELLIPSIS_START];
873 }
874
875 @Override
876 public int getEllipsizedWidth() {
877 return mEllipsizedWidth;
878 }
879
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800880 /**
881 * @hide
882 */
883 @Override
Gilles Debunnef3fa0cd2011-02-03 14:17:05 -0800884 public void setMaximumVisibleLineCount(int lineCount) {
885 mMaximumVisibleLineCount = lineCount;
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800886 }
887
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 private int mLineCount;
889 private int mTopPadding, mBottomPadding;
890 private int mColumns;
891 private int mEllipsizedWidth;
892
893 private static final int COLUMNS_NORMAL = 3;
894 private static final int COLUMNS_ELLIPSIZE = 5;
895 private static final int START = 0;
896 private static final int DIR = START;
897 private static final int TAB = START;
898 private static final int TOP = 1;
899 private static final int DESCENT = 2;
900 private static final int ELLIPSIS_START = 3;
901 private static final int ELLIPSIS_COUNT = 4;
902
903 private int[] mLines;
904 private Directions[] mLineDirections;
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800905 private int mMaximumVisibleLineCount = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906
907 private static final int START_MASK = 0x1FFFFFFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800908 private static final int DIR_SHIFT = 30;
909 private static final int TAB_MASK = 0x20000000;
910
Doug Feltc982f602010-05-25 11:51:40 -0700911 private static final int TAB_INCREMENT = 20; // same as Layout, but that's private
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800912
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800913 private static final char CHAR_FIRST_CJK = '\u2E80';
914
915 private static final char CHAR_NEW_LINE = '\n';
916 private static final char CHAR_TAB = '\t';
917 private static final char CHAR_SPACE = ' ';
918 private static final char CHAR_DOT = '.';
919 private static final char CHAR_COMMA = ',';
920 private static final char CHAR_COLON = ':';
921 private static final char CHAR_SEMICOLON = ';';
922 private static final char CHAR_SLASH = '/';
923 private static final char CHAR_HYPHEN = '-';
924
925 private static final double EXTRA_ROUNDING = 0.5;
926 private static final String HORIZONTAL_ELLIPSIS = "\u2026"; // this is "..."
927
928 private static final int CHAR_FIRST_HIGH_SURROGATE = 0xD800;
929 private static final int CHAR_LAST_LOW_SURROGATE = 0xDFFF;
930
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 /*
Doug Felte8e45f22010-03-29 14:58:40 -0700932 * This is reused across calls to generate()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 */
Doug Felte8e45f22010-03-29 14:58:40 -0700934 private MeasuredText mMeasured;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 private Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
936}