TextLine.java revision 893d6fe48d37f71e683f722457bea646994a10bf
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 android.graphics.Bitmap; 20e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Canvas; 21e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint; 22e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint.FontMetricsInt; 23cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunneimport android.graphics.RectF; 24e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.Layout.Directions; 25c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.Layout.TabStops; 26e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.CharacterStyle; 27e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.MetricAffectingSpan; 28e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.ReplacementSpan; 29e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.util.Log; 30e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 31dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunneimport com.android.internal.util.ArrayUtils; 32dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 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 { 46bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private static final boolean DEBUG = false; 47bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy 48e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private TextPaint mPaint; 49e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private CharSequence mText; 50e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private int mStart; 51e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private int mLen; 52e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private int mDir; 53e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private Directions mDirections; 54e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private boolean mHasTabs; 55c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private TabStops mTabs; 56e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private char[] mChars; 57e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private boolean mCharsValid; 58e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private Spanned mSpanned; 59345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne private final TextPaint mWorkPaint = new TextPaint(); 60c1f44830809f0a8526855f13822702ea756214faGilles Debunne private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet = 61c1f44830809f0a8526855f13822702ea756214faGilles Debunne new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class); 62c1f44830809f0a8526855f13822702ea756214faGilles Debunne private final SpanSet<CharacterStyle> mCharacterStyleSpanSet = 63c1f44830809f0a8526855f13822702ea756214faGilles Debunne new SpanSet<CharacterStyle>(CharacterStyle.class); 64c1f44830809f0a8526855f13822702ea756214faGilles Debunne private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet = 65c1f44830809f0a8526855f13822702ea756214faGilles Debunne new SpanSet<ReplacementSpan>(ReplacementSpan.class); 66e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 67bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private static final TextLine[] sCached = new TextLine[3]; 68e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 69e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 70e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns a new TextLine from the shared pool. 71e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 72e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return an uninitialized TextLine 73e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 74e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt static TextLine obtain() { 75e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl; 76bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy synchronized (sCached) { 77bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy for (int i = sCached.length; --i >= 0;) { 78bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy if (sCached[i] != null) { 79bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy tl = sCached[i]; 80bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy sCached[i] = null; 81e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return tl; 82e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 83e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 84e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 85e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl = new TextLine(); 86bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy if (DEBUG) { 87bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy Log.v("TLINE", "new: " + tl); 88bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy } 89e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return tl; 90e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 91e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 92e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 93e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Puts a TextLine back into the shared pool. Do not use this TextLine once 94e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * it has been returned. 95e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param tl the textLine 96e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return null, as a convenience from clearing references to the provided 97e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * TextLine 98e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 99e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt static TextLine recycle(TextLine tl) { 100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.mText = null; 101e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.mPaint = null; 102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.mDirections = null; 103893d6fe48d37f71e683f722457bea646994a10bfSvet Ganov tl.mSpanned = null; 104893d6fe48d37f71e683f722457bea646994a10bfSvet Ganov tl.mTabs = null; 105893d6fe48d37f71e683f722457bea646994a10bfSvet Ganov tl.mChars = null; 106c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne 107c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne tl.mMetricAffectingSpanSpanSet.recycle(); 108c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne tl.mCharacterStyleSpanSet.recycle(); 109c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne tl.mReplacementSpanSpanSet.recycle(); 110c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne 111bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy synchronized(sCached) { 112bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy for (int i = 0; i < sCached.length; ++i) { 113bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy if (sCached[i] == null) { 114bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy sCached[i] = tl; 115f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne break; 116e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 117e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 118e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 119e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return null; 120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 121e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Initializes a TextLine and prepares it for use. 124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param paint the base paint for the line 126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param text the text, can be Styled 127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the line relative to the text 128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the line relative to the text 129e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param dir the paragraph direction of this line 130e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param directions the directions information of this line 131e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param hasTabs true if the line might contain tabs or emoji 132c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param tabStops the tabStops. Can be null. 133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 134e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt void set(TextPaint paint, CharSequence text, int start, int limit, int dir, 135c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Directions directions, boolean hasTabs, TabStops tabStops) { 136e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mPaint = paint; 137e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mText = text; 138e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mStart = start; 139e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mLen = limit - start; 140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mDir = dir; 141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mDirections = directions; 1428059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (mDirections == null) { 1438059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio throw new IllegalArgumentException("Directions cannot be null"); 1448059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 145e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mHasTabs = hasTabs; 146e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mSpanned = null; 147e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 148e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean hasReplacement = false; 149e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (text instanceof Spanned) { 150e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mSpanned = (Spanned) text; 151c1f44830809f0a8526855f13822702ea756214faGilles Debunne mReplacementSpanSpanSet.init(mSpanned, start, limit); 152c1f44830809f0a8526855f13822702ea756214faGilles Debunne hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; 153e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 154e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1551e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; 156e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 157e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 158e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mChars == null || mChars.length < mLen) { 159776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski mChars = ArrayUtils.newUnpaddedCharArray(mLen); 160e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 161e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextUtils.getChars(text, start, limit, mChars, 0); 1620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (hasReplacement) { 1630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Handle these all at once so we don't have to do it as we go. 1640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Replace the first character of each replacement run with the 1650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // object-replacement character and the remainder with zero width 1660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // non-break space aka BOM. Cursor movement code skips these 1670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // zero-width characters. 1680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt char[] chars = mChars; 1690c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int i = start, inext; i < limit; i = inext) { 170c1f44830809f0a8526855f13822702ea756214faGilles Debunne inext = mReplacementSpanSpanSet.getNextTransition(i, limit); 171c1f44830809f0a8526855f13822702ea756214faGilles Debunne if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) { 1721e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne // transition into a span 1730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt chars[i - start] = '\ufffc'; 1740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int j = i - start + 1, e = inext - start; j < e; ++j) { 1750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip 1760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1790c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 180e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 181c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt mTabs = tabStops; 182e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 183e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 185e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Renders the TextLine. 186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 187e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas to render on 188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the leading margin position 189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 190e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 193e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt void draw(Canvas c, float x, int top, int y, int bottom) { 194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (!mHasTabs) { 195e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { 196bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy drawRun(c, 0, mLen, false, x, top, y, bottom, false); 197e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return; 198e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 199e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { 200bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy drawRun(c, 0, mLen, true, x, top, y, bottom, false); 201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return; 202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float h = 0; 206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt RectF emojiRect = null; 208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lastRunIndex = runs.length - 2; 210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int i = 0; i < runs.length; i += 2) { 211e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runStart = runs[i]; 212e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK); 213e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > mLen) { 214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = mLen; 215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0; 217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int segstart = runStart; 219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { 220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int codept = 0; 221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Bitmap bm = null; 222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mHasTabs && j < runLimit) { 224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = mChars[j]; 225e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) { 226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = Character.codePointAt(mChars, j); 227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) { 228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept); 229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (codept > 0xffff) { 230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt ++j; 231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 236e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (j == runLimit || codept == '\t' || bm != null) { 237bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom, 238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt i != lastRunIndex || j != mLen); 239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept == '\t') { 241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h = mDir * nextTab(h * mDir); 242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (bm != null) { 243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float bmAscent = ascent(j); 244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float bitmapHeight = bm.getHeight(); 245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float scale = -bmAscent / bitmapHeight; 246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float width = bm.getWidth() * scale; 247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (emojiRect == null) { 249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt emojiRect = new RectF(); 250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt emojiRect.set(x + h, y + bmAscent, 252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt x + h + width, y); 253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt c.drawBitmap(bm, null, emojiRect, mPaint); 254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += width; 255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt j++; 256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt segstart = j + 1; 258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns metrics information for the entire line. 265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives font metrics information, can be null 267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the line 268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float metrics(FontMetricsInt fmi) { 270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return measure(mLen, false, fmi); 271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns information about a position on the line. 275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the line-relative character offset, between 0 and the 277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * line length, inclusive 278e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param trailing true to measure the trailing edge of the character 279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * before offset, false to measure the leading edge of the character 280e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * at offset. 281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information about the requested 282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * character, can be null. 283e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed offset from the leading margin to the requested 284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * character edge. 285e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float measure(int offset, boolean trailing, FontMetricsInt fmi) { 287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int target = trailing ? offset - 1 : offset; 288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (target < 0) { 289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return 0; 290e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float h = 0; 293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (!mHasTabs) { 295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { 296bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return measureRun(0, offset, mLen, false, fmi); 297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 298e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { 299bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return measureRun(0, offset, mLen, true, fmi); 300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt char[] chars = mChars; 304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int i = 0; i < runs.length; i += 2) { 306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runStart = runs[i]; 307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK); 308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > mLen) { 309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = mLen; 310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0; 312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int segstart = runStart; 314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { 315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int codept = 0; 316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Bitmap bm = null; 317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mHasTabs && j < runLimit) { 319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = chars[j]; 320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) { 321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = Character.codePointAt(chars, j); 322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) { 323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept); 324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (codept > 0xffff) { 325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt ++j; 326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 328e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 329e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 330e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (j == runLimit || codept == '\t' || bm != null) { 332e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean inSegment = target >= segstart && target < j; 333e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 334e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; 335e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (inSegment && advance) { 336bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return h += measureRun(segstart, offset, j, runIsRtl, fmi); 337e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 338e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 339bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy float w = measureRun(segstart, j, j, runIsRtl, fmi); 340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += advance ? w : -w; 341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (inSegment) { 343bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return h += measureRun(segstart, offset, j, runIsRtl, null); 344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept == '\t') { 347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (offset == j) { 348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h = mDir * nextTab(h * mDir); 351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (target == j) { 352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (bm != null) { 357e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float bmAscent = ascent(j); 358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float wid = bm.getWidth() * -bmAscent / bm.getHeight(); 359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += mDir * wid; 360e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt j++; 361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt segstart = j + 1; 364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 365e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 368e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 369e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Draws a unidirectional (but possibly multi-styled) run of text. 373e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 374bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas to draw on 376e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start 377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the line-relative limit 378e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 379e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the position of the run that is closest to the leading margin 380e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 381e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 383e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width value is required. 384e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run, based on the paragraph direction. 385e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Only valid if needWidth is true. 386e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 387bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private float drawRun(Canvas c, int start, 388e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int limit, boolean runIsRtl, float x, int top, int y, int bottom, 389e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean needWidth) { 390e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 391e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { 392bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy float w = -measureRun(start, limit, limit, runIsRtl, null); 393bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy handleRun(start, limit, limit, runIsRtl, c, x + w, top, 3940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt y, bottom, null, false); 395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return w; 396e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 397e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 398bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return handleRun(start, limit, limit, runIsRtl, c, x, top, 3990c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt y, bottom, null, needWidth); 400e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 401e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 402e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 403e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Measures a unidirectional (but possibly multi-styled) run of text. 404e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 405bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 406e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start of the run 407e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the offset to measure to, between start and limit inclusive 408e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the line-relative limit of the run 409e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 410e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information about the requested 411e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * run, can be null. 412e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width from the start of the run to the leading edge 413e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * of the character at offset, based on the run (not paragraph) direction 414e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 415bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private float measureRun(int start, int offset, int limit, boolean runIsRtl, 416bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy FontMetricsInt fmi) { 417bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true); 418e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 419e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 420e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 421e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Walk the cursor through this line, skipping conjuncts and 422e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * zero-width characters. 423e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 424e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>This function cannot properly walk the cursor off the ends of the line 425e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * since it does not know about any shaping on the previous/following line 426e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * that might affect the cursor position. Callers must either avoid these 427e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * situations or handle the result specially. 428e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 429e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param cursor the starting position of the cursor, between 0 and the 430e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * length of the line, inclusive 431e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param toLeft true if the caret is moving to the left. 432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the new offset. If it is less than 0 or greater than the length 433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * of the line, the previous/following line should be examined to get the 434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * actual offset. 435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 436e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int getOffsetToLeftRightOf(int cursor, boolean toLeft) { 437e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 1) The caret marks the leading edge of a character. The character 438e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // logically before it might be on a different level, and the active caret 439e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // position is on the character at the lower level. If that character 440e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // was the previous character, the caret is on its trailing edge. 441e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 2) Take this character/edge and move it in the indicated direction. 442e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // This gives you a new character and a new edge. 443e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 3) This position is between two visually adjacent characters. One of 444e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // these might be at a lower level. The active position is on the 445e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // character at the lower level. 446e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 4) If the active position is on the trailing edge of the character, 447e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the new caret position is the following logical character, else it 448e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // is the character. 449e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 450e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineStart = 0; 451e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineEnd = mLen; 452e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean paraIsRtl = mDir == -1; 453e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 454e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 455e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1; 456e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean trailing = false; 457e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 458e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor == lineStart) { 459e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = -2; 460e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (cursor == lineEnd) { 461e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = runs.length; 462e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 463e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // First, get information about the run containing the character with 464e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the active caret. 465e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (runIndex = 0; runIndex < runs.length; runIndex += 2) { 466e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runStart = lineStart + runs[runIndex]; 467e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor >= runStart) { 468e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK); 469e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > lineEnd) { 470e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = lineEnd; 471e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 472e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor < runLimit) { 473e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) & 474e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Layout.RUN_LEVEL_MASK; 475e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor == runStart) { 476e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The caret is on a run boundary, see if we should 477e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // use the position on the trailing edge of the previous 478e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // logical character instead. 479e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit; 480e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int pos = cursor - 1; 481e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) { 482e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunStart = lineStart + runs[prevRunIndex]; 483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (pos >= prevRunStart) { 484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLimit = prevRunStart + 485e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK); 486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (prevRunLimit > lineEnd) { 487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLimit = lineEnd; 488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (pos < prevRunLimit) { 490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) 491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt & Layout.RUN_LEVEL_MASK; 492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (prevRunLevel < runLevel) { 493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Start from logically previous character. 494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = prevRunIndex; 495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = prevRunLevel; 496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runStart = prevRunStart; 497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = prevRunLimit; 498e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt trailing = true; 499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 503e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 504e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 505e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 506e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 507e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 509e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 510e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // caret might be == lineEnd. This is generally a space or paragraph 511e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // separator and has an associated run, but might be the end of 512e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // text, in which case it doesn't. If that happens, we ran off the 513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // end of the run list, and runIndex == runs.length. In this case, 514e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we are at a run boundary so we skip the below test. 515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runIndex != runs.length) { 516e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runLevel & 0x1) != 0; 517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == runIsRtl; 518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor != (advance ? runLimit : runStart) || advance != trailing) { 519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Moving within or into the run, so we can move logically. 5200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit, 5210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt runIsRtl, cursor, advance); 522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // If the new position is internal to the run, we're at the strong 523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // position already so we're finished. 524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret != (advance ? runLimit : runStart)) { 525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return newCaret; 526e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // If newCaret is -1, we're starting at a run boundary and crossing 532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // into another run. Otherwise we've arrived at a run boundary, and 533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // need to figure out which character to attach to. Note we might 534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // need to run this twice, if we cross a run boundary and end up at 535e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // another run boundary. 536e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt while (true) { 537e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == paraIsRtl; 538e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunIndex = runIndex + (advance ? 2 : -2); 539e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunIndex >= 0 && otherRunIndex < runs.length) { 540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunStart = lineStart + runs[otherRunIndex]; 541e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunLimit = otherRunStart + 542e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK); 543e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunLimit > lineEnd) { 544e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt otherRunLimit = lineEnd; 545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 546e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) & 547e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Layout.RUN_LEVEL_MASK; 548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean otherRunIsRtl = (otherRunLevel & 1) != 0; 549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt advance = toLeft == otherRunIsRtl; 551e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == -1) { 5520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart, 5530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt otherRunLimit, otherRunIsRtl, 554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt advance ? otherRunStart : otherRunLimit, advance); 555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == (advance ? otherRunLimit : otherRunStart)) { 556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Crossed and ended up at a new boundary, 557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // repeat a second and final time. 558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = otherRunIndex; 559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = otherRunLevel; 560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 562e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The new caret is at a boundary. 566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunLevel < runLevel) { 567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The strong character is in the other run. 568e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt newCaret = advance ? otherRunStart : otherRunLimit; 569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == -1) { 574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // We're walking off the end of the line. The paragraph 575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // level is always equal to or lower than any internal level, so 576e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the boundaries get the strong caret. 5770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = advance ? mLen + 1 : -1; 578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Else we've arrived at the end of the line. That's a strong position. 582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // We might have arrived here by crossing over a run with no internal 583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // breaks and dropping out of the above loop before advancing one final 584e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // time, so reset the caret. 585e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Note, we use '<=' below to handle a situation where the only run 586e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // on the line is a counter-directional run. If we're not advancing, 587e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we can end up at the 'lineEnd' position but the caret we want is at 588e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the lineStart. 589e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret <= lineEnd) { 590e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt newCaret = advance ? lineEnd : lineStart; 591e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 592e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 593e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 594e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 595e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return newCaret; 596e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 598e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 599e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the next valid offset within this directional run, skipping 600e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * conjuncts and zero-width characters. This should not be called to walk 601cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunne * off the end of the line, since the returned values might not be valid 6020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * on neighboring lines. If the returned offset is less than zero or 6030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * greater than the line length, the offset should be recomputed on the 6040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * preceding or following line, respectively. 605e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 606e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIndex the run index 6070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runStart the start of the run 6080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runLimit the limit of the run 6090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runIsRtl true if the run is right-to-left 610e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the offset 611e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param after true if the new offset should logically follow the provided 612e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * offset 613e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the new offset 614e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 6150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit, 6160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean runIsRtl, int offset, boolean after) { 617e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 6180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (runIndex < 0 || offset == (after ? mLen : 0)) { 6190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Walking off end of line. Since we don't know 6200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // what cursor positions are available on other lines, we can't 6210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // return accurate values. These are a guess. 622e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (after) { 6230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart; 6240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart; 6260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt TextPaint wp = mWorkPaint; 6290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt wp.set(mPaint); 6300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanStart = runStart; 6320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanLimit; 6330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (mSpanned == null) { 6340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanLimit = runLimit; 6350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int target = after ? offset + 1 : offset; 6370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int limit = mStart + runLimit; 6380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt while (true) { 6390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit, 6400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan.class) - mStart; 6410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (spanLimit >= target) { 6420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt break; 643e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 6440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanStart = spanLimit; 6450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart, 6480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt mStart + spanLimit, MetricAffectingSpan.class); 6491e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class); 6500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (spans.length > 0) { 6520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ReplacementSpan replacement = null; 6530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int j = 0; j < spans.length; j++) { 6540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan span = spans[j]; 6550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (span instanceof ReplacementSpan) { 6560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt replacement = (ReplacementSpan)span; 6570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt span.updateMeasureState(wp); 6590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (replacement != null) { 6630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // If we have a replacement span, we're moving either to 6640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // the start or end of this span. 6650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return after ? spanLimit : spanStart; 666e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 667e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 668e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 669e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 670051910b9f998030dacb8a0722588cc715813fde1Raph Levien int dir = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; 6710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE; 6720c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (mCharsValid) { 6730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart, 674051910b9f998030dacb8a0722588cc715813fde1Raph Levien dir, offset, cursorOpt); 6750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return wp.getTextRunCursor(mText, mStart + spanStart, 677051910b9f998030dacb8a0722588cc715813fde1Raph Levien mStart + spanLimit, dir, mStart + offset, cursorOpt) - mStart; 678e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 679e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 680e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 681e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 6820bb000931bb841e75903d655552d1626ae158707Gilles Debunne * @param wp 6830bb000931bb841e75903d655552d1626ae158707Gilles Debunne */ 6840bb000931bb841e75903d655552d1626ae158707Gilles Debunne private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) { 6850bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousTop = fmi.top; 6860bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousAscent = fmi.ascent; 6870bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousDescent = fmi.descent; 6880bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousBottom = fmi.bottom; 6890bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousLeading = fmi.leading; 6900bb000931bb841e75903d655552d1626ae158707Gilles Debunne 6910bb000931bb841e75903d655552d1626ae158707Gilles Debunne wp.getFontMetricsInt(fmi); 6920bb000931bb841e75903d655552d1626ae158707Gilles Debunne 6938a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom, 6948a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousLeading); 6958a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio } 6968a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 6978a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio static void updateMetrics(FontMetricsInt fmi, int previousTop, int previousAscent, 6988a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousDescent, int previousBottom, int previousLeading) { 6990bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.top = Math.min(fmi.top, previousTop); 7000bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.ascent = Math.min(fmi.ascent, previousAscent); 7010bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.descent = Math.max(fmi.descent, previousDescent); 7020bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.bottom = Math.max(fmi.bottom, previousBottom); 7030bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.leading = Math.max(fmi.leading, previousLeading); 7040bb000931bb841e75903d655552d1626ae158707Gilles Debunne } 7050bb000931bb841e75903d655552d1626ae158707Gilles Debunne 7060bb000931bb841e75903d655552d1626ae158707Gilles Debunne /** 707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for measuring and rendering text. The text must 708e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * not include a tab or emoji. 709e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 710e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the working paint 711e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the text 7120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param end the end of the text 713e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 714e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null if rendering is not needed 715e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the edge of the run closest to the leading margin 716e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 717e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 718e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 719e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 720e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width of the run is needed 721e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 722e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 723e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 7240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private float handleText(TextPaint wp, int start, int end, 7250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextStart, int contextEnd, boolean runIsRtl, 7260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt Canvas c, float x, int top, int y, int bottom, 727e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt FontMetricsInt fmi, boolean needWidth) { 728e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 729850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio // Get metrics first (even for empty strings or "0" width runs) 730850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio if (fmi != null) { 731850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio expandMetricsFromPaint(fmi, wp); 732850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio } 733e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 7340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int runLen = end - start; 735850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio // No need to do anything if the run width is "0" 736850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio if (runLen == 0) { 737850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio return 0f; 738850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio } 739850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio 740850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio float ret = 0; 741850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio 7420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextLen = contextEnd - contextStart; 743c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) { 744e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 7450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ret = wp.getTextRunAdvances(mChars, start, runLen, 746051910b9f998030dacb8a0722588cc715813fde1Raph Levien contextStart, contextLen, runIsRtl, null, 0); 747e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 7480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int delta = mStart; 749da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio ret = wp.getTextRunAdvances(mText, delta + start, 750da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio delta + end, delta + contextStart, delta + contextEnd, 751051910b9f998030dacb8a0722588cc715813fde1Raph Levien runIsRtl, null, 0); 752e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 753e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 754e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 755e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (c != null) { 756e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runIsRtl) { 757e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt x -= ret; 758e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 759e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 760e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (wp.bgColor != 0) { 761dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne int previousColor = wp.getColor(); 762dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne Paint.Style previousStyle = wp.getStyle(); 763dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 764e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.setColor(wp.bgColor); 765e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.setStyle(Paint.Style.FILL); 766e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt c.drawRect(x, top, x + ret, bottom, wp); 767e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 768dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setStyle(previousStyle); 769dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setColor(previousColor); 770dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne } 771dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 772c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne if (wp.underlineColor != 0) { 773dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h 774e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize(); 775dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 776dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne int previousColor = wp.getColor(); 777dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne Paint.Style previousStyle = wp.getStyle(); 778e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin boolean previousAntiAlias = wp.isAntiAlias(); 779dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 780dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setStyle(Paint.Style.FILL); 781e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin wp.setAntiAlias(true); 782e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin 783c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne wp.setColor(wp.underlineColor); 784c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThickness, wp); 785dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 786dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setStyle(previousStyle); 787dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setColor(previousColor); 788e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin wp.setAntiAlias(previousAntiAlias); 789e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 791da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl, 792da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio x, y + wp.baselineShift); 793e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 794e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 795e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return runIsRtl ? -ret : ret; 796e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 797e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 798e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 799e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for measuring and rendering a replacement. 800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 801bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 802e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param replacement the replacement 803e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the work paint 804e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the run 805e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the run 806e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 807e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null if not rendering 808e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the edge of the replacement closest to the leading margin 809e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 810e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 811e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 812e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 813e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width of the replacement is needed 814e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 815e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 816e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 817e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private float handleReplacement(ReplacementSpan replacement, TextPaint wp, 818bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy int start, int limit, boolean runIsRtl, Canvas c, 819e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float x, int top, int y, int bottom, FontMetricsInt fmi, 8200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean needWidth) { 821e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 822e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float ret = 0; 823e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int textStart = mStart + start; 8250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int textLimit = mStart + limit; 826e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (needWidth || (c != null && runIsRtl)) { 8288a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousTop = 0; 8298a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousAscent = 0; 8308a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousDescent = 0; 8318a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousBottom = 0; 8328a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousLeading = 0; 8338a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8348a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio boolean needUpdateMetrics = (fmi != null); 8358a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8368a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio if (needUpdateMetrics) { 8378a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousTop = fmi.top; 8388a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousAscent = fmi.ascent; 8398a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousDescent = fmi.descent; 8408a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousBottom = fmi.bottom; 8418a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousLeading = fmi.leading; 8428a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio } 8438a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ret = replacement.getSize(wp, mText, textStart, textLimit, fmi); 8458a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8468a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio if (needUpdateMetrics) { 8478a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom, 8488a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousLeading); 8498a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio } 8500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 851e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (c != null) { 8530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (runIsRtl) { 8540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x -= ret; 855e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 8560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt replacement.draw(c, mText, textStart, textLimit, 8570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x, top, y, bottom, wp); 858e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 859e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 860e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return runIsRtl ? -ret : ret; 861e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 862945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 863e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 864e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for handling a unidirectional run. The run must not 865e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * contain tabs or emoji but can contain styles. 866e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 867bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 868e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start of the run 8690c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param measureLimit the offset to measure to, between start and limit inclusive 870e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the run 871e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 872e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null 873e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the end of the run closest to the leading margin 874e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 875e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 876e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 877e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 878e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width is required 879e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 880e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 881e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 882bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private float handleRun(int start, int measureLimit, 883e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int limit, boolean runIsRtl, Canvas c, float x, int top, int y, 8840c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int bottom, FontMetricsInt fmi, boolean needWidth) { 885e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 886f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne // Case of an empty line, make sure we update fmi according to mPaint 887f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne if (start == measureLimit) { 888f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne TextPaint wp = mWorkPaint; 889f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne wp.set(mPaint); 89015c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio if (fmi != null) { 89115c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio expandMetricsFromPaint(fmi, wp); 89215c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio } 89315c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio return 0f; 894f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne } 895f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne 896945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (mSpanned == null) { 897945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne TextPaint wp = mWorkPaint; 898945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne wp.set(mPaint); 899945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final int mlimit = measureLimit; 900945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top, 901945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne y, bottom, fmi, needWidth || mlimit < measureLimit); 902945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 903945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 904c1f44830809f0a8526855f13822702ea756214faGilles Debunne mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); 905c1f44830809f0a8526855f13822702ea756214faGilles Debunne mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); 906945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 907e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Shaping needs to take into account context up to metric boundaries, 908e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // but rendering needs to take into account character style boundaries. 9090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // So we iterate through metric runs to get metric bounds, 9100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // then within each metric run iterate through character style runs 9110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // for the run bounds. 912945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final float originalX = x; 9130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int i = start, inext; i < measureLimit; i = inext) { 914e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextPaint wp = mWorkPaint; 915e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.set(mPaint); 916e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 917c1f44830809f0a8526855f13822702ea756214faGilles Debunne inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) - 918c1f44830809f0a8526855f13822702ea756214faGilles Debunne mStart; 919945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne int mlimit = Math.min(inext, measureLimit); 920945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 921945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne ReplacementSpan replacement = null; 922945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 923c1f44830809f0a8526855f13822702ea756214faGilles Debunne for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) { 924945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT 925945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // empty by construction. This special case in getSpans() explains the >= & <= tests 926c1f44830809f0a8526855f13822702ea756214faGilles Debunne if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) || 927c1f44830809f0a8526855f13822702ea756214faGilles Debunne (mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue; 928c1f44830809f0a8526855f13822702ea756214faGilles Debunne MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j]; 929945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (span instanceof ReplacementSpan) { 930945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne replacement = (ReplacementSpan)span; 931945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } else { 932945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // We might have a replacement that uses the draw 933945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // state, otherwise measure state would suffice. 934945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne span.updateDrawState(wp); 935e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 936e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 937e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 938945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (replacement != null) { 939945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y, 940945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne bottom, fmi, needWidth || mlimit < measureLimit); 941945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne continue; 942945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 943945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 94442ef515d185d4fc038d602172789cc264f1d9960Raph Levien for (int j = i, jnext; j < mlimit; j = jnext) { 94542ef515d185d4fc038d602172789cc264f1d9960Raph Levien jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) - 94642ef515d185d4fc038d602172789cc264f1d9960Raph Levien mStart; 94742ef515d185d4fc038d602172789cc264f1d9960Raph Levien 94842ef515d185d4fc038d602172789cc264f1d9960Raph Levien wp.set(mPaint); 94942ef515d185d4fc038d602172789cc264f1d9960Raph Levien for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) { 95042ef515d185d4fc038d602172789cc264f1d9960Raph Levien // Intentionally using >= and <= as explained above 95142ef515d185d4fc038d602172789cc264f1d9960Raph Levien if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) || 95242ef515d185d4fc038d602172789cc264f1d9960Raph Levien (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue; 9530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 95442ef515d185d4fc038d602172789cc264f1d9960Raph Levien CharacterStyle span = mCharacterStyleSpanSet.spans[k]; 95542ef515d185d4fc038d602172789cc264f1d9960Raph Levien span.updateDrawState(wp); 956e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 95742ef515d185d4fc038d602172789cc264f1d9960Raph Levien 95842ef515d185d4fc038d602172789cc264f1d9960Raph Levien x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x, 95942ef515d185d4fc038d602172789cc264f1d9960Raph Levien top, y, bottom, fmi, needWidth || jnext < measureLimit); 960e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 961e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 962e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 963945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne return x - originalX; 964e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 965e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 966e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 967e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Render a text run with the set-up paint. 968e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 969e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas 970e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the paint used to render the text 9710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param start the start of the run 9720c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param end the end of the run 9730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param contextStart the start of context for the run 9740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param contextEnd the end of the context for the run 975da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio * @param runIsRtl true if the run is right-to-left 976e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the x position of the left edge of the run 977e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline of the run 978e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 9790c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private void drawTextRun(Canvas c, TextPaint wp, int start, int end, 980da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio int contextStart, int contextEnd, boolean runIsRtl, float x, int y) { 981e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 982e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 9830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int count = end - start; 9840c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextCount = contextEnd - contextStart; 9850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt c.drawTextRun(mChars, start, count, contextStart, contextCount, 986051910b9f998030dacb8a0722588cc715813fde1Raph Levien x, y, runIsRtl, wp); 987e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 9880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int delta = mStart; 9890c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt c.drawTextRun(mText, delta + start, delta + end, 990051910b9f998030dacb8a0722588cc715813fde1Raph Levien delta + contextStart, delta + contextEnd, x, y, runIsRtl, wp); 991e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 992e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 993e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 994e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 995e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the ascent of the text at start. This is used for scaling 996e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * emoji. 997e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 998e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param pos the line-relative position 999e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the ascent of the text at start 1000e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 1001e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float ascent(int pos) { 1002e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mSpanned == null) { 1003e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return mPaint.ascent(); 1004e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1005e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1006e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt pos += mStart; 1007945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1, MetricAffectingSpan.class); 1008e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (spans.length == 0) { 1009e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return mPaint.ascent(); 1010e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1011e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1012e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextPaint wp = mWorkPaint; 1013e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.set(mPaint); 1014e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (MetricAffectingSpan span : spans) { 1015e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt span.updateMeasureState(wp); 1016e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1017e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return wp.ascent(); 1018e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1019e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1020e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 1021e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the next tab position. 1022e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 1023e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param h the (unsigned) offset from the leading margin 1024e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the (unsigned) tab position after this offset 1025e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 1026e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float nextTab(float h) { 1027c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mTabs != null) { 1028c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return mTabs.nextTab(h); 1029e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1030c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return TabStops.nextDefaultStop(h, TAB_INCREMENT); 1031e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1032e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1033e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private static final int TAB_INCREMENT = 20; 1034e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt} 1035