TextLine.java revision 345cb03315a0813ec57e44f97fc3fa4af6b3c309
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.RectF; 25e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint.FontMetricsInt; 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(); 78e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Log.e("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; 93e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (tl.mLen < 250) { 94e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt synchronized(cached) { 95e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int i = 0; i < cached.length; ++i) { 96e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cached[i] == null) { 97e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt cached[i] = tl; 98e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 99e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 101e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 103e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return null; 104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 105e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 106e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 107e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Initializes a TextLine and prepares it for use. 108e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 109e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param paint the base paint for the line 110e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param text the text, can be Styled 111e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the line relative to the text 112e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the line relative to the text 113e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param dir the paragraph direction of this line 114e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param directions the directions information of this line 115e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param hasTabs true if the line might contain tabs or emoji 116c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param tabStops the tabStops. Can be null. 117e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 118e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt void set(TextPaint paint, CharSequence text, int start, int limit, int dir, 119c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Directions directions, boolean hasTabs, TabStops tabStops) { 120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mPaint = paint; 121e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mText = text; 122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mStart = start; 123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mLen = limit - start; 124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mDir = dir; 125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mDirections = directions; 126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mHasTabs = hasTabs; 127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mSpanned = null; 128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 129e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean hasReplacement = false; 130e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (text instanceof Spanned) { 131e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mSpanned = (Spanned) text; 132e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt hasReplacement = mSpanned.getSpans(start, limit, 133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt ReplacementSpan.class).length > 0; 134e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 135e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 136e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mCharsValid = hasReplacement || hasTabs || 137e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; 138e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 139e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mChars == null || mChars.length < mLen) { 141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mChars = new char[ArrayUtils.idealCharArraySize(mLen)]; 142e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextUtils.getChars(text, start, limit, mChars, 0); 1440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (hasReplacement) { 1450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Handle these all at once so we don't have to do it as we go. 1460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Replace the first character of each replacement run with the 1470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // object-replacement character and the remainder with zero width 1480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // non-break space aka BOM. Cursor movement code skips these 1490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // zero-width characters. 1500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt char[] chars = mChars; 1510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int i = start, inext; i < limit; i = inext) { 1520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt inext = mSpanned.nextSpanTransition(i, limit, 1530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ReplacementSpan.class); 1540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (mSpanned.getSpans(i, inext, ReplacementSpan.class) 1550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt .length > 0) { // transition into a span 1560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt chars[i - start] = '\ufffc'; 1570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int j = i - start + 1, e = inext - start; j < e; ++j) { 1580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip 1590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 163e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 164c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt mTabs = tabStops; 165e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 166e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 167e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 168e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Renders the TextLine. 169e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 170e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas to render on 171e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the leading margin position 172e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 173e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 174e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 175e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 176e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt void draw(Canvas c, float x, int top, int y, int bottom) { 177e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (!mHasTabs) { 178e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { 179e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt drawRun(c, 0, 0, mLen, false, x, top, y, bottom, false); 180e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return; 181e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 182e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { 183e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt drawRun(c, 0, 0, mLen, true, x, top, y, bottom, false); 184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return; 185e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 187e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float h = 0; 189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 190e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt RectF emojiRect = null; 191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lastRunIndex = runs.length - 2; 193e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int i = 0; i < runs.length; i += 2) { 194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runStart = runs[i]; 195e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK); 196e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > mLen) { 197e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = mLen; 198e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 199e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0; 200e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int segstart = runStart; 202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt char[] chars = mChars; 203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { 204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int codept = 0; 205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Bitmap bm = null; 206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mHasTabs && j < runLimit) { 208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = mChars[j]; 209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) { 210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = Character.codePointAt(mChars, j); 211e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) { 212e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept); 213e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (codept > 0xffff) { 214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt ++j; 215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (j == runLimit || codept == '\t' || bm != null) { 221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += drawRun(c, i, segstart, j, runIsRtl, x+h, top, y, bottom, 222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt i != lastRunIndex || j != mLen); 223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept == '\t') { 225e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h = mDir * nextTab(h * mDir); 226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (bm != null) { 227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float bmAscent = ascent(j); 228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float bitmapHeight = bm.getHeight(); 229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float scale = -bmAscent / bitmapHeight; 230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float width = bm.getWidth() * scale; 231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (emojiRect == null) { 233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt emojiRect = new RectF(); 234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt emojiRect.set(x + h, y + bmAscent, 236e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt x + h + width, y); 237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt c.drawBitmap(bm, null, emojiRect, mPaint); 238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += width; 239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt j++; 240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt segstart = j + 1; 242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns metrics information for the entire line. 249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives font metrics information, can be null 251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the line 252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float metrics(FontMetricsInt fmi) { 254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return measure(mLen, false, fmi); 255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns information about a position on the line. 259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the line-relative character offset, between 0 and the 261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * line length, inclusive 262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param trailing true to measure the trailing edge of the character 263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * before offset, false to measure the leading edge of the character 264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * at offset. 265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information about the requested 266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * character, can be null. 267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed offset from the leading margin to the requested 268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * character edge. 269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float measure(int offset, boolean trailing, FontMetricsInt fmi) { 271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int target = trailing ? offset - 1 : offset; 272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (target < 0) { 273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return 0; 274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float h = 0; 277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 278e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (!mHasTabs) { 279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { 2800c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return measureRun(0, 0, offset, mLen, false, fmi); 281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { 2830b9d2ca6b6fc4574898cfff6acdee821d4723a96Doug Felt return measureRun(0, 0, offset, mLen, true, fmi); 284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 285e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt char[] chars = mChars; 288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int i = 0; i < runs.length; i += 2) { 290e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runStart = runs[i]; 291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK); 292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > mLen) { 293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = mLen; 294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0; 296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int segstart = runStart; 298e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { 299e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int codept = 0; 300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Bitmap bm = null; 301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mHasTabs && j < runLimit) { 303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = chars[j]; 304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) { 305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = Character.codePointAt(chars, j); 306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) { 307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept); 308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (codept > 0xffff) { 309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt ++j; 310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (j == runLimit || codept == '\t' || bm != null) { 316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean inSegment = target >= segstart && target < j; 317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; 319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (inSegment && advance) { 320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h += measureRun(i, segstart, offset, j, runIsRtl, fmi); 321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float w = measureRun(i, segstart, j, j, runIsRtl, fmi); 324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += advance ? w : -w; 325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (inSegment) { 327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h += measureRun(i, segstart, offset, j, runIsRtl, null); 328e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 329e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 330e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept == '\t') { 331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (offset == j) { 332e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 333e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 334e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h = mDir * nextTab(h * mDir); 335e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (target == j) { 336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 337e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 338e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (bm != null) { 341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float bmAscent = ascent(j); 342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float wid = bm.getWidth() * -bmAscent / bm.getHeight(); 343e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += mDir * wid; 344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt j++; 345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt segstart = j + 1; 348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Draws a unidirectional (but possibly multi-styled) run of text. 357e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas to draw on 359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIndex the index of this directional run 360e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start 361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the line-relative limit 362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the position of the run that is closest to the leading margin 364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 365e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width value is required. 368e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run, based on the paragraph direction. 369e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Only valid if needWidth is true. 370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private float drawRun(Canvas c, int runIndex, int start, 372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int limit, boolean runIsRtl, float x, int top, int y, int bottom, 373e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean needWidth) { 374e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { 376e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float w = -measureRun(runIndex, start, limit, limit, runIsRtl, null); 377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt handleRun(runIndex, start, limit, limit, runIsRtl, c, x + w, top, 3780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt y, bottom, null, false); 379e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return w; 380e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 381e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return handleRun(runIndex, start, limit, limit, runIsRtl, c, x, top, 3830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt y, bottom, null, needWidth); 384e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 385e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 386e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 387e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Measures a unidirectional (but possibly multi-styled) run of text. 388e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 389e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIndex the run index 390e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start of the run 391e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the offset to measure to, between start and limit inclusive 392e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the line-relative limit of the run 393e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 394e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information about the requested 395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * run, can be null. 396e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width from the start of the run to the leading edge 397e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * of the character at offset, based on the run (not paragraph) direction 398e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 399e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private float measureRun(int runIndex, int start, 400e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int offset, int limit, boolean runIsRtl, FontMetricsInt fmi) { 401e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return handleRun(runIndex, start, offset, limit, runIsRtl, null, 4020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 0, 0, 0, 0, fmi, true); 403e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 404e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 405e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 406e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Walk the cursor through this line, skipping conjuncts and 407e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * zero-width characters. 408e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 409e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>This function cannot properly walk the cursor off the ends of the line 410e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * since it does not know about any shaping on the previous/following line 411e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * that might affect the cursor position. Callers must either avoid these 412e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * situations or handle the result specially. 413e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 414e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param cursor the starting position of the cursor, between 0 and the 415e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * length of the line, inclusive 416e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param toLeft true if the caret is moving to the left. 417e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the new offset. If it is less than 0 or greater than the length 418e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * of the line, the previous/following line should be examined to get the 419e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * actual offset. 420e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 421e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int getOffsetToLeftRightOf(int cursor, boolean toLeft) { 422e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 1) The caret marks the leading edge of a character. The character 423e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // logically before it might be on a different level, and the active caret 424e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // position is on the character at the lower level. If that character 425e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // was the previous character, the caret is on its trailing edge. 426e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 2) Take this character/edge and move it in the indicated direction. 427e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // This gives you a new character and a new edge. 428e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 3) This position is between two visually adjacent characters. One of 429e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // these might be at a lower level. The active position is on the 430e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // character at the lower level. 431e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 4) If the active position is on the trailing edge of the character, 432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the new caret position is the following logical character, else it 433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // is the character. 434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineStart = 0; 436e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineEnd = mLen; 437e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean paraIsRtl = mDir == -1; 438e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 439e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 440e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1; 441e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean trailing = false; 442e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 443e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor == lineStart) { 444e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = -2; 445e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (cursor == lineEnd) { 446e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = runs.length; 447e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 448e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // First, get information about the run containing the character with 449e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the active caret. 450e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (runIndex = 0; runIndex < runs.length; runIndex += 2) { 451e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runStart = lineStart + runs[runIndex]; 452e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor >= runStart) { 453e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK); 454e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > lineEnd) { 455e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = lineEnd; 456e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 457e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor < runLimit) { 458e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) & 459e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Layout.RUN_LEVEL_MASK; 460e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor == runStart) { 461e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The caret is on a run boundary, see if we should 462e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // use the position on the trailing edge of the previous 463e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // logical character instead. 464e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit; 465e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int pos = cursor - 1; 466e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) { 467e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunStart = lineStart + runs[prevRunIndex]; 468e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (pos >= prevRunStart) { 469e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLimit = prevRunStart + 470e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK); 471e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (prevRunLimit > lineEnd) { 472e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLimit = lineEnd; 473e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 474e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (pos < prevRunLimit) { 475e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) 476e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt & Layout.RUN_LEVEL_MASK; 477e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (prevRunLevel < runLevel) { 478e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Start from logically previous character. 479e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = prevRunIndex; 480e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = prevRunLevel; 481e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runStart = prevRunStart; 482e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = prevRunLimit; 483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt trailing = true; 484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 485e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // caret might be == lineEnd. This is generally a space or paragraph 496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // separator and has an associated run, but might be the end of 497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // text, in which case it doesn't. If that happens, we ran off the 498e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // end of the run list, and runIndex == runs.length. In this case, 499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we are at a run boundary so we skip the below test. 500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runIndex != runs.length) { 501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runLevel & 0x1) != 0; 502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == runIsRtl; 503e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor != (advance ? runLimit : runStart) || advance != trailing) { 504e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Moving within or into the run, so we can move logically. 5050c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit, 5060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt runIsRtl, cursor, advance); 507e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // If the new position is internal to the run, we're at the strong 508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // position already so we're finished. 509e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret != (advance ? runLimit : runStart)) { 510e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return newCaret; 511e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 512e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 514e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 516e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // If newCaret is -1, we're starting at a run boundary and crossing 517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // into another run. Otherwise we've arrived at a run boundary, and 518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // need to figure out which character to attach to. Note we might 519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // need to run this twice, if we cross a run boundary and end up at 520e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // another run boundary. 521e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt while (true) { 522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == paraIsRtl; 523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunIndex = runIndex + (advance ? 2 : -2); 524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunIndex >= 0 && otherRunIndex < runs.length) { 525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunStart = lineStart + runs[otherRunIndex]; 526e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunLimit = otherRunStart + 527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK); 528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunLimit > lineEnd) { 529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt otherRunLimit = lineEnd; 530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) & 532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Layout.RUN_LEVEL_MASK; 533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean otherRunIsRtl = (otherRunLevel & 1) != 0; 534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 535e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt advance = toLeft == otherRunIsRtl; 536e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == -1) { 5370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart, 5380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt otherRunLimit, otherRunIsRtl, 539e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt advance ? otherRunStart : otherRunLimit, advance); 540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == (advance ? otherRunLimit : otherRunStart)) { 541e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Crossed and ended up at a new boundary, 542e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // repeat a second and final time. 543e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = otherRunIndex; 544e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = otherRunLevel; 545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 546e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 547e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The new caret is at a boundary. 551e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunLevel < runLevel) { 552e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The strong character is in the other run. 553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt newCaret = advance ? otherRunStart : otherRunLimit; 554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == -1) { 559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // We're walking off the end of the line. The paragraph 560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // level is always equal to or lower than any internal level, so 561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the boundaries get the strong caret. 5620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = advance ? mLen + 1 : -1; 563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Else we've arrived at the end of the line. That's a strong position. 567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // We might have arrived here by crossing over a run with no internal 568e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // breaks and dropping out of the above loop before advancing one final 569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // time, so reset the caret. 570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Note, we use '<=' below to handle a situation where the only run 571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // on the line is a counter-directional run. If we're not advancing, 572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we can end up at the 'lineEnd' position but the caret we want is at 573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the lineStart. 574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret <= lineEnd) { 575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt newCaret = advance ? lineEnd : lineStart; 576e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 577e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return newCaret; 581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 584e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the next valid offset within this directional run, skipping 585e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * conjuncts and zero-width characters. This should not be called to walk 5860c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * off the end of the line, since the the returned values might not be valid 5870c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * on neighboring lines. If the returned offset is less than zero or 5880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * greater than the line length, the offset should be recomputed on the 5890c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * preceding or following line, respectively. 590e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 591e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIndex the run index 5920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runStart the start of the run 5930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runLimit the limit of the run 5940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runIsRtl true if the run is right-to-left 595e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the offset 596e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param after true if the new offset should logically follow the provided 597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * offset 598e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the new offset 599e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 6000c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit, 6010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean runIsRtl, int offset, boolean after) { 602e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 6030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (runIndex < 0 || offset == (after ? mLen : 0)) { 6040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Walking off end of line. Since we don't know 6050c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // what cursor positions are available on other lines, we can't 6060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // return accurate values. These are a guess. 607e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (after) { 6080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart; 6090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart; 6110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt TextPaint wp = mWorkPaint; 6140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt wp.set(mPaint); 6150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanStart = runStart; 6170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanLimit; 6180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (mSpanned == null) { 6190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanLimit = runLimit; 6200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int target = after ? offset + 1 : offset; 6220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int limit = mStart + runLimit; 6230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt while (true) { 6240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit, 6250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan.class) - mStart; 6260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (spanLimit >= target) { 6270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt break; 628e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 6290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanStart = spanLimit; 6300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart, 6330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt mStart + spanLimit, MetricAffectingSpan.class); 6340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (spans.length > 0) { 6360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ReplacementSpan replacement = null; 6370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int j = 0; j < spans.length; j++) { 6380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan span = spans[j]; 6390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (span instanceof ReplacementSpan) { 6400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt replacement = (ReplacementSpan)span; 6410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt span.updateMeasureState(wp); 6430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (replacement != null) { 6470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // If we have a replacement span, we're moving either to 6480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // the start or end of this span. 6490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return after ? spanLimit : spanStart; 650e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 651e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 652e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 653e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 6540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; 6550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE; 6560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (mCharsValid) { 6570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart, 6580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt flags, offset, cursorOpt); 6590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return wp.getTextRunCursor(mText, mStart + spanStart, 661345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart; 662e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 663e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 664e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 665e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 666e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for measuring and rendering text. The text must 667e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * not include a tab or emoji. 668e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 669e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the working paint 670e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the text 6710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param end the end of the text 672e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 673e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null if rendering is not needed 674e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the edge of the run closest to the leading margin 675e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 676e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 677e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 678e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 679e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width of the run is needed 680e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 681e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 682e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 6830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private float handleText(TextPaint wp, int start, int end, 6840c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextStart, int contextEnd, boolean runIsRtl, 6850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt Canvas c, float x, int top, int y, int bottom, 686e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt FontMetricsInt fmi, boolean needWidth) { 687e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 688e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float ret = 0; 689e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 6900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int runLen = end - start; 6910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextLen = contextEnd - contextStart; 692e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (needWidth || (c != null && (wp.bgColor != 0 || runIsRtl))) { 6930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; 694e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 6950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ret = wp.getTextRunAdvances(mChars, start, runLen, 6960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt contextStart, contextLen, flags, null, 0); 697e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 6980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int delta = mStart; 6990c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ret = wp.getTextRunAdvances(mText, delta + start, 7000c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt delta + end, delta + contextStart, delta + contextEnd, 7010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt flags, null, 0); 702e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 703e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 704e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 705e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (fmi != null) { 706e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.getFontMetricsInt(fmi); 707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 708e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 709e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (c != null) { 710e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runIsRtl) { 711e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt x -= ret; 712e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 713e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 714e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (wp.bgColor != 0) { 715e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int color = wp.getColor(); 716e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Paint.Style s = wp.getStyle(); 717e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.setColor(wp.bgColor); 718e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.setStyle(Paint.Style.FILL); 719e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 720e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt c.drawRect(x, top, x + ret, bottom, wp); 721e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 722e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.setStyle(s); 723e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.setColor(color); 724e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 725e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 7260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl, 7270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x, y + wp.baselineShift); 728e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 729e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 730e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return runIsRtl ? -ret : ret; 731e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 732e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 733e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 734e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for measuring and rendering a replacement. 735e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 736e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param replacement the replacement 737e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the work paint 738e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIndex the run index 739e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the run 740e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the run 741e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 742e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null if not rendering 743e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the edge of the replacement closest to the leading margin 744e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 745e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 746e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 747e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 748e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width of the replacement is needed 749e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 750e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 751e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 752e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private float handleReplacement(ReplacementSpan replacement, TextPaint wp, 753e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runIndex, int start, int limit, boolean runIsRtl, Canvas c, 754e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float x, int top, int y, int bottom, FontMetricsInt fmi, 7550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean needWidth) { 756e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 757e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float ret = 0; 758e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 7590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int textStart = mStart + start; 7600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int textLimit = mStart + limit; 761e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 7620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (needWidth || (c != null && runIsRtl)) { 7630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ret = replacement.getSize(wp, mText, textStart, textLimit, fmi); 7640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 765e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 7660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (c != null) { 7670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (runIsRtl) { 7680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x -= ret; 769e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 7700c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt replacement.draw(c, mText, textStart, textLimit, 7710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x, top, y, bottom, wp); 772e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 773e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 774e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return runIsRtl ? -ret : ret; 775e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 776e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 777e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 778e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for handling a unidirectional run. The run must not 779e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * contain tabs or emoji but can contain styles. 780e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 781e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIndex the run index 782e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start of the run 7830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param measureLimit the offset to measure to, between start and limit inclusive 784e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the run 785e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 786e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null 787e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the end of the run closest to the leading margin 788e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 789e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 791e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 792e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width is required 793e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 794e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 795e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 7960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private float handleRun(int runIndex, int start, int measureLimit, 797e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int limit, boolean runIsRtl, Canvas c, float x, int top, int y, 7980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int bottom, FontMetricsInt fmi, boolean needWidth) { 799e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Shaping needs to take into account context up to metric boundaries, 801e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // but rendering needs to take into account character style boundaries. 8020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // So we iterate through metric runs to get metric bounds, 8030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // then within each metric run iterate through character style runs 8040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // for the run bounds. 805e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float ox = x; 8060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int i = start, inext; i < measureLimit; i = inext) { 807e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextPaint wp = mWorkPaint; 808e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.set(mPaint); 809e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int mlimit; 811e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mSpanned == null) { 812e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt inext = limit; 8130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt mlimit = measureLimit; 814e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 815e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt inext = mSpanned.nextSpanTransition(mStart + i, mStart + limit, 816e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MetricAffectingSpan.class) - mStart; 817e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt mlimit = inext < measureLimit ? inext : measureLimit; 819e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + i, 8200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt mStart + mlimit, MetricAffectingSpan.class); 821e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 822e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (spans.length > 0) { 823e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt ReplacementSpan replacement = null; 824e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int j = 0; j < spans.length; j++) { 825e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MetricAffectingSpan span = spans[j]; 826e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (span instanceof ReplacementSpan) { 827e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt replacement = (ReplacementSpan)span; 828e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 8290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // We might have a replacement that uses the draw 8300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // state, otherwise measure state would suffice. 8310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt span.updateDrawState(wp); 832e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 833e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 834e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 835e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (replacement != null) { 836e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt x += handleReplacement(replacement, wp, runIndex, i, 8370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt mlimit, runIsRtl, c, x, top, y, bottom, fmi, 8380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt needWidth || mlimit < measureLimit); 839e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 840e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 841e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 842e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 843e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (mSpanned == null || c == null) { 8450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x += handleText(wp, i, mlimit, i, inext, runIsRtl, c, x, top, 8460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt y, bottom, fmi, needWidth || mlimit < measureLimit); 8470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 8480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int j = i, jnext; j < mlimit; j = jnext) { 8490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt jnext = mSpanned.nextSpanTransition(mStart + j, 8500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt mStart + mlimit, CharacterStyle.class) - mStart; 851e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt CharacterStyle[] spans = mSpanned.getSpans(mStart + j, 8530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt mStart + jnext, CharacterStyle.class); 854e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt wp.set(mPaint); 8560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int k = 0; k < spans.length; k++) { 8570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt CharacterStyle span = spans[k]; 8580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt span.updateDrawState(wp); 859e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 8600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 8610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x, 8620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt top, y, bottom, fmi, needWidth || jnext < measureLimit); 863e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 864e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 865e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 866e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 867e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return x - ox; 868e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 869e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 870e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 871e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Render a text run with the set-up paint. 872e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 873e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas 874e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the paint used to render the text 8750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param start the start of the run 8760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param end the end of the run 8770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param contextStart the start of context for the run 8780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param contextEnd the end of the context for the run 879e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 880e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the x position of the left edge of the run 881e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline of the run 882e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 8830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private void drawTextRun(Canvas c, TextPaint wp, int start, int end, 8840c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextStart, int contextEnd, boolean runIsRtl, float x, int y) { 885e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 886f47d7405bbcb25d7cdf89ebb059f41520fe9ab87Doug Felt int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR; 887e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 8880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int count = end - start; 8890c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextCount = contextEnd - contextStart; 8900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt c.drawTextRun(mChars, start, count, contextStart, contextCount, 8910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x, y, flags, wp); 892e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 8930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int delta = mStart; 8940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt c.drawTextRun(mText, delta + start, delta + end, 8950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt delta + contextStart, delta + contextEnd, x, y, flags, wp); 896e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 897e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 898e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 899e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 900e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the ascent of the text at start. This is used for scaling 901e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * emoji. 902e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 903e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param pos the line-relative position 904e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the ascent of the text at start 905e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 906e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float ascent(int pos) { 907e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mSpanned == null) { 908e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return mPaint.ascent(); 909e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 910e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 911e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt pos += mStart; 912e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1, 913e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt MetricAffectingSpan.class); 914e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (spans.length == 0) { 915e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return mPaint.ascent(); 916e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 917e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 918e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextPaint wp = mWorkPaint; 919e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.set(mPaint); 920e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (MetricAffectingSpan span : spans) { 921e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt span.updateMeasureState(wp); 922e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 923e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return wp.ascent(); 924e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 925e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 926e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 927e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the next tab position. 928e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 929e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param h the (unsigned) offset from the leading margin 930e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the (unsigned) tab position after this offset 931e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 932e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float nextTab(float h) { 933c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mTabs != null) { 934c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return mTabs.nextTab(h); 935e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 936c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return TabStops.nextDefaultStop(h, TAB_INCREMENT); 937e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 938e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 939e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private static final int TAB_INCREMENT = 20; 940e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt} 941