1e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/*
2e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Copyright (C) 2010 The Android Open Source Project
3e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
4e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Licensed under the Apache License, Version 2.0 (the "License");
5e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * you may not use this file except in compliance with the License.
6e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * You may obtain a copy of the License at
7e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
8e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *      http://www.apache.org/licenses/LICENSE-2.0
9e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
10e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Unless required by applicable law or agreed to in writing, software
11e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * distributed under the License is distributed on an "AS IS" BASIS,
12e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * See the License for the specific language governing permissions and
14e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * limitations under the License.
15e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */
16e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
17e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltpackage android.text;
18e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
19538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournaderimport android.annotation.NonNull;
20538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournaderimport android.annotation.Nullable;
21e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Canvas;
22e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint;
23e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint.FontMetricsInt;
24e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.Layout.Directions;
25c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.Layout.TabStops;
26e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.CharacterStyle;
27e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.MetricAffectingSpan;
28e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.ReplacementSpan;
29e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.util.Log;
30e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
313630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournaderimport com.android.internal.annotations.VisibleForTesting;
32dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunneimport com.android.internal.util.ArrayUtils;
33dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
34538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournaderimport java.util.ArrayList;
35538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
36e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/**
37e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Represents a line of styled text, for measuring in visual order and
38e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * for rendering.
39e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
40e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Get a new instance using obtain(), and when finished with it, return it
41e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * to the pool using recycle().
42e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
43e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Call set to prepare the instance for use, then either draw, measure,
44e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * metrics, or caretToLeftRightOf.
45e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
46e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @hide
47e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */
483630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
493630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournaderpublic class TextLine {
50bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private static final boolean DEBUG = false;
51bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy
52e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private TextPaint mPaint;
53e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private CharSequence mText;
54e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mStart;
55e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mLen;
56e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mDir;
57e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Directions mDirections;
58e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mHasTabs;
59c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private TabStops mTabs;
60e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private char[] mChars;
61e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mCharsValid;
62e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Spanned mSpanned;
63beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka    private PrecomputedText mComputed;
6409da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka
6509da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    // Additional width of whitespace for justification. This value is per whitespace, thus
6609da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    // the line width will increase by mAddedWidth x (number of stretchable whitespaces).
6709da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    private float mAddedWidth;
68538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
69345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne    private final TextPaint mWorkPaint = new TextPaint();
70538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader    private final TextPaint mActivePaint = new TextPaint();
71c1f44830809f0a8526855f13822702ea756214faGilles Debunne    private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet =
72c1f44830809f0a8526855f13822702ea756214faGilles Debunne            new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class);
73c1f44830809f0a8526855f13822702ea756214faGilles Debunne    private final SpanSet<CharacterStyle> mCharacterStyleSpanSet =
74c1f44830809f0a8526855f13822702ea756214faGilles Debunne            new SpanSet<CharacterStyle>(CharacterStyle.class);
75c1f44830809f0a8526855f13822702ea756214faGilles Debunne    private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet =
76c1f44830809f0a8526855f13822702ea756214faGilles Debunne            new SpanSet<ReplacementSpan>(ReplacementSpan.class);
77e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
781378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader    private final DecorationInfo mDecorationInfo = new DecorationInfo();
79a273a70ecbb0d1aaf9aeceec7aa91591290a4871Siyamed Sinir    private final ArrayList<DecorationInfo> mDecorations = new ArrayList<>();
80538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
81bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private static final TextLine[] sCached = new TextLine[3];
82e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
83e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
84e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns a new TextLine from the shared pool.
85e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
86e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return an uninitialized TextLine
87e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
883630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
893630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader    public static TextLine obtain() {
90e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl;
91bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        synchronized (sCached) {
92bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            for (int i = sCached.length; --i >= 0;) {
93bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                if (sCached[i] != null) {
94bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    tl = sCached[i];
95bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    sCached[i] = null;
96e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return tl;
97e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
98e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
99e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl = new TextLine();
101bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        if (DEBUG) {
102bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            Log.v("TLINE", "new: " + tl);
103bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        }
104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return tl;
105e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
106e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
107e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
108e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Puts a TextLine back into the shared pool. Do not use this TextLine once
109e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * it has been returned.
110e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param tl the textLine
111e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return null, as a convenience from clearing references to the provided
112e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * TextLine
113e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
1143630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
1153630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader    public static TextLine recycle(TextLine tl) {
116e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mText = null;
117e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mPaint = null;
118e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mDirections = null;
119893d6fe48d37f71e683f722457bea646994a10bfSvet Ganov        tl.mSpanned = null;
120893d6fe48d37f71e683f722457bea646994a10bfSvet Ganov        tl.mTabs = null;
121893d6fe48d37f71e683f722457bea646994a10bfSvet Ganov        tl.mChars = null;
122beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka        tl.mComputed = null;
123c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne
124c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne        tl.mMetricAffectingSpanSpanSet.recycle();
125c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne        tl.mCharacterStyleSpanSet.recycle();
126c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne        tl.mReplacementSpanSpanSet.recycle();
127c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne
128bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        synchronized(sCached) {
129bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            for (int i = 0; i < sCached.length; ++i) {
130bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                if (sCached[i] == null) {
131bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    sCached[i] = tl;
132f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne                    break;
133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
134e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
135e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
136e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return null;
137e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
138e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
139e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Initializes a TextLine and prepares it for use.
141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
142e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param paint the base paint for the line
143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param text the text, can be Styled
144e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the line relative to the text
145e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the line relative to the text
146e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param dir the paragraph direction of this line
147e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param directions the directions information of this line
148112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader     * @param hasTabs true if the line might contain tabs
149c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param tabStops the tabStops. Can be null.
150e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
1513630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
1524e90fa262d57c1c1ee72166e2ddfce391696ca24Seigo Nonaka    public void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
1534e90fa262d57c1c1ee72166e2ddfce391696ca24Seigo Nonaka            Directions directions, boolean hasTabs, TabStops tabStops) {
154e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mPaint = paint;
155e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mText = text;
156e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mStart = start;
157e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mLen = limit - start;
158e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDir = dir;
159e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDirections = directions;
1608059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if (mDirections == null) {
1618059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            throw new IllegalArgumentException("Directions cannot be null");
1628059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        }
163e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mHasTabs = hasTabs;
164e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mSpanned = null;
165e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
166e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean hasReplacement = false;
167e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (text instanceof Spanned) {
168e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            mSpanned = (Spanned) text;
169c1f44830809f0a8526855f13822702ea756214faGilles Debunne            mReplacementSpanSpanSet.init(mSpanned, start, limit);
170c1f44830809f0a8526855f13822702ea756214faGilles Debunne            hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
171e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
172e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
173beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka        mComputed = null;
174beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka        if (text instanceof PrecomputedText) {
17553145635e4fd724208e01db1ef6187a2212d6090Seigo Nonaka            // Here, no need to check line break strategy or hyphenation frequency since there is no
17653145635e4fd724208e01db1ef6187a2212d6090Seigo Nonaka            // line break concept here.
177beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka            mComputed = (PrecomputedText) text;
17853145635e4fd724208e01db1ef6187a2212d6090Seigo Nonaka            if (!mComputed.getParams().getTextPaint().equalsForTextMeasurement(paint)) {
17953145635e4fd724208e01db1ef6187a2212d6090Seigo Nonaka                mComputed = null;
18053145635e4fd724208e01db1ef6187a2212d6090Seigo Nonaka            }
1814e90fa262d57c1c1ee72166e2ddfce391696ca24Seigo Nonaka        }
1824e90fa262d57c1c1ee72166e2ddfce391696ca24Seigo Nonaka
1831e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
185e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mChars == null || mChars.length < mLen) {
187776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                mChars = ArrayUtils.newUnpaddedCharArray(mLen);
188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextUtils.getChars(text, start, limit, mChars, 0);
1900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (hasReplacement) {
1910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Handle these all at once so we don't have to do it as we go.
1920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Replace the first character of each replacement run with the
1930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // object-replacement character and the remainder with zero width
1940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // non-break space aka BOM.  Cursor movement code skips these
1950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // zero-width characters.
1960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                char[] chars = mChars;
1970c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int i = start, inext; i < limit; i = inext) {
198c1f44830809f0a8526855f13822702ea756214faGilles Debunne                    inext = mReplacementSpanSpanSet.getNextTransition(i, limit);
199c1f44830809f0a8526855f13822702ea756214faGilles Debunne                    if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) {
2001e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                        // transition into a span
2010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        chars[i - start] = '\ufffc';
2020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        for (int j = i - start + 1, e = inext - start; j < e; ++j) {
2030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip
2040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        }
2050c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
2060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
2070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
209c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        mTabs = tabStops;
21009da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        mAddedWidth = 0;
21109da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    }
21209da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka
21309da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    /**
21409da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka     * Justify the line to the given width.
21509da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka     */
2163630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
2173630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader    public void justify(float justifyWidth) {
21809da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        int end = mLen;
21909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        while (end > 0 && isLineEndSpace(mText.charAt(mStart + end - 1))) {
22009da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka            end--;
22109da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        }
22209da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        final int spaces = countStretchableSpaces(0, end);
22309da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        if (spaces == 0) {
22409da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka            // There are no stretchable spaces, so we can't help the justification by adding any
22509da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka            // width.
22609da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka            return;
22709da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        }
22809da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        final float width = Math.abs(measure(end, false, null));
22909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        mAddedWidth = (justifyWidth - width) / spaces;
230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Renders the TextLine.
234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to render on
236e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the leading margin position
237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void draw(Canvas c, float x, int top, int y, int bottom) {
242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
244bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                drawRun(c, 0, mLen, false, x, top, y, bottom, false);
245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
248bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                drawRun(c, 0, mLen, true, x, top, y, bottom, false);
249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lastRunIndex = runs.length - 2;
257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = mChars[j];
270112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader                    if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(mChars, j);
272112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader                        if (codept > 0xFFFF) {
273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
278e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
279112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader                if (j == runLimit || codept == '\t') {
280bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom,
281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            i != lastRunIndex || j != mLen);
282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
283e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
285e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
290e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns metrics information for the entire line.
294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives font metrics information, can be null
296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the line
297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
2983630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
2993630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader    public float metrics(FontMetricsInt fmi) {
300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return measure(mLen, false, fmi);
301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns information about a position on the line.
305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the line-relative character offset, between 0 and the
307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * line length, inclusive
308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param trailing true to measure the trailing edge of the character
309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * before offset, false to measure the leading edge of the character
310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * at offset.
311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character, can be null.
313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed offset from the leading margin to the requested
314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character edge.
315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float measure(int offset, boolean trailing, FontMetricsInt fmi) {
317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int target = trailing ? offset - 1 : offset;
318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (target < 0) {
319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return 0;
320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
326bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                return measureRun(0, offset, mLen, false, fmi);
327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
328e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
329bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                return measureRun(0, offset, mLen, true, fmi);
330e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
332e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
333e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        char[] chars = mChars;
334e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
335e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
337e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
338e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
343e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = chars[j];
348112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader                    if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(chars, j);
350112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader                        if (codept > 0xFFFF) {
351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
357112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader                if (j == runLimit || codept == '\t') {
358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean inSegment = target >= segstart && target < j;
359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
360e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment && advance) {
362a273a70ecbb0d1aaf9aeceec7aa91591290a4871Siyamed Sinir                        return h + measureRun(segstart, offset, j, runIsRtl, fmi);
363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
365bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    float w = measureRun(segstart, j, j, runIsRtl, fmi);
366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    h += advance ? w : -w;
367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
368e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment) {
369a273a70ecbb0d1aaf9aeceec7aa91591290a4871Siyamed Sinir                        return h + measureRun(segstart, offset, j, runIsRtl, null);
370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
373e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (offset == j) {
374e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
376e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (target == j) {
378e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
379e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
380e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
381e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
383e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
384e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
385e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
386e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
387e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return h;
388e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
389e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
390e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
391138b106402d86653eaf296a02708737a6e360b58Mihai Popa     * @see #measure(int, boolean, FontMetricsInt)
392138b106402d86653eaf296a02708737a6e360b58Mihai Popa     * @return The measure results for all possible offsets
393138b106402d86653eaf296a02708737a6e360b58Mihai Popa     */
394138b106402d86653eaf296a02708737a6e360b58Mihai Popa    float[] measureAllOffsets(boolean[] trailing, FontMetricsInt fmi) {
395138b106402d86653eaf296a02708737a6e360b58Mihai Popa        float[] measurement = new float[mLen + 1];
396138b106402d86653eaf296a02708737a6e360b58Mihai Popa
397138b106402d86653eaf296a02708737a6e360b58Mihai Popa        int[] target = new int[mLen + 1];
398138b106402d86653eaf296a02708737a6e360b58Mihai Popa        for (int offset = 0; offset < target.length; ++offset) {
399138b106402d86653eaf296a02708737a6e360b58Mihai Popa            target[offset] = trailing[offset] ? offset - 1 : offset;
400138b106402d86653eaf296a02708737a6e360b58Mihai Popa        }
401138b106402d86653eaf296a02708737a6e360b58Mihai Popa        if (target[0] < 0) {
402138b106402d86653eaf296a02708737a6e360b58Mihai Popa            measurement[0] = 0;
403138b106402d86653eaf296a02708737a6e360b58Mihai Popa        }
404138b106402d86653eaf296a02708737a6e360b58Mihai Popa
405138b106402d86653eaf296a02708737a6e360b58Mihai Popa        float h = 0;
406138b106402d86653eaf296a02708737a6e360b58Mihai Popa
407138b106402d86653eaf296a02708737a6e360b58Mihai Popa        if (!mHasTabs) {
408138b106402d86653eaf296a02708737a6e360b58Mihai Popa            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
409138b106402d86653eaf296a02708737a6e360b58Mihai Popa                for (int offset = 0; offset <= mLen; ++offset) {
410138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    measurement[offset] = measureRun(0, offset, mLen, false, fmi);
411138b106402d86653eaf296a02708737a6e360b58Mihai Popa                }
412138b106402d86653eaf296a02708737a6e360b58Mihai Popa                return measurement;
413138b106402d86653eaf296a02708737a6e360b58Mihai Popa            }
414138b106402d86653eaf296a02708737a6e360b58Mihai Popa            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
415138b106402d86653eaf296a02708737a6e360b58Mihai Popa                for (int offset = 0; offset <= mLen; ++offset) {
416138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    measurement[offset] = measureRun(0, offset, mLen, true, fmi);
417138b106402d86653eaf296a02708737a6e360b58Mihai Popa                }
418138b106402d86653eaf296a02708737a6e360b58Mihai Popa                return measurement;
419138b106402d86653eaf296a02708737a6e360b58Mihai Popa            }
420138b106402d86653eaf296a02708737a6e360b58Mihai Popa        }
421138b106402d86653eaf296a02708737a6e360b58Mihai Popa
422138b106402d86653eaf296a02708737a6e360b58Mihai Popa        char[] chars = mChars;
423138b106402d86653eaf296a02708737a6e360b58Mihai Popa        int[] runs = mDirections.mDirections;
424138b106402d86653eaf296a02708737a6e360b58Mihai Popa        for (int i = 0; i < runs.length; i += 2) {
425138b106402d86653eaf296a02708737a6e360b58Mihai Popa            int runStart = runs[i];
426138b106402d86653eaf296a02708737a6e360b58Mihai Popa            int runLimit = runStart + (runs[i + 1] & Layout.RUN_LENGTH_MASK);
427138b106402d86653eaf296a02708737a6e360b58Mihai Popa            if (runLimit > mLen) {
428138b106402d86653eaf296a02708737a6e360b58Mihai Popa                runLimit = mLen;
429138b106402d86653eaf296a02708737a6e360b58Mihai Popa            }
430138b106402d86653eaf296a02708737a6e360b58Mihai Popa            boolean runIsRtl = (runs[i + 1] & Layout.RUN_RTL_FLAG) != 0;
431138b106402d86653eaf296a02708737a6e360b58Mihai Popa
432138b106402d86653eaf296a02708737a6e360b58Mihai Popa            int segstart = runStart;
433138b106402d86653eaf296a02708737a6e360b58Mihai Popa            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; ++j) {
434138b106402d86653eaf296a02708737a6e360b58Mihai Popa                int codept = 0;
435138b106402d86653eaf296a02708737a6e360b58Mihai Popa                if (mHasTabs && j < runLimit) {
436138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    codept = chars[j];
437138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
438138b106402d86653eaf296a02708737a6e360b58Mihai Popa                        codept = Character.codePointAt(chars, j);
439138b106402d86653eaf296a02708737a6e360b58Mihai Popa                        if (codept > 0xFFFF) {
440138b106402d86653eaf296a02708737a6e360b58Mihai Popa                            ++j;
441138b106402d86653eaf296a02708737a6e360b58Mihai Popa                            continue;
442138b106402d86653eaf296a02708737a6e360b58Mihai Popa                        }
443138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    }
444138b106402d86653eaf296a02708737a6e360b58Mihai Popa                }
445138b106402d86653eaf296a02708737a6e360b58Mihai Popa
446138b106402d86653eaf296a02708737a6e360b58Mihai Popa                if (j == runLimit || codept == '\t') {
447138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    float oldh = h;
448138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
449138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    float w = measureRun(segstart, j, j, runIsRtl, fmi);
450138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    h += advance ? w : -w;
451138b106402d86653eaf296a02708737a6e360b58Mihai Popa
452138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    float baseh = advance ? oldh : h;
453138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    FontMetricsInt crtfmi = advance ? fmi : null;
454138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    for (int offset = segstart; offset <= j && offset <= mLen; ++offset) {
455138b106402d86653eaf296a02708737a6e360b58Mihai Popa                        if (target[offset] >= segstart && target[offset] < j) {
456138b106402d86653eaf296a02708737a6e360b58Mihai Popa                            measurement[offset] =
457138b106402d86653eaf296a02708737a6e360b58Mihai Popa                                    baseh + measureRun(segstart, offset, j, runIsRtl, crtfmi);
458138b106402d86653eaf296a02708737a6e360b58Mihai Popa                        }
459138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    }
460138b106402d86653eaf296a02708737a6e360b58Mihai Popa
461138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    if (codept == '\t') {
462138b106402d86653eaf296a02708737a6e360b58Mihai Popa                        if (target[j] == j) {
463138b106402d86653eaf296a02708737a6e360b58Mihai Popa                            measurement[j] = h;
464138b106402d86653eaf296a02708737a6e360b58Mihai Popa                        }
465138b106402d86653eaf296a02708737a6e360b58Mihai Popa                        h = mDir * nextTab(h * mDir);
466138b106402d86653eaf296a02708737a6e360b58Mihai Popa                        if (target[j + 1] == j) {
467138b106402d86653eaf296a02708737a6e360b58Mihai Popa                            measurement[j + 1] =  h;
468138b106402d86653eaf296a02708737a6e360b58Mihai Popa                        }
469138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    }
470138b106402d86653eaf296a02708737a6e360b58Mihai Popa
471138b106402d86653eaf296a02708737a6e360b58Mihai Popa                    segstart = j + 1;
472138b106402d86653eaf296a02708737a6e360b58Mihai Popa                }
473138b106402d86653eaf296a02708737a6e360b58Mihai Popa            }
474138b106402d86653eaf296a02708737a6e360b58Mihai Popa        }
475138b106402d86653eaf296a02708737a6e360b58Mihai Popa        if (target[mLen] == mLen) {
476138b106402d86653eaf296a02708737a6e360b58Mihai Popa            measurement[mLen] = h;
477138b106402d86653eaf296a02708737a6e360b58Mihai Popa        }
478138b106402d86653eaf296a02708737a6e360b58Mihai Popa
479138b106402d86653eaf296a02708737a6e360b58Mihai Popa        return measurement;
480138b106402d86653eaf296a02708737a6e360b58Mihai Popa    }
481138b106402d86653eaf296a02708737a6e360b58Mihai Popa
482138b106402d86653eaf296a02708737a6e360b58Mihai Popa    /**
483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Draws a unidirectional (but possibly multi-styled) run of text.
484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
485bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to draw on
487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start
488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit
489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the position of the run that is closest to the leading margin
491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width value is required.
495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run, based on the paragraph direction.
496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Only valid if needWidth is true.
497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
498bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float drawRun(Canvas c, int start,
499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, float x, int top, int y, int bottom,
500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean needWidth) {
501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
503bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            float w = -measureRun(start, limit, limit, runIsRtl, null);
504bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            handleRun(start, limit, limit, runIsRtl, c, x + w, top,
5050c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    y, bottom, null, false);
506e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return w;
507e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
509bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        return handleRun(start, limit, limit, runIsRtl, c, x, top,
5100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                y, bottom, null, needWidth);
511e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
512e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
514e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Measures a unidirectional (but possibly multi-styled) run of text.
515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
516bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset to measure to, between start and limit inclusive
519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit of the run
520e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
521e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * run, can be null.
523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width from the start of the run to the leading edge
524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the character at offset, based on the run (not paragraph) direction
525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
526bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float measureRun(int start, int offset, int limit, boolean runIsRtl,
527bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            FontMetricsInt fmi) {
528bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true);
529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Walk the cursor through this line, skipping conjuncts and
533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * zero-width characters.
534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
535e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * <p>This function cannot properly walk the cursor off the ends of the line
536e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * since it does not know about any shaping on the previous/following line
537e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * that might affect the cursor position. Callers must either avoid these
538e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * situations or handle the result specially.
539e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param cursor the starting position of the cursor, between 0 and the
541e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * length of the line, inclusive
542e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param toLeft true if the caret is moving to the left.
543e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset.  If it is less than 0 or greater than the length
544e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the line, the previous/following line should be examined to get the
545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * actual offset.
546e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
547e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    int getOffsetToLeftRightOf(int cursor, boolean toLeft) {
548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 1) The caret marks the leading edge of a character. The character
549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // logically before it might be on a different level, and the active caret
550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // position is on the character at the lower level. If that character
551e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // was the previous character, the caret is on its trailing edge.
552e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 2) Take this character/edge and move it in the indicated direction.
553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // This gives you a new character and a new edge.
554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 3) This position is between two visually adjacent characters.  One of
555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // these might be at a lower level.  The active position is on the
556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // character at the lower level.
557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 4) If the active position is on the trailing edge of the character,
558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // the new caret position is the following logical character, else it
559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // is the character.
560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineStart = 0;
562e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineEnd = mLen;
563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean paraIsRtl = mDir == -1;
564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1;
567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean trailing = false;
568e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (cursor == lineStart) {
570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = -2;
571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else if (cursor == lineEnd) {
572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = runs.length;
573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // First, get information about the run containing the character with
575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the active caret.
576e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          for (runIndex = 0; runIndex < runs.length; runIndex += 2) {
577e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runStart = lineStart + runs[runIndex];
578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (cursor >= runStart) {
579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK);
580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (runLimit > lineEnd) {
581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  runLimit = lineEnd;
582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor < runLimit) {
584e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
585e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    Layout.RUN_LEVEL_MASK;
586e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (cursor == runStart) {
587e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // The caret is on a run boundary, see if we should
588e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // use the position on the trailing edge of the previous
589e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // logical character instead.
590e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit;
591e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int pos = cursor - 1;
592e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) {
593e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    prevRunStart = lineStart + runs[prevRunIndex];
594e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (pos >= prevRunStart) {
595e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      prevRunLimit = prevRunStart +
596e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK);
597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (prevRunLimit > lineEnd) {
598e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          prevRunLimit = lineEnd;
599e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
600e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (pos < prevRunLimit) {
601e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT)
602e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            & Layout.RUN_LEVEL_MASK;
603e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (prevRunLevel < runLevel) {
604e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          // Start from logically previous character.
605e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runIndex = prevRunIndex;
606e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLevel = prevRunLevel;
607e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runStart = prevRunStart;
608e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLimit = prevRunLimit;
609e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          trailing = true;
610e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          break;
611e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
612e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
613e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
614e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
615e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
616e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
617e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
618e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
619e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
620e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
621e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // caret might be == lineEnd.  This is generally a space or paragraph
622e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // separator and has an associated run, but might be the end of
623e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // text, in which case it doesn't.  If that happens, we ran off the
624e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // end of the run list, and runIndex == runs.length.  In this case,
625e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we are at a run boundary so we skip the below test.
626e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (runIndex != runs.length) {
627e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean runIsRtl = (runLevel & 0x1) != 0;
628e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean advance = toLeft == runIsRtl;
629e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor != (advance ? runLimit : runStart) || advance != trailing) {
630e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // Moving within or into the run, so we can move logically.
6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                  newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit,
6320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                          runIsRtl, cursor, advance);
633e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // If the new position is internal to the run, we're at the strong
634e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // position already so we're finished.
635e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  if (newCaret != (advance ? runLimit : runStart)) {
636e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      return newCaret;
637e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
638e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
639e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
640e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
641e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
642e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // If newCaret is -1, we're starting at a run boundary and crossing
643e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // into another run. Otherwise we've arrived at a run boundary, and
644e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to figure out which character to attach to.  Note we might
645e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to run this twice, if we cross a run boundary and end up at
646e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // another run boundary.
647e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        while (true) {
648e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          boolean advance = toLeft == paraIsRtl;
649e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          int otherRunIndex = runIndex + (advance ? 2 : -2);
650e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (otherRunIndex >= 0 && otherRunIndex < runs.length) {
651e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunStart = lineStart + runs[otherRunIndex];
652e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLimit = otherRunStart +
653e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK);
654e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLimit > lineEnd) {
655e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                otherRunLimit = lineEnd;
656e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
657e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
658e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Layout.RUN_LEVEL_MASK;
659e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean otherRunIsRtl = (otherRunLevel & 1) != 0;
660e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
661e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            advance = toLeft == otherRunIsRtl;
662e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (newCaret == -1) {
6630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart,
6640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        otherRunLimit, otherRunIsRtl,
665e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        advance ? otherRunStart : otherRunLimit, advance);
666e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (newCaret == (advance ? otherRunLimit : otherRunStart)) {
667e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // Crossed and ended up at a new boundary,
668e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // repeat a second and final time.
669e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runIndex = otherRunIndex;
670e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runLevel = otherRunLevel;
671e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    continue;
672e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
673e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
674e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
675e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
676e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            // The new caret is at a boundary.
677e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLevel < runLevel) {
678e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // The strong character is in the other run.
679e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? otherRunStart : otherRunLimit;
680e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
681e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            break;
682e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
683e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
684e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret == -1) {
685e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // We're walking off the end of the line.  The paragraph
686e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // level is always equal to or lower than any internal level, so
687e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // the boundaries get the strong caret.
6880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt              newCaret = advance ? mLen + 1 : -1;
689e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              break;
690e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
691e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
692e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Else we've arrived at the end of the line.  That's a strong position.
693e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // We might have arrived here by crossing over a run with no internal
694e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // breaks and dropping out of the above loop before advancing one final
695e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // time, so reset the caret.
696e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Note, we use '<=' below to handle a situation where the only run
697e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // on the line is a counter-directional run.  If we're not advancing,
698e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we can end up at the 'lineEnd' position but the caret we want is at
699e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the lineStart.
700e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret <= lineEnd) {
701e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? lineEnd : lineStart;
702e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
703e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          break;
704e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
705e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
706e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return newCaret;
707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
708e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
709e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
710e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next valid offset within this directional run, skipping
711e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * conjuncts and zero-width characters.  This should not be called to walk
712cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunne     * off the end of the line, since the returned values might not be valid
7130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * on neighboring lines.  If the returned offset is less than zero or
7140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * greater than the line length, the offset should be recomputed on the
7150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * preceding or following line, respectively.
716e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
717e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
7180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runStart the start of the run
7190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runLimit the limit of the run
7200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runIsRtl true if the run is right-to-left
721e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset
722e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param after true if the new offset should logically follow the provided
723e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * offset
724e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset
725e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
7260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit,
7270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean runIsRtl, int offset, boolean after) {
728e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (runIndex < 0 || offset == (after ? mLen : 0)) {
7300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // Walking off end of line.  Since we don't know
7310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // what cursor positions are available on other lines, we can't
7320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // return accurate values.  These are a guess.
733e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (after) {
7340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart;
7350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
7360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart;
7370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
7380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
7390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        TextPaint wp = mWorkPaint;
7400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        wp.set(mPaint);
74109da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        wp.setWordSpacing(mAddedWidth);
7420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
7430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanStart = runStart;
7440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanLimit;
7450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mSpanned == null) {
7460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            spanLimit = runLimit;
7470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
7480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int target = after ? offset + 1 : offset;
7490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int limit = mStart + runLimit;
7500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            while (true) {
7510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit,
7520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        MetricAffectingSpan.class) - mStart;
7530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (spanLimit >= target) {
7540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    break;
755e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
7560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanStart = spanLimit;
7570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
7580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
7590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart,
7600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    mStart + spanLimit, MetricAffectingSpan.class);
7611e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
7620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
7630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (spans.length > 0) {
7640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ReplacementSpan replacement = null;
7650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = 0; j < spans.length; j++) {
7660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    MetricAffectingSpan span = spans[j];
7670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    if (span instanceof ReplacementSpan) {
7680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        replacement = (ReplacementSpan)span;
7690c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    } else {
7700c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateMeasureState(wp);
7710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
7720c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
7730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
7740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (replacement != null) {
7750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // If we have a replacement span, we're moving either to
7760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // the start or end of this span.
7770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    return after ? spanLimit : spanStart;
778e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
779e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
780e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
781e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
782051910b9f998030dacb8a0722588cc715813fde1Raph Levien        int dir = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
7830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
7840c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mCharsValid) {
7850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
786051910b9f998030dacb8a0722588cc715813fde1Raph Levien                    dir, offset, cursorOpt);
7870c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
7880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mText, mStart + spanStart,
789051910b9f998030dacb8a0722588cc715813fde1Raph Levien                    mStart + spanLimit, dir, mStart + offset, cursorOpt) - mStart;
790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
791e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
792e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
793e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
7940bb000931bb841e75903d655552d1626ae158707Gilles Debunne     * @param wp
7950bb000931bb841e75903d655552d1626ae158707Gilles Debunne     */
7960bb000931bb841e75903d655552d1626ae158707Gilles Debunne    private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) {
7970bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousTop     = fmi.top;
7980bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousAscent  = fmi.ascent;
7990bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousDescent = fmi.descent;
8000bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousBottom  = fmi.bottom;
8010bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousLeading = fmi.leading;
8020bb000931bb841e75903d655552d1626ae158707Gilles Debunne
8030bb000931bb841e75903d655552d1626ae158707Gilles Debunne        wp.getFontMetricsInt(fmi);
8040bb000931bb841e75903d655552d1626ae158707Gilles Debunne
8058a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio        updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,
8068a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousLeading);
8078a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio    }
8088a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
8098a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio    static void updateMetrics(FontMetricsInt fmi, int previousTop, int previousAscent,
8108a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousDescent, int previousBottom, int previousLeading) {
8110bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.top     = Math.min(fmi.top,     previousTop);
8120bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.ascent  = Math.min(fmi.ascent,  previousAscent);
8130bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.descent = Math.max(fmi.descent, previousDescent);
8140bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.bottom  = Math.max(fmi.bottom,  previousBottom);
8150bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.leading = Math.max(fmi.leading, previousLeading);
8160bb000931bb841e75903d655552d1626ae158707Gilles Debunne    }
8170bb000931bb841e75903d655552d1626ae158707Gilles Debunne
8181378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader    private static void drawStroke(TextPaint wp, Canvas c, int color, float position,
8191378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader            float thickness, float xleft, float xright, float baseline) {
8201378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader        final float strokeTop = baseline + wp.baselineShift + position;
821538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
822538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        final int previousColor = wp.getColor();
823538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        final Paint.Style previousStyle = wp.getStyle();
824538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        final boolean previousAntiAlias = wp.isAntiAlias();
825538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
826538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        wp.setStyle(Paint.Style.FILL);
827538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        wp.setAntiAlias(true);
828538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
829538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        wp.setColor(color);
8301378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader        c.drawRect(xleft, strokeTop, xright, strokeTop + thickness, wp);
831538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
832538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        wp.setStyle(previousStyle);
833538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        wp.setColor(previousColor);
834538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        wp.setAntiAlias(previousAntiAlias);
835538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader    }
836538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
837538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader    private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd,
838538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            boolean runIsRtl, int offset) {
839538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        if (mCharsValid) {
840538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset);
841538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        } else {
842538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            final int delta = mStart;
843beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka            if (mComputed == null) {
844783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                // TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text.
845783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                return wp.getRunAdvance(mText, delta + start, delta + end,
846783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                        delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
847783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            } else {
848beafa1f9d2845ee9b5ca352087de03ed0afe7db7Seigo Nonaka                return mComputed.getWidth(start + delta, end + delta);
849783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            }
850538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        }
851538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader    }
852538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
8530bb000931bb841e75903d655552d1626ae158707Gilles Debunne    /**
854e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering text.  The text must
855112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader     * not include a tab.
856e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
857e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the working paint
858e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the text
8590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the text
860e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
861e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if rendering is not needed
862e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the run closest to the leading margin
863e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
864e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
865e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
866e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
867e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the run is needed
868909c7bca570b6f50650d0872e2037389b29252e3Raph Levien     * @param offset the offset for the purpose of measuring
8691378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader     * @param decorations the list of locations and paremeters for drawing decorations
870e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
871e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
872e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
8730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private float handleText(TextPaint wp, int start, int end,
8740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl,
8750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            Canvas c, float x, int top, int y, int bottom,
876538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            FontMetricsInt fmi, boolean needWidth, int offset,
8771378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader            @Nullable ArrayList<DecorationInfo> decorations) {
878e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
87909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        wp.setWordSpacing(mAddedWidth);
880850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        // Get metrics first (even for empty strings or "0" width runs)
881850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        if (fmi != null) {
882850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio            expandMetricsFromPaint(fmi, wp);
883850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        }
884e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
885850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        // No need to do anything if the run width is "0"
886538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        if (end == start) {
887850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio            return 0f;
888850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        }
889850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio
890538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        float totalWidth = 0;
891850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio
8921378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader        final int numDecorations = decorations == null ? 0 : decorations.size();
8931378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader        if (needWidth || (c != null && (wp.bgColor != 0 || numDecorations != 0 || runIsRtl))) {
894538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset);
895e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
896e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
897e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (c != null) {
898538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            final float leftX, rightX;
899e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runIsRtl) {
900538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                leftX = x - totalWidth;
901538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                rightX = x;
902538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            } else {
903538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                leftX = x;
904538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                rightX = x + totalWidth;
905e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
906e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
907e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (wp.bgColor != 0) {
908dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                int previousColor = wp.getColor();
909dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                Paint.Style previousStyle = wp.getStyle();
910dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
911e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setColor(wp.bgColor);
912e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setStyle(Paint.Style.FILL);
913538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                c.drawRect(leftX, top, rightX, bottom, wp);
914e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
915dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setStyle(previousStyle);
916dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setColor(previousColor);
917dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne            }
918dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
9191378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader            if (numDecorations != 0) {
9201378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                for (int i = 0; i < numDecorations; i++) {
9211378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    final DecorationInfo info = decorations.get(i);
9221378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader
9231378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    final int decorationStart = Math.max(info.start, start);
9241378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    final int decorationEnd = Math.min(info.end, offset);
9251378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    float decorationStartAdvance = getRunAdvance(
9261378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                            wp, start, end, contextStart, contextEnd, runIsRtl, decorationStart);
9271378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    float decorationEndAdvance = getRunAdvance(
9281378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                            wp, start, end, contextStart, contextEnd, runIsRtl, decorationEnd);
9291378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    final float decorationXLeft, decorationXRight;
930538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    if (runIsRtl) {
9311378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                        decorationXLeft = rightX - decorationEndAdvance;
9321378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                        decorationXRight = rightX - decorationStartAdvance;
933538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    } else {
9341378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                        decorationXLeft = leftX + decorationStartAdvance;
9351378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                        decorationXRight = leftX + decorationEndAdvance;
936538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    }
937702c9f92fcb1fbdfdca07a2627d5f75dbca7b557Siyamed Sinir
938538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // Theoretically, there could be cases where both Paint's and TextPaint's
939538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // setUnderLineText() are called. For backward compatibility, we need to draw
940538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // both underlines, the one with custom color first.
941538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    if (info.underlineColor != 0) {
9421378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                        drawStroke(wp, c, info.underlineColor, wp.getUnderlinePosition(),
9431378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                                info.underlineThickness, decorationXLeft, decorationXRight, y);
944538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    }
945538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    if (info.isUnderlineText) {
946ca8a04a36640eb227a556ad9ced925c48ced2495Roozbeh Pournader                        final float thickness =
947a273a70ecbb0d1aaf9aeceec7aa91591290a4871Siyamed Sinir                                Math.max(wp.getUnderlineThickness(), 1.0f);
9481378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                        drawStroke(wp, c, wp.getColor(), wp.getUnderlinePosition(), thickness,
9491378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                                decorationXLeft, decorationXRight, y);
9501378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    }
9511378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader
9521378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    if (info.isStrikeThruText) {
9531378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                        final float thickness =
954a273a70ecbb0d1aaf9aeceec7aa91591290a4871Siyamed Sinir                                Math.max(wp.getStrikeThruThickness(), 1.0f);
9551378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                        drawStroke(wp, c, wp.getColor(), wp.getStrikeThruPosition(), thickness,
9561378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                                decorationXLeft, decorationXRight, y);
957538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    }
958538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                }
959e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
960e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
961da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio            drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
962538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    leftX, y + wp.baselineShift);
963e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
964e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
965538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        return runIsRtl ? -totalWidth : totalWidth;
966e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
967e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
968e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
969e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering a replacement.
970e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
971bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
972e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param replacement the replacement
973e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the work paint
974e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the run
975e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
976e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
977e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if not rendering
978e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the replacement closest to the leading margin
979e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
980e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
981e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
982e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
983e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the replacement is needed
984e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
985e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
986e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
987e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float handleReplacement(ReplacementSpan replacement, TextPaint wp,
988bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            int start, int limit, boolean runIsRtl, Canvas c,
989e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float x, int top, int y, int bottom, FontMetricsInt fmi,
9900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean needWidth) {
991e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
992e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ret = 0;
993e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
9940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textStart = mStart + start;
9950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textLimit = mStart + limit;
996e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
9970c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (needWidth || (c != null && runIsRtl)) {
9988a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousTop = 0;
9998a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousAscent = 0;
10008a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousDescent = 0;
10018a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousBottom = 0;
10028a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousLeading = 0;
10038a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
10048a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            boolean needUpdateMetrics = (fmi != null);
10058a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
10068a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            if (needUpdateMetrics) {
10078a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousTop     = fmi.top;
10088a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousAscent  = fmi.ascent;
10098a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousDescent = fmi.descent;
10108a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousBottom  = fmi.bottom;
10118a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousLeading = fmi.leading;
10128a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            }
10138a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
10140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            ret = replacement.getSize(wp, mText, textStart, textLimit, fmi);
10158a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
10168a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            if (needUpdateMetrics) {
10178a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,
10188a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                        previousLeading);
10198a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            }
10200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
1021e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
10220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (c != null) {
10230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (runIsRtl) {
10240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x -= ret;
1025e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
10260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            replacement.draw(c, mText, textStart, textLimit,
10270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, top, y, bottom, wp);
1028e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1029e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1030e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
1031e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1032945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
103346c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader    private int adjustHyphenEdit(int start, int limit, int hyphenEdit) {
103446c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader        int result = hyphenEdit;
103546c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader        // Only draw hyphens on first or last run in line. Disable them otherwise.
103646c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader        if (start > 0) { // not the first run
103746c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader            result &= ~Paint.HYPHENEDIT_MASK_START_OF_LINE;
103846c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader        }
103946c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader        if (limit < mLen) { // not the last run
104046c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader            result &= ~Paint.HYPHENEDIT_MASK_END_OF_LINE;
104146c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader        }
104246c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader        return result;
104346c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader    }
104446c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader
10451378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader    private static final class DecorationInfo {
10461378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader        public boolean isStrikeThruText;
1047538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        public boolean isUnderlineText;
1048538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        public int underlineColor;
1049538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        public float underlineThickness;
1050538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        public int start = -1;
1051538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        public int end = -1;
1052538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
10531378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader        public boolean hasDecoration() {
10541378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader            return isStrikeThruText || isUnderlineText || underlineColor != 0;
1055538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        }
1056538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
1057538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        // Copies the info, but not the start and end range.
10581378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader        public DecorationInfo copyInfo() {
10591378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader            final DecorationInfo copy = new DecorationInfo();
10601378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader            copy.isStrikeThruText = isStrikeThruText;
1061538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            copy.isUnderlineText = isUnderlineText;
1062538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            copy.underlineColor = underlineColor;
1063538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            copy.underlineThickness = underlineThickness;
1064538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            return copy;
1065538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        }
1066538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader    }
1067538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
10681378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader    private void extractDecorationInfo(@NonNull TextPaint paint, @NonNull DecorationInfo info) {
10691378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader        info.isStrikeThruText = paint.isStrikeThruText();
10701378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader        if (info.isStrikeThruText) {
10711378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader            paint.setStrikeThruText(false);
10721378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader        }
1073538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        info.isUnderlineText = paint.isUnderlineText();
1074538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        if (info.isUnderlineText) {
1075538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            paint.setUnderlineText(false);
1076538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        }
1077538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        info.underlineColor = paint.underlineColor;
1078538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        info.underlineThickness = paint.underlineThickness;
1079538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader        paint.setUnderlineText(0, 0.0f);
1080538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader    }
1081538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
1082e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
1083e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for handling a unidirectional run.  The run must not
1084112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader     * contain tabs but can contain styles.
1085e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
1086bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
1087e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
10880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param measureLimit the offset to measure to, between start and limit inclusive
1089e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
1090e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
1091e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null
1092e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the end of the run closest to the leading margin
1093e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
1094e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
1095e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
1096e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
1097e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width is required
1098e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
1099e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
1100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
1101bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float handleRun(int start, int measureLimit,
1102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
11030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int bottom, FontMetricsInt fmi, boolean needWidth) {
1104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
11059f3958cc2ba3da1406caac64650620d226bf8562Siyamed Sinir        if (measureLimit < start || measureLimit > limit) {
11069f3958cc2ba3da1406caac64650620d226bf8562Siyamed Sinir            throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of "
11079f3958cc2ba3da1406caac64650620d226bf8562Siyamed Sinir                    + "start (" + start + ") and limit (" + limit + ") bounds");
11089f3958cc2ba3da1406caac64650620d226bf8562Siyamed Sinir        }
11099f3958cc2ba3da1406caac64650620d226bf8562Siyamed Sinir
1110f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        // Case of an empty line, make sure we update fmi according to mPaint
1111f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        if (start == measureLimit) {
1112538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            final TextPaint wp = mWorkPaint;
1113f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne            wp.set(mPaint);
111415c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            if (fmi != null) {
111515c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio                expandMetricsFromPaint(fmi, wp);
111615c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            }
111715c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            return 0f;
1118f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        }
1119f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne
112008836d4ca5e1dab422575f1f0a1ad617a59e6ba0Roozbeh Pournader        final boolean needsSpanMeasurement;
1121945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        if (mSpanned == null) {
112208836d4ca5e1dab422575f1f0a1ad617a59e6ba0Roozbeh Pournader            needsSpanMeasurement = false;
112308836d4ca5e1dab422575f1f0a1ad617a59e6ba0Roozbeh Pournader        } else {
112408836d4ca5e1dab422575f1f0a1ad617a59e6ba0Roozbeh Pournader            mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit);
112508836d4ca5e1dab422575f1f0a1ad617a59e6ba0Roozbeh Pournader            mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit);
112608836d4ca5e1dab422575f1f0a1ad617a59e6ba0Roozbeh Pournader            needsSpanMeasurement = mMetricAffectingSpanSpanSet.numberOfSpans != 0
112708836d4ca5e1dab422575f1f0a1ad617a59e6ba0Roozbeh Pournader                    || mCharacterStyleSpanSet.numberOfSpans != 0;
112808836d4ca5e1dab422575f1f0a1ad617a59e6ba0Roozbeh Pournader        }
112908836d4ca5e1dab422575f1f0a1ad617a59e6ba0Roozbeh Pournader
113008836d4ca5e1dab422575f1f0a1ad617a59e6ba0Roozbeh Pournader        if (!needsSpanMeasurement) {
1131538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            final TextPaint wp = mWorkPaint;
1132945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            wp.set(mPaint);
113346c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader            wp.setHyphenEdit(adjustHyphenEdit(start, limit, wp.getHyphenEdit()));
1134909c7bca570b6f50650d0872e2037389b29252e3Raph Levien            return handleText(wp, start, limit, start, limit, runIsRtl, c, x, top,
1135538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    y, bottom, fmi, needWidth, measureLimit, null);
1136945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        }
1137945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
1138e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // Shaping needs to take into account context up to metric boundaries,
1139e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // but rendering needs to take into account character style boundaries.
11400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // So we iterate through metric runs to get metric bounds,
11410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // then within each metric run iterate through character style runs
11420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // for the run bounds.
1143945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        final float originalX = x;
11440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        for (int i = start, inext; i < measureLimit; i = inext) {
1145538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            final TextPaint wp = mWorkPaint;
1146e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            wp.set(mPaint);
1147e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1148c1f44830809f0a8526855f13822702ea756214faGilles Debunne            inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) -
1149c1f44830809f0a8526855f13822702ea756214faGilles Debunne                    mStart;
1150945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            int mlimit = Math.min(inext, measureLimit);
1151945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
1152945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            ReplacementSpan replacement = null;
1153945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
1154c1f44830809f0a8526855f13822702ea756214faGilles Debunne            for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) {
1155945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT
1156945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                // empty by construction. This special case in getSpans() explains the >= & <= tests
1157c1f44830809f0a8526855f13822702ea756214faGilles Debunne                if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) ||
1158c1f44830809f0a8526855f13822702ea756214faGilles Debunne                        (mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue;
1159538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                final MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j];
1160945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if (span instanceof ReplacementSpan) {
1161945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    replacement = (ReplacementSpan)span;
1162945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                } else {
1163945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    // We might have a replacement that uses the draw
1164945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    // state, otherwise measure state would suffice.
1165945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    span.updateDrawState(wp);
1166e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
1167e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
1168e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1169945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            if (replacement != null) {
1170945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y,
1171945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                        bottom, fmi, needWidth || mlimit < measureLimit);
1172945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                continue;
1173945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            }
1174945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
1175538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            final TextPaint activePaint = mActivePaint;
1176538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            activePaint.set(mPaint);
1177538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            int activeStart = i;
1178538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            int activeEnd = mlimit;
11791378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader            final DecorationInfo decorationInfo = mDecorationInfo;
11801378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader            mDecorations.clear();
118142ef515d185d4fc038d602172789cc264f1d9960Raph Levien            for (int j = i, jnext; j < mlimit; j = jnext) {
1182702c9f92fcb1fbdfdca07a2627d5f75dbca7b557Siyamed Sinir                jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) -
1183702c9f92fcb1fbdfdca07a2627d5f75dbca7b557Siyamed Sinir                        mStart;
118442ef515d185d4fc038d602172789cc264f1d9960Raph Levien
1185538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                final int offset = Math.min(jnext, mlimit);
118642ef515d185d4fc038d602172789cc264f1d9960Raph Levien                wp.set(mPaint);
118742ef515d185d4fc038d602172789cc264f1d9960Raph Levien                for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
118842ef515d185d4fc038d602172789cc264f1d9960Raph Levien                    // Intentionally using >= and <= as explained above
1189702c9f92fcb1fbdfdca07a2627d5f75dbca7b557Siyamed Sinir                    if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) ||
1190702c9f92fcb1fbdfdca07a2627d5f75dbca7b557Siyamed Sinir                            (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;
11910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
1192538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    final CharacterStyle span = mCharacterStyleSpanSet.spans[k];
1193702c9f92fcb1fbdfdca07a2627d5f75dbca7b557Siyamed Sinir                    span.updateDrawState(wp);
1194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
119542ef515d185d4fc038d602172789cc264f1d9960Raph Levien
11961378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                extractDecorationInfo(wp, decorationInfo);
1197538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
1198538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                if (j == i) {
1199538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // First chunk of text. We can't handle it yet, since we may need to merge it
1200538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // with the next chunk. So we just save the TextPaint for future comparisons
1201538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // and use.
1202538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    activePaint.set(wp);
1203538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                } else if (!wp.hasEqualAttributes(activePaint)) {
1204538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // The style of the present chunk of text is substantially different from the
1205538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // style of the previous chunk. We need to handle the active piece of text
1206538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // and restart with the present chunk.
1207538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    activePaint.setHyphenEdit(adjustHyphenEdit(
1208538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                            activeStart, activeEnd, mPaint.getHyphenEdit()));
1209538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, x,
1210538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                            top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
12111378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                            Math.min(activeEnd, mlimit), mDecorations);
1212538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader
1213538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    activeStart = j;
1214538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    activePaint.set(wp);
12151378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    mDecorations.clear();
1216538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                } else {
1217538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // The present TextPaint is substantially equal to the last TextPaint except
12181378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    // perhaps for decorations. We just need to expand the active piece of text to
1219538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // include the present chunk, which we always do anyway. We don't need to save
1220538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    // wp to activePaint, since they are already equal.
1221538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                }
122246c6f4c5ea7846fee4e6ef40c035ef2bee1adcbbRoozbeh Pournader
1223538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                activeEnd = jnext;
12241378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                if (decorationInfo.hasDecoration()) {
12251378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    final DecorationInfo copy = decorationInfo.copyInfo();
1226538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    copy.start = j;
1227538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    copy.end = jnext;
12281378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    mDecorations.add(copy);
1229538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                }
1230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
1231538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            // Handle the final piece of text.
1232538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            activePaint.setHyphenEdit(adjustHyphenEdit(
1233538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    activeStart, activeEnd, mPaint.getHyphenEdit()));
1234538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader            x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, x,
1235538de5bb3b3662c51c17cd9dd24e660c85e88b78Roozbeh Pournader                    top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
12361378a9d72cb4a9f7a939f0a28a78fcfb87fb7879Roozbeh Pournader                    Math.min(activeEnd, mlimit), mDecorations);
1237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1239945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        return x - originalX;
1240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
1243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Render a text run with the set-up paint.
1244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
1245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas
1246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the paint used to render the text
12470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param start the start of the run
12480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the run
12490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextStart the start of context for the run
12500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextEnd the end of the context for the run
1251da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio     * @param runIsRtl true if the run is right-to-left
1252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the x position of the left edge of the run
1253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline of the run
1254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
12550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private void drawTextRun(Canvas c, TextPaint wp, int start, int end,
1256da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio            int contextStart, int contextEnd, boolean runIsRtl, float x, int y) {
1257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
12590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int count = end - start;
12600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextCount = contextEnd - contextStart;
12610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mChars, start, count, contextStart, contextCount,
1262051910b9f998030dacb8a0722588cc715813fde1Raph Levien                    x, y, runIsRtl, wp);
1263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
12640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int delta = mStart;
12650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mText, delta + start, delta + end,
1266051910b9f998030dacb8a0722588cc715813fde1Raph Levien                    delta + contextStart, delta + contextEnd, x, y, runIsRtl, wp);
1267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
1271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next tab position.
1272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
1273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param h the (unsigned) offset from the leading margin
1274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the (unsigned) tab position after this offset
1275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
1276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float nextTab(float h) {
1277c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (mTabs != null) {
1278c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return mTabs.nextTab(h);
1279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1280c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return TabStops.nextDefaultStop(h, TAB_INCREMENT);
1281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
128309da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    private boolean isStretchableWhitespace(int ch) {
12843630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader        // TODO: Support NBSP and other stretchable whitespace (b/34013491 and b/68204709).
12853630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader        return ch == 0x0020;
128609da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    }
128709da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka
128809da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    /* Return the number of spaces in the text line, for the purpose of justification */
128909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    private int countStretchableSpaces(int start, int end) {
129009da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        int count = 0;
12913630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader        for (int i = start; i < end; i++) {
12923630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader            final char c = mCharsValid ? mChars[i] : mText.charAt(i + mStart);
12933630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader            if (isStretchableWhitespace(c)) {
12943630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader                count++;
12953630d0861bc703cef843d5c196726235d72f1e80Roozbeh Pournader            }
129609da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        }
129709da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        return count;
129809da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    }
129909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka
130009da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
130109da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    public static boolean isLineEndSpace(char ch) {
130209da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka        return ch == ' ' || ch == '\t' || ch == 0x1680
130309da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka                || (0x2000 <= ch && ch <= 0x200A && ch != 0x2007)
130409da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka                || ch == 0x205F || ch == 0x3000;
130509da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka    }
130609da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka
1307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static final int TAB_INCREMENT = 20;
1308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt}
1309