TextLine.java revision 1e3ac18e7ad03e02819f3e1a89d6a80a2bb7645f
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
19e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport com.android.internal.util.ArrayUtils;
20e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
21e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Bitmap;
22e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Canvas;
23e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint;
24e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint.FontMetricsInt;
25cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunneimport android.graphics.RectF;
26e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.Layout.Directions;
27c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.Layout.TabStops;
28e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.CharacterStyle;
29e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.MetricAffectingSpan;
30e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.ReplacementSpan;
31e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.util.Log;
32e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
33e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/**
34e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Represents a line of styled text, for measuring in visual order and
35e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * for rendering.
36e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
37e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Get a new instance using obtain(), and when finished with it, return it
38e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * to the pool using recycle().
39e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
40e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Call set to prepare the instance for use, then either draw, measure,
41e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * metrics, or caretToLeftRightOf.
42e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
43e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @hide
44e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */
45e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltclass TextLine {
46e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private TextPaint mPaint;
47e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private CharSequence mText;
48e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mStart;
49e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mLen;
50e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mDir;
51e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Directions mDirections;
52e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mHasTabs;
53c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private TabStops mTabs;
54e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private char[] mChars;
55e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mCharsValid;
56e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Spanned mSpanned;
57345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne    private final TextPaint mWorkPaint = new TextPaint();
58e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
59e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static TextLine[] cached = new TextLine[3];
60e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
61e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
62e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns a new TextLine from the shared pool.
63e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
64e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return an uninitialized TextLine
65e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
66e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine obtain() {
67e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl;
68e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        synchronized (cached) {
69e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int i = cached.length; --i >= 0;) {
70e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (cached[i] != null) {
71e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    tl = cached[i];
72e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    cached[i] = null;
73e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return tl;
74e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
75e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
76e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
77e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl = new TextLine();
78673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase        Log.v("TLINE", "new: " + tl);
79e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return tl;
80e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
81e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
82e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
83e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Puts a TextLine back into the shared pool. Do not use this TextLine once
84e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * it has been returned.
85e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param tl the textLine
86e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return null, as a convenience from clearing references to the provided
87e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * TextLine
88e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
89e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine recycle(TextLine tl) {
90e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mText = null;
91e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mPaint = null;
92e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mDirections = null;
93f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne        synchronized(cached) {
94f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne            for (int i = 0; i < cached.length; ++i) {
95f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne                if (cached[i] == null) {
96f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne                    cached[i] = tl;
97f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne                    break;
98e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
99e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
101e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return null;
102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
103e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
105e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Initializes a TextLine and prepares it for use.
106e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
107e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param paint the base paint for the line
108e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param text the text, can be Styled
109e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the line relative to the text
110e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the line relative to the text
111e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param dir the paragraph direction of this line
112e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param directions the directions information of this line
113e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param hasTabs true if the line might contain tabs or emoji
114c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param tabStops the tabStops. Can be null.
115e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
116e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
117c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            Directions directions, boolean hasTabs, TabStops tabStops) {
118e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mPaint = paint;
119e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mText = text;
120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mStart = start;
121e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mLen = limit - start;
122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDir = dir;
123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDirections = directions;
124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mHasTabs = hasTabs;
125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mSpanned = null;
126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean hasReplacement = false;
128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (text instanceof Spanned) {
129e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            mSpanned = (Spanned) text;
1301e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            ReplacementSpan[] spans = mSpanned.getSpans(start, limit, ReplacementSpan.class);
1311e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class);
1321e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            hasReplacement = spans.length > 0;
133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
134e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1351e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
136e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
137e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
138e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mChars == null || mChars.length < mLen) {
139e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                mChars = new char[ArrayUtils.idealCharArraySize(mLen)];
140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextUtils.getChars(text, start, limit, mChars, 0);
1420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (hasReplacement) {
1430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Handle these all at once so we don't have to do it as we go.
1440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Replace the first character of each replacement run with the
1450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // object-replacement character and the remainder with zero width
1460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // non-break space aka BOM.  Cursor movement code skips these
1470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // zero-width characters.
1480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                char[] chars = mChars;
1490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int i = start, inext; i < limit; i = inext) {
1501e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    inext = mSpanned.nextSpanTransition(i, limit, ReplacementSpan.class);
1511e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    ReplacementSpan[] spans = mSpanned.getSpans(i, inext, ReplacementSpan.class);
1521e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class);
1531e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    if (spans.length > 0) {
1541e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                        // transition into a span
1550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        chars[i - start] = '\ufffc';
1560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        for (int j = i - start + 1, e = inext - start; j < e; ++j) {
1570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip
1580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        }
1590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
1600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
1610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
162e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
163c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        mTabs = tabStops;
164e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
165e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
166e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
167e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Renders the TextLine.
168e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
169e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to render on
170e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the leading margin position
171e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
172e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
173e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
174e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
175e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void draw(Canvas c, float x, int top, int y, int bottom) {
176e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
177e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
178e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                drawRun(c, 0, 0, mLen, false, x, top, y, bottom, false);
179e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
180e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
181e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
182e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                drawRun(c, 0, 0, mLen, true, x, top, y, bottom, false);
183e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
185e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
187e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        RectF emojiRect = null;
190e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lastRunIndex = runs.length - 2;
192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
193e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
195e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
196e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
197e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
198e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
199e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
200e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = mChars[j];
207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(mChars, j);
209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
211e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
212e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
213e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    h += drawRun(c, i, segstart, j, runIsRtl, x+h, top, y, bottom,
220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            i != lastRunIndex || j != mLen);
221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    } else if (bm != null) {
225e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bitmapHeight = bm.getHeight();
227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float scale = -bmAscent / bitmapHeight;
228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float width = bm.getWidth() * scale;
229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (emojiRect == null) {
231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            emojiRect = new RectF();
232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        emojiRect.set(x + h, y + bmAscent,
234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                                x + h + width, y);
235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        c.drawBitmap(bm, null, emojiRect, mPaint);
236e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += width;
237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns metrics information for the entire line.
247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives font metrics information, can be null
249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the line
250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float metrics(FontMetricsInt fmi) {
252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return measure(mLen, false, fmi);
253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns information about a position on the line.
257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the line-relative character offset, between 0 and the
259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * line length, inclusive
260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param trailing true to measure the trailing edge of the character
261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * before offset, false to measure the leading edge of the character
262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * at offset.
263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character, can be null.
265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed offset from the leading margin to the requested
266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character edge.
267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float measure(int offset, boolean trailing, FontMetricsInt fmi) {
269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int target = trailing ? offset - 1 : offset;
270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (target < 0) {
271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return 0;
272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
2780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                return measureRun(0, 0, offset, mLen, false, fmi);
279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
280e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
2810b9d2ca6b6fc4574898cfff6acdee821d4723a96Doug Felt                return measureRun(0, 0, offset, mLen, true, fmi);
282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
283e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
285e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        char[] chars = mChars;
286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
290e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
298e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
299e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = chars[j];
302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(chars, j);
304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean inSegment = target >= segstart && target < j;
315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment && advance) {
318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        return h += measureRun(i, segstart, offset, j, runIsRtl, fmi);
319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    float w = measureRun(i, segstart, j, j, runIsRtl, fmi);
322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    h += advance ? w : -w;
323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment) {
325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        return h += measureRun(i, segstart, offset, j, runIsRtl, null);
326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
328e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
329e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (offset == j) {
330e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
332e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
333e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (target == j) {
334e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
335e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
337e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
338e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (bm != null) {
339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float wid = bm.getWidth() * -bmAscent / bm.getHeight();
341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += mDir * wid;
342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
343e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return h;
351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Draws a unidirectional (but possibly multi-styled) run of text.
355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to draw on
357e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the index of this directional run
358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start
359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit
360e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the position of the run that is closest to the leading margin
362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
365e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width value is required.
366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run, based on the paragraph direction.
367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Only valid if needWidth is true.
368e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
369e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float drawRun(Canvas c, int runIndex, int start,
370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, float x, int top, int y, int bottom,
371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean needWidth) {
372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
373e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
374e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float w = -measureRun(runIndex, start, limit, limit, runIsRtl, null);
375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            handleRun(runIndex, start, limit, limit, runIsRtl, c, x + w, top,
3760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    y, bottom, null, false);
377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return w;
378e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
379e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
380e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return handleRun(runIndex, start, limit, limit, runIsRtl, c, x, top,
3810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                y, bottom, null, needWidth);
382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
383e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
384e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
385e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Measures a unidirectional (but possibly multi-styled) run of text.
386e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
387e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
388e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
389e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset to measure to, between start and limit inclusive
390e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit of the run
391e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
392e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
393e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * run, can be null.
394e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width from the start of the run to the leading edge
395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the character at offset, based on the run (not paragraph) direction
396e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
397e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float measureRun(int runIndex, int start,
398e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int offset, int limit, boolean runIsRtl, FontMetricsInt fmi) {
399e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return handleRun(runIndex, start, offset, limit, runIsRtl, null,
4000c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                0, 0, 0, 0, fmi, true);
401e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
402e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
403e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
404e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Walk the cursor through this line, skipping conjuncts and
405e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * zero-width characters.
406e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
407e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * <p>This function cannot properly walk the cursor off the ends of the line
408e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * since it does not know about any shaping on the previous/following line
409e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * that might affect the cursor position. Callers must either avoid these
410e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * situations or handle the result specially.
411e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
412e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param cursor the starting position of the cursor, between 0 and the
413e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * length of the line, inclusive
414e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param toLeft true if the caret is moving to the left.
415e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset.  If it is less than 0 or greater than the length
416e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the line, the previous/following line should be examined to get the
417e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * actual offset.
418e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
419e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    int getOffsetToLeftRightOf(int cursor, boolean toLeft) {
420e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 1) The caret marks the leading edge of a character. The character
421e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // logically before it might be on a different level, and the active caret
422e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // position is on the character at the lower level. If that character
423e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // was the previous character, the caret is on its trailing edge.
424e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 2) Take this character/edge and move it in the indicated direction.
425e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // This gives you a new character and a new edge.
426e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 3) This position is between two visually adjacent characters.  One of
427e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // these might be at a lower level.  The active position is on the
428e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // character at the lower level.
429e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 4) If the active position is on the trailing edge of the character,
430e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // the new caret position is the following logical character, else it
431e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // is the character.
432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineStart = 0;
434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineEnd = mLen;
435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean paraIsRtl = mDir == -1;
436e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
437e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
438e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1;
439e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean trailing = false;
440e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
441e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (cursor == lineStart) {
442e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = -2;
443e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else if (cursor == lineEnd) {
444e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = runs.length;
445e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
446e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // First, get information about the run containing the character with
447e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the active caret.
448e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          for (runIndex = 0; runIndex < runs.length; runIndex += 2) {
449e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runStart = lineStart + runs[runIndex];
450e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (cursor >= runStart) {
451e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK);
452e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (runLimit > lineEnd) {
453e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  runLimit = lineEnd;
454e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
455e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor < runLimit) {
456e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
457e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    Layout.RUN_LEVEL_MASK;
458e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (cursor == runStart) {
459e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // The caret is on a run boundary, see if we should
460e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // use the position on the trailing edge of the previous
461e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // logical character instead.
462e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit;
463e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int pos = cursor - 1;
464e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) {
465e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    prevRunStart = lineStart + runs[prevRunIndex];
466e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (pos >= prevRunStart) {
467e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      prevRunLimit = prevRunStart +
468e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK);
469e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (prevRunLimit > lineEnd) {
470e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          prevRunLimit = lineEnd;
471e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
472e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (pos < prevRunLimit) {
473e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT)
474e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            & Layout.RUN_LEVEL_MASK;
475e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (prevRunLevel < runLevel) {
476e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          // Start from logically previous character.
477e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runIndex = prevRunIndex;
478e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLevel = prevRunLevel;
479e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runStart = prevRunStart;
480e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLimit = prevRunLimit;
481e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          trailing = true;
482e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          break;
483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
485e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // caret might be == lineEnd.  This is generally a space or paragraph
494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // separator and has an associated run, but might be the end of
495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // text, in which case it doesn't.  If that happens, we ran off the
496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // end of the run list, and runIndex == runs.length.  In this case,
497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we are at a run boundary so we skip the below test.
498e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (runIndex != runs.length) {
499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean runIsRtl = (runLevel & 0x1) != 0;
500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean advance = toLeft == runIsRtl;
501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor != (advance ? runLimit : runStart) || advance != trailing) {
502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // Moving within or into the run, so we can move logically.
5030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                  newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit,
5040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                          runIsRtl, cursor, advance);
505e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // If the new position is internal to the run, we're at the strong
506e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // position already so we're finished.
507e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  if (newCaret != (advance ? runLimit : runStart)) {
508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      return newCaret;
509e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
510e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
511e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
512e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
514e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // If newCaret is -1, we're starting at a run boundary and crossing
515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // into another run. Otherwise we've arrived at a run boundary, and
516e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to figure out which character to attach to.  Note we might
517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to run this twice, if we cross a run boundary and end up at
518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // another run boundary.
519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        while (true) {
520e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          boolean advance = toLeft == paraIsRtl;
521e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          int otherRunIndex = runIndex + (advance ? 2 : -2);
522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (otherRunIndex >= 0 && otherRunIndex < runs.length) {
523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunStart = lineStart + runs[otherRunIndex];
524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLimit = otherRunStart +
525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK);
526e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLimit > lineEnd) {
527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                otherRunLimit = lineEnd;
528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Layout.RUN_LEVEL_MASK;
531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean otherRunIsRtl = (otherRunLevel & 1) != 0;
532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            advance = toLeft == otherRunIsRtl;
534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (newCaret == -1) {
5350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart,
5360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        otherRunLimit, otherRunIsRtl,
537e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        advance ? otherRunStart : otherRunLimit, advance);
538e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (newCaret == (advance ? otherRunLimit : otherRunStart)) {
539e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // Crossed and ended up at a new boundary,
540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // repeat a second and final time.
541e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runIndex = otherRunIndex;
542e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runLevel = otherRunLevel;
543e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    continue;
544e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
546e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
547e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            // The new caret is at a boundary.
549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLevel < runLevel) {
550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // The strong character is in the other run.
551e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? otherRunStart : otherRunLimit;
552e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            break;
554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret == -1) {
557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // We're walking off the end of the line.  The paragraph
558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // level is always equal to or lower than any internal level, so
559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // the boundaries get the strong caret.
5600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt              newCaret = advance ? mLen + 1 : -1;
561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              break;
562e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Else we've arrived at the end of the line.  That's a strong position.
565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // We might have arrived here by crossing over a run with no internal
566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // breaks and dropping out of the above loop before advancing one final
567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // time, so reset the caret.
568e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Note, we use '<=' below to handle a situation where the only run
569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // on the line is a counter-directional run.  If we're not advancing,
570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we can end up at the 'lineEnd' position but the caret we want is at
571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the lineStart.
572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret <= lineEnd) {
573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? lineEnd : lineStart;
574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          break;
576e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
577e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return newCaret;
579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next valid offset within this directional run, skipping
583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * conjuncts and zero-width characters.  This should not be called to walk
584cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunne     * off the end of the line, since the returned values might not be valid
5850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * on neighboring lines.  If the returned offset is less than zero or
5860c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * greater than the line length, the offset should be recomputed on the
5870c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * preceding or following line, respectively.
588e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
589e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
5900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runStart the start of the run
5910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runLimit the limit of the run
5920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runIsRtl true if the run is right-to-left
593e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset
594e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param after true if the new offset should logically follow the provided
595e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * offset
596e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset
597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
5980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit,
5990c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean runIsRtl, int offset, boolean after) {
600e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (runIndex < 0 || offset == (after ? mLen : 0)) {
6020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // Walking off end of line.  Since we don't know
6030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // what cursor positions are available on other lines, we can't
6040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // return accurate values.  These are a guess.
605e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (after) {
6060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart;
6070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart;
6090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
6100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        TextPaint wp = mWorkPaint;
6120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        wp.set(mPaint);
6130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanStart = runStart;
6150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanLimit;
6160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mSpanned == null) {
6170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            spanLimit = runLimit;
6180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int target = after ? offset + 1 : offset;
6200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int limit = mStart + runLimit;
6210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            while (true) {
6220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit,
6230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        MetricAffectingSpan.class) - mStart;
6240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (spanLimit >= target) {
6250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    break;
626e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
6270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanStart = spanLimit;
6280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart,
6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    mStart + spanLimit, MetricAffectingSpan.class);
6321e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
6330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (spans.length > 0) {
6350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ReplacementSpan replacement = null;
6360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = 0; j < spans.length; j++) {
6370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    MetricAffectingSpan span = spans[j];
6380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    if (span instanceof ReplacementSpan) {
6390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        replacement = (ReplacementSpan)span;
6400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    } else {
6410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateMeasureState(wp);
6420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
6430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
6440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (replacement != null) {
6460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // If we have a replacement span, we're moving either to
6470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // the start or end of this span.
6480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    return after ? spanLimit : spanStart;
649e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
650e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
651e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
652e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
6540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
6550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mCharsValid) {
6560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
6570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    flags, offset, cursorOpt);
6580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mText, mStart + spanStart,
660345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne                    mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart;
661e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
662e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
663e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
664e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
6650bb000931bb841e75903d655552d1626ae158707Gilles Debunne     * @param wp
6660bb000931bb841e75903d655552d1626ae158707Gilles Debunne     */
6670bb000931bb841e75903d655552d1626ae158707Gilles Debunne    private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) {
6680bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousTop     = fmi.top;
6690bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousAscent  = fmi.ascent;
6700bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousDescent = fmi.descent;
6710bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousBottom  = fmi.bottom;
6720bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousLeading = fmi.leading;
6730bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6740bb000931bb841e75903d655552d1626ae158707Gilles Debunne        wp.getFontMetricsInt(fmi);
6750bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6760bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.top     = Math.min(fmi.top,     previousTop);
6770bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.ascent  = Math.min(fmi.ascent,  previousAscent);
6780bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.descent = Math.max(fmi.descent, previousDescent);
6790bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.bottom  = Math.max(fmi.bottom,  previousBottom);
6800bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.leading = Math.max(fmi.leading, previousLeading);
6810bb000931bb841e75903d655552d1626ae158707Gilles Debunne    }
6820bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6830bb000931bb841e75903d655552d1626ae158707Gilles Debunne    /**
684e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering text.  The text must
685e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * not include a tab or emoji.
686e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
687e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the working paint
688e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the text
6890c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the text
690e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
691e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if rendering is not needed
692e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the run closest to the leading margin
693e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
694e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
695e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
696e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
697e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the run is needed
698e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
699e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
700e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
7010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private float handleText(TextPaint wp, int start, int end,
7020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl,
7030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            Canvas c, float x, int top, int y, int bottom,
704e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            FontMetricsInt fmi, boolean needWidth) {
705e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
706e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ret = 0;
707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int runLen = end - start;
7090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int contextLen = contextEnd - contextStart;
710e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (needWidth || (c != null && (wp.bgColor != 0 || runIsRtl))) {
7110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
712e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mCharsValid) {
7130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mChars, start, runLen,
7140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        contextStart, contextLen, flags, null, 0);
715e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
7160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                int delta = mStart;
7170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mText, delta + start,
7180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        delta + end, delta + contextStart, delta + contextEnd,
7190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        flags, null, 0);
720e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
721e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
722e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
723e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (fmi != null) {
7240bb000931bb841e75903d655552d1626ae158707Gilles Debunne            expandMetricsFromPaint(fmi, wp);
725e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
726e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
727e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (c != null) {
728e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runIsRtl) {
729e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                x -= ret;
730e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
731e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
732e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (wp.bgColor != 0) {
733e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int color = wp.getColor();
734e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Paint.Style s = wp.getStyle();
735e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setColor(wp.bgColor);
736e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setStyle(Paint.Style.FILL);
737e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
738e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                c.drawRect(x, top, x + ret, bottom, wp);
739e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
740e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setStyle(s);
741e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setColor(color);
742e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
743e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
7450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y + wp.baselineShift);
746e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
747e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
748e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
749e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
750e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
751e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
752e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering a replacement.
753e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
754e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param replacement the replacement
755e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the work paint
756e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
757e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the run
758e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
759e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
760e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if not rendering
761e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the replacement closest to the leading margin
762e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
763e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
764e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
765e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
766e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the replacement is needed
767e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
768e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
769e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
770e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float handleReplacement(ReplacementSpan replacement, TextPaint wp,
771e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runIndex, int start, int limit, boolean runIsRtl, Canvas c,
772e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float x, int top, int y, int bottom, FontMetricsInt fmi,
7730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean needWidth) {
774e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
775e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ret = 0;
776e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textStart = mStart + start;
7780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textLimit = mStart + limit;
779e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7800c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (needWidth || (c != null && runIsRtl)) {
7810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            ret = replacement.getSize(wp, mText, textStart, textLimit, fmi);
7820c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
783e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7840c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (c != null) {
7850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (runIsRtl) {
7860c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x -= ret;
787e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
7880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            replacement.draw(c, mText, textStart, textLimit,
7890c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, top, y, bottom, wp);
790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
791e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
792e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
793e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
794e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
795e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
796e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for handling a unidirectional run.  The run must not
797e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * contain tabs or emoji but can contain styles.
798e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
799e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
8010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param measureLimit the offset to measure to, between start and limit inclusive
802e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
803e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
804e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null
805e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the end of the run closest to the leading margin
806e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
807e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
808e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
809e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
810e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width is required
811e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
812e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
813e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
8140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private float handleRun(int runIndex, int start, int measureLimit,
815e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
8160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int bottom, FontMetricsInt fmi, boolean needWidth) {
817e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
818e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // Shaping needs to take into account context up to metric boundaries,
819e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // but rendering needs to take into account character style boundaries.
8200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // So we iterate through metric runs to get metric bounds,
8210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // then within each metric run iterate through character style runs
8220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // for the run bounds.
823e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ox = x;
8240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        for (int i = start, inext; i < measureLimit; i = inext) {
825e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextPaint wp = mWorkPaint;
826e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            wp.set(mPaint);
827e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int mlimit;
829e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mSpanned == null) {
830e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                inext = limit;
8310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                mlimit = measureLimit;
832e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
833e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                inext = mSpanned.nextSpanTransition(mStart + i, mStart + limit,
834e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        MetricAffectingSpan.class) - mStart;
835e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                mlimit = inext < measureLimit ? inext : measureLimit;
837e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + i,
8380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        mStart + mlimit, MetricAffectingSpan.class);
8391e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
840e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
841e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (spans.length > 0) {
842e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    ReplacementSpan replacement = null;
843e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    for (int j = 0; j < spans.length; j++) {
844e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        MetricAffectingSpan span = spans[j];
845e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (span instanceof ReplacementSpan) {
846e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            replacement = (ReplacementSpan)span;
847e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else {
8480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            // We might have a replacement that uses the draw
8490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            // state, otherwise measure state would suffice.
8500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            span.updateDrawState(wp);
851e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
852e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
853e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
854e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (replacement != null) {
855e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        x += handleReplacement(replacement, wp, runIndex, i,
8560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                                mlimit, runIsRtl, c, x, top, y, bottom, fmi,
8570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                                needWidth || mlimit < measureLimit);
858e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        continue;
859e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
860e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
861e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
862e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (mSpanned == null || c == null) {
8640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x += handleText(wp, i, mlimit, i, inext, runIsRtl, c, x, top,
8650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        y, bottom, fmi, needWidth || mlimit < measureLimit);
8660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            } else {
8670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = i, jnext; j < mlimit; j = jnext) {
8680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    jnext = mSpanned.nextSpanTransition(mStart + j,
8690c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            mStart + mlimit, CharacterStyle.class) - mStart;
870e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    CharacterStyle[] spans = mSpanned.getSpans(mStart + j,
8720c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            mStart + jnext, CharacterStyle.class);
8731e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    spans = TextUtils.removeEmptySpans(spans, mSpanned, CharacterStyle.class);
874e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    wp.set(mPaint);
8760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    for (int k = 0; k < spans.length; k++) {
8770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        CharacterStyle span = spans[k];
8780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateDrawState(wp);
879e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
8800c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
8810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
8820c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            top, y, bottom, fmi, needWidth || jnext < measureLimit);
883e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
884e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
885e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
886e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
887e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return x - ox;
888e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
889e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
890e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
891e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Render a text run with the set-up paint.
892e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
893e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas
894e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the paint used to render the text
8950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param start the start of the run
8960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the run
8970c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextStart the start of context for the run
8980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextEnd the end of the context for the run
899e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
900e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the x position of the left edge of the run
901e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline of the run
902e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
9030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private void drawTextRun(Canvas c, TextPaint wp, int start, int end,
9040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl, float x, int y) {
905e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
906f47d7405bbcb25d7cdf89ebb059f41520fe9ab87Doug Felt        int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR;
907e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
9080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int count = end - start;
9090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextCount = contextEnd - contextStart;
9100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mChars, start, count, contextStart, contextCount,
9110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y, flags, wp);
912e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
9130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int delta = mStart;
9140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mText, delta + start, delta + end,
9150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    delta + contextStart, delta + contextEnd, x, y, flags, wp);
916e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
917e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
918e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
919e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
920e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the ascent of the text at start.  This is used for scaling
921e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * emoji.
922e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
923e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param pos the line-relative position
924e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the ascent of the text at start
925e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
926e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float ascent(int pos) {
927e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mSpanned == null) {
928e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
929e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
930e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
931e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        pos += mStart;
932e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1,
933e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                MetricAffectingSpan.class);
934e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (spans.length == 0) {
935e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
936e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
937e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
938e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextPaint wp = mWorkPaint;
939e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        wp.set(mPaint);
940e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (MetricAffectingSpan span : spans) {
941e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            span.updateMeasureState(wp);
942e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
943e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return wp.ascent();
944e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
945e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
946e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
947e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next tab position.
948e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
949e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param h the (unsigned) offset from the leading margin
950e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the (unsigned) tab position after this offset
951e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
952e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float nextTab(float h) {
953c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (mTabs != null) {
954c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return mTabs.nextTab(h);
955e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
956c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return TabStops.nextDefaultStop(h, TAB_INCREMENT);
957e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
958e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
959e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static final int TAB_INCREMENT = 20;
960e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt}
961