StaticLayout.java revision 531c30c62b14881aab31a5133920a971b1fbb50e
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;
94d3ab692d28018825578ff05832644cfad60233fbRaph Levien
95d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mMeasuredText = MeasuredText.obtain();
96d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return b;
97d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
98d3ab692d28018825578ff05832644cfad60233fbRaph Levien
9939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        private static void recycle(Builder b) {
100d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mPaint = null;
101d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mText = null;
102d3ab692d28018825578ff05832644cfad60233fbRaph Levien            MeasuredText.recycle(b.mMeasuredText);
10339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            sPool.release(b);
104d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
105d3ab692d28018825578ff05832644cfad60233fbRaph Levien
106d3ab692d28018825578ff05832644cfad60233fbRaph Levien        // release any expensive state
107d3ab692d28018825578ff05832644cfad60233fbRaph Levien        /* package */ void finish() {
1084c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            nFinishBuilder(mNativePtr);
109d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mMeasuredText.finish();
110d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
111d3ab692d28018825578ff05832644cfad60233fbRaph Levien
112d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setText(CharSequence source) {
113d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return setText(source, 0, source.length());
114d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
115d3ab692d28018825578ff05832644cfad60233fbRaph Levien
116531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
117531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the text. Only useful when re-using the builder, which is done for
118531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * the internal implementation of {@link DynamicLayout} but not as part
119531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * of normal {@link StaticLayout} usage.
120531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
121531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param source The text to be laid out, optionally with spans
122531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param start The index of the start of the text
123531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param end The index + 1 of the end of the text
124531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
125531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
126531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @hide
127531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
128d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setText(CharSequence source, int start, int end) {
129d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mText = source;
130d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mStart = start;
131d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mEnd = end;
132d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
133d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
134d3ab692d28018825578ff05832644cfad60233fbRaph Levien
135531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
136531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the paint. Internal for reuse cases only.
137531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
138531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param paint The base paint used for layout
139531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
140531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
141531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @hide
142531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
143d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setPaint(TextPaint paint) {
144d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mPaint = paint;
145d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
146d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
147d3ab692d28018825578ff05832644cfad60233fbRaph Levien
148531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
149531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the width. Internal for reuse cases only.
150531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
151531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param width The width in pixels
152531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
153531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
154531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @hide
155531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
156d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setWidth(int width) {
157d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mWidth = width;
158d3ab692d28018825578ff05832644cfad60233fbRaph Levien            if (mEllipsize == null) {
159d3ab692d28018825578ff05832644cfad60233fbRaph Levien                mEllipsizedWidth = width;
160d3ab692d28018825578ff05832644cfad60233fbRaph Levien            }
161d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
162d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
163d3ab692d28018825578ff05832644cfad60233fbRaph Levien
164531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
165531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the alignment. The default is {@link Layout.Alignment#ALIGN_NORMAL}.
166531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
167531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param alignment Alignment for the resulting {@link StaticLayout}
168531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
169531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
17039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        public Builder setAlignment(Alignment alignment) {
17139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mAlignment = alignment;
17239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            return this;
17339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        }
17439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
175531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
176531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the text direction heuristic. The text direction heuristic is used to
177531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * resolve text direction based per-paragraph based on the input text. The default is
178531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
179531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
180531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param textDir text direction heuristic for resolving BiDi behavior.
181531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
182531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
183d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setTextDir(TextDirectionHeuristic textDir) {
184d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mTextDir = textDir;
185d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
186d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
187d3ab692d28018825578ff05832644cfad60233fbRaph Levien
188531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
189531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set line spacing parameters. The default is 0.0 for {@code spacingAdd}
190531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * and 1.0 for {@code spacingMult}.
191531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
192531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param spacingAdd line spacing add
193531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param spacingMult line spacing multiplier
194531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
195531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setLineSpacing
196531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
197531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        public Builder setLineSpacing(float spacingAdd, float spacingMult) {
198d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mSpacingAdd = spacingAdd;
199531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien            mSpacingMult = spacingMult;
200d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
201d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
202d3ab692d28018825578ff05832644cfad60233fbRaph Levien
203531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
204531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set whether to include extra space beyond font ascent and descent (which is
205531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * needed to avoid clipping in some languages, such as Arabic and Kannada). The
206531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * default is {@code true}.
207531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
208531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param includePad whether to include padding
209531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
210531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setIncludeFontPadding
211531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
212d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setIncludePad(boolean includePad) {
213d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mIncludePad = includePad;
214d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
215d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
216d3ab692d28018825578ff05832644cfad60233fbRaph Levien
217531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
218531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the width as used for ellipsizing purposes, if it differs from the
219531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * normal layout width. The default is the {@code width}
220531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * passed to {@link #obtain}.
221531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
222531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param ellipsizedWidth width used for ellipsizing, in pixels
223531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
224531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setEllipsize
225531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
226d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setEllipsizedWidth(int ellipsizedWidth) {
227d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mEllipsizedWidth = ellipsizedWidth;
228d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
229d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
230d3ab692d28018825578ff05832644cfad60233fbRaph Levien
231531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
232531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set ellipsizing on the layout. Causes words that are longer than the view
233531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * is wide, or exceeding the number of lines (see #setMaxLines) in the case
234531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * of {@link android.text.TextUtils.TruncateAt#END} or
235531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * {@link android.text.TextUtils.TruncateAt#MARQUEE}, to be ellipsized instead
236531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * of broken. The default is
237531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * {@code null}, indicating no ellipsis is to be applied.
238531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
239531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param ellipsize type of ellipsis behavior
240531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
241531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setEllipsize
242531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
243531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
244d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mEllipsize = ellipsize;
245d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
246d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
247d3ab692d28018825578ff05832644cfad60233fbRaph Levien
248531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
249531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set maximum number of lines. This is particularly useful in the case of
250531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * ellipsizing, where it changes the layout of the last line. The default is
251531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * unlimited.
252531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
253531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param maxLines maximum number of lines in the layout
254531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
255531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setMaxLines
256531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
257d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setMaxLines(int maxLines) {
258d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mMaxLines = maxLines;
259d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
260d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
261d3ab692d28018825578ff05832644cfad60233fbRaph Levien
262531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
263531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set break strategy, useful for selecting high quality or balanced paragraph
264531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * layout options. The default is {@link Layout#BREAK_STRATEGY_SIMPLE}.
265531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
266531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param breakStrategy break strategy for paragraph layout
267531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
268531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setBreakStrategy
269531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
27039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
27139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mBreakStrategy = breakStrategy;
27239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            return this;
27339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        }
27439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
275531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
276531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set indents. Arguments are arrays holding an indent amount, one per line, measured in
277531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * pixels. For lines past the last element in the array, the last element repeats.
278531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
279531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param leftIndents array of indent values for left margin, in pixels
280531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param rightIndents array of indent values for right margin, in pixels
281531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
282531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setIndents
283531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
284e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien        public Builder setIndents(int[] leftIndents, int[] rightIndents) {
285e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            int leftLen = leftIndents == null ? 0 : leftIndents.length;
286e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            int rightLen = rightIndents == null ? 0 : rightIndents.length;
287e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            int[] indents = new int[Math.max(leftLen, rightLen)];
288e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            for (int i = 0; i < indents.length; i++) {
289e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien                int leftMargin = i < leftLen ? leftIndents[i] : 0;
290e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien                int rightMargin = i < rightLen ? rightIndents[i] : 0;
291e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien                indents[i] = leftMargin + rightMargin;
292e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            }
293e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            nSetIndents(mNativePtr, indents);
294e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            return this;
295e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien        }
296e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien
29770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /**
29870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * Measurement and break iteration is done in native code. The protocol for using
29970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * the native code is as follows.
30070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *
30126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien         * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab
30226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien         * stops, break strategy (and possibly other parameters in the future).
303c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien         *
304c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien         * Then, for each run within the paragraph:
30570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *  - setLocale (this must be done at least for the first run, optional afterwards)
30670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *  - one of the following, depending on the type of run:
30770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *    + addStyleRun (a text run, to be measured in native code)
30870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *    + addMeasuredRun (a run already measured in Java, passed into native code)
30970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *    + addReplacementRun (a replacement run, width is given)
31070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *
31170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * After measurement, nGetWidths() is valid if the widths are needed (eg for ellipsis).
31270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * Run nComputeLineBreaks() to obtain line breaks for the paragraph.
31370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *
31470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * After all paragraphs, call finish() to release expensive buffers.
31570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         */
31670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
31770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        private void setLocale(Locale locale) {
3184c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            if (!locale.equals(mLocale)) {
31926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien                nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale));
3204c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien                mLocale = locale;
3214c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            }
3224c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        }
3234c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
32470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /* package */ float addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
32570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            return nAddStyleRun(mNativePtr, paint.getNativeInstance(), paint.mNativeTypeface,
32670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien                    start, end, isRtl);
32770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        }
32870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
32970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /* package */ void addMeasuredRun(int start, int end, float[] widths) {
33070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            nAddMeasuredRun(mNativePtr, start, end, widths);
33170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        }
33270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
33370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /* package */ void addReplacementRun(int start, int end, float width) {
33470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            nAddReplacementRun(mNativePtr, start, end, width);
33570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        }
33670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
337531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
338531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Build the {@link StaticLayout} after options have been set.
339531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
340531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * <p>Note: the builder object must not be reused in any way after calling this
341531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * method. Setting parameters after calling this method, or calling it a second
342531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * time on the same builder object, will likely lead to unexpected results.
343531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
344531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return the newly constructed {@link StaticLayout} object
345531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
346d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public StaticLayout build() {
34739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            StaticLayout result = new StaticLayout(this);
34839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            Builder.recycle(this);
349d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return result;
350d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
351d3ab692d28018825578ff05832644cfad60233fbRaph Levien
3524c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        @Override
3534c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        protected void finalize() throws Throwable {
3544c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            try {
3554c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien                nFreeBuilder(mNativePtr);
3564c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            } finally {
3574c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien                super.finalize();
3584c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            }
3594c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        }
3604c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
3614c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        /* package */ long mNativePtr;
3624c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
363d3ab692d28018825578ff05832644cfad60233fbRaph Levien        CharSequence mText;
364d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mStart;
365d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mEnd;
366d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextPaint mPaint;
367d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mWidth;
36839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        Alignment mAlignment;
369d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextDirectionHeuristic mTextDir;
370d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float mSpacingMult;
371d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float mSpacingAdd;
372d3ab692d28018825578ff05832644cfad60233fbRaph Levien        boolean mIncludePad;
373d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mEllipsizedWidth;
374d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextUtils.TruncateAt mEllipsize;
375d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mMaxLines;
37639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        int mBreakStrategy;
377d3ab692d28018825578ff05832644cfad60233fbRaph Levien
378d3ab692d28018825578ff05832644cfad60233fbRaph Levien        Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
379d3ab692d28018825578ff05832644cfad60233fbRaph Levien
380d3ab692d28018825578ff05832644cfad60233fbRaph Levien        // This will go away and be subsumed by native builder code
381d3ab692d28018825578ff05832644cfad60233fbRaph Levien        MeasuredText mMeasuredText;
382d3ab692d28018825578ff05832644cfad60233fbRaph Levien
3834c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        Locale mLocale;
3844c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
38539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
386d3ab692d28018825578ff05832644cfad60233fbRaph Levien    }
387d3ab692d28018825578ff05832644cfad60233fbRaph Levien
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, TextPaint paint,
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int width,
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Alignment align, float spacingmult, float spacingadd,
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad) {
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(source, 0, source.length(), paint, width, align,
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             spacingmult, spacingadd, includepad);
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
396cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
397cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
398cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
399cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, TextPaint paint,
400cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            int width, Alignment align, TextDirectionHeuristic textDir,
401cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
402cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad) {
403cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, 0, source.length(), paint, width, align, textDir,
404cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                spacingmult, spacingadd, includepad);
405cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    }
406cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, int bufstart, int bufend,
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        TextPaint paint, int outerwidth,
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Alignment align,
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        float spacingmult, float spacingadd,
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad) {
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(source, bufstart, bufend, paint, outerwidth, align,
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             spacingmult, spacingadd, includepad, null, 0);
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
416cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
417cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
418cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
419cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, int bufstart, int bufend,
420cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextPaint paint, int outerwidth,
421cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            Alignment align, TextDirectionHeuristic textDir,
422cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
423cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad) {
424cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, bufstart, bufend, paint, outerwidth, align, textDir,
4258059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                spacingmult, spacingadd, includepad, null, 0, Integer.MAX_VALUE);
426cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt}
427cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
428cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, int bufstart, int bufend,
429cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextPaint paint, int outerwidth,
430cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            Alignment align,
431cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
432cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad,
433cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
434cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, bufstart, bufend, paint, outerwidth, align,
435cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                TextDirectionHeuristics.FIRSTSTRONG_LTR,
4368059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE);
437cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    }
438cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
439cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
440cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
441cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, int bufstart, int bufend,
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        TextPaint paint, int outerwidth,
444cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                        Alignment align, TextDirectionHeuristic textDir,
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        float spacingmult, float spacingadd,
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad,
4478059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super((ellipsize == null)
4494e0c5e55e171532760d5f51e0165563827129d4eDoug Felt                ? source
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                : (source instanceof Spanned)
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? new SpannedEllipsizer(source)
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : new Ellipsizer(source),
453cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt              paint, outerwidth, align, textDir, spacingmult, spacingadd);
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
455ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien        Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
45639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            .setAlignment(align)
457d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setTextDir(textDir)
458531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien            .setLineSpacing(spacingadd, spacingmult)
459d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setIncludePad(includepad)
460d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setEllipsizedWidth(ellipsizedWidth)
461d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setEllipsize(ellipsize)
462d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setMaxLines(maxLines);
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /*
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This is annoying, but we can't refer to the layout until
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * superclass construction is finished, and the superclass
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * constructor wants the reference to the display text.
4674e0c5e55e171532760d5f51e0165563827129d4eDoug Felt         *
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This will break if the superclass constructor ever actually
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * cares about the content instead of just holding the reference.
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (ellipsize != null) {
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Ellipsizer e = (Ellipsizer) getText();
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mLayout = this;
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mWidth = ellipsizedWidth;
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mMethod = ellipsize;
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mEllipsizedWidth = ellipsizedWidth;
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mColumns = COLUMNS_ELLIPSIZE;
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mColumns = COLUMNS_NORMAL;
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mEllipsizedWidth = outerwidth;
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
485776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
486776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLines = new int[mLineDirections.length];
4878059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        mMaximumVisibleLineCount = maxLines;
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
48970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        generate(b, b.mIncludePad, b.mIncludePad);
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
491d3ab692d28018825578ff05832644cfad60233fbRaph Levien        Builder.recycle(b);
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4948059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    /* package */ StaticLayout(CharSequence text) {
4958059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        super(text, null, 0, null, 0, 0);
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mColumns = COLUMNS_ELLIPSIZE;
498776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
499776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLines = new int[mLineDirections.length];
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
50239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien    private StaticLayout(Builder b) {
50339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        super((b.mEllipsize == null)
50439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                ? b.mText
50539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                : (b.mText instanceof Spanned)
50639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                    ? new SpannedEllipsizer(b.mText)
50739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                    : new Ellipsizer(b.mText),
50839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                b.mPaint, b.mWidth, b.mAlignment, b.mSpacingMult, b.mSpacingAdd);
50939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
51039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        if (b.mEllipsize != null) {
51139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            Ellipsizer e = (Ellipsizer) getText();
51239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
51339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            e.mLayout = this;
51439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            e.mWidth = b.mEllipsizedWidth;
51539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            e.mMethod = b.mEllipsize;
51639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mEllipsizedWidth = b.mEllipsizedWidth;
51739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
51839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mColumns = COLUMNS_ELLIPSIZE;
51939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        } else {
52039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mColumns = COLUMNS_NORMAL;
52139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mEllipsizedWidth = b.mWidth;
52239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        }
52339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
52439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
52539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        mLines = new int[mLineDirections.length];
52639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        mMaximumVisibleLineCount = b.mMaxLines;
52739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
52839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        generate(b, b.mIncludePad, b.mIncludePad);
52939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien    }
53039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
531d3ab692d28018825578ff05832644cfad60233fbRaph Levien    /* package */ void generate(Builder b, boolean includepad, boolean trackpad) {
532d3ab692d28018825578ff05832644cfad60233fbRaph Levien        CharSequence source = b.mText;
533d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int bufStart = b.mStart;
534d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int bufEnd = b.mEnd;
535d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextPaint paint = b.mPaint;
536d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int outerWidth = b.mWidth;
537d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextDirectionHeuristic textDir = b.mTextDir;
538d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float spacingmult = b.mSpacingMult;
539d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float spacingadd = b.mSpacingAdd;
540d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float ellipsizedWidth = b.mEllipsizedWidth;
541d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextUtils.TruncateAt ellipsize = b.mEllipsize;
5424c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        LineBreaks lineBreaks = new LineBreaks();  // TODO: move to builder to avoid allocation costs
543c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // store span end locations
544c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        int[] spanEndCache = new int[4];
545c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // store fontMetrics per span range
546c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
547c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        int[] fmCache = new int[4 * 4];
5484c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        b.setLocale(paint.getTextLocale());  // TODO: also respect LocaleSpan within the text
54988b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLineCount = 0;
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int v = 0;
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
555d3ab692d28018825578ff05832644cfad60233fbRaph Levien        Paint.FontMetricsInt fm = b.mFontMetricsInt;
556121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        int[] chooseHtv = null;
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
558d3ab692d28018825578ff05832644cfad60233fbRaph Levien        MeasuredText measured = b.mMeasuredText;
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Spanned spanned = null;
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof Spanned)
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            spanned = (Spanned) source;
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int paraEnd;
565121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
566121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (paraEnd < 0)
568121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                paraEnd = bufEnd;
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                paraEnd++;
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
572c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int firstWidthLineCount = 1;
573121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            int firstWidth = outerWidth;
574121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            int restWidth = outerWidth;
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
576121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            LineHeightSpan[] chooseHt = null;
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spanned != null) {
57974d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer                LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        LeadingMarginSpan.class);
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < sp.length; i++) {
5827b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    LeadingMarginSpan lms = sp[i];
583121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    firstWidth -= sp[i].getLeadingMargin(true);
584121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    restWidth -= sp[i].getLeadingMargin(false);
585cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
586c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    // LeadingMarginSpan2 is odd.  The count affects all
587ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye                    // leading margin spans, not just this particular one
588c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    if (lms instanceof LeadingMarginSpan2) {
589c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
590c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        firstWidthLineCount = Math.max(firstWidthLineCount,
591c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                                lms2.getLeadingMarginLineCount());
5927b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    }
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
595121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
597121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (chooseHt.length != 0) {
598121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    if (chooseHtv == null ||
599121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        chooseHtv.length < chooseHt.length) {
600776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                        chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
603121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    for (int i = 0; i < chooseHt.length; i++) {
604121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        int o = spanned.getSpanStart(chooseHt[i]);
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
606e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (o < paraStart) {
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // starts in this layout, before the
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // current paragraph
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
610121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                            chooseHtv[i] = getLineTop(getLineForOffset(o));
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else {
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // starts in this paragraph
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
614121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                            chooseHtv[i] = v;
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
62070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            measured.setPara(source, paraStart, paraEnd, textDir, b);
621e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            char[] chs = measured.mChars;
622e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float[] widths = measured.mWidths;
623e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            byte[] chdirs = measured.mLevels;
624e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int dir = measured.mDir;
625e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean easy = measured.mEasy;
626c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien
627c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            // tab stop locations
628c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            int[] variableTabStops = null;
629c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            if (spanned != null) {
630c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
631c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                        paraEnd, TabStopSpan.class);
632c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                if (spans.length > 0) {
633c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    int[] stops = new int[spans.length];
634c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    for (int i = 0; i < spans.length; i++) {
635c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                        stops[i] = spans[i].getTabStop();
636c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    }
637c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    Arrays.sort(stops, 0, stops.length);
638c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    variableTabStops = stops;
639c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                }
640c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            }
641c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien
642c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
643c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    firstWidth, firstWidthLineCount, restWidth,
64439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                    variableTabStops, TAB_INCREMENT, b.mBreakStrategy);
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
646c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // measurement has to be done before performing line breaking
647c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // but we don't want to recompute fontmetrics or span ranges the
648c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // second time, so we cache those and then use those stored values
649c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmCacheCount = 0;
650c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int spanEndCacheCount = 0;
651cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne            for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
652c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fmCacheCount * 4 >= fmCache.length) {
653c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int[] grow = new int[fmCacheCount * 4 * 2];
654c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
655c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmCache = grow;
656c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
657c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
658c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (spanEndCacheCount >= spanEndCache.length) {
659c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int[] grow = new int[spanEndCacheCount * 2];
660c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
661c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    spanEndCache = grow;
662c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
66323241887515ed77687c23e29a4a3ffff671666bdDoug Felt
664cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                if (spanned == null) {
665cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spanEnd = paraEnd;
66623241887515ed77687c23e29a4a3ffff671666bdDoug Felt                    int spanLen = spanEnd - spanStart;
667cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    measured.addStyleRun(paint, spanLen, fm);
668cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                } else {
669cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
670cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                            MetricAffectingSpan.class);
671cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    int spanLen = spanEnd - spanStart;
672cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    MetricAffectingSpan[] spans =
67323241887515ed77687c23e29a4a3ffff671666bdDoug Felt                            spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
674cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
675cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    measured.addStyleRun(paint, spans, spanLen, fm);
67623241887515ed77687c23e29a4a3ffff671666bdDoug Felt                }
67723241887515ed77687c23e29a4a3ffff671666bdDoug Felt
678c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // the order of storage here (top, bottom, ascent, descent) has to match the code below
679c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // where these values are retrieved
680c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 0] = fm.top;
681c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 1] = fm.bottom;
682c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 2] = fm.ascent;
683c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 3] = fm.descent;
684c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCacheCount++;
685c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
686c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEndCache[spanEndCacheCount] = spanEnd;
687c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEndCacheCount++;
688c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            }
689c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
69070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            nGetWidths(b.mNativePtr, widths);
691c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
692c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
69381541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne
694c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int[] breaks = lineBreaks.breaks;
695c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            float[] lineWidths = lineBreaks.widths;
69626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien            int[] flags = lineBreaks.flags;
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
698c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // here is the offset of the starting character of the line we are currently measuring
699c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int here = paraStart;
700d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne
701c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
702c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmCacheIndex = 0;
703c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int spanEndCacheIndex = 0;
704c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int breakIndex = 0;
705c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
706c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // retrieve end of span
707c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEnd = spanEndCache[spanEndCacheIndex++];
708c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
709c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // retrieve cached metrics, order matches above
710c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.top = fmCache[fmCacheIndex * 4 + 0];
711c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.bottom = fmCache[fmCacheIndex * 4 + 1];
712c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.ascent = fmCache[fmCacheIndex * 4 + 2];
713c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.descent = fmCache[fmCacheIndex * 4 + 3];
714c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCacheIndex++;
715c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
716c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.top < fmTop) {
717c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmTop = fm.top;
718c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
719c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.ascent < fmAscent) {
720c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmAscent = fm.ascent;
721c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
722c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.descent > fmDescent) {
723c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmDescent = fm.descent;
724c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
725c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.bottom > fmBottom) {
726c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmBottom = fm.bottom;
727c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
728cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne
729c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // skip breaks ending before current span range
730c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
731c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    breakIndex++;
732c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
73381541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne
734c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
735c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int endPos = paraStart + breaks[breakIndex];
736c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
737ce4155a204144ae5462f547f7738af24be5a1f77Raph Levien                    boolean moreChars = (endPos < bufEnd);
7384c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien
739c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    v = out(source, here, endPos,
740c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            fmAscent, fmDescent, fmTop, fmBottom,
741c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex],
742c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
743c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            chs, widths, paraStart, ellipsize, ellipsizedWidth,
7444c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien                            lineWidths[breakIndex], paint, moreChars);
745c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
746c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    if (endPos < spanEnd) {
747c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        // preserve metrics for current span
748c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmTop = fm.top;
749c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmBottom = fm.bottom;
750c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmAscent = fm.ascent;
751c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmDescent = fm.descent;
752c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    } else {
753c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmTop = fmBottom = fmAscent = fmDescent = 0;
7548059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
756c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    here = endPos;
757c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    breakIndex++;
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    if (mLineCount >= mMaximumVisibleLineCount) {
760c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        return;
761c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    }
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
765121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (paraEnd == bufEnd)
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7698059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) &&
770ad0b051b133baf92f199c96a8ac1e81b3393190cFabrice Di Meglio                mLineCount < mMaximumVisibleLineCount) {
771121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            // Log.e("text", "output last " + bufEnd);
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
77370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            measured.setPara(source, bufEnd, bufEnd, textDir, b);
774e631889e1ae7edc6a2fae495ba504f85820b6a4bFabrice Di Meglio
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            paint.getFontMetricsInt(fm);
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            v = out(source,
778121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    bufEnd, bufEnd, fm.ascent, fm.descent,
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fm.top, fm.bottom,
7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    v,
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    spacingmult, spacingadd, null,
78226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien                    null, fm, 0,
783e631889e1ae7edc6a2fae495ba504f85820b6a4bFabrice Di Meglio                    needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
784d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    includepad, trackpad, null,
785d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    null, bufStart, ellipsize,
786d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    ellipsizedWidth, 0, paint, false);
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int out(CharSequence text, int start, int end,
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                      int above, int below, int top, int bottom, int v,
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                      float spacingmult, float spacingadd,
793121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                      LineHeightSpan[] chooseHt, int[] chooseHtv,
79426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien                      Paint.FontMetricsInt fm, int flags,
795d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean needMultiply, byte[] chdirs, int dir,
796d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean easy, int bufEnd, boolean includePad,
797d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean trackPad, char[] chs,
798d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      float[] widths, int widthStart, TextUtils.TruncateAt ellipsize,
799d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      float ellipsisWidth, float textWidth,
800d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      TextPaint paint, boolean moreChars) {
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int j = mLineCount;
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int off = j * mColumns;
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int want = off + mColumns + TOP;
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] lines = mLines;
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (want >= lines.length) {
807776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            Directions[] grow2 = ArrayUtils.newUnpaddedArray(
808776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                    Directions.class, GrowingArrayUtils.growSize(want));
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(mLineDirections, 0, grow2, 0,
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                             mLineDirections.length);
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLineDirections = grow2;
812776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski
813776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            int[] grow = new int[grow2.length];
814776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            System.arraycopy(lines, 0, grow, 0, lines.length);
815776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            mLines = grow;
816776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            lines = grow;
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
819121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        if (chooseHt != null) {
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.ascent = above;
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.descent = below;
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.top = top;
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.bottom = bottom;
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
825121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            for (int i = 0; i < chooseHt.length; i++) {
826121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (chooseHt[i] instanceof LineHeightSpan.WithDensity) {
827121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    ((LineHeightSpan.WithDensity) chooseHt[i]).
828121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        chooseHeight(text, start, end, chooseHtv[i], v, fm, paint);
829a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer
830a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                } else {
831121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm);
832a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                }
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            above = fm.ascent;
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            below = fm.descent;
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            top = fm.top;
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bottom = fm.bottom;
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
841d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean firstLine = (j == 0);
842d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
843d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd);
844d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
845d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (firstLine) {
846121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (trackPad) {
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mTopPadding = top - above;
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
850121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (includePad) {
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                above = top;
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
854d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
855d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        int extra;
856d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
857d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (lastLine) {
858121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (trackPad) {
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mBottomPadding = bottom - below;
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
862121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (includePad) {
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                below = bottom;
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
868d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (needMultiply && !lastLine) {
8691065758a0f8966a8597a61492112f7859a7050a4Doug Felt            double ex = (below - above) * (spacingmult - 1) + spacingadd;
8701065758a0f8966a8597a61492112f7859a7050a4Doug Felt            if (ex >= 0) {
871121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                extra = (int)(ex + EXTRA_ROUNDING);
8721065758a0f8966a8597a61492112f7859a7050a4Doug Felt            } else {
873121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                extra = -(int)(-ex + EXTRA_ROUNDING);
8741065758a0f8966a8597a61492112f7859a7050a4Doug Felt            }
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            extra = 0;
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + START] = start;
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + TOP] = v;
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + DESCENT] = below + extra;
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        v += (below - above) + extra;
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + mColumns + START] = end;
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + mColumns + TOP] = v;
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
88726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
88826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        // one bit for start field
88926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        lines[off + TAB] |= flags & TAB_MASK;
89026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        lines[off + HYPHEN] = flags;
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8929f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        lines[off + DIR] |= dir << DIR_SHIFT;
8939f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
8949f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // easy means all chars < the first RTL, so no emoji, no nothing
8954e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        // XXX a run with no text or all spaces is easy but might be an empty
8969f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // RTL paragraph.  Make sure easy is false if this is the case.
8979f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (easy) {
8989f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            mLineDirections[j] = linedirs;
8999f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        } else {
900f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne            mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
901f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne                    start - widthStart, end - start);
9020a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        }
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
904aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio        if (ellipsize != null) {
905aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            // If there is only one line, then do any type of ellipsis except when it is MARQUEE
906aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            // if there are multiple lines, just allow END ellipsis on the last line
9078059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
908aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio
90934a126e51aaf22e32c7af808ec6b5a0c41ae3311Fabrice Di Meglio            boolean doEllipsis =
91034a126e51aaf22e32c7af808ec6b5a0c41ae3311Fabrice Di Meglio                        (((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
911aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                                ellipsize != TextUtils.TruncateAt.MARQUEE) ||
912aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
913aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                                ellipsize == TextUtils.TruncateAt.END);
914aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            if (doEllipsis) {
915aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                calculateEllipsis(start, end, widths, widthStart,
916aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        ellipsisWidth, ellipsize, j,
917aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        textWidth, paint, forceEllipsis);
918aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            }
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLineCount++;
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return v;
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
925121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private void calculateEllipsis(int lineStart, int lineEnd,
926121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                                   float[] widths, int widthStart,
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                   float avail, TextUtils.TruncateAt where,
9288059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                                   int line, float textWidth, TextPaint paint,
9298059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                                   boolean forceEllipsis) {
9308059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if (textWidth <= avail && !forceEllipsis) {
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Everything fits!
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLines[mColumns * line + ELLIPSIS_START] = 0;
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLines[mColumns * line + ELLIPSIS_COUNT] = 0;
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
937cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio        float ellipsisWidth = paint.measureText(
9388d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio                (where == TextUtils.TruncateAt.END_SMALL) ?
939d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller                        TextUtils.ELLIPSIS_TWO_DOTS : TextUtils.ELLIPSIS_NORMAL, 0, 1);
9408059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        int ellipsisStart = 0;
9418059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        int ellipsisCount = 0;
942121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        int len = lineEnd - lineStart;
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9448059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        // We only support start ellipsis on a single line
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (where == TextUtils.TruncateAt.START) {
9468059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            if (mMaximumVisibleLineCount == 1) {
9478059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float sum = 0;
9488059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                int i;
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
950ed2eea1bfc1fb57d3b34f2c1e6062b541737e73eKeisuke Kuroyanagi                for (i = len; i > 0; i--) {
9518059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[i - 1 + lineStart - widthStart];
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9538059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + sum + ellipsisWidth > avail) {
9548059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
9558059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
9568059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
9578059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    sum += w;
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9608059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisStart = 0;
9618059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = i;
9628059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            } else {
9638059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                if (Log.isLoggable(TAG, Log.WARN)) {
9648059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    Log.w(TAG, "Start Ellipsis only supported with one line");
9658059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                }
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
967cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio        } else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE ||
968cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio                where == TextUtils.TruncateAt.END_SMALL) {
9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float sum = 0;
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i;
9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (i = 0; i < len; i++) {
973121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                float w = widths[i + lineStart - widthStart];
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
975121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (w + sum + ellipsisWidth > avail) {
9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sum += w;
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ellipsisStart = i;
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ellipsisCount = len - i;
984aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            if (forceEllipsis && ellipsisCount == 0 && len > 0) {
985aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                ellipsisStart = len - 1;
9868059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = 1;
9878059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            }
9888059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        } else {
9898059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            // where = TextUtils.TruncateAt.MIDDLE We only support middle ellipsis on a single line
9908059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            if (mMaximumVisibleLineCount == 1) {
9918059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float lsum = 0, rsum = 0;
9928059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                int left = 0, right = len;
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9948059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float ravail = (avail - ellipsisWidth) / 2;
9950e3c5e827235911d33312e431975533f046421e7Raph Levien                for (right = len; right > 0; right--) {
9968059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[right - 1 + lineStart - widthStart];
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9988059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + rsum > ravail) {
9998059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
10008059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
10018059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
10028059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    rsum += w;
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10058059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float lavail = avail - ellipsisWidth - rsum;
10068059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                for (left = 0; left < right; left++) {
10078059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[left + lineStart - widthStart];
10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10098059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + lsum > lavail) {
10108059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
10118059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10138059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    lsum += w;
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10168059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisStart = left;
10178059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = right - left;
10188059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            } else {
10198059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                if (Log.isLoggable(TAG, Log.WARN)) {
10208059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    Log.w(TAG, "Middle Ellipsis only supported with one line");
10218059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                }
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart;
10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1029e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    // Override the base class so we can directly access our members,
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // rather than relying on member functions.
10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The logic mirrors that of Layout.getLineForVertical
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // FIXME: It may be faster to do a linear search for layouts without many lines.
10336611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineForVertical(int vertical) {
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int high = mLineCount;
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int low = -1;
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int guess;
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] lines = mLines;
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (high - low > 1) {
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            guess = (high + low) >> 1;
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (lines[mColumns * guess + TOP] > vertical){
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                high = guess;
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = guess;
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (low < 0) {
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return low;
10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10546611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineCount() {
10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLineCount;
10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10596611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineTop(int line) {
10610a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        int top = mLines[mColumns * line + TOP];
10620a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount &&
10630a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne                line != mLineCount) {
10640a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne            top += getBottomPadding();
10650a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        }
10660a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        return top;
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10696611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineDescent(int line) {
10710a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        int descent = mLines[mColumns * line + DESCENT];
1072f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne        if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount - 1 && // -1 intended
10730a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne                line != mLineCount) {
10740a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne            descent += getBottomPadding();
10750a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        }
10760a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        return descent;
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10796611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineStart(int line) {
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + START] & START_MASK;
10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10846611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getParagraphDirection(int line) {
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + DIR] >> DIR_SHIFT;
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10896611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean getLineContainsTab(int line) {
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return (mLines[mColumns * line + TAB] & TAB_MASK) != 0;
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10946611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final Directions getLineDirections(int line) {
10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLineDirections[line];
10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10996611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getTopPadding() {
11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mTopPadding;
11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11046611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getBottomPadding() {
11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mBottomPadding;
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
110926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    /**
111026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien     * @hide
111126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien     */
111226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    @Override
111326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    public int getHyphen(int line) {
111426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        return mLines[mColumns * line + HYPHEN] & 0xff;
111526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    }
111626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsisCount(int line) {
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mColumns < COLUMNS_ELLIPSIZE) {
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + ELLIPSIS_COUNT];
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsisStart(int line) {
11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mColumns < COLUMNS_ELLIPSIZE) {
11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + ELLIPSIS_START];
11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsizedWidth() {
11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mEllipsizedWidth;
11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
114070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native long nNewBuilder();
114170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nFreeBuilder(long nativePtr);
114270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nFinishBuilder(long nativePtr);
114326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien
114426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    /* package */ static native long nLoadHyphenator(String patternData);
114526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien
114626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
114770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
1148e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien    private static native void nSetIndents(long nativePtr, int[] indents);
1149e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien
1150c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien    // Set up paragraph text and settings; done as one big method to minimize jni crossings
1151c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien    private static native void nSetupParagraph(long nativePtr, char[] text, int length,
1152c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            float firstWidth, int firstWidthLineCount, float restWidth,
1153c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            int[] variableTabStops, int defaultTabStop, int breakStrategy);
115470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
115570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native float nAddStyleRun(long nativePtr, long nativePaint,
115670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            long nativeTypeface, int start, int end, boolean isRtl);
115770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
115870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nAddMeasuredRun(long nativePtr,
115970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            int start, int end, float[] widths);
116070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
116170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nAddReplacementRun(long nativePtr, int start, int end, float width);
116270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
116370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nGetWidths(long nativePtr, float[] widths);
116470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
1165c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // populates LineBreaks and returns the number of breaks found
1166c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    //
1167c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // the arrays inside the LineBreaks objects are passed in as well
1168c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // to reduce the number of JNI calls in the common case where the
1169c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // arrays do not have to be resized
1170c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien    private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
117126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien            int[] recycleBreaks, float[] recycleWidths, int[] recycleFlags, int recycleLength);
117288b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye
11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mLineCount;
11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mTopPadding, mBottomPadding;
11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mColumns;
11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mEllipsizedWidth;
11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
117826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int COLUMNS_NORMAL = 4;
117926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int COLUMNS_ELLIPSIZE = 6;
11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int START = 0;
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DIR = START;
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAB = START;
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TOP = 1;
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DESCENT = 2;
118526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int HYPHEN = 3;
118626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int ELLIPSIS_START = 4;
118726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int ELLIPSIS_COUNT = 5;
11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int[] mLines;
11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Directions[] mLineDirections;
11918059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    private int mMaximumVisibleLineCount = Integer.MAX_VALUE;
11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int START_MASK = 0x1FFFFFFF;
11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DIR_SHIFT  = 30;
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAB_MASK   = 0x20000000;
11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1197c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private static final int TAB_INCREMENT = 20; // same as Layout, but that's private
11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1199121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private static final char CHAR_NEW_LINE = '\n';
1200121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio
1201121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private static final double EXTRA_ROUNDING = 0.5;
1202cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio
1203c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // This is used to return three arrays from a single JNI call when
1204c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // performing line breaking
12057053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    /*package*/ static class LineBreaks {
1206c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        private static final int INITIAL_SIZE = 16;
1207c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        public int[] breaks = new int[INITIAL_SIZE];
1208c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        public float[] widths = new float[INITIAL_SIZE];
120926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        public int[] flags = new int[INITIAL_SIZE]; // hasTabOrEmoji
1210c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // breaks, widths, and flags should all have the same length
1211c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    }
1212c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1214