blob: 24fc6f23b4e00e25f1d00fc26d376a6a3bad9ca0 [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
Raph Levien531c30c2015-04-30 16:29:59 -070019import android.annotation.Nullable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.graphics.Paint;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.text.style.LeadingMarginSpan;
Gilles Debunne66111472010-11-19 11:04:37 -080022import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.text.style.LineHeightSpan;
24import android.text.style.MetricAffectingSpan;
Doug Feltc982f602010-05-25 11:51:40 -070025import android.text.style.TabStopSpan;
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -070026import android.util.Log;
Raph Levien39b4db72015-03-25 13:18:20 -070027import android.util.Pools.SynchronizedPool;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028
Doug Feltcb3791202011-07-07 11:57:48 -070029import com.android.internal.util.ArrayUtils;
Adam Lesinski776abc22014-03-07 11:30:59 -050030import com.android.internal.util.GrowingArrayUtils;
Doug Feltcb3791202011-07-07 11:57:48 -070031
Anish Athalyec8f9e622014-07-21 15:26:34 -070032import java.util.Arrays;
Raph Levien4c1f12e2015-03-02 16:29:23 -080033import java.util.Locale;
Anish Athalyec8f9e622014-07-21 15:26:34 -070034
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035/**
36 * StaticLayout is a Layout for text that will not be edited after it
37 * is laid out. Use {@link DynamicLayout} for text that may change.
38 * <p>This is used by widgets to control text layout. You should not need
39 * to use this class directly unless you are implementing your own widget
40 * or custom display object, or would be tempted to call
Doug Felt4e0c5e52010-03-15 16:56:02 -070041 * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int,
42 * float, float, android.graphics.Paint)
43 * Canvas.drawText()} directly.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 */
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -080045public class StaticLayout extends Layout {
46
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -070047 static final String TAG = "StaticLayout";
48
Raph Leviend3ab6922015-03-02 14:30:53 -080049 /**
Raph Levien531c30c2015-04-30 16:29:59 -070050 * Builder for static layouts. The builder is a newer pattern for constructing
51 * StaticLayout objects and should be preferred over the constructors,
52 * particularly to access newer features. To build a static layout, first
53 * call {@link #obtain} with the required arguments (text, paint, and width),
54 * then call setters for optional parameters, and finally {@link #build}
55 * to build the StaticLayout object. Parameters not explicitly set will get
56 * default values.
Raph Leviend3ab6922015-03-02 14:30:53 -080057 */
58 public final static class Builder {
Raph Levien4c1f12e2015-03-02 16:29:23 -080059 private Builder() {
60 mNativePtr = nNewBuilder();
61 }
62
Raph Levien531c30c2015-04-30 16:29:59 -070063 /**
64 * Obtain a builder for constructing StaticLayout objects
65 *
66 * @param source The text to be laid out, optionally with spans
67 * @param start The index of the start of the text
68 * @param end The index + 1 of the end of the text
69 * @param paint The base paint used for layout
70 * @param width The width in pixels
71 * @return a builder object used for constructing the StaticLayout
72 */
Raph Levienebd66ca2015-04-30 15:27:57 -070073 public static Builder obtain(CharSequence source, int start, int end, TextPaint paint,
74 int width) {
Raph Levien39b4db72015-03-25 13:18:20 -070075 Builder b = sPool.acquire();
Raph Leviend3ab6922015-03-02 14:30:53 -080076 if (b == null) {
77 b = new Builder();
78 }
79
80 // set default initial values
Raph Levien39b4db72015-03-25 13:18:20 -070081 b.mText = source;
82 b.mStart = start;
83 b.mEnd = end;
Raph Levienebd66ca2015-04-30 15:27:57 -070084 b.mPaint = paint;
Raph Levien39b4db72015-03-25 13:18:20 -070085 b.mWidth = width;
86 b.mAlignment = Alignment.ALIGN_NORMAL;
Raph Leviend3ab6922015-03-02 14:30:53 -080087 b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
88 b.mSpacingMult = 1.0f;
89 b.mSpacingAdd = 0.0f;
90 b.mIncludePad = true;
Raph Levien39b4db72015-03-25 13:18:20 -070091 b.mEllipsizedWidth = width;
Raph Leviend3ab6922015-03-02 14:30:53 -080092 b.mEllipsize = null;
93 b.mMaxLines = Integer.MAX_VALUE;
Raph Levien3bd60c72015-05-06 14:26:35 -070094 b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
Roozbeh Pournader95c7a132015-05-12 12:01:06 -070095 b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
Raph Leviend3ab6922015-03-02 14:30:53 -080096
97 b.mMeasuredText = MeasuredText.obtain();
98 return b;
99 }
100
Raph Levien39b4db72015-03-25 13:18:20 -0700101 private static void recycle(Builder b) {
Raph Leviend3ab6922015-03-02 14:30:53 -0800102 b.mPaint = null;
103 b.mText = null;
104 MeasuredText.recycle(b.mMeasuredText);
Raph Levien3bd60c72015-05-06 14:26:35 -0700105 b.mMeasuredText = null;
Raph Levien2ea52902015-07-01 14:39:31 -0700106 b.mLeftIndents = null;
107 b.mRightIndents = null;
Raph Levien3bd60c72015-05-06 14:26:35 -0700108 nFinishBuilder(b.mNativePtr);
Raph Levien39b4db72015-03-25 13:18:20 -0700109 sPool.release(b);
Raph Leviend3ab6922015-03-02 14:30:53 -0800110 }
111
112 // release any expensive state
113 /* package */ void finish() {
Raph Levien4c1f12e2015-03-02 16:29:23 -0800114 nFinishBuilder(mNativePtr);
Raph Levien22ba7862015-07-29 12:34:13 -0700115 mText = null;
116 mPaint = null;
117 mLeftIndents = null;
118 mRightIndents = null;
Raph Leviend3ab6922015-03-02 14:30:53 -0800119 mMeasuredText.finish();
120 }
121
122 public Builder setText(CharSequence source) {
123 return setText(source, 0, source.length());
124 }
125
Raph Levien531c30c2015-04-30 16:29:59 -0700126 /**
127 * Set the text. Only useful when re-using the builder, which is done for
128 * the internal implementation of {@link DynamicLayout} but not as part
129 * of normal {@link StaticLayout} usage.
130 *
131 * @param source The text to be laid out, optionally with spans
132 * @param start The index of the start of the text
133 * @param end The index + 1 of the end of the text
134 * @return this builder, useful for chaining
135 *
136 * @hide
137 */
Raph Leviend3ab6922015-03-02 14:30:53 -0800138 public Builder setText(CharSequence source, int start, int end) {
139 mText = source;
140 mStart = start;
141 mEnd = end;
142 return this;
143 }
144
Raph Levien531c30c2015-04-30 16:29:59 -0700145 /**
146 * Set the paint. Internal for reuse cases only.
147 *
148 * @param paint The base paint used for layout
149 * @return this builder, useful for chaining
150 *
151 * @hide
152 */
Raph Leviend3ab6922015-03-02 14:30:53 -0800153 public Builder setPaint(TextPaint paint) {
154 mPaint = paint;
155 return this;
156 }
157
Raph Levien531c30c2015-04-30 16:29:59 -0700158 /**
159 * Set the width. Internal for reuse cases only.
160 *
161 * @param width The width in pixels
162 * @return this builder, useful for chaining
163 *
164 * @hide
165 */
Raph Leviend3ab6922015-03-02 14:30:53 -0800166 public Builder setWidth(int width) {
167 mWidth = width;
168 if (mEllipsize == null) {
169 mEllipsizedWidth = width;
170 }
171 return this;
172 }
173
Raph Levien531c30c2015-04-30 16:29:59 -0700174 /**
175 * Set the alignment. The default is {@link Layout.Alignment#ALIGN_NORMAL}.
176 *
177 * @param alignment Alignment for the resulting {@link StaticLayout}
178 * @return this builder, useful for chaining
179 */
Raph Levien39b4db72015-03-25 13:18:20 -0700180 public Builder setAlignment(Alignment alignment) {
181 mAlignment = alignment;
182 return this;
183 }
184
Raph Levien531c30c2015-04-30 16:29:59 -0700185 /**
186 * Set the text direction heuristic. The text direction heuristic is used to
187 * resolve text direction based per-paragraph based on the input text. The default is
188 * {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
189 *
190 * @param textDir text direction heuristic for resolving BiDi behavior.
191 * @return this builder, useful for chaining
192 */
Raph Leviena6a08282015-06-03 13:20:45 -0700193 public Builder setTextDirection(TextDirectionHeuristic textDir) {
Raph Leviend3ab6922015-03-02 14:30:53 -0800194 mTextDir = textDir;
195 return this;
196 }
197
Raph Levien531c30c2015-04-30 16:29:59 -0700198 /**
199 * Set line spacing parameters. The default is 0.0 for {@code spacingAdd}
200 * and 1.0 for {@code spacingMult}.
201 *
202 * @param spacingAdd line spacing add
203 * @param spacingMult line spacing multiplier
204 * @return this builder, useful for chaining
205 * @see android.widget.TextView#setLineSpacing
206 */
207 public Builder setLineSpacing(float spacingAdd, float spacingMult) {
208 mSpacingAdd = spacingAdd;
Raph Leviend3ab6922015-03-02 14:30:53 -0800209 mSpacingMult = spacingMult;
210 return this;
211 }
212
Raph Levien531c30c2015-04-30 16:29:59 -0700213 /**
214 * Set whether to include extra space beyond font ascent and descent (which is
215 * needed to avoid clipping in some languages, such as Arabic and Kannada). The
216 * default is {@code true}.
217 *
218 * @param includePad whether to include padding
219 * @return this builder, useful for chaining
220 * @see android.widget.TextView#setIncludeFontPadding
221 */
Raph Leviend3ab6922015-03-02 14:30:53 -0800222 public Builder setIncludePad(boolean includePad) {
223 mIncludePad = includePad;
224 return this;
225 }
226
Raph Levien531c30c2015-04-30 16:29:59 -0700227 /**
228 * Set the width as used for ellipsizing purposes, if it differs from the
229 * normal layout width. The default is the {@code width}
230 * passed to {@link #obtain}.
231 *
232 * @param ellipsizedWidth width used for ellipsizing, in pixels
233 * @return this builder, useful for chaining
234 * @see android.widget.TextView#setEllipsize
235 */
Raph Leviend3ab6922015-03-02 14:30:53 -0800236 public Builder setEllipsizedWidth(int ellipsizedWidth) {
237 mEllipsizedWidth = ellipsizedWidth;
238 return this;
239 }
240
Raph Levien531c30c2015-04-30 16:29:59 -0700241 /**
242 * Set ellipsizing on the layout. Causes words that are longer than the view
243 * is wide, or exceeding the number of lines (see #setMaxLines) in the case
244 * of {@link android.text.TextUtils.TruncateAt#END} or
245 * {@link android.text.TextUtils.TruncateAt#MARQUEE}, to be ellipsized instead
246 * of broken. The default is
247 * {@code null}, indicating no ellipsis is to be applied.
248 *
249 * @param ellipsize type of ellipsis behavior
250 * @return this builder, useful for chaining
251 * @see android.widget.TextView#setEllipsize
252 */
253 public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
Raph Leviend3ab6922015-03-02 14:30:53 -0800254 mEllipsize = ellipsize;
255 return this;
256 }
257
Raph Levien531c30c2015-04-30 16:29:59 -0700258 /**
259 * Set maximum number of lines. This is particularly useful in the case of
260 * ellipsizing, where it changes the layout of the last line. The default is
261 * unlimited.
262 *
263 * @param maxLines maximum number of lines in the layout
264 * @return this builder, useful for chaining
265 * @see android.widget.TextView#setMaxLines
266 */
Raph Leviend3ab6922015-03-02 14:30:53 -0800267 public Builder setMaxLines(int maxLines) {
268 mMaxLines = maxLines;
269 return this;
270 }
271
Raph Levien531c30c2015-04-30 16:29:59 -0700272 /**
273 * Set break strategy, useful for selecting high quality or balanced paragraph
274 * layout options. The default is {@link Layout#BREAK_STRATEGY_SIMPLE}.
275 *
276 * @param breakStrategy break strategy for paragraph layout
277 * @return this builder, useful for chaining
278 * @see android.widget.TextView#setBreakStrategy
279 */
Raph Levien39b4db72015-03-25 13:18:20 -0700280 public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
281 mBreakStrategy = breakStrategy;
282 return this;
283 }
284
Raph Levien531c30c2015-04-30 16:29:59 -0700285 /**
Roozbeh Pournader95c7a132015-05-12 12:01:06 -0700286 * Set hyphenation frequency, to control the amount of automatic hyphenation used. The
287 * default is {@link Layout#HYPHENATION_FREQUENCY_NONE}.
288 *
289 * @param hyphenationFrequency hyphenation frequency for the paragraph
290 * @return this builder, useful for chaining
291 * @see android.widget.TextView#setHyphenationFrequency
292 */
293 public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
294 mHyphenationFrequency = hyphenationFrequency;
295 return this;
296 }
297
298 /**
Raph Levien531c30c2015-04-30 16:29:59 -0700299 * Set indents. Arguments are arrays holding an indent amount, one per line, measured in
300 * pixels. For lines past the last element in the array, the last element repeats.
301 *
302 * @param leftIndents array of indent values for left margin, in pixels
303 * @param rightIndents array of indent values for right margin, in pixels
304 * @return this builder, useful for chaining
Raph Levien531c30c2015-04-30 16:29:59 -0700305 */
Raph Leviene319d5a2015-04-14 23:51:07 -0700306 public Builder setIndents(int[] leftIndents, int[] rightIndents) {
Raph Levien2ea52902015-07-01 14:39:31 -0700307 mLeftIndents = leftIndents;
308 mRightIndents = rightIndents;
Raph Leviene319d5a2015-04-14 23:51:07 -0700309 int leftLen = leftIndents == null ? 0 : leftIndents.length;
310 int rightLen = rightIndents == null ? 0 : rightIndents.length;
311 int[] indents = new int[Math.max(leftLen, rightLen)];
312 for (int i = 0; i < indents.length; i++) {
313 int leftMargin = i < leftLen ? leftIndents[i] : 0;
314 int rightMargin = i < rightLen ? rightIndents[i] : 0;
315 indents[i] = leftMargin + rightMargin;
316 }
317 nSetIndents(mNativePtr, indents);
318 return this;
319 }
320
Raph Levien70616ec2015-03-04 10:41:30 -0800321 /**
322 * Measurement and break iteration is done in native code. The protocol for using
323 * the native code is as follows.
324 *
Raph Levien26d443a2015-03-30 14:18:32 -0700325 * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab
Roozbeh Pournader95c7a132015-05-12 12:01:06 -0700326 * stops, break strategy, and hyphenation frequency (and possibly other parameters in the
327 * future).
Raph Levienc94f7422015-03-06 19:19:48 -0800328 *
329 * Then, for each run within the paragraph:
Raph Levien70616ec2015-03-04 10:41:30 -0800330 * - setLocale (this must be done at least for the first run, optional afterwards)
331 * - one of the following, depending on the type of run:
332 * + addStyleRun (a text run, to be measured in native code)
333 * + addMeasuredRun (a run already measured in Java, passed into native code)
334 * + addReplacementRun (a replacement run, width is given)
335 *
336 * After measurement, nGetWidths() is valid if the widths are needed (eg for ellipsis).
337 * Run nComputeLineBreaks() to obtain line breaks for the paragraph.
338 *
339 * After all paragraphs, call finish() to release expensive buffers.
340 */
341
342 private void setLocale(Locale locale) {
Raph Levien4c1f12e2015-03-02 16:29:23 -0800343 if (!locale.equals(mLocale)) {
Raph Levien26d443a2015-03-30 14:18:32 -0700344 nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale));
Raph Levien4c1f12e2015-03-02 16:29:23 -0800345 mLocale = locale;
346 }
347 }
348
Raph Levien70616ec2015-03-04 10:41:30 -0800349 /* package */ float addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
350 return nAddStyleRun(mNativePtr, paint.getNativeInstance(), paint.mNativeTypeface,
351 start, end, isRtl);
352 }
353
354 /* package */ void addMeasuredRun(int start, int end, float[] widths) {
355 nAddMeasuredRun(mNativePtr, start, end, widths);
356 }
357
358 /* package */ void addReplacementRun(int start, int end, float width) {
359 nAddReplacementRun(mNativePtr, start, end, width);
360 }
361
Raph Levien531c30c2015-04-30 16:29:59 -0700362 /**
363 * Build the {@link StaticLayout} after options have been set.
364 *
365 * <p>Note: the builder object must not be reused in any way after calling this
366 * method. Setting parameters after calling this method, or calling it a second
367 * time on the same builder object, will likely lead to unexpected results.
368 *
369 * @return the newly constructed {@link StaticLayout} object
370 */
Raph Leviend3ab6922015-03-02 14:30:53 -0800371 public StaticLayout build() {
Raph Levien39b4db72015-03-25 13:18:20 -0700372 StaticLayout result = new StaticLayout(this);
373 Builder.recycle(this);
Raph Leviend3ab6922015-03-02 14:30:53 -0800374 return result;
375 }
376
Raph Levien4c1f12e2015-03-02 16:29:23 -0800377 @Override
378 protected void finalize() throws Throwable {
379 try {
380 nFreeBuilder(mNativePtr);
381 } finally {
382 super.finalize();
383 }
384 }
385
386 /* package */ long mNativePtr;
387
Raph Leviend3ab6922015-03-02 14:30:53 -0800388 CharSequence mText;
389 int mStart;
390 int mEnd;
391 TextPaint mPaint;
392 int mWidth;
Raph Levien39b4db72015-03-25 13:18:20 -0700393 Alignment mAlignment;
Raph Leviend3ab6922015-03-02 14:30:53 -0800394 TextDirectionHeuristic mTextDir;
395 float mSpacingMult;
396 float mSpacingAdd;
397 boolean mIncludePad;
398 int mEllipsizedWidth;
399 TextUtils.TruncateAt mEllipsize;
400 int mMaxLines;
Raph Levien39b4db72015-03-25 13:18:20 -0700401 int mBreakStrategy;
Roozbeh Pournader95c7a132015-05-12 12:01:06 -0700402 int mHyphenationFrequency;
Raph Levien2ea52902015-07-01 14:39:31 -0700403 int[] mLeftIndents;
404 int[] mRightIndents;
Raph Leviend3ab6922015-03-02 14:30:53 -0800405
406 Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
407
408 // This will go away and be subsumed by native builder code
409 MeasuredText mMeasuredText;
410
Raph Levien4c1f12e2015-03-02 16:29:23 -0800411 Locale mLocale;
412
Raph Levien39b4db72015-03-25 13:18:20 -0700413 private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
Raph Leviend3ab6922015-03-02 14:30:53 -0800414 }
415
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 public StaticLayout(CharSequence source, TextPaint paint,
417 int width,
418 Alignment align, float spacingmult, float spacingadd,
419 boolean includepad) {
420 this(source, 0, source.length(), paint, width, align,
421 spacingmult, spacingadd, includepad);
422 }
423
Doug Feltcb3791202011-07-07 11:57:48 -0700424 /**
425 * @hide
426 */
427 public StaticLayout(CharSequence source, TextPaint paint,
428 int width, Alignment align, TextDirectionHeuristic textDir,
429 float spacingmult, float spacingadd,
430 boolean includepad) {
431 this(source, 0, source.length(), paint, width, align, textDir,
432 spacingmult, spacingadd, includepad);
433 }
434
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 public StaticLayout(CharSequence source, int bufstart, int bufend,
436 TextPaint paint, int outerwidth,
437 Alignment align,
438 float spacingmult, float spacingadd,
439 boolean includepad) {
440 this(source, bufstart, bufend, paint, outerwidth, align,
441 spacingmult, spacingadd, includepad, null, 0);
442 }
443
Doug Feltcb3791202011-07-07 11:57:48 -0700444 /**
445 * @hide
446 */
447 public StaticLayout(CharSequence source, int bufstart, int bufend,
448 TextPaint paint, int outerwidth,
449 Alignment align, TextDirectionHeuristic textDir,
450 float spacingmult, float spacingadd,
451 boolean includepad) {
452 this(source, bufstart, bufend, paint, outerwidth, align, textDir,
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -0700453 spacingmult, spacingadd, includepad, null, 0, Integer.MAX_VALUE);
Doug Feltcb3791202011-07-07 11:57:48 -0700454}
455
456 public StaticLayout(CharSequence source, int bufstart, int bufend,
457 TextPaint paint, int outerwidth,
458 Alignment align,
459 float spacingmult, float spacingadd,
460 boolean includepad,
461 TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
462 this(source, bufstart, bufend, paint, outerwidth, align,
463 TextDirectionHeuristics.FIRSTSTRONG_LTR,
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -0700464 spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE);
Doug Feltcb3791202011-07-07 11:57:48 -0700465 }
466
467 /**
468 * @hide
469 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 public StaticLayout(CharSequence source, int bufstart, int bufend,
471 TextPaint paint, int outerwidth,
Doug Feltcb3791202011-07-07 11:57:48 -0700472 Alignment align, TextDirectionHeuristic textDir,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 float spacingmult, float spacingadd,
474 boolean includepad,
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -0700475 TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 super((ellipsize == null)
Doug Felt4e0c5e52010-03-15 16:56:02 -0700477 ? source
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 : (source instanceof Spanned)
479 ? new SpannedEllipsizer(source)
480 : new Ellipsizer(source),
Doug Feltcb3791202011-07-07 11:57:48 -0700481 paint, outerwidth, align, textDir, spacingmult, spacingadd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482
Raph Levienebd66ca2015-04-30 15:27:57 -0700483 Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
Raph Levien39b4db72015-03-25 13:18:20 -0700484 .setAlignment(align)
Raph Leviena6a08282015-06-03 13:20:45 -0700485 .setTextDirection(textDir)
Raph Levien531c30c2015-04-30 16:29:59 -0700486 .setLineSpacing(spacingadd, spacingmult)
Raph Leviend3ab6922015-03-02 14:30:53 -0800487 .setIncludePad(includepad)
488 .setEllipsizedWidth(ellipsizedWidth)
489 .setEllipsize(ellipsize)
490 .setMaxLines(maxLines);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 /*
492 * This is annoying, but we can't refer to the layout until
493 * superclass construction is finished, and the superclass
494 * constructor wants the reference to the display text.
Doug Felt4e0c5e52010-03-15 16:56:02 -0700495 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 * This will break if the superclass constructor ever actually
497 * cares about the content instead of just holding the reference.
498 */
499 if (ellipsize != null) {
500 Ellipsizer e = (Ellipsizer) getText();
501
502 e.mLayout = this;
503 e.mWidth = ellipsizedWidth;
504 e.mMethod = ellipsize;
505 mEllipsizedWidth = ellipsizedWidth;
506
507 mColumns = COLUMNS_ELLIPSIZE;
508 } else {
509 mColumns = COLUMNS_NORMAL;
510 mEllipsizedWidth = outerwidth;
511 }
512
Adam Lesinski776abc22014-03-07 11:30:59 -0500513 mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
514 mLines = new int[mLineDirections.length];
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -0700515 mMaximumVisibleLineCount = maxLines;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516
Raph Levien70616ec2015-03-04 10:41:30 -0800517 generate(b, b.mIncludePad, b.mIncludePad);
Doug Felte8e45f22010-03-29 14:58:40 -0700518
Raph Leviend3ab6922015-03-02 14:30:53 -0800519 Builder.recycle(b);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 }
521
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -0700522 /* package */ StaticLayout(CharSequence text) {
523 super(text, null, 0, null, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524
525 mColumns = COLUMNS_ELLIPSIZE;
Adam Lesinski776abc22014-03-07 11:30:59 -0500526 mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
527 mLines = new int[mLineDirections.length];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 }
529
Raph Levien39b4db72015-03-25 13:18:20 -0700530 private StaticLayout(Builder b) {
531 super((b.mEllipsize == null)
532 ? b.mText
533 : (b.mText instanceof Spanned)
534 ? new SpannedEllipsizer(b.mText)
535 : new Ellipsizer(b.mText),
536 b.mPaint, b.mWidth, b.mAlignment, b.mSpacingMult, b.mSpacingAdd);
537
538 if (b.mEllipsize != null) {
539 Ellipsizer e = (Ellipsizer) getText();
540
541 e.mLayout = this;
542 e.mWidth = b.mEllipsizedWidth;
543 e.mMethod = b.mEllipsize;
544 mEllipsizedWidth = b.mEllipsizedWidth;
545
546 mColumns = COLUMNS_ELLIPSIZE;
547 } else {
548 mColumns = COLUMNS_NORMAL;
549 mEllipsizedWidth = b.mWidth;
550 }
551
552 mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
553 mLines = new int[mLineDirections.length];
554 mMaximumVisibleLineCount = b.mMaxLines;
555
Raph Levien2ea52902015-07-01 14:39:31 -0700556 mLeftIndents = b.mLeftIndents;
557 mRightIndents = b.mRightIndents;
558
Raph Levien39b4db72015-03-25 13:18:20 -0700559 generate(b, b.mIncludePad, b.mIncludePad);
560 }
561
Raph Leviend3ab6922015-03-02 14:30:53 -0800562 /* package */ void generate(Builder b, boolean includepad, boolean trackpad) {
563 CharSequence source = b.mText;
564 int bufStart = b.mStart;
565 int bufEnd = b.mEnd;
566 TextPaint paint = b.mPaint;
567 int outerWidth = b.mWidth;
568 TextDirectionHeuristic textDir = b.mTextDir;
569 float spacingmult = b.mSpacingMult;
570 float spacingadd = b.mSpacingAdd;
571 float ellipsizedWidth = b.mEllipsizedWidth;
572 TextUtils.TruncateAt ellipsize = b.mEllipsize;
Raph Levien4c1f12e2015-03-02 16:29:23 -0800573 LineBreaks lineBreaks = new LineBreaks(); // TODO: move to builder to avoid allocation costs
Anish Athalyec8f9e622014-07-21 15:26:34 -0700574 // store span end locations
575 int[] spanEndCache = new int[4];
576 // store fontMetrics per span range
577 // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
578 int[] fmCache = new int[4 * 4];
Raph Levien4c1f12e2015-03-02 16:29:23 -0800579 b.setLocale(paint.getTextLocale()); // TODO: also respect LocaleSpan within the text
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700580
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 mLineCount = 0;
582
583 int v = 0;
584 boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
585
Raph Leviend3ab6922015-03-02 14:30:53 -0800586 Paint.FontMetricsInt fm = b.mFontMetricsInt;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800587 int[] chooseHtv = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588
Raph Leviend3ab6922015-03-02 14:30:53 -0800589 MeasuredText measured = b.mMeasuredText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 Spanned spanned = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 if (source instanceof Spanned)
593 spanned = (Spanned) source;
594
Doug Felte8e45f22010-03-29 14:58:40 -0700595 int paraEnd;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800596 for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
597 paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
Doug Felte8e45f22010-03-29 14:58:40 -0700598 if (paraEnd < 0)
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800599 paraEnd = bufEnd;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 else
Doug Felte8e45f22010-03-29 14:58:40 -0700601 paraEnd++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602
Anish Athalyec8f9e622014-07-21 15:26:34 -0700603 int firstWidthLineCount = 1;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800604 int firstWidth = outerWidth;
605 int restWidth = outerWidth;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800607 LineHeightSpan[] chooseHt = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608
609 if (spanned != null) {
Eric Fischer74d31ef2010-08-05 15:29:36 -0700610 LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
Doug Felte8e45f22010-03-29 14:58:40 -0700611 LeadingMarginSpan.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 for (int i = 0; i < sp.length; i++) {
Mark Wagner7b5676e2009-10-16 11:44:23 -0700613 LeadingMarginSpan lms = sp[i];
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800614 firstWidth -= sp[i].getLeadingMargin(true);
615 restWidth -= sp[i].getLeadingMargin(false);
Doug Feltcb3791202011-07-07 11:57:48 -0700616
Doug Feltc982f602010-05-25 11:51:40 -0700617 // LeadingMarginSpan2 is odd. The count affects all
Anish Athalyeab08c6d2014-08-08 12:09:58 -0700618 // leading margin spans, not just this particular one
Doug Feltc982f602010-05-25 11:51:40 -0700619 if (lms instanceof LeadingMarginSpan2) {
620 LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
Anish Athalyec8f9e622014-07-21 15:26:34 -0700621 firstWidthLineCount = Math.max(firstWidthLineCount,
622 lms2.getLeadingMarginLineCount());
Mark Wagner7b5676e2009-10-16 11:44:23 -0700623 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 }
625
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800626 chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800628 if (chooseHt.length != 0) {
629 if (chooseHtv == null ||
630 chooseHtv.length < chooseHt.length) {
Adam Lesinski776abc22014-03-07 11:30:59 -0500631 chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 }
633
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800634 for (int i = 0; i < chooseHt.length; i++) {
635 int o = spanned.getSpanStart(chooseHt[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636
Doug Felte8e45f22010-03-29 14:58:40 -0700637 if (o < paraStart) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 // starts in this layout, before the
639 // current paragraph
640
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800641 chooseHtv[i] = getLineTop(getLineForOffset(o));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 } else {
643 // starts in this paragraph
644
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800645 chooseHtv[i] = v;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 }
647 }
648 }
649 }
650
Raph Levien70616ec2015-03-04 10:41:30 -0800651 measured.setPara(source, paraStart, paraEnd, textDir, b);
Doug Felte8e45f22010-03-29 14:58:40 -0700652 char[] chs = measured.mChars;
653 float[] widths = measured.mWidths;
654 byte[] chdirs = measured.mLevels;
655 int dir = measured.mDir;
656 boolean easy = measured.mEasy;
Raph Levienc94f7422015-03-06 19:19:48 -0800657
658 // tab stop locations
659 int[] variableTabStops = null;
660 if (spanned != null) {
661 TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
662 paraEnd, TabStopSpan.class);
663 if (spans.length > 0) {
664 int[] stops = new int[spans.length];
665 for (int i = 0; i < spans.length; i++) {
666 stops[i] = spans[i].getTabStop();
667 }
668 Arrays.sort(stops, 0, stops.length);
669 variableTabStops = stops;
670 }
671 }
672
Raph Levienc94f7422015-03-06 19:19:48 -0800673 nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
674 firstWidth, firstWidthLineCount, restWidth,
Roozbeh Pournader95c7a132015-05-12 12:01:06 -0700675 variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
Raph Levien2ea52902015-07-01 14:39:31 -0700676 if (mLeftIndents != null || mRightIndents != null) {
677 // TODO(raph) performance: it would be better to do this once per layout rather
678 // than once per paragraph, but that would require a change to the native
679 // interface.
680 int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
681 int rightLen = mRightIndents == null ? 0 : mRightIndents.length;
682 int indentsLen = Math.max(1, Math.min(leftLen, rightLen) - mLineCount);
683 int[] indents = new int[indentsLen];
684 for (int i = 0; i < indentsLen; i++) {
685 int leftMargin = mLeftIndents == null ? 0 :
686 mLeftIndents[Math.min(i + mLineCount, leftLen - 1)];
687 int rightMargin = mRightIndents == null ? 0 :
688 mRightIndents[Math.min(i + mLineCount, rightLen - 1)];
689 indents[i] = leftMargin + rightMargin;
690 }
691 nSetIndents(b.mNativePtr, indents);
692 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693
Anish Athalyec8f9e622014-07-21 15:26:34 -0700694 // measurement has to be done before performing line breaking
695 // but we don't want to recompute fontmetrics or span ranges the
696 // second time, so we cache those and then use those stored values
697 int fmCacheCount = 0;
698 int spanEndCacheCount = 0;
Gilles Debunnecd943a72012-06-07 17:54:47 -0700699 for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
Anish Athalyec8f9e622014-07-21 15:26:34 -0700700 if (fmCacheCount * 4 >= fmCache.length) {
701 int[] grow = new int[fmCacheCount * 4 * 2];
702 System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
703 fmCache = grow;
704 }
705
706 if (spanEndCacheCount >= spanEndCache.length) {
707 int[] grow = new int[spanEndCacheCount * 2];
708 System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
709 spanEndCache = grow;
710 }
Doug Felte8e45f22010-03-29 14:58:40 -0700711
Gilles Debunnecd943a72012-06-07 17:54:47 -0700712 if (spanned == null) {
713 spanEnd = paraEnd;
Doug Felt23241882010-06-02 14:41:06 -0700714 int spanLen = spanEnd - spanStart;
Gilles Debunnecd943a72012-06-07 17:54:47 -0700715 measured.addStyleRun(paint, spanLen, fm);
716 } else {
717 spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
718 MetricAffectingSpan.class);
719 int spanLen = spanEnd - spanStart;
720 MetricAffectingSpan[] spans =
Doug Felt23241882010-06-02 14:41:06 -0700721 spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
Gilles Debunnecd943a72012-06-07 17:54:47 -0700722 spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
723 measured.addStyleRun(paint, spans, spanLen, fm);
Doug Felt23241882010-06-02 14:41:06 -0700724 }
725
Anish Athalyec8f9e622014-07-21 15:26:34 -0700726 // the order of storage here (top, bottom, ascent, descent) has to match the code below
727 // where these values are retrieved
728 fmCache[fmCacheCount * 4 + 0] = fm.top;
729 fmCache[fmCacheCount * 4 + 1] = fm.bottom;
730 fmCache[fmCacheCount * 4 + 2] = fm.ascent;
731 fmCache[fmCacheCount * 4 + 3] = fm.descent;
732 fmCacheCount++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733
Anish Athalyec8f9e622014-07-21 15:26:34 -0700734 spanEndCache[spanEndCacheCount] = spanEnd;
735 spanEndCacheCount++;
736 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737
Raph Levien70616ec2015-03-04 10:41:30 -0800738 nGetWidths(b.mNativePtr, widths);
Raph Levienc94f7422015-03-06 19:19:48 -0800739 int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
740 lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741
Anish Athalyec8f9e622014-07-21 15:26:34 -0700742 int[] breaks = lineBreaks.breaks;
743 float[] lineWidths = lineBreaks.widths;
Raph Levien26d443a2015-03-30 14:18:32 -0700744 int[] flags = lineBreaks.flags;
Anish Athalyec8f9e622014-07-21 15:26:34 -0700745
Keisuke Kuroyanagif4a3f3a2015-06-10 09:37:32 +0900746 final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
747 final boolean ellipsisMayBeApplied = ellipsize != null
748 && (ellipsize == TextUtils.TruncateAt.END
749 || (mMaximumVisibleLineCount == 1
750 && ellipsize != TextUtils.TruncateAt.MARQUEE));
Raph Levien04a84552015-07-09 15:38:04 -0700751 if (remainingLineCount > 0 && remainingLineCount < breakCount &&
752 ellipsisMayBeApplied) {
Keisuke Kuroyanagif4a3f3a2015-06-10 09:37:32 +0900753 // Treat the last line and overflowed lines as a single line.
754 breaks[remainingLineCount - 1] = breaks[breakCount - 1];
755 // Calculate width and flag.
756 float width = 0;
757 int flag = 0;
758 for (int i = remainingLineCount - 1; i < breakCount; i++) {
759 width += lineWidths[i];
760 flag |= flags[i] & TAB_MASK;
761 }
762 lineWidths[remainingLineCount - 1] = width;
763 flags[remainingLineCount - 1] = flag;
764
765 breakCount = remainingLineCount;
766 }
767
Anish Athalyec8f9e622014-07-21 15:26:34 -0700768 // here is the offset of the starting character of the line we are currently measuring
769 int here = paraStart;
770
771 int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
772 int fmCacheIndex = 0;
773 int spanEndCacheIndex = 0;
774 int breakIndex = 0;
775 for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
776 // retrieve end of span
777 spanEnd = spanEndCache[spanEndCacheIndex++];
778
779 // retrieve cached metrics, order matches above
780 fm.top = fmCache[fmCacheIndex * 4 + 0];
781 fm.bottom = fmCache[fmCacheIndex * 4 + 1];
782 fm.ascent = fmCache[fmCacheIndex * 4 + 2];
783 fm.descent = fmCache[fmCacheIndex * 4 + 3];
784 fmCacheIndex++;
785
786 if (fm.top < fmTop) {
787 fmTop = fm.top;
788 }
789 if (fm.ascent < fmAscent) {
790 fmAscent = fm.ascent;
791 }
792 if (fm.descent > fmDescent) {
793 fmDescent = fm.descent;
794 }
795 if (fm.bottom > fmBottom) {
796 fmBottom = fm.bottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 }
798
Anish Athalyec8f9e622014-07-21 15:26:34 -0700799 // skip breaks ending before current span range
800 while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
801 breakIndex++;
802 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803
Anish Athalyec8f9e622014-07-21 15:26:34 -0700804 while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
805 int endPos = paraStart + breaks[breakIndex];
806
Raph Levience4155a2015-03-11 11:02:33 -0700807 boolean moreChars = (endPos < bufEnd);
Raph Levien4c02e832014-12-12 11:17:01 -0800808
Anish Athalyec8f9e622014-07-21 15:26:34 -0700809 v = out(source, here, endPos,
810 fmAscent, fmDescent, fmTop, fmBottom,
811 v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex],
812 needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
813 chs, widths, paraStart, ellipsize, ellipsizedWidth,
Raph Levien4c02e832014-12-12 11:17:01 -0800814 lineWidths[breakIndex], paint, moreChars);
Anish Athalyec8f9e622014-07-21 15:26:34 -0700815
816 if (endPos < spanEnd) {
817 // preserve metrics for current span
818 fmTop = fm.top;
819 fmBottom = fm.bottom;
820 fmAscent = fm.ascent;
821 fmDescent = fm.descent;
822 } else {
823 fmTop = fmBottom = fmAscent = fmDescent = 0;
824 }
825
826 here = endPos;
827 breakIndex++;
828
829 if (mLineCount >= mMaximumVisibleLineCount) {
830 return;
831 }
832 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 }
834
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800835 if (paraEnd == bufEnd)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 break;
837 }
838
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -0700839 if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) &&
Fabrice Di Meglioad0b0512011-10-04 17:21:26 -0700840 mLineCount < mMaximumVisibleLineCount) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800841 // Log.e("text", "output last " + bufEnd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842
Raph Levien70616ec2015-03-04 10:41:30 -0800843 measured.setPara(source, bufEnd, bufEnd, textDir, b);
Fabrice Di Meglioe6318892013-06-18 20:03:41 -0700844
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 paint.getFontMetricsInt(fm);
846
847 v = out(source,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800848 bufEnd, bufEnd, fm.ascent, fm.descent,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 fm.top, fm.bottom,
850 v,
851 spacingmult, spacingadd, null,
Raph Levien26d443a2015-03-30 14:18:32 -0700852 null, fm, 0,
Fabrice Di Meglioe6318892013-06-18 20:03:41 -0700853 needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
Gilles Debunned300e752011-10-17 13:37:36 -0700854 includepad, trackpad, null,
855 null, bufStart, ellipsize,
856 ellipsizedWidth, 0, paint, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 }
858 }
859
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 private int out(CharSequence text, int start, int end,
861 int above, int below, int top, int bottom, int v,
862 float spacingmult, float spacingadd,
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800863 LineHeightSpan[] chooseHt, int[] chooseHtv,
Raph Levien26d443a2015-03-30 14:18:32 -0700864 Paint.FontMetricsInt fm, int flags,
Gilles Debunned300e752011-10-17 13:37:36 -0700865 boolean needMultiply, byte[] chdirs, int dir,
866 boolean easy, int bufEnd, boolean includePad,
867 boolean trackPad, char[] chs,
868 float[] widths, int widthStart, TextUtils.TruncateAt ellipsize,
869 float ellipsisWidth, float textWidth,
870 TextPaint paint, boolean moreChars) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 int j = mLineCount;
872 int off = j * mColumns;
873 int want = off + mColumns + TOP;
874 int[] lines = mLines;
875
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 if (want >= lines.length) {
Adam Lesinski776abc22014-03-07 11:30:59 -0500877 Directions[] grow2 = ArrayUtils.newUnpaddedArray(
878 Directions.class, GrowingArrayUtils.growSize(want));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 System.arraycopy(mLineDirections, 0, grow2, 0,
880 mLineDirections.length);
881 mLineDirections = grow2;
Adam Lesinski776abc22014-03-07 11:30:59 -0500882
883 int[] grow = new int[grow2.length];
884 System.arraycopy(lines, 0, grow, 0, lines.length);
885 mLines = grow;
886 lines = grow;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 }
888
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800889 if (chooseHt != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 fm.ascent = above;
891 fm.descent = below;
892 fm.top = top;
893 fm.bottom = bottom;
894
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800895 for (int i = 0; i < chooseHt.length; i++) {
896 if (chooseHt[i] instanceof LineHeightSpan.WithDensity) {
897 ((LineHeightSpan.WithDensity) chooseHt[i]).
898 chooseHeight(text, start, end, chooseHtv[i], v, fm, paint);
Eric Fischera9f1dd02009-08-12 15:00:10 -0700899
900 } else {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800901 chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm);
Eric Fischera9f1dd02009-08-12 15:00:10 -0700902 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 }
904
905 above = fm.ascent;
906 below = fm.descent;
907 top = fm.top;
908 bottom = fm.bottom;
909 }
910
Raph Leviend97b0972014-04-24 12:51:35 -0700911 boolean firstLine = (j == 0);
912 boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
913 boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd);
914
915 if (firstLine) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800916 if (trackPad) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 mTopPadding = top - above;
918 }
919
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800920 if (includePad) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 above = top;
922 }
923 }
Raph Leviend97b0972014-04-24 12:51:35 -0700924
925 int extra;
926
927 if (lastLine) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800928 if (trackPad) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 mBottomPadding = bottom - below;
930 }
931
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800932 if (includePad) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 below = bottom;
934 }
935 }
936
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937
Raph Leviend97b0972014-04-24 12:51:35 -0700938 if (needMultiply && !lastLine) {
Doug Felt10657582010-02-22 11:19:01 -0800939 double ex = (below - above) * (spacingmult - 1) + spacingadd;
940 if (ex >= 0) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800941 extra = (int)(ex + EXTRA_ROUNDING);
Doug Felt10657582010-02-22 11:19:01 -0800942 } else {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800943 extra = -(int)(-ex + EXTRA_ROUNDING);
Doug Felt10657582010-02-22 11:19:01 -0800944 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 } else {
946 extra = 0;
947 }
948
949 lines[off + START] = start;
950 lines[off + TOP] = v;
951 lines[off + DESCENT] = below + extra;
952
953 v += (below - above) + extra;
954 lines[off + mColumns + START] = end;
955 lines[off + mColumns + TOP] = v;
956
Raph Levien26d443a2015-03-30 14:18:32 -0700957 // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
958 // one bit for start field
959 lines[off + TAB] |= flags & TAB_MASK;
960 lines[off + HYPHEN] = flags;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961
Doug Felt9f7a4442010-03-01 12:45:56 -0800962 lines[off + DIR] |= dir << DIR_SHIFT;
963 Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
964 // easy means all chars < the first RTL, so no emoji, no nothing
Doug Felt4e0c5e52010-03-15 16:56:02 -0700965 // XXX a run with no text or all spaces is easy but might be an empty
Doug Felt9f7a4442010-03-01 12:45:56 -0800966 // RTL paragraph. Make sure easy is false if this is the case.
967 if (easy) {
968 mLineDirections[j] = linedirs;
969 } else {
Gilles Debunnef3fa0cd2011-02-03 14:17:05 -0800970 mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
971 start - widthStart, end - start);
Gilles Debunne0a4db3c2011-01-14 12:12:04 -0800972 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973
Fabrice Di Meglioaef455f2011-08-29 15:39:11 -0700974 if (ellipsize != null) {
975 // If there is only one line, then do any type of ellipsis except when it is MARQUEE
976 // if there are multiple lines, just allow END ellipsis on the last line
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -0700977 boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
Fabrice Di Meglioaef455f2011-08-29 15:39:11 -0700978
Fabrice Di Meglio34a126e2012-02-29 18:43:14 -0800979 boolean doEllipsis =
980 (((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
Fabrice Di Meglioaef455f2011-08-29 15:39:11 -0700981 ellipsize != TextUtils.TruncateAt.MARQUEE) ||
982 (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
983 ellipsize == TextUtils.TruncateAt.END);
984 if (doEllipsis) {
985 calculateEllipsis(start, end, widths, widthStart,
986 ellipsisWidth, ellipsize, j,
987 textWidth, paint, forceEllipsis);
988 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 }
990
991 mLineCount++;
992 return v;
993 }
994
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -0800995 private void calculateEllipsis(int lineStart, int lineEnd,
996 float[] widths, int widthStart,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 float avail, TextUtils.TruncateAt where,
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -0700998 int line, float textWidth, TextPaint paint,
999 boolean forceEllipsis) {
1000 if (textWidth <= avail && !forceEllipsis) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 // Everything fits!
1002 mLines[mColumns * line + ELLIPSIS_START] = 0;
1003 mLines[mColumns * line + ELLIPSIS_COUNT] = 0;
1004 return;
1005 }
1006
Fabrice Di Megliocb332642011-09-23 19:08:04 -07001007 float ellipsisWidth = paint.measureText(
Fabrice Di Meglio8d44fff2012-06-13 15:45:38 -07001008 (where == TextUtils.TruncateAt.END_SMALL) ?
Neil Fullerd29bdb22015-02-06 10:03:08 +00001009 TextUtils.ELLIPSIS_TWO_DOTS : TextUtils.ELLIPSIS_NORMAL, 0, 1);
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001010 int ellipsisStart = 0;
1011 int ellipsisCount = 0;
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -08001012 int len = lineEnd - lineStart;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001014 // We only support start ellipsis on a single line
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 if (where == TextUtils.TruncateAt.START) {
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001016 if (mMaximumVisibleLineCount == 1) {
1017 float sum = 0;
1018 int i;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019
Keisuke Kuroyanagied2eea12015-04-14 18:18:35 +09001020 for (i = len; i > 0; i--) {
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001021 float w = widths[i - 1 + lineStart - widthStart];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001023 if (w + sum + ellipsisWidth > avail) {
1024 break;
1025 }
1026
1027 sum += w;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 }
1029
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001030 ellipsisStart = 0;
1031 ellipsisCount = i;
1032 } else {
1033 if (Log.isLoggable(TAG, Log.WARN)) {
1034 Log.w(TAG, "Start Ellipsis only supported with one line");
1035 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 }
Fabrice Di Megliocb332642011-09-23 19:08:04 -07001037 } else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE ||
1038 where == TextUtils.TruncateAt.END_SMALL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 float sum = 0;
1040 int i;
1041
1042 for (i = 0; i < len; i++) {
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -08001043 float w = widths[i + lineStart - widthStart];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -08001045 if (w + sum + ellipsisWidth > avail) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 break;
1047 }
1048
1049 sum += w;
1050 }
1051
1052 ellipsisStart = i;
1053 ellipsisCount = len - i;
Fabrice Di Meglioaef455f2011-08-29 15:39:11 -07001054 if (forceEllipsis && ellipsisCount == 0 && len > 0) {
1055 ellipsisStart = len - 1;
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001056 ellipsisCount = 1;
1057 }
1058 } else {
1059 // where = TextUtils.TruncateAt.MIDDLE We only support middle ellipsis on a single line
1060 if (mMaximumVisibleLineCount == 1) {
1061 float lsum = 0, rsum = 0;
1062 int left = 0, right = len;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001064 float ravail = (avail - ellipsisWidth) / 2;
Raph Levien0e3c5e82014-12-04 13:26:07 -08001065 for (right = len; right > 0; right--) {
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001066 float w = widths[right - 1 + lineStart - widthStart];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001068 if (w + rsum > ravail) {
1069 break;
1070 }
1071
1072 rsum += w;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 }
1074
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001075 float lavail = avail - ellipsisWidth - rsum;
1076 for (left = 0; left < right; left++) {
1077 float w = widths[left + lineStart - widthStart];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001079 if (w + lsum > lavail) {
1080 break;
1081 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001083 lsum += w;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 }
1085
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001086 ellipsisStart = left;
1087 ellipsisCount = right - left;
1088 } else {
1089 if (Log.isLoggable(TAG, Log.WARN)) {
1090 Log.w(TAG, "Middle Ellipsis only supported with one line");
1091 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 }
1094
1095 mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart;
1096 mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
1097 }
1098
Doug Felte8e45f22010-03-29 14:58:40 -07001099 // Override the base class so we can directly access our members,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 // rather than relying on member functions.
1101 // The logic mirrors that of Layout.getLineForVertical
1102 // FIXME: It may be faster to do a linear search for layouts without many lines.
Gilles Debunne66111472010-11-19 11:04:37 -08001103 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 public int getLineForVertical(int vertical) {
1105 int high = mLineCount;
1106 int low = -1;
1107 int guess;
1108 int[] lines = mLines;
1109 while (high - low > 1) {
1110 guess = (high + low) >> 1;
1111 if (lines[mColumns * guess + TOP] > vertical){
1112 high = guess;
1113 } else {
1114 low = guess;
1115 }
1116 }
1117 if (low < 0) {
1118 return 0;
1119 } else {
1120 return low;
1121 }
1122 }
1123
Gilles Debunne66111472010-11-19 11:04:37 -08001124 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 public int getLineCount() {
1126 return mLineCount;
1127 }
1128
Gilles Debunne66111472010-11-19 11:04:37 -08001129 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001130 public int getLineTop(int line) {
Gilles Debunne0a4db3c2011-01-14 12:12:04 -08001131 int top = mLines[mColumns * line + TOP];
1132 if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount &&
1133 line != mLineCount) {
1134 top += getBottomPadding();
1135 }
1136 return top;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 }
1138
Gilles Debunne66111472010-11-19 11:04:37 -08001139 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 public int getLineDescent(int line) {
Gilles Debunne0a4db3c2011-01-14 12:12:04 -08001141 int descent = mLines[mColumns * line + DESCENT];
Gilles Debunnef3fa0cd2011-02-03 14:17:05 -08001142 if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount - 1 && // -1 intended
Gilles Debunne0a4db3c2011-01-14 12:12:04 -08001143 line != mLineCount) {
1144 descent += getBottomPadding();
1145 }
1146 return descent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 }
1148
Gilles Debunne66111472010-11-19 11:04:37 -08001149 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 public int getLineStart(int line) {
1151 return mLines[mColumns * line + START] & START_MASK;
1152 }
1153
Gilles Debunne66111472010-11-19 11:04:37 -08001154 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001155 public int getParagraphDirection(int line) {
1156 return mLines[mColumns * line + DIR] >> DIR_SHIFT;
1157 }
1158
Gilles Debunne66111472010-11-19 11:04:37 -08001159 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 public boolean getLineContainsTab(int line) {
1161 return (mLines[mColumns * line + TAB] & TAB_MASK) != 0;
1162 }
1163
Gilles Debunne66111472010-11-19 11:04:37 -08001164 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001165 public final Directions getLineDirections(int line) {
1166 return mLineDirections[line];
1167 }
1168
Gilles Debunne66111472010-11-19 11:04:37 -08001169 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 public int getTopPadding() {
1171 return mTopPadding;
1172 }
1173
Gilles Debunne66111472010-11-19 11:04:37 -08001174 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 public int getBottomPadding() {
1176 return mBottomPadding;
1177 }
1178
Raph Levien26d443a2015-03-30 14:18:32 -07001179 /**
1180 * @hide
1181 */
1182 @Override
1183 public int getHyphen(int line) {
1184 return mLines[mColumns * line + HYPHEN] & 0xff;
1185 }
1186
Raph Levien2ea52902015-07-01 14:39:31 -07001187 /**
1188 * @hide
1189 */
1190 @Override
1191 public int getIndentAdjust(int line, Alignment align) {
1192 if (align == Alignment.ALIGN_LEFT) {
1193 if (mLeftIndents == null) {
1194 return 0;
1195 } else {
1196 return mLeftIndents[Math.min(line, mLeftIndents.length - 1)];
1197 }
1198 } else if (align == Alignment.ALIGN_RIGHT) {
1199 if (mRightIndents == null) {
1200 return 0;
1201 } else {
1202 return -mRightIndents[Math.min(line, mRightIndents.length - 1)];
1203 }
1204 } else if (align == Alignment.ALIGN_CENTER) {
1205 int left = 0;
1206 if (mLeftIndents != null) {
1207 left = mLeftIndents[Math.min(line, mLeftIndents.length - 1)];
1208 }
1209 int right = 0;
1210 if (mRightIndents != null) {
1211 right = mRightIndents[Math.min(line, mRightIndents.length - 1)];
1212 }
1213 return (left - right) >> 1;
1214 } else {
1215 throw new AssertionError("unhandled alignment " + align);
1216 }
1217 }
1218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001219 @Override
1220 public int getEllipsisCount(int line) {
1221 if (mColumns < COLUMNS_ELLIPSIZE) {
1222 return 0;
1223 }
1224
1225 return mLines[mColumns * line + ELLIPSIS_COUNT];
1226 }
1227
1228 @Override
1229 public int getEllipsisStart(int line) {
1230 if (mColumns < COLUMNS_ELLIPSIZE) {
1231 return 0;
1232 }
1233
1234 return mLines[mColumns * line + ELLIPSIS_START];
1235 }
1236
1237 @Override
1238 public int getEllipsizedWidth() {
1239 return mEllipsizedWidth;
1240 }
1241
Raph Levien70616ec2015-03-04 10:41:30 -08001242 private static native long nNewBuilder();
1243 private static native void nFreeBuilder(long nativePtr);
1244 private static native void nFinishBuilder(long nativePtr);
Raph Levien26d443a2015-03-30 14:18:32 -07001245
1246 /* package */ static native long nLoadHyphenator(String patternData);
1247
1248 private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
Raph Levien70616ec2015-03-04 10:41:30 -08001249
Raph Leviene319d5a2015-04-14 23:51:07 -07001250 private static native void nSetIndents(long nativePtr, int[] indents);
1251
Raph Levienc94f7422015-03-06 19:19:48 -08001252 // Set up paragraph text and settings; done as one big method to minimize jni crossings
1253 private static native void nSetupParagraph(long nativePtr, char[] text, int length,
1254 float firstWidth, int firstWidthLineCount, float restWidth,
Roozbeh Pournader95c7a132015-05-12 12:01:06 -07001255 int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency);
Raph Levien70616ec2015-03-04 10:41:30 -08001256
1257 private static native float nAddStyleRun(long nativePtr, long nativePaint,
1258 long nativeTypeface, int start, int end, boolean isRtl);
1259
1260 private static native void nAddMeasuredRun(long nativePtr,
1261 int start, int end, float[] widths);
1262
1263 private static native void nAddReplacementRun(long nativePtr, int start, int end, float width);
1264
1265 private static native void nGetWidths(long nativePtr, float[] widths);
1266
Anish Athalyec8f9e622014-07-21 15:26:34 -07001267 // populates LineBreaks and returns the number of breaks found
1268 //
1269 // the arrays inside the LineBreaks objects are passed in as well
1270 // to reduce the number of JNI calls in the common case where the
1271 // arrays do not have to be resized
Raph Levienc94f7422015-03-06 19:19:48 -08001272 private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
Raph Levien26d443a2015-03-30 14:18:32 -07001273 int[] recycleBreaks, float[] recycleWidths, int[] recycleFlags, int recycleLength);
Anish Athalye88b5b0b2014-06-24 14:39:43 -07001274
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 private int mLineCount;
1276 private int mTopPadding, mBottomPadding;
1277 private int mColumns;
1278 private int mEllipsizedWidth;
1279
Raph Levien26d443a2015-03-30 14:18:32 -07001280 private static final int COLUMNS_NORMAL = 4;
1281 private static final int COLUMNS_ELLIPSIZE = 6;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282 private static final int START = 0;
1283 private static final int DIR = START;
1284 private static final int TAB = START;
1285 private static final int TOP = 1;
1286 private static final int DESCENT = 2;
Raph Levien26d443a2015-03-30 14:18:32 -07001287 private static final int HYPHEN = 3;
1288 private static final int ELLIPSIS_START = 4;
1289 private static final int ELLIPSIS_COUNT = 5;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290
1291 private int[] mLines;
1292 private Directions[] mLineDirections;
Fabrice Di Meglio8059e0902011-08-10 16:31:58 -07001293 private int mMaximumVisibleLineCount = Integer.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294
1295 private static final int START_MASK = 0x1FFFFFFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 private static final int DIR_SHIFT = 30;
1297 private static final int TAB_MASK = 0x20000000;
1298
Doug Feltc982f602010-05-25 11:51:40 -07001299 private static final int TAB_INCREMENT = 20; // same as Layout, but that's private
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -08001301 private static final char CHAR_NEW_LINE = '\n';
Fabrice Di Meglio121c82c2011-02-15 15:44:49 -08001302
1303 private static final double EXTRA_ROUNDING = 0.5;
Fabrice Di Megliocb332642011-09-23 19:08:04 -07001304
Anish Athalyec8f9e622014-07-21 15:26:34 -07001305 // This is used to return three arrays from a single JNI call when
1306 // performing line breaking
Deepanshu Gupta70539192014-10-15 15:57:40 -07001307 /*package*/ static class LineBreaks {
Anish Athalyec8f9e622014-07-21 15:26:34 -07001308 private static final int INITIAL_SIZE = 16;
1309 public int[] breaks = new int[INITIAL_SIZE];
1310 public float[] widths = new float[INITIAL_SIZE];
Roozbeh Pournader112d9c72015-08-07 12:44:41 -07001311 public int[] flags = new int[INITIAL_SIZE]; // hasTab
Anish Athalyec8f9e622014-07-21 15:26:34 -07001312 // breaks, widths, and flags should all have the same length
1313 }
1314
Raph Levien2ea52902015-07-01 14:39:31 -07001315 private int[] mLeftIndents;
1316 private int[] mRightIndents;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317}