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
32091dba2de1e5fa7d4db9f8ccbf1f86e5825d0f52Raph Levienimport java.nio.ByteBuffer;
33c8f9e6218681640d5f384c12edf06619be56a583Anish Athalyeimport java.util.Arrays;
344c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levienimport java.util.Locale;
35c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * StaticLayout is a Layout for text that will not be edited after it
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * is laid out.  Use {@link DynamicLayout} for text that may change.
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>This is used by widgets to control text layout. You should not need
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * to use this class directly unless you are implementing your own widget
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * or custom display object, or would be tempted to call
424e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int,
434e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * float, float, android.graphics.Paint)
444e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * Canvas.drawText()} directly.</p>
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
46121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Megliopublic class StaticLayout extends Layout {
47121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio
488059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    static final String TAG = "StaticLayout";
498059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
50d3ab692d28018825578ff05832644cfad60233fbRaph Levien    /**
51531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * Builder for static layouts. The builder is a newer pattern for constructing
52531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * StaticLayout objects and should be preferred over the constructors,
53531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * particularly to access newer features. To build a static layout, first
54531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * call {@link #obtain} with the required arguments (text, paint, and width),
55531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * then call setters for optional parameters, and finally {@link #build}
56531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * to build the StaticLayout object. Parameters not explicitly set will get
57531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien     * default values.
58d3ab692d28018825578ff05832644cfad60233fbRaph Levien     */
59d3ab692d28018825578ff05832644cfad60233fbRaph Levien    public final static class Builder {
604c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        private Builder() {
614c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            mNativePtr = nNewBuilder();
624c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        }
634c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
64531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
65531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Obtain a builder for constructing StaticLayout objects
66531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
67531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param source The text to be laid out, optionally with spans
68531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param start The index of the start of the text
69531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param end The index + 1 of the end of the text
70531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param paint The base paint used for layout
71531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param width The width in pixels
72531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return a builder object used for constructing the StaticLayout
73531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
74ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien        public static Builder obtain(CharSequence source, int start, int end, TextPaint paint,
75ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien                int width) {
7639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            Builder b = sPool.acquire();
77d3ab692d28018825578ff05832644cfad60233fbRaph Levien            if (b == null) {
78d3ab692d28018825578ff05832644cfad60233fbRaph Levien                b = new Builder();
79d3ab692d28018825578ff05832644cfad60233fbRaph Levien            }
80d3ab692d28018825578ff05832644cfad60233fbRaph Levien
81d3ab692d28018825578ff05832644cfad60233fbRaph Levien            // set default initial values
8239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mText = source;
8339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mStart = start;
8439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mEnd = end;
85ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien            b.mPaint = paint;
8639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mWidth = width;
8739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mAlignment = Alignment.ALIGN_NORMAL;
88d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
89d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mSpacingMult = 1.0f;
90d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mSpacingAdd = 0.0f;
91d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mIncludePad = true;
9239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            b.mEllipsizedWidth = width;
93d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mEllipsize = null;
94d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mMaxLines = Integer.MAX_VALUE;
953bd60c7b1125ee42cd7e8746aeaebdb43e8211d8Raph Levien            b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
9695c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader            b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
97d3ab692d28018825578ff05832644cfad60233fbRaph Levien
98d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mMeasuredText = MeasuredText.obtain();
99d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return b;
100d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
101d3ab692d28018825578ff05832644cfad60233fbRaph Levien
10239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        private static void recycle(Builder b) {
103d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mPaint = null;
104d3ab692d28018825578ff05832644cfad60233fbRaph Levien            b.mText = null;
105d3ab692d28018825578ff05832644cfad60233fbRaph Levien            MeasuredText.recycle(b.mMeasuredText);
1063bd60c7b1125ee42cd7e8746aeaebdb43e8211d8Raph Levien            b.mMeasuredText = null;
1072ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            b.mLeftIndents = null;
1082ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            b.mRightIndents = null;
1093bd60c7b1125ee42cd7e8746aeaebdb43e8211d8Raph Levien            nFinishBuilder(b.mNativePtr);
11039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            sPool.release(b);
111d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
112d3ab692d28018825578ff05832644cfad60233fbRaph Levien
113d3ab692d28018825578ff05832644cfad60233fbRaph Levien        // release any expensive state
114d3ab692d28018825578ff05832644cfad60233fbRaph Levien        /* package */ void finish() {
1154c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            nFinishBuilder(mNativePtr);
11622ba7869f6e211f5a692235f52687a6acdd71afcRaph Levien            mText = null;
11722ba7869f6e211f5a692235f52687a6acdd71afcRaph Levien            mPaint = null;
11822ba7869f6e211f5a692235f52687a6acdd71afcRaph Levien            mLeftIndents = null;
11922ba7869f6e211f5a692235f52687a6acdd71afcRaph Levien            mRightIndents = null;
120d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mMeasuredText.finish();
121d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
122d3ab692d28018825578ff05832644cfad60233fbRaph Levien
123d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setText(CharSequence source) {
124d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return setText(source, 0, source.length());
125d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
126d3ab692d28018825578ff05832644cfad60233fbRaph Levien
127531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
128531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the text. Only useful when re-using the builder, which is done for
129531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * the internal implementation of {@link DynamicLayout} but not as part
130531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * of normal {@link StaticLayout} usage.
131531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
132531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param source The text to be laid out, optionally with spans
133531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param start The index of the start of the text
134531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param end The index + 1 of the end of the text
135531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
136531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
137531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @hide
138531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
139d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setText(CharSequence source, int start, int end) {
140d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mText = source;
141d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mStart = start;
142d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mEnd = end;
143d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
144d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
145d3ab692d28018825578ff05832644cfad60233fbRaph Levien
146531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
147531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the paint. Internal for reuse cases only.
148531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
149531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param paint The base paint used for layout
150531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
151531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
152531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @hide
153531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
154d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setPaint(TextPaint paint) {
155d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mPaint = paint;
156d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
157d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
158d3ab692d28018825578ff05832644cfad60233fbRaph Levien
159531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
160531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the width. Internal for reuse cases only.
161531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
162531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param width The width in pixels
163531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
164531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
165531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @hide
166531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
167d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setWidth(int width) {
168d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mWidth = width;
169d3ab692d28018825578ff05832644cfad60233fbRaph Levien            if (mEllipsize == null) {
170d3ab692d28018825578ff05832644cfad60233fbRaph Levien                mEllipsizedWidth = width;
171d3ab692d28018825578ff05832644cfad60233fbRaph Levien            }
172d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
173d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
174d3ab692d28018825578ff05832644cfad60233fbRaph Levien
175531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
176531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the alignment. The default is {@link Layout.Alignment#ALIGN_NORMAL}.
177531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
178531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param alignment Alignment for the resulting {@link StaticLayout}
179531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
180531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
18139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        public Builder setAlignment(Alignment alignment) {
18239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mAlignment = alignment;
18339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            return this;
18439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        }
18539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
186531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
187531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the text direction heuristic. The text direction heuristic is used to
188531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * resolve text direction based per-paragraph based on the input text. The default is
189531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
190531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
191531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param textDir text direction heuristic for resolving BiDi behavior.
192531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
193531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
194a6a082862b9e2ea4c9e9a1a945927c4040993f6eRaph Levien        public Builder setTextDirection(TextDirectionHeuristic textDir) {
195d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mTextDir = textDir;
196d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
197d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
198d3ab692d28018825578ff05832644cfad60233fbRaph Levien
199531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
200531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set line spacing parameters. The default is 0.0 for {@code spacingAdd}
201531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * and 1.0 for {@code spacingMult}.
202531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
203531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param spacingAdd line spacing add
204531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param spacingMult line spacing multiplier
205531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
206531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setLineSpacing
207531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
208531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        public Builder setLineSpacing(float spacingAdd, float spacingMult) {
209d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mSpacingAdd = spacingAdd;
210531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien            mSpacingMult = spacingMult;
211d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
212d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
213d3ab692d28018825578ff05832644cfad60233fbRaph Levien
214531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
215531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set whether to include extra space beyond font ascent and descent (which is
216531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * needed to avoid clipping in some languages, such as Arabic and Kannada). The
217531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * default is {@code true}.
218531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
219531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param includePad whether to include padding
220531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
221531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setIncludeFontPadding
222531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
223d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setIncludePad(boolean includePad) {
224d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mIncludePad = includePad;
225d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
226d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
227d3ab692d28018825578ff05832644cfad60233fbRaph Levien
228531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
229531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set the width as used for ellipsizing purposes, if it differs from the
230531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * normal layout width. The default is the {@code width}
231531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * passed to {@link #obtain}.
232531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
233531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param ellipsizedWidth width used for ellipsizing, in pixels
234531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
235531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setEllipsize
236531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
237d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setEllipsizedWidth(int ellipsizedWidth) {
238d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mEllipsizedWidth = ellipsizedWidth;
239d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
240d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
241d3ab692d28018825578ff05832644cfad60233fbRaph Levien
242531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
243531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set ellipsizing on the layout. Causes words that are longer than the view
244531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * is wide, or exceeding the number of lines (see #setMaxLines) in the case
245531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * of {@link android.text.TextUtils.TruncateAt#END} or
246531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * {@link android.text.TextUtils.TruncateAt#MARQUEE}, to be ellipsized instead
247531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * of broken. The default is
248531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * {@code null}, indicating no ellipsis is to be applied.
249531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
250531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param ellipsize type of ellipsis behavior
251531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
252531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setEllipsize
253531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
254531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
255d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mEllipsize = ellipsize;
256d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
257d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
258d3ab692d28018825578ff05832644cfad60233fbRaph Levien
259531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
260531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set maximum number of lines. This is particularly useful in the case of
261531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * ellipsizing, where it changes the layout of the last line. The default is
262531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * unlimited.
263531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
264531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param maxLines maximum number of lines in the layout
265531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
266531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setMaxLines
267531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
268d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public Builder setMaxLines(int maxLines) {
269d3ab692d28018825578ff05832644cfad60233fbRaph Levien            mMaxLines = maxLines;
270d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return this;
271d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
272d3ab692d28018825578ff05832644cfad60233fbRaph Levien
273531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
274531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set break strategy, useful for selecting high quality or balanced paragraph
275531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * layout options. The default is {@link Layout#BREAK_STRATEGY_SIMPLE}.
276531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
277531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param breakStrategy break strategy for paragraph layout
278531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
279531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @see android.widget.TextView#setBreakStrategy
280531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
28139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
28239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mBreakStrategy = breakStrategy;
28339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            return this;
28439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        }
28539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
286531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
28795c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * Set hyphenation frequency, to control the amount of automatic hyphenation used. The
28895c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * default is {@link Layout#HYPHENATION_FREQUENCY_NONE}.
28995c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         *
29095c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * @param hyphenationFrequency hyphenation frequency for the paragraph
29195c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * @return this builder, useful for chaining
29295c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * @see android.widget.TextView#setHyphenationFrequency
29395c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         */
29495c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader        public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
29595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader            mHyphenationFrequency = hyphenationFrequency;
29695c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader            return this;
29795c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader        }
29895c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader
29995c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader        /**
300531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Set indents. Arguments are arrays holding an indent amount, one per line, measured in
301531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * pixels. For lines past the last element in the array, the last element repeats.
302531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
303531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param leftIndents array of indent values for left margin, in pixels
304531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @param rightIndents array of indent values for right margin, in pixels
305531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return this builder, useful for chaining
306531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
307e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien        public Builder setIndents(int[] leftIndents, int[] rightIndents) {
3082ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            mLeftIndents = leftIndents;
3092ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            mRightIndents = rightIndents;
310e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            int leftLen = leftIndents == null ? 0 : leftIndents.length;
311e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            int rightLen = rightIndents == null ? 0 : rightIndents.length;
312e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            int[] indents = new int[Math.max(leftLen, rightLen)];
313e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            for (int i = 0; i < indents.length; i++) {
314e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien                int leftMargin = i < leftLen ? leftIndents[i] : 0;
315e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien                int rightMargin = i < rightLen ? rightIndents[i] : 0;
316e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien                indents[i] = leftMargin + rightMargin;
317e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            }
318e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            nSetIndents(mNativePtr, indents);
319e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien            return this;
320e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien        }
321e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien
32270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /**
32370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * Measurement and break iteration is done in native code. The protocol for using
32470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * the native code is as follows.
32570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *
32626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien         * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab
32795c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * stops, break strategy, and hyphenation frequency (and possibly other parameters in the
32895c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader         * future).
329c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien         *
330c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien         * Then, for each run within the paragraph:
33170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *  - setLocale (this must be done at least for the first run, optional afterwards)
33270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *  - one of the following, depending on the type of run:
33370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *    + addStyleRun (a text run, to be measured in native code)
33470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *    + addMeasuredRun (a run already measured in Java, passed into native code)
33570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *    + addReplacementRun (a replacement run, width is given)
33670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *
33770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * After measurement, nGetWidths() is valid if the widths are needed (eg for ellipsis).
33870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * Run nComputeLineBreaks() to obtain line breaks for the paragraph.
33970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         *
34070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         * After all paragraphs, call finish() to release expensive buffers.
34170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien         */
34270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
34370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        private void setLocale(Locale locale) {
3444c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            if (!locale.equals(mLocale)) {
345e7eac6f540b09892172edd5b584605ea90f5a1baRoozbeh Pournader                nSetLocale(mNativePtr, locale.toLanguageTag(),
346e7eac6f540b09892172edd5b584605ea90f5a1baRoozbeh Pournader                        Hyphenator.get(locale).getNativePtr());
3474c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien                mLocale = locale;
3484c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            }
3494c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        }
3504c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
35170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /* package */ float addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
35270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            return nAddStyleRun(mNativePtr, paint.getNativeInstance(), paint.mNativeTypeface,
35370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien                    start, end, isRtl);
35470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        }
35570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
35670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /* package */ void addMeasuredRun(int start, int end, float[] widths) {
35770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            nAddMeasuredRun(mNativePtr, start, end, widths);
35870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        }
35970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
36070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        /* package */ void addReplacementRun(int start, int end, float width) {
36170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            nAddReplacementRun(mNativePtr, start, end, width);
36270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        }
36370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
364531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien        /**
365531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * Build the {@link StaticLayout} after options have been set.
366531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
367531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * <p>Note: the builder object must not be reused in any way after calling this
368531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * method. Setting parameters after calling this method, or calling it a second
369531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * time on the same builder object, will likely lead to unexpected results.
370531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         *
371531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         * @return the newly constructed {@link StaticLayout} object
372531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien         */
373d3ab692d28018825578ff05832644cfad60233fbRaph Levien        public StaticLayout build() {
37439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            StaticLayout result = new StaticLayout(this);
37539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            Builder.recycle(this);
376d3ab692d28018825578ff05832644cfad60233fbRaph Levien            return result;
377d3ab692d28018825578ff05832644cfad60233fbRaph Levien        }
378d3ab692d28018825578ff05832644cfad60233fbRaph Levien
3794c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        @Override
3804c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        protected void finalize() throws Throwable {
3814c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            try {
3824c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien                nFreeBuilder(mNativePtr);
3834c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            } finally {
3844c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien                super.finalize();
3854c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien            }
3864c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        }
3874c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
3884c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        /* package */ long mNativePtr;
3894c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
390d3ab692d28018825578ff05832644cfad60233fbRaph Levien        CharSequence mText;
391d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mStart;
392d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mEnd;
393d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextPaint mPaint;
394d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mWidth;
39539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        Alignment mAlignment;
396d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextDirectionHeuristic mTextDir;
397d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float mSpacingMult;
398d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float mSpacingAdd;
399d3ab692d28018825578ff05832644cfad60233fbRaph Levien        boolean mIncludePad;
400d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mEllipsizedWidth;
401d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextUtils.TruncateAt mEllipsize;
402d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int mMaxLines;
40339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        int mBreakStrategy;
40495c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader        int mHyphenationFrequency;
4052ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien        int[] mLeftIndents;
4062ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien        int[] mRightIndents;
407d3ab692d28018825578ff05832644cfad60233fbRaph Levien
408d3ab692d28018825578ff05832644cfad60233fbRaph Levien        Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
409d3ab692d28018825578ff05832644cfad60233fbRaph Levien
410d3ab692d28018825578ff05832644cfad60233fbRaph Levien        // This will go away and be subsumed by native builder code
411d3ab692d28018825578ff05832644cfad60233fbRaph Levien        MeasuredText mMeasuredText;
412d3ab692d28018825578ff05832644cfad60233fbRaph Levien
4134c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        Locale mLocale;
4144c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien
41539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
416d3ab692d28018825578ff05832644cfad60233fbRaph Levien    }
417d3ab692d28018825578ff05832644cfad60233fbRaph Levien
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, TextPaint paint,
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int width,
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Alignment align, float spacingmult, float spacingadd,
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad) {
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(source, 0, source.length(), paint, width, align,
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             spacingmult, spacingadd, includepad);
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
426cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
427cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
428cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
429cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, TextPaint paint,
430cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            int width, Alignment align, TextDirectionHeuristic textDir,
431cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
432cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad) {
433cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, 0, source.length(), paint, width, align, textDir,
434cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                spacingmult, spacingadd, includepad);
435cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    }
436cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, int bufstart, int bufend,
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        TextPaint paint, int outerwidth,
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Alignment align,
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        float spacingmult, float spacingadd,
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad) {
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(source, bufstart, bufend, paint, outerwidth, align,
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             spacingmult, spacingadd, includepad, null, 0);
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
446cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
447cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
448cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
449cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, int bufstart, int bufend,
450cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextPaint paint, int outerwidth,
451cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            Alignment align, TextDirectionHeuristic textDir,
452cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
453cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad) {
454cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, bufstart, bufend, paint, outerwidth, align, textDir,
4558059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                spacingmult, spacingadd, includepad, null, 0, Integer.MAX_VALUE);
456cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt}
457cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
458cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, int bufstart, int bufend,
459cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextPaint paint, int outerwidth,
460cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            Alignment align,
461cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
462cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad,
463cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
464cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, bufstart, bufend, paint, outerwidth, align,
465cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                TextDirectionHeuristics.FIRSTSTRONG_LTR,
4668059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE);
467cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    }
468cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
469cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
470cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
471cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, int bufstart, int bufend,
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        TextPaint paint, int outerwidth,
474cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                        Alignment align, TextDirectionHeuristic textDir,
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        float spacingmult, float spacingadd,
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad,
4778059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super((ellipsize == null)
4794e0c5e55e171532760d5f51e0165563827129d4eDoug Felt                ? source
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                : (source instanceof Spanned)
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? new SpannedEllipsizer(source)
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : new Ellipsizer(source),
483cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt              paint, outerwidth, align, textDir, spacingmult, spacingadd);
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
485ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien        Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
48639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            .setAlignment(align)
487a6a082862b9e2ea4c9e9a1a945927c4040993f6eRaph Levien            .setTextDirection(textDir)
488531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien            .setLineSpacing(spacingadd, spacingmult)
489d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setIncludePad(includepad)
490d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setEllipsizedWidth(ellipsizedWidth)
491d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setEllipsize(ellipsize)
492d3ab692d28018825578ff05832644cfad60233fbRaph Levien            .setMaxLines(maxLines);
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /*
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This is annoying, but we can't refer to the layout until
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * superclass construction is finished, and the superclass
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * constructor wants the reference to the display text.
4974e0c5e55e171532760d5f51e0165563827129d4eDoug Felt         *
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This will break if the superclass constructor ever actually
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * cares about the content instead of just holding the reference.
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (ellipsize != null) {
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Ellipsizer e = (Ellipsizer) getText();
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mLayout = this;
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mWidth = ellipsizedWidth;
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mMethod = ellipsize;
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mEllipsizedWidth = ellipsizedWidth;
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mColumns = COLUMNS_ELLIPSIZE;
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mColumns = COLUMNS_NORMAL;
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mEllipsizedWidth = outerwidth;
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
515776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
516776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLines = new int[mLineDirections.length];
5178059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        mMaximumVisibleLineCount = maxLines;
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
51970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien        generate(b, b.mIncludePad, b.mIncludePad);
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
521d3ab692d28018825578ff05832644cfad60233fbRaph Levien        Builder.recycle(b);
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5248059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    /* package */ StaticLayout(CharSequence text) {
5258059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        super(text, null, 0, null, 0, 0);
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mColumns = COLUMNS_ELLIPSIZE;
528776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
529776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLines = new int[mLineDirections.length];
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
53239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien    private StaticLayout(Builder b) {
53339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        super((b.mEllipsize == null)
53439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                ? b.mText
53539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                : (b.mText instanceof Spanned)
53639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                    ? new SpannedEllipsizer(b.mText)
53739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                    : new Ellipsizer(b.mText),
53839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien                b.mPaint, b.mWidth, b.mAlignment, b.mSpacingMult, b.mSpacingAdd);
53939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
54039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        if (b.mEllipsize != null) {
54139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            Ellipsizer e = (Ellipsizer) getText();
54239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
54339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            e.mLayout = this;
54439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            e.mWidth = b.mEllipsizedWidth;
54539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            e.mMethod = b.mEllipsize;
54639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mEllipsizedWidth = b.mEllipsizedWidth;
54739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
54839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mColumns = COLUMNS_ELLIPSIZE;
54939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        } else {
55039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mColumns = COLUMNS_NORMAL;
55139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien            mEllipsizedWidth = b.mWidth;
55239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        }
55339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
55439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
55539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        mLines = new int[mLineDirections.length];
55639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        mMaximumVisibleLineCount = b.mMaxLines;
55739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
5582ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien        mLeftIndents = b.mLeftIndents;
5592ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien        mRightIndents = b.mRightIndents;
5602ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien
56139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien        generate(b, b.mIncludePad, b.mIncludePad);
56239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien    }
56339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien
564d3ab692d28018825578ff05832644cfad60233fbRaph Levien    /* package */ void generate(Builder b, boolean includepad, boolean trackpad) {
565d3ab692d28018825578ff05832644cfad60233fbRaph Levien        CharSequence source = b.mText;
566d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int bufStart = b.mStart;
567d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int bufEnd = b.mEnd;
568d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextPaint paint = b.mPaint;
569d3ab692d28018825578ff05832644cfad60233fbRaph Levien        int outerWidth = b.mWidth;
570d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextDirectionHeuristic textDir = b.mTextDir;
571d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float spacingmult = b.mSpacingMult;
572d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float spacingadd = b.mSpacingAdd;
573d3ab692d28018825578ff05832644cfad60233fbRaph Levien        float ellipsizedWidth = b.mEllipsizedWidth;
574d3ab692d28018825578ff05832644cfad60233fbRaph Levien        TextUtils.TruncateAt ellipsize = b.mEllipsize;
5754c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        LineBreaks lineBreaks = new LineBreaks();  // TODO: move to builder to avoid allocation costs
576c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // store span end locations
577c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        int[] spanEndCache = new int[4];
578c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // store fontMetrics per span range
579c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
580c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        int[] fmCache = new int[4 * 4];
5814c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien        b.setLocale(paint.getTextLocale());  // TODO: also respect LocaleSpan within the text
58288b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLineCount = 0;
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int v = 0;
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
588d3ab692d28018825578ff05832644cfad60233fbRaph Levien        Paint.FontMetricsInt fm = b.mFontMetricsInt;
589121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        int[] chooseHtv = null;
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
591d3ab692d28018825578ff05832644cfad60233fbRaph Levien        MeasuredText measured = b.mMeasuredText;
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Spanned spanned = null;
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof Spanned)
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            spanned = (Spanned) source;
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int paraEnd;
598121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
599121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
600e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (paraEnd < 0)
601121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                paraEnd = bufEnd;
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
603e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                paraEnd++;
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
605c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int firstWidthLineCount = 1;
606121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            int firstWidth = outerWidth;
607121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            int restWidth = outerWidth;
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
609121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            LineHeightSpan[] chooseHt = null;
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spanned != null) {
61274d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer                LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
613e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        LeadingMarginSpan.class);
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < sp.length; i++) {
6157b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    LeadingMarginSpan lms = sp[i];
616121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    firstWidth -= sp[i].getLeadingMargin(true);
617121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    restWidth -= sp[i].getLeadingMargin(false);
618cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
619c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    // LeadingMarginSpan2 is odd.  The count affects all
620ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye                    // leading margin spans, not just this particular one
621c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    if (lms instanceof LeadingMarginSpan2) {
622c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
623c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        firstWidthLineCount = Math.max(firstWidthLineCount,
624c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                                lms2.getLeadingMarginLineCount());
6257b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    }
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
628121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
630431e506b53e59b21a7d18bf46476b8c1528fabf1Roozbeh Pournader                if (chooseHt.length == 0) {
631431e506b53e59b21a7d18bf46476b8c1528fabf1Roozbeh Pournader                    chooseHt = null; // So that out() would not assume it has any contents
632431e506b53e59b21a7d18bf46476b8c1528fabf1Roozbeh Pournader                } else {
633121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    if (chooseHtv == null ||
634121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        chooseHtv.length < chooseHt.length) {
635776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                        chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
638121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    for (int i = 0; i < chooseHt.length; i++) {
639121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        int o = spanned.getSpanStart(chooseHt[i]);
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
641e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (o < paraStart) {
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // starts in this layout, before the
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // current paragraph
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
645121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                            chooseHtv[i] = getLineTop(getLineForOffset(o));
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else {
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // starts in this paragraph
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
649121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                            chooseHtv[i] = v;
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
65570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            measured.setPara(source, paraStart, paraEnd, textDir, b);
656e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            char[] chs = measured.mChars;
657e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float[] widths = measured.mWidths;
658e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            byte[] chdirs = measured.mLevels;
659e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int dir = measured.mDir;
660e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean easy = measured.mEasy;
661c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien
662c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            // tab stop locations
663c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            int[] variableTabStops = null;
664c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            if (spanned != null) {
665c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
666c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                        paraEnd, TabStopSpan.class);
667c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                if (spans.length > 0) {
668c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    int[] stops = new int[spans.length];
669c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    for (int i = 0; i < spans.length; i++) {
670c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                        stops[i] = spans[i].getTabStop();
671c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    }
672c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    Arrays.sort(stops, 0, stops.length);
673c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    variableTabStops = stops;
674c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                }
675c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            }
676c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien
677c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
678c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    firstWidth, firstWidthLineCount, restWidth,
67995c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader                    variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
6802ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            if (mLeftIndents != null || mRightIndents != null) {
6812ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                // TODO(raph) performance: it would be better to do this once per layout rather
6822ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                // than once per paragraph, but that would require a change to the native
6832ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                // interface.
6842ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
6852ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                int rightLen = mRightIndents == null ? 0 : mRightIndents.length;
686f9a0886da0bddcb7db84b46bfee0129acc8276b8Siyamed Sinir                int indentsLen = Math.max(1, Math.max(leftLen, rightLen) - mLineCount);
6872ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                int[] indents = new int[indentsLen];
6882ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                for (int i = 0; i < indentsLen; i++) {
6892ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                    int leftMargin = mLeftIndents == null ? 0 :
6902ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                            mLeftIndents[Math.min(i + mLineCount, leftLen - 1)];
6912ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                    int rightMargin = mRightIndents == null ? 0 :
6922ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                            mRightIndents[Math.min(i + mLineCount, rightLen - 1)];
6932ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                    indents[i] = leftMargin + rightMargin;
6942ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                }
6952ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                nSetIndents(b.mNativePtr, indents);
6962ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            }
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
698c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // measurement has to be done before performing line breaking
699c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // but we don't want to recompute fontmetrics or span ranges the
700c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // second time, so we cache those and then use those stored values
701c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmCacheCount = 0;
702c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int spanEndCacheCount = 0;
703cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne            for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
704c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fmCacheCount * 4 >= fmCache.length) {
705c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int[] grow = new int[fmCacheCount * 4 * 2];
706c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
707c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmCache = grow;
708c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
709c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
710c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (spanEndCacheCount >= spanEndCache.length) {
711c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int[] grow = new int[spanEndCacheCount * 2];
712c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
713c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    spanEndCache = grow;
714c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
71523241887515ed77687c23e29a4a3ffff671666bdDoug Felt
716cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                if (spanned == null) {
717cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spanEnd = paraEnd;
71823241887515ed77687c23e29a4a3ffff671666bdDoug Felt                    int spanLen = spanEnd - spanStart;
719cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    measured.addStyleRun(paint, spanLen, fm);
720cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                } else {
721cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
722cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                            MetricAffectingSpan.class);
723cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    int spanLen = spanEnd - spanStart;
724cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    MetricAffectingSpan[] spans =
72523241887515ed77687c23e29a4a3ffff671666bdDoug Felt                            spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
726cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
727cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    measured.addStyleRun(paint, spans, spanLen, fm);
72823241887515ed77687c23e29a4a3ffff671666bdDoug Felt                }
72923241887515ed77687c23e29a4a3ffff671666bdDoug Felt
730c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // the order of storage here (top, bottom, ascent, descent) has to match the code below
731c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // where these values are retrieved
732c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 0] = fm.top;
733c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 1] = fm.bottom;
734c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 2] = fm.ascent;
735c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 3] = fm.descent;
736c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCacheCount++;
737c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
738c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEndCache[spanEndCacheCount] = spanEnd;
739c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEndCacheCount++;
740c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            }
741c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
74270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            nGetWidths(b.mNativePtr, widths);
743c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
744c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien                    lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
74581541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne
746c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int[] breaks = lineBreaks.breaks;
747c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            float[] lineWidths = lineBreaks.widths;
74826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien            int[] flags = lineBreaks.flags;
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
750f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi            final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
751f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi            final boolean ellipsisMayBeApplied = ellipsize != null
752f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                    && (ellipsize == TextUtils.TruncateAt.END
753f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                        || (mMaximumVisibleLineCount == 1
754f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                                && ellipsize != TextUtils.TruncateAt.MARQUEE));
75504a84559fd11b36196872b1ac0cb82e5f7367806Raph Levien            if (remainingLineCount > 0 && remainingLineCount < breakCount &&
75604a84559fd11b36196872b1ac0cb82e5f7367806Raph Levien                    ellipsisMayBeApplied) {
757f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                // Calculate width and flag.
758f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                float width = 0;
759f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                int flag = 0;
760f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                for (int i = remainingLineCount - 1; i < breakCount; i++) {
76178f0d83550d70f043e1b9d35f56efa8c7572f44fKeisuke Kuroyanagi                    if (i == breakCount - 1) {
76278f0d83550d70f043e1b9d35f56efa8c7572f44fKeisuke Kuroyanagi                        width += lineWidths[i];
76378f0d83550d70f043e1b9d35f56efa8c7572f44fKeisuke Kuroyanagi                    } else {
76478f0d83550d70f043e1b9d35f56efa8c7572f44fKeisuke Kuroyanagi                        for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
76578f0d83550d70f043e1b9d35f56efa8c7572f44fKeisuke Kuroyanagi                            width += widths[j];
76678f0d83550d70f043e1b9d35f56efa8c7572f44fKeisuke Kuroyanagi                        }
76778f0d83550d70f043e1b9d35f56efa8c7572f44fKeisuke Kuroyanagi                    }
768f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                    flag |= flags[i] & TAB_MASK;
769f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                }
77078f0d83550d70f043e1b9d35f56efa8c7572f44fKeisuke Kuroyanagi                // Treat the last line and overflowed lines as a single line.
77178f0d83550d70f043e1b9d35f56efa8c7572f44fKeisuke Kuroyanagi                breaks[remainingLineCount - 1] = breaks[breakCount - 1];
772f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                lineWidths[remainingLineCount - 1] = width;
773f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                flags[remainingLineCount - 1] = flag;
774f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi
775f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi                breakCount = remainingLineCount;
776f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi            }
777f4a3f3a03c72b6a38dc2f3f965531dc2a12460dfKeisuke Kuroyanagi
778c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // here is the offset of the starting character of the line we are currently measuring
779c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int here = paraStart;
780d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne
781c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
782c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmCacheIndex = 0;
783c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int spanEndCacheIndex = 0;
784c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int breakIndex = 0;
785c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
786c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // retrieve end of span
787c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEnd = spanEndCache[spanEndCacheIndex++];
788c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
789c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // retrieve cached metrics, order matches above
790c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.top = fmCache[fmCacheIndex * 4 + 0];
791c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.bottom = fmCache[fmCacheIndex * 4 + 1];
792c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.ascent = fmCache[fmCacheIndex * 4 + 2];
793c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.descent = fmCache[fmCacheIndex * 4 + 3];
794c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCacheIndex++;
795c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
796c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.top < fmTop) {
797c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmTop = fm.top;
798c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
799c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.ascent < fmAscent) {
800c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmAscent = fm.ascent;
801c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
802c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.descent > fmDescent) {
803c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmDescent = fm.descent;
804c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
805c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.bottom > fmBottom) {
806c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmBottom = fm.bottom;
807c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
808cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne
809c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // skip breaks ending before current span range
810c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
811c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    breakIndex++;
812c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
81381541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne
814c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
815c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int endPos = paraStart + breaks[breakIndex];
816c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
817ce4155a204144ae5462f547f7738af24be5a1f77Raph Levien                    boolean moreChars = (endPos < bufEnd);
8184c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien
819c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    v = out(source, here, endPos,
820c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            fmAscent, fmDescent, fmTop, fmBottom,
821431e506b53e59b21a7d18bf46476b8c1528fabf1Roozbeh Pournader                            v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex],
822c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
823c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            chs, widths, paraStart, ellipsize, ellipsizedWidth,
8244c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien                            lineWidths[breakIndex], paint, moreChars);
825c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
826c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    if (endPos < spanEnd) {
827c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        // preserve metrics for current span
828c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmTop = fm.top;
829c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmBottom = fm.bottom;
830c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmAscent = fm.ascent;
831c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmDescent = fm.descent;
832c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    } else {
833c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmTop = fmBottom = fmAscent = fmDescent = 0;
8348059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
836c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    here = endPos;
837c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    breakIndex++;
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
839c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    if (mLineCount >= mMaximumVisibleLineCount) {
840c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        return;
841c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    }
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
845121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (paraEnd == bufEnd)
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8498059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) &&
850ad0b051b133baf92f199c96a8ac1e81b3393190cFabrice Di Meglio                mLineCount < mMaximumVisibleLineCount) {
851121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            // Log.e("text", "output last " + bufEnd);
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
85370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            measured.setPara(source, bufEnd, bufEnd, textDir, b);
854e631889e1ae7edc6a2fae495ba504f85820b6a4bFabrice Di Meglio
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            paint.getFontMetricsInt(fm);
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            v = out(source,
858121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    bufEnd, bufEnd, fm.ascent, fm.descent,
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fm.top, fm.bottom,
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    v,
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    spacingmult, spacingadd, null,
86226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien                    null, fm, 0,
863e631889e1ae7edc6a2fae495ba504f85820b6a4bFabrice Di Meglio                    needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
864d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    includepad, trackpad, null,
865d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    null, bufStart, ellipsize,
866d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    ellipsizedWidth, 0, paint, false);
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int out(CharSequence text, int start, int end,
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                      int above, int below, int top, int bottom, int v,
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                      float spacingmult, float spacingadd,
873121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                      LineHeightSpan[] chooseHt, int[] chooseHtv,
87426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien                      Paint.FontMetricsInt fm, int flags,
875d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean needMultiply, byte[] chdirs, int dir,
876d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean easy, int bufEnd, boolean includePad,
877d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean trackPad, char[] chs,
878d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      float[] widths, int widthStart, TextUtils.TruncateAt ellipsize,
879d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      float ellipsisWidth, float textWidth,
880d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      TextPaint paint, boolean moreChars) {
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int j = mLineCount;
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int off = j * mColumns;
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int want = off + mColumns + TOP;
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] lines = mLines;
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (want >= lines.length) {
887776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            Directions[] grow2 = ArrayUtils.newUnpaddedArray(
888776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                    Directions.class, GrowingArrayUtils.growSize(want));
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(mLineDirections, 0, grow2, 0,
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                             mLineDirections.length);
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLineDirections = grow2;
892776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski
893776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            int[] grow = new int[grow2.length];
894776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            System.arraycopy(lines, 0, grow, 0, lines.length);
895776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            mLines = grow;
896776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            lines = grow;
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
899121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        if (chooseHt != null) {
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.ascent = above;
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.descent = below;
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.top = top;
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.bottom = bottom;
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
905121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            for (int i = 0; i < chooseHt.length; i++) {
906121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (chooseHt[i] instanceof LineHeightSpan.WithDensity) {
907121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    ((LineHeightSpan.WithDensity) chooseHt[i]).
908121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        chooseHeight(text, start, end, chooseHtv[i], v, fm, paint);
909a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer
910a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                } else {
911121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm);
912a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                }
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            above = fm.ascent;
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            below = fm.descent;
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            top = fm.top;
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bottom = fm.bottom;
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
921d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean firstLine = (j == 0);
922d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
923d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd);
924d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
925d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (firstLine) {
926121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (trackPad) {
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mTopPadding = top - above;
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
930121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (includePad) {
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                above = top;
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
934d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
935d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        int extra;
936d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
937d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (lastLine) {
938121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (trackPad) {
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mBottomPadding = bottom - below;
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
942121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (includePad) {
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                below = bottom;
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
948d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (needMultiply && !lastLine) {
9491065758a0f8966a8597a61492112f7859a7050a4Doug Felt            double ex = (below - above) * (spacingmult - 1) + spacingadd;
9501065758a0f8966a8597a61492112f7859a7050a4Doug Felt            if (ex >= 0) {
951121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                extra = (int)(ex + EXTRA_ROUNDING);
9521065758a0f8966a8597a61492112f7859a7050a4Doug Felt            } else {
953121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                extra = -(int)(-ex + EXTRA_ROUNDING);
9541065758a0f8966a8597a61492112f7859a7050a4Doug Felt            }
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            extra = 0;
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + START] = start;
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + TOP] = v;
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + DESCENT] = below + extra;
9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        v += (below - above) + extra;
9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + mColumns + START] = end;
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + mColumns + TOP] = v;
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
96726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
96826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        // one bit for start field
96926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        lines[off + TAB] |= flags & TAB_MASK;
97026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        lines[off + HYPHEN] = flags;
9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        lines[off + DIR] |= dir << DIR_SHIFT;
9739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
9749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // easy means all chars < the first RTL, so no emoji, no nothing
9754e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        // XXX a run with no text or all spaces is easy but might be an empty
9769f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // RTL paragraph.  Make sure easy is false if this is the case.
9779f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (easy) {
9789f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            mLineDirections[j] = linedirs;
9799f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        } else {
980f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne            mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
981f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne                    start - widthStart, end - start);
9820a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        }
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
984aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio        if (ellipsize != null) {
985aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            // If there is only one line, then do any type of ellipsis except when it is MARQUEE
986aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            // if there are multiple lines, just allow END ellipsis on the last line
9878059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
988aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio
98934a126e51aaf22e32c7af808ec6b5a0c41ae3311Fabrice Di Meglio            boolean doEllipsis =
99034a126e51aaf22e32c7af808ec6b5a0c41ae3311Fabrice Di Meglio                        (((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
991aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                                ellipsize != TextUtils.TruncateAt.MARQUEE) ||
992aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
993aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                                ellipsize == TextUtils.TruncateAt.END);
994aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            if (doEllipsis) {
995aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                calculateEllipsis(start, end, widths, widthStart,
996aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        ellipsisWidth, ellipsize, j,
997aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        textWidth, paint, forceEllipsis);
998aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            }
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLineCount++;
10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return v;
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1005121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private void calculateEllipsis(int lineStart, int lineEnd,
1006121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                                   float[] widths, int widthStart,
10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                   float avail, TextUtils.TruncateAt where,
10088059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                                   int line, float textWidth, TextPaint paint,
10098059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                                   boolean forceEllipsis) {
10108059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if (textWidth <= avail && !forceEllipsis) {
10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Everything fits!
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLines[mColumns * line + ELLIPSIS_START] = 0;
10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLines[mColumns * line + ELLIPSIS_COUNT] = 0;
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1017cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio        float ellipsisWidth = paint.measureText(
10188d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio                (where == TextUtils.TruncateAt.END_SMALL) ?
1019d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller                        TextUtils.ELLIPSIS_TWO_DOTS : TextUtils.ELLIPSIS_NORMAL, 0, 1);
10208059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        int ellipsisStart = 0;
10218059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        int ellipsisCount = 0;
1022121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        int len = lineEnd - lineStart;
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10248059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        // We only support start ellipsis on a single line
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (where == TextUtils.TruncateAt.START) {
10268059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            if (mMaximumVisibleLineCount == 1) {
10278059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float sum = 0;
10288059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                int i;
10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1030ed2eea1bfc1fb57d3b34f2c1e6062b541737e73eKeisuke Kuroyanagi                for (i = len; i > 0; i--) {
10318059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[i - 1 + lineStart - widthStart];
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10338059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + sum + ellipsisWidth > avail) {
10348059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
10358059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
10368059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
10378059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    sum += w;
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10408059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisStart = 0;
10418059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = i;
10428059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            } else {
10438059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                if (Log.isLoggable(TAG, Log.WARN)) {
10448059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    Log.w(TAG, "Start Ellipsis only supported with one line");
10458059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                }
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1047cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio        } else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE ||
1048cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio                where == TextUtils.TruncateAt.END_SMALL) {
10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float sum = 0;
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i;
10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (i = 0; i < len; i++) {
1053121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                float w = widths[i + lineStart - widthStart];
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1055121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (w + sum + ellipsisWidth > avail) {
10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sum += w;
10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ellipsisStart = i;
10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ellipsisCount = len - i;
1064aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            if (forceEllipsis && ellipsisCount == 0 && len > 0) {
1065aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                ellipsisStart = len - 1;
10668059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = 1;
10678059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            }
10688059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        } else {
10698059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            // where = TextUtils.TruncateAt.MIDDLE We only support middle ellipsis on a single line
10708059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            if (mMaximumVisibleLineCount == 1) {
10718059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float lsum = 0, rsum = 0;
10728059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                int left = 0, right = len;
10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10748059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float ravail = (avail - ellipsisWidth) / 2;
10750e3c5e827235911d33312e431975533f046421e7Raph Levien                for (right = len; right > 0; right--) {
10768059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[right - 1 + lineStart - widthStart];
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10788059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + rsum > ravail) {
10798059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
10808059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
10818059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
10828059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    rsum += w;
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10858059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float lavail = avail - ellipsisWidth - rsum;
10868059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                for (left = 0; left < right; left++) {
10878059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[left + lineStart - widthStart];
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10898059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + lsum > lavail) {
10908059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
10918059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10938059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    lsum += w;
10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10968059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisStart = left;
10978059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = right - left;
10988059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            } else {
10998059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                if (Log.isLoggable(TAG, Log.WARN)) {
11008059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    Log.w(TAG, "Middle Ellipsis only supported with one line");
11018059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                }
11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart;
11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1109e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    // Override the base class so we can directly access our members,
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // rather than relying on member functions.
11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The logic mirrors that of Layout.getLineForVertical
11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // FIXME: It may be faster to do a linear search for layouts without many lines.
11136611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineForVertical(int vertical) {
11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int high = mLineCount;
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int low = -1;
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int guess;
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] lines = mLines;
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (high - low > 1) {
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            guess = (high + low) >> 1;
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (lines[mColumns * guess + TOP] > vertical){
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                high = guess;
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = guess;
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (low < 0) {
11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return low;
11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11346611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineCount() {
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLineCount;
11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11396611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineTop(int line) {
114107e6c237d3f19b98bbec237892a785fd3895368aRaph Levien        return mLines[mColumns * line + TOP];
11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11446611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineDescent(int line) {
114607e6c237d3f19b98bbec237892a785fd3895368aRaph Levien        return mLines[mColumns * line + DESCENT];
11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11496611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineStart(int line) {
11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + START] & START_MASK;
11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11546611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getParagraphDirection(int line) {
11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + DIR] >> DIR_SHIFT;
11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11596611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean getLineContainsTab(int line) {
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return (mLines[mColumns * line + TAB] & TAB_MASK) != 0;
11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11646611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final Directions getLineDirections(int line) {
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLineDirections[line];
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11696611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getTopPadding() {
11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mTopPadding;
11729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11746611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getBottomPadding() {
11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mBottomPadding;
11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
117926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    /**
118026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien     * @hide
118126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien     */
118226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    @Override
118326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    public int getHyphen(int line) {
118426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien        return mLines[mColumns * line + HYPHEN] & 0xff;
118526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    }
118626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien
11872ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien    /**
11882ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien     * @hide
11892ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien     */
11902ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien    @Override
11912ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien    public int getIndentAdjust(int line, Alignment align) {
11922ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien        if (align == Alignment.ALIGN_LEFT) {
11932ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            if (mLeftIndents == null) {
11942ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                return 0;
11952ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            } else {
11962ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                return mLeftIndents[Math.min(line, mLeftIndents.length - 1)];
11972ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            }
11982ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien        } else if (align == Alignment.ALIGN_RIGHT) {
11992ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            if (mRightIndents == null) {
12002ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                return 0;
12012ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            } else {
12022ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                return -mRightIndents[Math.min(line, mRightIndents.length - 1)];
12032ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            }
12042ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien        } else if (align == Alignment.ALIGN_CENTER) {
12052ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            int left = 0;
12062ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            if (mLeftIndents != null) {
12072ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                left = mLeftIndents[Math.min(line, mLeftIndents.length - 1)];
12082ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            }
12092ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            int right = 0;
12102ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            if (mRightIndents != null) {
12112ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien                right = mRightIndents[Math.min(line, mRightIndents.length - 1)];
12122ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            }
12132ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            return (left - right) >> 1;
12142ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien        } else {
12152ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien            throw new AssertionError("unhandled alignment " + align);
12162ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien        }
12172ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien    }
12182ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien
12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsisCount(int line) {
12219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mColumns < COLUMNS_ELLIPSIZE) {
12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + ELLIPSIS_COUNT];
12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsisStart(int line) {
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mColumns < COLUMNS_ELLIPSIZE) {
12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + ELLIPSIS_START];
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsizedWidth() {
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mEllipsizedWidth;
12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
124270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native long nNewBuilder();
124370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nFreeBuilder(long nativePtr);
124470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nFinishBuilder(long nativePtr);
124526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien
1246091dba2de1e5fa7d4db9f8ccbf1f86e5825d0f52Raph Levien    /* package */ static native long nLoadHyphenator(ByteBuffer buf, int offset);
124726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien
124826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
124970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
1250e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien    private static native void nSetIndents(long nativePtr, int[] indents);
1251e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien
1252c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien    // Set up paragraph text and settings; done as one big method to minimize jni crossings
1253c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien    private static native void nSetupParagraph(long nativePtr, char[] text, int length,
1254c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien            float firstWidth, int firstWidthLineCount, float restWidth,
125595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader            int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency);
125670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
125770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native float nAddStyleRun(long nativePtr, long nativePaint,
125870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            long nativeTypeface, int start, int end, boolean isRtl);
125970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
126070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nAddMeasuredRun(long nativePtr,
126170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien            int start, int end, float[] widths);
126270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
126370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nAddReplacementRun(long nativePtr, int start, int end, float width);
126470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
126570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien    private static native void nGetWidths(long nativePtr, float[] widths);
126670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien
1267c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // populates LineBreaks and returns the number of breaks found
1268c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    //
1269c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // the arrays inside the LineBreaks objects are passed in as well
1270c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // to reduce the number of JNI calls in the common case where the
1271c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // arrays do not have to be resized
1272c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien    private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
127326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien            int[] recycleBreaks, float[] recycleWidths, int[] recycleFlags, int recycleLength);
127488b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mLineCount;
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mTopPadding, mBottomPadding;
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mColumns;
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mEllipsizedWidth;
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
128026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int COLUMNS_NORMAL = 4;
128126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int COLUMNS_ELLIPSIZE = 6;
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int START = 0;
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DIR = START;
12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAB = START;
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TOP = 1;
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DESCENT = 2;
128726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int HYPHEN = 3;
128826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int ELLIPSIS_START = 4;
128926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien    private static final int ELLIPSIS_COUNT = 5;
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int[] mLines;
12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Directions[] mLineDirections;
12938059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    private int mMaximumVisibleLineCount = Integer.MAX_VALUE;
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int START_MASK = 0x1FFFFFFF;
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DIR_SHIFT  = 30;
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAB_MASK   = 0x20000000;
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1299c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private static final int TAB_INCREMENT = 20; // same as Layout, but that's private
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1301121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private static final char CHAR_NEW_LINE = '\n';
1302121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio
1303121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private static final double EXTRA_ROUNDING = 0.5;
1304cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio
1305c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // This is used to return three arrays from a single JNI call when
1306c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // performing line breaking
13077053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    /*package*/ static class LineBreaks {
1308c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        private static final int INITIAL_SIZE = 16;
1309c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        public int[] breaks = new int[INITIAL_SIZE];
1310c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        public float[] widths = new float[INITIAL_SIZE];
1311112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader        public int[] flags = new int[INITIAL_SIZE]; // hasTab
1312c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // breaks, widths, and flags should all have the same length
1313c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    }
1314c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
13152ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien    private int[] mLeftIndents;
13162ea5290ffb9efe0a7c187fb1177ef8ecd089803cRaph Levien    private int[] mRightIndents;
13179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1318