StaticLayout.java revision f4a3f3a03c72b6a38dc2f3f965531dc2a12460df
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19531c30c62b14881aab31a5133920a971b1fbb50eRaph Levienimport android.annotation.Nullable;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Paint;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.LeadingMarginSpan;
226611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunneimport android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.LineHeightSpan;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.MetricAffectingSpan;
25c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.style.TabStopSpan;
268059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglioimport android.util.Log;
2739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levienimport android.util.Pools.SynchronizedPool;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
29cb379120456d8065d742021fc5c66748fc8a11a8Doug Feltimport com.android.internal.util.ArrayUtils;
30776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinskiimport com.android.internal.util.GrowingArrayUtils;
31cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
32c8f9e6218681640d5f384c12edf06619be56a583Anish Athalyeimport java.util.Arrays;
334c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levienimport java.util.Locale;
34c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * StaticLayout is a Layout for text that will not be edited after it
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * is laid out.  Use {@link DynamicLayout} for text that may change.
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>This is used by widgets to control text layout. You should not need
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * to use this class directly unless you are implementing your own widget
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * or custom display object, or would be tempted to call
414e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int,
424e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * float, float, android.graphics.Paint)
434e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * Canvas.drawText()} directly.</p>
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
45121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Megliopublic class StaticLayout extends Layout {
46121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio
478059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    static final String TAG = "StaticLayout";
488059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
49d3ab692d28018825578ff05832644cfad60233fbRaph Levien    /**
50531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * Builder for static layouts. The builder is a newer pattern for constructing
51531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * StaticLayout objects and should be preferred over the constructors,
52531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * particularly to access newer features. To build a static layout, first
53531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * call {@link #obtain} with the required arguments (text, paint, and width),
54531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * then call setters for optional parameters, and finally {@link #build}
55531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * to build the StaticLayout object. Parameters not explicitly set will get
56531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * default values.
57d3ab692d28018825578ff05832644cfad60233fbRaph Levien     */
58d3ab692d28018825578ff05832644cfad60233fbRaph Levien    public final static class Builder {
594c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        private Builder() {
604c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            mNativePtr = nNewBuilder();
614c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        }
624c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
63531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
64531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Obtain a builder for constructing StaticLayout objects
65531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
66531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param source The text to be laid out, optionally with spans
67531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param start The index of the start of the text
68531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param end The index + 1 of the end of the text
69531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param paint The base paint used for layout
70531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param width The width in pixels
71531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return a builder object used for constructing the StaticLayout
72531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
73ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien        public static Builder obtain(CharSequence source, int start, int end, TextPaint paint,
74ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien                int width) {
7539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            Builder b = sPool.acquire();
76d3ab692d28018825578ff05832644cfad60233fbRaph Levien            if (b == null) {
77d3ab692d28018825578ff05832644cfad60233fbRaph Levien                b = new Builder();
78d3ab692d28018825578ff05832644cfad60233fbRaph Levien            }
79d3ab692d28018825578ff05832644cfad60233fbRaph Levien
80d3ab692d28018825578ff05832644cfad60233fbRaph Levien            // set default initial values
8139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mText = source;
8239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mStart = start;
8339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mEnd = end;
84ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien            b.mPaint = paint;
8539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mWidth = width;
8639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mAlignment = Alignment.ALIGN_NORMAL;
87d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
88d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mSpacingMult = 1.0f;
89d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mSpacingAdd = 0.0f;
90d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mIncludePad = true;
9139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mEllipsizedWidth = width;
92d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mEllipsize = null;
93d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mMaxLines = Integer.MAX_VALUE;
943bd60c7b1125ee42cd7e8746aeaebdb43e8211d8Raph Levien            b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
9595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader            b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
96d3ab692d28018825578ff05832644cfad60233fbRaph Levien
97d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mMeasuredText = MeasuredText.obtain();
98d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return b;
99d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
100d3ab692d28018825578ff05832644cfad60233fbRaph Levien
10139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        private static void recycle(Builder b) {
102d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mPaint = null;
103d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mText = null;
104d3ab692d28018825578ff05832644cfad60233fbRaph Levien            MeasuredText.recycle(b.mMeasuredText);
1053bd60c7b1125ee42cd7e8746aeaebdb43e8211d8Raph Levien            b.mMeasuredText = null;
1063bd60c7b1125ee42cd7e8746aeaebdb43e8211d8Raph Levien            nFinishBuilder(b.mNativePtr);
10739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            sPool.release(b);
108d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
109d3ab692d28018825578ff05832644cfad60233fbRaph Levien
110d3ab692d28018825578ff05832644cfad60233fbRaph Levien        // release any expensive state
111d3ab692d28018825578ff05832644cfad60233fbRaph Levien        /* package */ void finish() {
1124c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            nFinishBuilder(mNativePtr);
113d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mMeasuredText.finish();
114d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
115d3ab692d28018825578ff05832644cfad60233fbRaph Levien
116d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setText(CharSequence source) {
117d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return setText(source, 0, source.length());
118d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
119d3ab692d28018825578ff05832644cfad60233fbRaph Levien
120531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
121531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the text. Only useful when re-using the builder, which is done for
122531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * the internal implementation of {@link DynamicLayout} but not as part
123531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * of normal {@link StaticLayout} usage.
124531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
125531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param source The text to be laid out, optionally with spans
126531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param start The index of the start of the text
127531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param end The index + 1 of the end of the text
128531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
129531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
130531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @hide
131531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
132d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setText(CharSequence source, int start, int end) {
133d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mText = source;
134d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mStart = start;
135d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mEnd = end;
136d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
137d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
138d3ab692d28018825578ff05832644cfad60233fbRaph Levien
139531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
140531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the paint. Internal for reuse cases only.
141531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
142531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param paint The base paint used for layout
143531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
144531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
145531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @hide
146531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
147d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setPaint(TextPaint paint) {
148d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mPaint = paint;
149d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
150d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
151d3ab692d28018825578ff05832644cfad60233fbRaph Levien
152531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
153531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the width. Internal for reuse cases only.
154531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
155531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param width The width in pixels
156531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
157531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
158531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @hide
159531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
160d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setWidth(int width) {
161d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mWidth = width;
162d3ab692d28018825578ff05832644cfad60233fbRaph Levien            if (mEllipsize == null) {
163d3ab692d28018825578ff05832644cfad60233fbRaph Levien                mEllipsizedWidth = width;
164d3ab692d28018825578ff05832644cfad60233fbRaph Levien            }
165d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
166d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
167d3ab692d28018825578ff05832644cfad60233fbRaph Levien
168531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
169531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the alignment. The default is {@link Layout.Alignment#ALIGN_NORMAL}.
170531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
171531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param alignment Alignment for the resulting {@link StaticLayout}
172531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
173531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
17439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        public Builder setAlignment(Alignment alignment) {
17539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mAlignment = alignment;
17639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            return this;
17739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        }
17839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
179531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
180531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the text direction heuristic. The text direction heuristic is used to
181531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * resolve text direction based per-paragraph based on the input text. The default is
182531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
183531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
184531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param textDir text direction heuristic for resolving BiDi behavior.
185531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
186531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
187a6a082862b9e2ea4c9e9a1a945927c4040993f6eRaph Levien        public Builder setTextDirection(TextDirectionHeuristic textDir) {
188d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mTextDir = textDir;
189d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
190d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
191d3ab692d28018825578ff05832644cfad60233fbRaph Levien
192531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
193531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set line spacing parameters. The default is 0.0 for {@code spacingAdd}
194531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * and 1.0 for {@code spacingMult}.
195531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
196531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param spacingAdd line spacing add
197531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param spacingMult line spacing multiplier
198531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
199531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setLineSpacing
200531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
201531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        public Builder setLineSpacing(float spacingAdd, float spacingMult) {
202d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mSpacingAdd = spacingAdd;
203531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien            mSpacingMult = spacingMult;
204d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
205d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
206d3ab692d28018825578ff05832644cfad60233fbRaph Levien
207531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
208531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set whether to include extra space beyond font ascent and descent (which is
209531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * needed to avoid clipping in some languages, such as Arabic and Kannada). The
210531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * default is {@code true}.
211531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
212531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param includePad whether to include padding
213531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
214531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setIncludeFontPadding
215531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
216d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setIncludePad(boolean includePad) {
217d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mIncludePad = includePad;
218d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
219d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
220d3ab692d28018825578ff05832644cfad60233fbRaph Levien
221531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
222531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the width as used for ellipsizing purposes, if it differs from the
223531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * normal layout width. The default is the {@code width}
224531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * passed to {@link #obtain}.
225531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
226531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param ellipsizedWidth width used for ellipsizing, in pixels
227531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
228531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setEllipsize
229531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
230d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setEllipsizedWidth(int ellipsizedWidth) {
231d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mEllipsizedWidth = ellipsizedWidth;
232d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
233d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
234d3ab692d28018825578ff05832644cfad60233fbRaph Levien
235531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
236531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set ellipsizing on the layout. Causes words that are longer than the view
237531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * is wide, or exceeding the number of lines (see #setMaxLines) in the case
238531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * of {@link android.text.TextUtils.TruncateAt#END} or
239531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * {@link android.text.TextUtils.TruncateAt#MARQUEE}, to be ellipsized instead
240531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * of broken. The default is
241531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * {@code null}, indicating no ellipsis is to be applied.
242531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
243531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param ellipsize type of ellipsis behavior
244531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
245531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setEllipsize
246531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
247531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
248d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mEllipsize = ellipsize;
249d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
250d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
251d3ab692d28018825578ff05832644cfad60233fbRaph Levien
252531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
253531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set maximum number of lines. This is particularly useful in the case of
254531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * ellipsizing, where it changes the layout of the last line. The default is
255531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * unlimited.
256531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
257531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param maxLines maximum number of lines in the layout
258531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
259531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setMaxLines
260531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
261d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setMaxLines(int maxLines) {
262d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mMaxLines = maxLines;
263d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
264d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
265d3ab692d28018825578ff05832644cfad60233fbRaph Levien
266531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
267531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set break strategy, useful for selecting high quality or balanced paragraph
268531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * layout options. The default is {@link Layout#BREAK_STRATEGY_SIMPLE}.
269531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
270531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param breakStrategy break strategy for paragraph layout
271531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
272531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setBreakStrategy
273531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
27439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
27539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mBreakStrategy = breakStrategy;
27639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            return this;
27739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        }
27839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
279531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
28095c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * Set hyphenation frequency, to control the amount of automatic hyphenation used. The
28195c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * default is {@link Layout#HYPHENATION_FREQUENCY_NONE}.
28295c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         *
28395c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * @param hyphenationFrequency hyphenation frequency for the paragraph
28495c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * @return this builder, useful for chaining
28595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * @see android.widget.TextView#setHyphenationFrequency
28695c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         */
28795c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader        public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
28895c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader            mHyphenationFrequency = hyphenationFrequency;
28995c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader            return this;
29095c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader        }
29195c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader
29295c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader        /**
293531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set indents. Arguments are arrays holding an indent amount, one per line, measured in
294531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * pixels. For lines past the last element in the array, the last element repeats.
295531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
296531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param leftIndents array of indent values for left margin, in pixels
297531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param rightIndents array of indent values for right margin, in pixels
298531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
299531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setIndents
300531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
301e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien        public Builder setIndents(int[] leftIndents, int[] rightIndents) {
302e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            int leftLen = leftIndents == null ? 0 : leftIndents.length;
303e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            int rightLen = rightIndents == null ? 0 : rightIndents.length;
304e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            int[] indents = new int[Math.max(leftLen, rightLen)];
305e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            for (int i = 0; i < indents.length; i++) {
306e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien                int leftMargin = i < leftLen ? leftIndents[i] : 0;
307e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien                int rightMargin = i < rightLen ? rightIndents[i] : 0;
308e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien                indents[i] = leftMargin + rightMargin;
309e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            }
310e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            nSetIndents(mNativePtr, indents);
311e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            return this;
312e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien        }
313e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien
31470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /**
31570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * Measurement and break iteration is done in native code. The protocol for using
31670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * the native code is as follows.
31770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *
31826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien         * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab
31995c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * stops, break strategy, and hyphenation frequency (and possibly other parameters in the
32095c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * future).
321c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien         *
322c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien         * Then, for each run within the paragraph:
32370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *  - setLocale (this must be done at least for the first run, optional afterwards)
32470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *  - one of the following, depending on the type of run:
32570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *    + addStyleRun (a text run, to be measured in native code)
32670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *    + addMeasuredRun (a run already measured in Java, passed into native code)
32770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *    + addReplacementRun (a replacement run, width is given)
32870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *
32970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * After measurement, nGetWidths() is valid if the widths are needed (eg for ellipsis).
33070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * Run nComputeLineBreaks() to obtain line breaks for the paragraph.
33170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *
33270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * After all paragraphs, call finish() to release expensive buffers.
33370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         */
33470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
33570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        private void setLocale(Locale locale) {
3364c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            if (!locale.equals(mLocale)) {
33726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien                nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale));
3384c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien                mLocale = locale;
3394c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            }
3404c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        }
3414c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
34270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /* package */ float addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
34370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            return nAddStyleRun(mNativePtr, paint.getNativeInstance(), paint.mNativeTypeface,
34470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien                    start, end, isRtl);
34570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        }
34670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
34770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /* package */ void addMeasuredRun(int start, int end, float[] widths) {
34870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            nAddMeasuredRun(mNativePtr, start, end, widths);
34970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        }
35070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
35170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /* package */ void addReplacementRun(int start, int end, float width) {
35270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            nAddReplacementRun(mNativePtr, start, end, width);
35370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        }
35470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
355531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
356531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Build the {@link StaticLayout} after options have been set.
357531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
358531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * <p>Note: the builder object must not be reused in any way after calling this
359531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * method. Setting parameters after calling this method, or calling it a second
360531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * time on the same builder object, will likely lead to unexpected results.
361531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
362531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return the newly constructed {@link StaticLayout} object
363531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
364d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public StaticLayout build() {
36539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            StaticLayout result = new StaticLayout(this);
36639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            Builder.recycle(this);
367d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return result;
368d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
369d3ab692d28018825578ff05832644cfad60233fbRaph Levien
3704c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        @Override
3714c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        protected void finalize() throws Throwable {
3724c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            try {
3734c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien                nFreeBuilder(mNativePtr);
3744c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            } finally {
3754c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien                super.finalize();
3764c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            }
3774c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        }
3784c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
3794c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        /* package */ long mNativePtr;
3804c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
381d3ab692d28018825578ff05832644cfad60233fbRaph Levien        CharSequence mText;
382d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mStart;
383d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mEnd;
384d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextPaint mPaint;
385d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mWidth;
38639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        Alignment mAlignment;
387d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextDirectionHeuristic mTextDir;
388d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float mSpacingMult;
389d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float mSpacingAdd;
390d3ab692d28018825578ff05832644cfad60233fbRaph Levien        boolean mIncludePad;
391d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mEllipsizedWidth;
392d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextUtils.TruncateAt mEllipsize;
393d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mMaxLines;
39439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        int mBreakStrategy;
39595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader        int mHyphenationFrequency;
396d3ab692d28018825578ff05832644cfad60233fbRaph Levien
397d3ab692d28018825578ff05832644cfad60233fbRaph Levien        Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
398d3ab692d28018825578ff05832644cfad60233fbRaph Levien
399d3ab692d28018825578ff05832644cfad60233fbRaph Levien        // This will go away and be subsumed by native builder code
400d3ab692d28018825578ff05832644cfad60233fbRaph Levien        MeasuredText mMeasuredText;
401d3ab692d28018825578ff05832644cfad60233fbRaph Levien
4024c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        Locale mLocale;
4034c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
40439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
405d3ab692d28018825578ff05832644cfad60233fbRaph Levien    }
406d3ab692d28018825578ff05832644cfad60233fbRaph Levien
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, TextPaint paint,
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int width,
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Alignment align, float spacingmult, float spacingadd,
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad) {
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(source, 0, source.length(), paint, width, align,
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             spacingmult, spacingadd, includepad);
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
415cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
416cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
417cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
418cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, TextPaint paint,
419cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            int width, Alignment align, TextDirectionHeuristic textDir,
420cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
421cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad) {
422cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, 0, source.length(), paint, width, align, textDir,
423cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                spacingmult, spacingadd, includepad);
424cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    }
425cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, int bufstart, int bufend,
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        TextPaint paint, int outerwidth,
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Alignment align,
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        float spacingmult, float spacingadd,
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad) {
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(source, bufstart, bufend, paint, outerwidth, align,
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             spacingmult, spacingadd, includepad, null, 0);
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
435cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
436cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
437cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
438cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, int bufstart, int bufend,
439cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextPaint paint, int outerwidth,
440cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            Alignment align, TextDirectionHeuristic textDir,
441cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
442cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad) {
443cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, bufstart, bufend, paint, outerwidth, align, textDir,
4448059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                spacingmult, spacingadd, includepad, null, 0, Integer.MAX_VALUE);
445cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt}
446cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
447cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, int bufstart, int bufend,
448cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextPaint paint, int outerwidth,
449cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            Alignment align,
450cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
451cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad,
452cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
453cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, bufstart, bufend, paint, outerwidth, align,
454cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                TextDirectionHeuristics.FIRSTSTRONG_LTR,
4558059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE);
456cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    }
457cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
458cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
459cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
460cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, int bufstart, int bufend,
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        TextPaint paint, int outerwidth,
463cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                        Alignment align, TextDirectionHeuristic textDir,
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        float spacingmult, float spacingadd,
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad,
4668059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super((ellipsize == null)
4684e0c5e55e171532760d5f51e0165563827129d4eDoug Felt                ? source
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                : (source instanceof Spanned)
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? new SpannedEllipsizer(source)
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : new Ellipsizer(source),
472cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt              paint, outerwidth, align, textDir, spacingmult, spacingadd);
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
474ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien        Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
47539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            .setAlignment(align)
476a6a082862b9e2ea4c9e9a1a945927c4040993f6eRaph Levien            .setTextDirection(textDir)
477531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien            .setLineSpacing(spacingadd, spacingmult)
478d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setIncludePad(includepad)
479d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setEllipsizedWidth(ellipsizedWidth)
480d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setEllipsize(ellipsize)
481d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setMaxLines(maxLines);
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /*
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This is annoying, but we can't refer to the layout until
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * superclass construction is finished, and the superclass
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * constructor wants the reference to the display text.
4864e0c5e55e171532760d5f51e0165563827129d4eDoug Felt         *
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This will break if the superclass constructor ever actually
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * cares about the content instead of just holding the reference.
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (ellipsize != null) {
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Ellipsizer e = (Ellipsizer) getText();
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mLayout = this;
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mWidth = ellipsizedWidth;
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mMethod = ellipsize;
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mEllipsizedWidth = ellipsizedWidth;
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mColumns = COLUMNS_ELLIPSIZE;
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mColumns = COLUMNS_NORMAL;
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mEllipsizedWidth = outerwidth;
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
504776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
505776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLines = new int[mLineDirections.length];
5068059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        mMaximumVisibleLineCount = maxLines;
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
50870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        generate(b, b.mIncludePad, b.mIncludePad);
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
510d3ab692d28018825578ff05832644cfad60233fbRaph Levien        Builder.recycle(b);
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5138059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    /* package */ StaticLayout(CharSequence text) {
5148059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        super(text, null, 0, null, 0, 0);
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mColumns = COLUMNS_ELLIPSIZE;
517776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
518776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLines = new int[mLineDirections.length];
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
52139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien    private StaticLayout(Builder b) {
52239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        super((b.mEllipsize == null)
52339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                ? b.mText
52439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                : (b.mText instanceof Spanned)
52539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                    ? new SpannedEllipsizer(b.mText)
52639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                    : new Ellipsizer(b.mText),
52739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                b.mPaint, b.mWidth, b.mAlignment, b.mSpacingMult, b.mSpacingAdd);
52839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
52939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        if (b.mEllipsize != null) {
53039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            Ellipsizer e = (Ellipsizer) getText();
53139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
53239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            e.mLayout = this;
53339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            e.mWidth = b.mEllipsizedWidth;
53439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            e.mMethod = b.mEllipsize;
53539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mEllipsizedWidth = b.mEllipsizedWidth;
53639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
53739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mColumns = COLUMNS_ELLIPSIZE;
53839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        } else {
53939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mColumns = COLUMNS_NORMAL;
54039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mEllipsizedWidth = b.mWidth;
54139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        }
54239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
54339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
54439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        mLines = new int[mLineDirections.length];
54539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        mMaximumVisibleLineCount = b.mMaxLines;
54639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
54739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        generate(b, b.mIncludePad, b.mIncludePad);
54839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien    }
54939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
550d3ab692d28018825578ff05832644cfad60233fbRaph Levien    /* package */ void generate(Builder b, boolean includepad, boolean trackpad) {
551d3ab692d28018825578ff05832644cfad60233fbRaph Levien        CharSequence source = b.mText;
552d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int bufStart = b.mStart;
553d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int bufEnd = b.mEnd;
554d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextPaint paint = b.mPaint;
555d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int outerWidth = b.mWidth;
556d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextDirectionHeuristic textDir = b.mTextDir;
557d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float spacingmult = b.mSpacingMult;
558d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float spacingadd = b.mSpacingAdd;
559d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float ellipsizedWidth = b.mEllipsizedWidth;
560d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextUtils.TruncateAt ellipsize = b.mEllipsize;
5614c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        LineBreaks lineBreaks = new LineBreaks();  // TODO: move to builder to avoid allocation costs
562c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // store span end locations
563c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        int[] spanEndCache = new int[4];
564c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // store fontMetrics per span range
565c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
566c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        int[] fmCache = new int[4 * 4];
5674c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        b.setLocale(paint.getTextLocale());  // TODO: also respect LocaleSpan within the text
56888b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLineCount = 0;
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int v = 0;
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
574d3ab692d28018825578ff05832644cfad60233fbRaph Levien        Paint.FontMetricsInt fm = b.mFontMetricsInt;
575121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        int[] chooseHtv = null;
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
577d3ab692d28018825578ff05832644cfad60233fbRaph Levien        MeasuredText measured = b.mMeasuredText;
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Spanned spanned = null;
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof Spanned)
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            spanned = (Spanned) source;
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int paraEnd;
584121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
585121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
586e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (paraEnd < 0)
587121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                paraEnd = bufEnd;
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
589e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                paraEnd++;
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
591c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int firstWidthLineCount = 1;
592121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            int firstWidth = outerWidth;
593121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            int restWidth = outerWidth;
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
595121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            LineHeightSpan[] chooseHt = null;
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spanned != null) {
59874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer                LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
599e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        LeadingMarginSpan.class);
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < sp.length; i++) {
6017b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    LeadingMarginSpan lms = sp[i];
602121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    firstWidth -= sp[i].getLeadingMargin(true);
603121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    restWidth -= sp[i].getLeadingMargin(false);
604cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
605c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    // LeadingMarginSpan2 is odd.  The count affects all
606ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye                    // leading margin spans, not just this particular one
607c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    if (lms instanceof LeadingMarginSpan2) {
608c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
609c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        firstWidthLineCount = Math.max(firstWidthLineCount,
610c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                                lms2.getLeadingMarginLineCount());
6117b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    }
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
614121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
616121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (chooseHt.length != 0) {
617121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    if (chooseHtv == null ||
618121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        chooseHtv.length < chooseHt.length) {
619776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                        chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
622121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    for (int i = 0; i < chooseHt.length; i++) {
623121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        int o = spanned.getSpanStart(chooseHt[i]);
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
625e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (o < paraStart) {
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // starts in this layout, before the
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // current paragraph
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
629121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                            chooseHtv[i] = getLineTop(getLineForOffset(o));
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else {
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // starts in this paragraph
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
633121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                            chooseHtv[i] = v;
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
63970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            measured.setPara(source, paraStart, paraEnd, textDir, b);
640e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            char[] chs = measured.mChars;
641e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float[] widths = measured.mWidths;
642e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            byte[] chdirs = measured.mLevels;
643e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int dir = measured.mDir;
644e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean easy = measured.mEasy;
645c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien
646c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            // tab stop locations
647c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            int[] variableTabStops = null;
648c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            if (spanned != null) {
649c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
650c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                        paraEnd, TabStopSpan.class);
651c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                if (spans.length > 0) {
652c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    int[] stops = new int[spans.length];
653c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    for (int i = 0; i < spans.length; i++) {
654c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                        stops[i] = spans[i].getTabStop();
655c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    }
656c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    Arrays.sort(stops, 0, stops.length);
657c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    variableTabStops = stops;
658c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                }
659c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            }
660c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien
661c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
662c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    firstWidth, firstWidthLineCount, restWidth,
66395c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader                    variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
665c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // measurement has to be done before performing line breaking
666c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // but we don't want to recompute fontmetrics or span ranges the
667c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // second time, so we cache those and then use those stored values
668c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmCacheCount = 0;
669c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int spanEndCacheCount = 0;
670cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne            for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
671c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fmCacheCount * 4 >= fmCache.length) {
672c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int[] grow = new int[fmCacheCount * 4 * 2];
673c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
674c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmCache = grow;
675c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
676c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
677c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (spanEndCacheCount >= spanEndCache.length) {
678c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int[] grow = new int[spanEndCacheCount * 2];
679c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
680c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    spanEndCache = grow;
681c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
68223241887515ed77687c23e29a4a3ffff671666bdDoug Felt
683cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                if (spanned == null) {
684cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spanEnd = paraEnd;
68523241887515ed77687c23e29a4a3ffff671666bdDoug Felt                    int spanLen = spanEnd - spanStart;
686cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    measured.addStyleRun(paint, spanLen, fm);
687cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                } else {
688cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
689cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                            MetricAffectingSpan.class);
690cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    int spanLen = spanEnd - spanStart;
691cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    MetricAffectingSpan[] spans =
69223241887515ed77687c23e29a4a3ffff671666bdDoug Felt                            spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
693cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
694cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    measured.addStyleRun(paint, spans, spanLen, fm);
69523241887515ed77687c23e29a4a3ffff671666bdDoug Felt                }
69623241887515ed77687c23e29a4a3ffff671666bdDoug Felt
697c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // the order of storage here (top, bottom, ascent, descent) has to match the code below
698c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // where these values are retrieved
699c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 0] = fm.top;
700c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 1] = fm.bottom;
701c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 2] = fm.ascent;
702c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 3] = fm.descent;
703c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCacheCount++;
704c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
705c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEndCache[spanEndCacheCount] = spanEnd;
706c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEndCacheCount++;
707c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            }
708c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
70970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            nGetWidths(b.mNativePtr, widths);
710c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
711c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
71281541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne
713c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int[] breaks = lineBreaks.breaks;
714c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            float[] lineWidths = lineBreaks.widths;
71526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien            int[] flags = lineBreaks.flags;
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
717f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi            final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
718f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi            final boolean ellipsisMayBeApplied = ellipsize != null
719f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                    && (ellipsize == TextUtils.TruncateAt.END
720f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                        || (mMaximumVisibleLineCount == 1
721f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                                && ellipsize != TextUtils.TruncateAt.MARQUEE));
722f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi            if (remainingLineCount < breakCount && ellipsisMayBeApplied) {
723f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                // Treat the last line and overflowed lines as a single line.
724f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                breaks[remainingLineCount - 1] = breaks[breakCount - 1];
725f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                // Calculate width and flag.
726f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                float width = 0;
727f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                int flag = 0;
728f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                for (int i = remainingLineCount - 1; i < breakCount; i++) {
729f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                    width += lineWidths[i];
730f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                    flag |= flags[i] & TAB_MASK;
731f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                }
732f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                lineWidths[remainingLineCount - 1] = width;
733f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                flags[remainingLineCount - 1] = flag;
734f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi
735f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                breakCount = remainingLineCount;
736f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi            }
737f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi
738c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // here is the offset of the starting character of the line we are currently measuring
739c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int here = paraStart;
740d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne
741c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
742c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmCacheIndex = 0;
743c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int spanEndCacheIndex = 0;
744c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int breakIndex = 0;
745c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
746c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // retrieve end of span
747c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEnd = spanEndCache[spanEndCacheIndex++];
748c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
749c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // retrieve cached metrics, order matches above
750c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.top = fmCache[fmCacheIndex * 4 + 0];
751c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.bottom = fmCache[fmCacheIndex * 4 + 1];
752c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.ascent = fmCache[fmCacheIndex * 4 + 2];
753c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.descent = fmCache[fmCacheIndex * 4 + 3];
754c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCacheIndex++;
755c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
756c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.top < fmTop) {
757c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmTop = fm.top;
758c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
759c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.ascent < fmAscent) {
760c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmAscent = fm.ascent;
761c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
762c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.descent > fmDescent) {
763c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmDescent = fm.descent;
764c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
765c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.bottom > fmBottom) {
766c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmBottom = fm.bottom;
767c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
768cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne
769c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // skip breaks ending before current span range
770c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
771c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    breakIndex++;
772c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
77381541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne
774c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
775c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int endPos = paraStart + breaks[breakIndex];
776c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
777ce4155a204144ae5462f547f7738af24be5a1f77Raph Levien                    boolean moreChars = (endPos < bufEnd);
7784c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien
779c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    v = out(source, here, endPos,
780c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            fmAscent, fmDescent, fmTop, fmBottom,
781c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex],
782c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
783c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            chs, widths, paraStart, ellipsize, ellipsizedWidth,
7844c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien                            lineWidths[breakIndex], paint, moreChars);
785c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
786c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    if (endPos < spanEnd) {
787c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        // preserve metrics for current span
788c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmTop = fm.top;
789c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmBottom = fm.bottom;
790c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmAscent = fm.ascent;
791c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmDescent = fm.descent;
792c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    } else {
793c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmTop = fmBottom = fmAscent = fmDescent = 0;
7948059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
796c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    here = endPos;
797c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    breakIndex++;
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
799c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    if (mLineCount >= mMaximumVisibleLineCount) {
800c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        return;
801c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    }
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
805121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (paraEnd == bufEnd)
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8098059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) &&
810ad0b051b133baf92f199c96a8ac1e81b3393190cFabrice Di Meglio                mLineCount < mMaximumVisibleLineCount) {
811121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            // Log.e("text", "output last " + bufEnd);
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
81370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            measured.setPara(source, bufEnd, bufEnd, textDir, b);
814e631889e1ae7edc6a2fae495ba504f85820b6a4bFabrice Di Meglio
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            paint.getFontMetricsInt(fm);
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            v = out(source,
818121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    bufEnd, bufEnd, fm.ascent, fm.descent,
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fm.top, fm.bottom,
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    v,
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    spacingmult, spacingadd, null,
82226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien                    null, fm, 0,
823e631889e1ae7edc6a2fae495ba504f85820b6a4bFabrice Di Meglio                    needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
824d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    includepad, trackpad, null,
825d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    null, bufStart, ellipsize,
826d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    ellipsizedWidth, 0, paint, false);
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int out(CharSequence text, int start, int end,
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                      int above, int below, int top, int bottom, int v,
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                      float spacingmult, float spacingadd,
833121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                      LineHeightSpan[] chooseHt, int[] chooseHtv,
83426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien                      Paint.FontMetricsInt fm, int flags,
835d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean needMultiply, byte[] chdirs, int dir,
836d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean easy, int bufEnd, boolean includePad,
837d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean trackPad, char[] chs,
838d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      float[] widths, int widthStart, TextUtils.TruncateAt ellipsize,
839d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      float ellipsisWidth, float textWidth,
840d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      TextPaint paint, boolean moreChars) {
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int j = mLineCount;
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int off = j * mColumns;
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int want = off + mColumns + TOP;
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] lines = mLines;
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (want >= lines.length) {
847776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            Directions[] grow2 = ArrayUtils.newUnpaddedArray(
848776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                    Directions.class, GrowingArrayUtils.growSize(want));
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(mLineDirections, 0, grow2, 0,
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                             mLineDirections.length);
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLineDirections = grow2;
852776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski
853776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            int[] grow = new int[grow2.length];
854776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            System.arraycopy(lines, 0, grow, 0, lines.length);
855776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            mLines = grow;
856776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            lines = grow;
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
859121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        if (chooseHt != null) {
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.ascent = above;
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.descent = below;
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.top = top;
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.bottom = bottom;
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
865121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            for (int i = 0; i < chooseHt.length; i++) {
866121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (chooseHt[i] instanceof LineHeightSpan.WithDensity) {
867121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    ((LineHeightSpan.WithDensity) chooseHt[i]).
868121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        chooseHeight(text, start, end, chooseHtv[i], v, fm, paint);
869a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer
870a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                } else {
871121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm);
872a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                }
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            above = fm.ascent;
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            below = fm.descent;
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            top = fm.top;
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bottom = fm.bottom;
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
881d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean firstLine = (j == 0);
882d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
883d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd);
884d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
885d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (firstLine) {
886121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (trackPad) {
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mTopPadding = top - above;
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
890121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (includePad) {
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                above = top;
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
894d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
895d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        int extra;
896d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
897d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (lastLine) {
898121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (trackPad) {
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mBottomPadding = bottom - below;
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
902121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (includePad) {
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                below = bottom;
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
908d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (needMultiply && !lastLine) {
9091065758a0f8966a8597a61492112f7859a7050a4Doug Felt            double ex = (below - above) * (spacingmult - 1) + spacingadd;
9101065758a0f8966a8597a61492112f7859a7050a4Doug Felt            if (ex >= 0) {
911121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                extra = (int)(ex + EXTRA_ROUNDING);
9121065758a0f8966a8597a61492112f7859a7050a4Doug Felt            } else {
913121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                extra = -(int)(-ex + EXTRA_ROUNDING);
9141065758a0f8966a8597a61492112f7859a7050a4Doug Felt            }
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            extra = 0;
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + START] = start;
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + TOP] = v;
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + DESCENT] = below + extra;
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        v += (below - above) + extra;
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + mColumns + START] = end;
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + mColumns + TOP] = v;
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
92726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
92826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        // one bit for start field
92926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        lines[off + TAB] |= flags & TAB_MASK;
93026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        lines[off + HYPHEN] = flags;
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9329f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        lines[off + DIR] |= dir << DIR_SHIFT;
9339f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
9349f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // easy means all chars < the first RTL, so no emoji, no nothing
9354e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        // XXX a run with no text or all spaces is easy but might be an empty
9369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // RTL paragraph.  Make sure easy is false if this is the case.
9379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (easy) {
9389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            mLineDirections[j] = linedirs;
9399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        } else {
940f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne            mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
941f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne                    start - widthStart, end - start);
9420a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        }
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
944aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio        if (ellipsize != null) {
945aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            // If there is only one line, then do any type of ellipsis except when it is MARQUEE
946aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            // if there are multiple lines, just allow END ellipsis on the last line
9478059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
948aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio
94934a126e51aaf22e32c7af808ec6b5a0c41ae3311Fabrice Di Meglio            boolean doEllipsis =
95034a126e51aaf22e32c7af808ec6b5a0c41ae3311Fabrice Di Meglio                        (((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
951aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                                ellipsize != TextUtils.TruncateAt.MARQUEE) ||
952aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
953aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                                ellipsize == TextUtils.TruncateAt.END);
954aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            if (doEllipsis) {
955aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                calculateEllipsis(start, end, widths, widthStart,
956aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        ellipsisWidth, ellipsize, j,
957aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        textWidth, paint, forceEllipsis);
958aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            }
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLineCount++;
9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return v;
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
965121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private void calculateEllipsis(int lineStart, int lineEnd,
966121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                                   float[] widths, int widthStart,
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                   float avail, TextUtils.TruncateAt where,
9688059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                                   int line, float textWidth, TextPaint paint,
9698059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                                   boolean forceEllipsis) {
9708059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if (textWidth <= avail && !forceEllipsis) {
9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Everything fits!
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLines[mColumns * line + ELLIPSIS_START] = 0;
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLines[mColumns * line + ELLIPSIS_COUNT] = 0;
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
977cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio        float ellipsisWidth = paint.measureText(
9788d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio                (where == TextUtils.TruncateAt.END_SMALL) ?
979d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller                        TextUtils.ELLIPSIS_TWO_DOTS : TextUtils.ELLIPSIS_NORMAL, 0, 1);
9808059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        int ellipsisStart = 0;
9818059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        int ellipsisCount = 0;
982121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        int len = lineEnd - lineStart;
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9848059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        // We only support start ellipsis on a single line
9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (where == TextUtils.TruncateAt.START) {
9868059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            if (mMaximumVisibleLineCount == 1) {
9878059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float sum = 0;
9888059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                int i;
9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
990ed2eea1bfc1fb57d3b34f2c1e6062b541737e73eKeisuke Kuroyanagi                for (i = len; i > 0; i--) {
9918059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[i - 1 + lineStart - widthStart];
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9938059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + sum + ellipsisWidth > avail) {
9948059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
9958059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
9968059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
9978059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    sum += w;
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10008059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisStart = 0;
10018059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = i;
10028059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            } else {
10038059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                if (Log.isLoggable(TAG, Log.WARN)) {
10048059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    Log.w(TAG, "Start Ellipsis only supported with one line");
10058059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                }
10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1007cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio        } else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE ||
1008cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio                where == TextUtils.TruncateAt.END_SMALL) {
10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float sum = 0;
10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i;
10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (i = 0; i < len; i++) {
1013121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                float w = widths[i + lineStart - widthStart];
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1015121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (w + sum + ellipsisWidth > avail) {
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sum += w;
10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ellipsisStart = i;
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ellipsisCount = len - i;
1024aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            if (forceEllipsis && ellipsisCount == 0 && len > 0) {
1025aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                ellipsisStart = len - 1;
10268059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = 1;
10278059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            }
10288059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        } else {
10298059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            // where = TextUtils.TruncateAt.MIDDLE We only support middle ellipsis on a single line
10308059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            if (mMaximumVisibleLineCount == 1) {
10318059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float lsum = 0, rsum = 0;
10328059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                int left = 0, right = len;
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10348059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float ravail = (avail - ellipsisWidth) / 2;
10350e3c5e827235911d33312e431975533f046421e7Raph Levien                for (right = len; right > 0; right--) {
10368059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[right - 1 + lineStart - widthStart];
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10388059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + rsum > ravail) {
10398059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
10408059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
10418059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
10428059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    rsum += w;
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10458059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float lavail = avail - ellipsisWidth - rsum;
10468059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                for (left = 0; left < right; left++) {
10478059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[left + lineStart - widthStart];
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10498059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + lsum > lavail) {
10508059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
10518059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10538059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    lsum += w;
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10568059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisStart = left;
10578059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = right - left;
10588059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            } else {
10598059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                if (Log.isLoggable(TAG, Log.WARN)) {
10608059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    Log.w(TAG, "Middle Ellipsis only supported with one line");
10618059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                }
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart;
10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1069e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    // Override the base class so we can directly access our members,
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // rather than relying on member functions.
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The logic mirrors that of Layout.getLineForVertical
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // FIXME: It may be faster to do a linear search for layouts without many lines.
10736611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineForVertical(int vertical) {
10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int high = mLineCount;
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int low = -1;
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int guess;
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] lines = mLines;
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (high - low > 1) {
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            guess = (high + low) >> 1;
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (lines[mColumns * guess + TOP] > vertical){
10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                high = guess;
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = guess;
10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (low < 0) {
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return low;
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10946611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineCount() {
10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLineCount;
10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10996611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineTop(int line) {
11010a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        int top = mLines[mColumns * line + TOP];
11020a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount &&
11030a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne                line != mLineCount) {
11040a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne            top += getBottomPadding();
11050a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        }
11060a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        return top;
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11096611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineDescent(int line) {
11110a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        int descent = mLines[mColumns * line + DESCENT];
1112f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne        if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount - 1 && // -1 intended
11130a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne                line != mLineCount) {
11140a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne            descent += getBottomPadding();
11150a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        }
11160a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        return descent;
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11196611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineStart(int line) {
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + START] & START_MASK;
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11246611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getParagraphDirection(int line) {
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + DIR] >> DIR_SHIFT;
11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11296611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean getLineContainsTab(int line) {
11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return (mLines[mColumns * line + TAB] & TAB_MASK) != 0;
11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11346611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final Directions getLineDirections(int line) {
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLineDirections[line];
11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11396611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getTopPadding() {
11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mTopPadding;
11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11446611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getBottomPadding() {
11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mBottomPadding;
11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
114926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    /**
115026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien     * @hide
115126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien     */
115226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    @Override
115326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    public int getHyphen(int line) {
115426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        return mLines[mColumns * line + HYPHEN] & 0xff;
115526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    }
115626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien
11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsisCount(int line) {
11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mColumns < COLUMNS_ELLIPSIZE) {
11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + ELLIPSIS_COUNT];
11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsisStart(int line) {
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mColumns < COLUMNS_ELLIPSIZE) {
11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + ELLIPSIS_START];
11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsizedWidth() {
11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mEllipsizedWidth;
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
118070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native long nNewBuilder();
118170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nFreeBuilder(long nativePtr);
118270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nFinishBuilder(long nativePtr);
118326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien
118426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    /* package */ static native long nLoadHyphenator(String patternData);
118526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien
118626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
118770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
1188e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien    private static native void nSetIndents(long nativePtr, int[] indents);
1189e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien
1190c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien    // Set up paragraph text and settings; done as one big method to minimize jni crossings
1191c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien    private static native void nSetupParagraph(long nativePtr, char[] text, int length,
1192c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            float firstWidth, int firstWidthLineCount, float restWidth,
119395c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader            int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency);
119470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
119570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native float nAddStyleRun(long nativePtr, long nativePaint,
119670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            long nativeTypeface, int start, int end, boolean isRtl);
119770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
119870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nAddMeasuredRun(long nativePtr,
119970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            int start, int end, float[] widths);
120070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
120170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nAddReplacementRun(long nativePtr, int start, int end, float width);
120270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
120370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nGetWidths(long nativePtr, float[] widths);
120470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
1205c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // populates LineBreaks and returns the number of breaks found
1206c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    //
1207c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // the arrays inside the LineBreaks objects are passed in as well
1208c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // to reduce the number of JNI calls in the common case where the
1209c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // arrays do not have to be resized
1210c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien    private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
121126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien            int[] recycleBreaks, float[] recycleWidths, int[] recycleFlags, int recycleLength);
121288b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye
12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mLineCount;
12149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mTopPadding, mBottomPadding;
12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mColumns;
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mEllipsizedWidth;
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
121826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int COLUMNS_NORMAL = 4;
121926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int COLUMNS_ELLIPSIZE = 6;
12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int START = 0;
12219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DIR = START;
12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAB = START;
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TOP = 1;
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DESCENT = 2;
122526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int HYPHEN = 3;
122626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int ELLIPSIS_START = 4;
122726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int ELLIPSIS_COUNT = 5;
12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int[] mLines;
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Directions[] mLineDirections;
12318059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    private int mMaximumVisibleLineCount = Integer.MAX_VALUE;
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int START_MASK = 0x1FFFFFFF;
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DIR_SHIFT  = 30;
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAB_MASK   = 0x20000000;
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1237c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private static final int TAB_INCREMENT = 20; // same as Layout, but that's private
12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1239121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private static final char CHAR_NEW_LINE = '\n';
1240121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio
1241121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private static final double EXTRA_ROUNDING = 0.5;
1242cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio
1243c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // This is used to return three arrays from a single JNI call when
1244c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // performing line breaking
12457053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    /*package*/ static class LineBreaks {
1246c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        private static final int INITIAL_SIZE = 16;
1247c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        public int[] breaks = new int[INITIAL_SIZE];
1248c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        public float[] widths = new float[INITIAL_SIZE];
124926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        public int[] flags = new int[INITIAL_SIZE]; // hasTabOrEmoji
1250c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // breaks, widths, and flags should all have the same length
1251c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    }
1252c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1254