TextLine.java revision 09da71a6dcfe07e0efdc35933322fba16091f555
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.Canvas; 20e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint; 21e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint.FontMetricsInt; 22e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.Layout.Directions; 23c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.Layout.TabStops; 24e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.CharacterStyle; 25e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.MetricAffectingSpan; 26e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.ReplacementSpan; 27e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.util.Log; 28e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 29dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunneimport com.android.internal.util.ArrayUtils; 30dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 31e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/** 32e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Represents a line of styled text, for measuring in visual order and 33e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * for rendering. 34e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 35e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Get a new instance using obtain(), and when finished with it, return it 36e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * to the pool using recycle(). 37e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 38e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Call set to prepare the instance for use, then either draw, measure, 39e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * metrics, or caretToLeftRightOf. 40e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 41e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @hide 42e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 43e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltclass TextLine { 44bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private static final boolean DEBUG = false; 45bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy 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; 5709da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka 5809da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka // Additional width of whitespace for justification. This value is per whitespace, thus 5909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka // the line width will increase by mAddedWidth x (number of stretchable whitespaces). 6009da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka private float mAddedWidth; 61345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne private final TextPaint mWorkPaint = new TextPaint(); 62c1f44830809f0a8526855f13822702ea756214faGilles Debunne private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet = 63c1f44830809f0a8526855f13822702ea756214faGilles Debunne new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class); 64c1f44830809f0a8526855f13822702ea756214faGilles Debunne private final SpanSet<CharacterStyle> mCharacterStyleSpanSet = 65c1f44830809f0a8526855f13822702ea756214faGilles Debunne new SpanSet<CharacterStyle>(CharacterStyle.class); 66c1f44830809f0a8526855f13822702ea756214faGilles Debunne private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet = 67c1f44830809f0a8526855f13822702ea756214faGilles Debunne new SpanSet<ReplacementSpan>(ReplacementSpan.class); 68e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 69bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private static final TextLine[] sCached = new TextLine[3]; 70e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 71e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 72e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns a new TextLine from the shared pool. 73e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 74e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return an uninitialized TextLine 75e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 76e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt static TextLine obtain() { 77e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl; 78bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy synchronized (sCached) { 79bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy for (int i = sCached.length; --i >= 0;) { 80bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy if (sCached[i] != null) { 81bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy tl = sCached[i]; 82bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy sCached[i] = null; 83e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return tl; 84e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 85e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 86e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 87e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl = new TextLine(); 88bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy if (DEBUG) { 89bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy Log.v("TLINE", "new: " + tl); 90bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy } 91e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return tl; 92e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 93e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 94e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 95e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Puts a TextLine back into the shared pool. Do not use this TextLine once 96e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * it has been returned. 97e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param tl the textLine 98e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return null, as a convenience from clearing references to the provided 99e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * TextLine 100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 101e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt static TextLine recycle(TextLine tl) { 102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.mText = null; 103e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.mPaint = null; 104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.mDirections = null; 105893d6fe48d37f71e683f722457bea646994a10bfSvet Ganov tl.mSpanned = null; 106893d6fe48d37f71e683f722457bea646994a10bfSvet Ganov tl.mTabs = null; 107893d6fe48d37f71e683f722457bea646994a10bfSvet Ganov tl.mChars = null; 108c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne 109c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne tl.mMetricAffectingSpanSpanSet.recycle(); 110c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne tl.mCharacterStyleSpanSet.recycle(); 111c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne tl.mReplacementSpanSpanSet.recycle(); 112c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne 113bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy synchronized(sCached) { 114bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy for (int i = 0; i < sCached.length; ++i) { 115bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy if (sCached[i] == null) { 116bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy sCached[i] = tl; 117f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne break; 118e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 119e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 121e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return null; 122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Initializes a TextLine and prepares it for use. 126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param paint the base paint for the line 128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param text the text, can be Styled 129e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the line relative to the text 130e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the line relative to the text 131e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param dir the paragraph direction of this line 132e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param directions the directions information of this line 133112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader * @param hasTabs true if the line might contain tabs 134c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param tabStops the tabStops. Can be null. 135e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 136e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt void set(TextPaint paint, CharSequence text, int start, int limit, int dir, 137c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Directions directions, boolean hasTabs, TabStops tabStops) { 138e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mPaint = paint; 139e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mText = text; 140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mStart = start; 141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mLen = limit - start; 142e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mDir = dir; 143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mDirections = directions; 1448059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (mDirections == null) { 1458059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio throw new IllegalArgumentException("Directions cannot be null"); 1468059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 147e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mHasTabs = hasTabs; 148e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mSpanned = null; 149e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 150e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean hasReplacement = false; 151e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (text instanceof Spanned) { 152e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mSpanned = (Spanned) text; 153c1f44830809f0a8526855f13822702ea756214faGilles Debunne mReplacementSpanSpanSet.init(mSpanned, start, limit); 154c1f44830809f0a8526855f13822702ea756214faGilles Debunne hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; 155e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 156e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1571e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; 158e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 159e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 160e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mChars == null || mChars.length < mLen) { 161776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski mChars = ArrayUtils.newUnpaddedCharArray(mLen); 162e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 163e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextUtils.getChars(text, start, limit, mChars, 0); 1640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (hasReplacement) { 1650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Handle these all at once so we don't have to do it as we go. 1660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Replace the first character of each replacement run with the 1670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // object-replacement character and the remainder with zero width 1680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // non-break space aka BOM. Cursor movement code skips these 1690c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // zero-width characters. 1700c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt char[] chars = mChars; 1710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int i = start, inext; i < limit; i = inext) { 172c1f44830809f0a8526855f13822702ea756214faGilles Debunne inext = mReplacementSpanSpanSet.getNextTransition(i, limit); 173c1f44830809f0a8526855f13822702ea756214faGilles Debunne if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) { 1741e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne // transition into a span 1750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt chars[i - start] = '\ufffc'; 1760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int j = i - start + 1, e = inext - start; j < e; ++j) { 1770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip 1780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1790c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1800c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 182e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 183c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt mTabs = tabStops; 18409da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka mAddedWidth = 0; 18509da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka } 18609da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka 18709da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka /** 18809da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka * Justify the line to the given width. 18909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka */ 19009da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka void justify(float justifyWidth) { 19109da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka int end = mLen; 19209da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka while (end > 0 && isLineEndSpace(mText.charAt(mStart + end - 1))) { 19309da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka end--; 19409da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka } 19509da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka final int spaces = countStretchableSpaces(0, end); 19609da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka if (spaces == 0) { 19709da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka // There are no stretchable spaces, so we can't help the justification by adding any 19809da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka // width. 19909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka return; 20009da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka } 20109da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka final float width = Math.abs(measure(end, false, null)); 20209da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka mAddedWidth = (justifyWidth - width) / spaces; 203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Renders the TextLine. 207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas to render on 209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the leading margin position 210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 211e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 212e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 213e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt void draw(Canvas c, float x, int top, int y, int bottom) { 215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (!mHasTabs) { 216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { 217bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy drawRun(c, 0, mLen, false, x, top, y, bottom, false); 218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return; 219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { 221bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy drawRun(c, 0, mLen, true, x, top, y, bottom, false); 222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return; 223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 225e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float h = 0; 227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lastRunIndex = runs.length - 2; 230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int i = 0; i < runs.length; i += 2) { 231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runStart = runs[i]; 232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK); 233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > mLen) { 234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = mLen; 235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 236e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0; 237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int segstart = runStart; 239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { 240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int codept = 0; 241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mHasTabs && j < runLimit) { 242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = mChars[j]; 243112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) { 244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = Character.codePointAt(mChars, j); 245112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader if (codept > 0xFFFF) { 246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt ++j; 247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 252112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader if (j == runLimit || codept == '\t') { 253bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom, 254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt i != lastRunIndex || j != mLen); 255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept == '\t') { 257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h = mDir * nextTab(h * mDir); 258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt segstart = j + 1; 260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns metrics information for the entire line. 267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives font metrics information, can be null 269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the line 270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float metrics(FontMetricsInt fmi) { 272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return measure(mLen, false, fmi); 273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns information about a position on the line. 277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 278e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the line-relative character offset, between 0 and the 279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * line length, inclusive 280e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param trailing true to measure the trailing edge of the character 281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * before offset, false to measure the leading edge of the character 282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * at offset. 283e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information about the requested 284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * character, can be null. 285e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed offset from the leading margin to the requested 286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * character edge. 287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float measure(int offset, boolean trailing, FontMetricsInt fmi) { 289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int target = trailing ? offset - 1 : offset; 290e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (target < 0) { 291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return 0; 292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float h = 0; 295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (!mHasTabs) { 297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { 298bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return measureRun(0, offset, mLen, false, fmi); 299e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { 301bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return measureRun(0, offset, mLen, true, fmi); 302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt char[] chars = mChars; 306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int i = 0; i < runs.length; i += 2) { 308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runStart = runs[i]; 309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK); 310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > mLen) { 311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = mLen; 312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0; 314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int segstart = runStart; 316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { 317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int codept = 0; 318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mHasTabs && j < runLimit) { 319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = chars[j]; 320112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) { 321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = Character.codePointAt(chars, j); 322112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader if (codept > 0xFFFF) { 323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt ++j; 324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 328e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 329112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader if (j == runLimit || codept == '\t') { 330e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean inSegment = target >= segstart && target < j; 331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 332e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; 333e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (inSegment && advance) { 334bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return h += measureRun(segstart, offset, j, runIsRtl, fmi); 335e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 337bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy float w = measureRun(segstart, j, j, runIsRtl, fmi); 338e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += advance ? w : -w; 339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (inSegment) { 341bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return h += measureRun(segstart, offset, j, runIsRtl, null); 342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 343e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept == '\t') { 345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (offset == j) { 346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h = mDir * nextTab(h * mDir); 349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (target == j) { 350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt segstart = j + 1; 355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 357e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 360e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Draws a unidirectional (but possibly multi-styled) run of text. 364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 365bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas to draw on 367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start 368e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the line-relative limit 369e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the position of the run that is closest to the leading margin 371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 373e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 374e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width value is required. 375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run, based on the paragraph direction. 376e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Only valid if needWidth is true. 377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 378bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private float drawRun(Canvas c, int start, 379e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int limit, boolean runIsRtl, float x, int top, int y, int bottom, 380e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean needWidth) { 381e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { 383bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy float w = -measureRun(start, limit, limit, runIsRtl, null); 384bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy handleRun(start, limit, limit, runIsRtl, c, x + w, top, 3850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt y, bottom, null, false); 386e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return w; 387e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 388e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 389bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return handleRun(start, limit, limit, runIsRtl, c, x, top, 3900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt y, bottom, null, needWidth); 391e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 392e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 393e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 394e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Measures a unidirectional (but possibly multi-styled) run of text. 395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 396bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 397e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start of the run 398e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the offset to measure to, between start and limit inclusive 399e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the line-relative limit of the run 400e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 401e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information about the requested 402e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * run, can be null. 403e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width from the start of the run to the leading edge 404e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * of the character at offset, based on the run (not paragraph) direction 405e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 406bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private float measureRun(int start, int offset, int limit, boolean runIsRtl, 407bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy FontMetricsInt fmi) { 408bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true); 409e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 410e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 411e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 412e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Walk the cursor through this line, skipping conjuncts and 413e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * zero-width characters. 414e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 415e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>This function cannot properly walk the cursor off the ends of the line 416e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * since it does not know about any shaping on the previous/following line 417e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * that might affect the cursor position. Callers must either avoid these 418e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * situations or handle the result specially. 419e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 420e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param cursor the starting position of the cursor, between 0 and the 421e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * length of the line, inclusive 422e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param toLeft true if the caret is moving to the left. 423e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the new offset. If it is less than 0 or greater than the length 424e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * of the line, the previous/following line should be examined to get the 425e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * actual offset. 426e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 427e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int getOffsetToLeftRightOf(int cursor, boolean toLeft) { 428e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 1) The caret marks the leading edge of a character. The character 429e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // logically before it might be on a different level, and the active caret 430e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // position is on the character at the lower level. If that character 431e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // was the previous character, the caret is on its trailing edge. 432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 2) Take this character/edge and move it in the indicated direction. 433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // This gives you a new character and a new edge. 434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 3) This position is between two visually adjacent characters. One of 435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // these might be at a lower level. The active position is on the 436e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // character at the lower level. 437e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 4) If the active position is on the trailing edge of the character, 438e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the new caret position is the following logical character, else it 439e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // is the character. 440e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 441e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineStart = 0; 442e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineEnd = mLen; 443e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean paraIsRtl = mDir == -1; 444e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 445e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 446e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1; 447e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean trailing = false; 448e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 449e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor == lineStart) { 450e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = -2; 451e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (cursor == lineEnd) { 452e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = runs.length; 453e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 454e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // First, get information about the run containing the character with 455e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the active caret. 456e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (runIndex = 0; runIndex < runs.length; runIndex += 2) { 457e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runStart = lineStart + runs[runIndex]; 458e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor >= runStart) { 459e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK); 460e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > lineEnd) { 461e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = lineEnd; 462e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 463e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor < runLimit) { 464e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) & 465e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Layout.RUN_LEVEL_MASK; 466e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor == runStart) { 467e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The caret is on a run boundary, see if we should 468e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // use the position on the trailing edge of the previous 469e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // logical character instead. 470e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit; 471e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int pos = cursor - 1; 472e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) { 473e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunStart = lineStart + runs[prevRunIndex]; 474e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (pos >= prevRunStart) { 475e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLimit = prevRunStart + 476e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK); 477e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (prevRunLimit > lineEnd) { 478e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLimit = lineEnd; 479e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 480e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (pos < prevRunLimit) { 481e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) 482e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt & Layout.RUN_LEVEL_MASK; 483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (prevRunLevel < runLevel) { 484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Start from logically previous character. 485e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = prevRunIndex; 486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = prevRunLevel; 487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runStart = prevRunStart; 488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = prevRunLimit; 489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt trailing = true; 490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 498e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // caret might be == lineEnd. This is generally a space or paragraph 502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // separator and has an associated run, but might be the end of 503e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // text, in which case it doesn't. If that happens, we ran off the 504e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // end of the run list, and runIndex == runs.length. In this case, 505e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we are at a run boundary so we skip the below test. 506e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runIndex != runs.length) { 507e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runLevel & 0x1) != 0; 508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == runIsRtl; 509e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor != (advance ? runLimit : runStart) || advance != trailing) { 510e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Moving within or into the run, so we can move logically. 5110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit, 5120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt runIsRtl, cursor, advance); 513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // If the new position is internal to the run, we're at the strong 514e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // position already so we're finished. 515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret != (advance ? runLimit : runStart)) { 516e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return newCaret; 517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 520e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 521e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // If newCaret is -1, we're starting at a run boundary and crossing 523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // into another run. Otherwise we've arrived at a run boundary, and 524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // need to figure out which character to attach to. Note we might 525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // need to run this twice, if we cross a run boundary and end up at 526e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // another run boundary. 527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt while (true) { 528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == paraIsRtl; 529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunIndex = runIndex + (advance ? 2 : -2); 530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunIndex >= 0 && otherRunIndex < runs.length) { 531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunStart = lineStart + runs[otherRunIndex]; 532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunLimit = otherRunStart + 533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK); 534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunLimit > lineEnd) { 535e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt otherRunLimit = lineEnd; 536e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 537e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) & 538e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Layout.RUN_LEVEL_MASK; 539e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean otherRunIsRtl = (otherRunLevel & 1) != 0; 540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 541e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt advance = toLeft == otherRunIsRtl; 542e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == -1) { 5430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart, 5440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt otherRunLimit, otherRunIsRtl, 545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt advance ? otherRunStart : otherRunLimit, advance); 546e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == (advance ? otherRunLimit : otherRunStart)) { 547e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Crossed and ended up at a new boundary, 548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // repeat a second and final time. 549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = otherRunIndex; 550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = otherRunLevel; 551e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 552e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The new caret is at a boundary. 557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunLevel < runLevel) { 558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The strong character is in the other run. 559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt newCaret = advance ? otherRunStart : otherRunLimit; 560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 562e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == -1) { 565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // We're walking off the end of the line. The paragraph 566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // level is always equal to or lower than any internal level, so 567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the boundaries get the strong caret. 5680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = advance ? mLen + 1 : -1; 569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Else we've arrived at the end of the line. That's a strong position. 573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // We might have arrived here by crossing over a run with no internal 574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // breaks and dropping out of the above loop before advancing one final 575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // time, so reset the caret. 576e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Note, we use '<=' below to handle a situation where the only run 577e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // on the line is a counter-directional run. If we're not advancing, 578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we can end up at the 'lineEnd' position but the caret we want is at 579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the lineStart. 580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret <= lineEnd) { 581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt newCaret = advance ? lineEnd : lineStart; 582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 584e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 585e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 586e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return newCaret; 587e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 588e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 589e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 590e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the next valid offset within this directional run, skipping 591e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * conjuncts and zero-width characters. This should not be called to walk 592cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunne * off the end of the line, since the returned values might not be valid 5930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * on neighboring lines. If the returned offset is less than zero or 5940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * greater than the line length, the offset should be recomputed on the 5950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * preceding or following line, respectively. 596e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIndex the run index 5980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runStart the start of the run 5990c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runLimit the limit of the run 6000c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runIsRtl true if the run is right-to-left 601e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the offset 602e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param after true if the new offset should logically follow the provided 603e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * offset 604e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the new offset 605e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 6060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit, 6070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean runIsRtl, int offset, boolean after) { 608e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 6090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (runIndex < 0 || offset == (after ? mLen : 0)) { 6100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Walking off end of line. Since we don't know 6110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // what cursor positions are available on other lines, we can't 6120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // return accurate values. These are a guess. 613e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (after) { 6140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart; 6150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart; 6170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt TextPaint wp = mWorkPaint; 6200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt wp.set(mPaint); 62109da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka wp.setWordSpacing(mAddedWidth); 6220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanStart = runStart; 6240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanLimit; 6250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (mSpanned == null) { 6260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanLimit = runLimit; 6270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int target = after ? offset + 1 : offset; 6290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int limit = mStart + runLimit; 6300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt while (true) { 6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit, 6320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan.class) - mStart; 6330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (spanLimit >= target) { 6340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt break; 635e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 6360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanStart = spanLimit; 6370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart, 6400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt mStart + spanLimit, MetricAffectingSpan.class); 6411e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class); 6420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (spans.length > 0) { 6440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ReplacementSpan replacement = null; 6450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int j = 0; j < spans.length; j++) { 6460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan span = spans[j]; 6470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (span instanceof ReplacementSpan) { 6480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt replacement = (ReplacementSpan)span; 6490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt span.updateMeasureState(wp); 6510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (replacement != null) { 6550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // If we have a replacement span, we're moving either to 6560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // the start or end of this span. 6570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return after ? spanLimit : spanStart; 658e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 659e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 660e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 661e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 662051910b9f998030dacb8a0722588cc715813fde1Raph Levien int dir = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; 6630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE; 6640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (mCharsValid) { 6650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart, 666051910b9f998030dacb8a0722588cc715813fde1Raph Levien dir, offset, cursorOpt); 6670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return wp.getTextRunCursor(mText, mStart + spanStart, 669051910b9f998030dacb8a0722588cc715813fde1Raph Levien mStart + spanLimit, dir, mStart + offset, cursorOpt) - mStart; 670e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 671e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 672e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 673e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 6740bb000931bb841e75903d655552d1626ae158707Gilles Debunne * @param wp 6750bb000931bb841e75903d655552d1626ae158707Gilles Debunne */ 6760bb000931bb841e75903d655552d1626ae158707Gilles Debunne private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) { 6770bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousTop = fmi.top; 6780bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousAscent = fmi.ascent; 6790bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousDescent = fmi.descent; 6800bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousBottom = fmi.bottom; 6810bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousLeading = fmi.leading; 6820bb000931bb841e75903d655552d1626ae158707Gilles Debunne 6830bb000931bb841e75903d655552d1626ae158707Gilles Debunne wp.getFontMetricsInt(fmi); 6840bb000931bb841e75903d655552d1626ae158707Gilles Debunne 6858a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom, 6868a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousLeading); 6878a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio } 6888a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 6898a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio static void updateMetrics(FontMetricsInt fmi, int previousTop, int previousAscent, 6908a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousDescent, int previousBottom, int previousLeading) { 6910bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.top = Math.min(fmi.top, previousTop); 6920bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.ascent = Math.min(fmi.ascent, previousAscent); 6930bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.descent = Math.max(fmi.descent, previousDescent); 6940bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.bottom = Math.max(fmi.bottom, previousBottom); 6950bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.leading = Math.max(fmi.leading, previousLeading); 6960bb000931bb841e75903d655552d1626ae158707Gilles Debunne } 6970bb000931bb841e75903d655552d1626ae158707Gilles Debunne 6980bb000931bb841e75903d655552d1626ae158707Gilles Debunne /** 699e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for measuring and rendering text. The text must 700112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader * not include a tab. 701e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 702e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the working paint 703e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the text 7040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param end the end of the text 705e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 706e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null if rendering is not needed 707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the edge of the run closest to the leading margin 708e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 709e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 710e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 711e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 712e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width of the run is needed 713909c7bca570b6f50650d0872e2037389b29252e3Raph Levien * @param offset the offset for the purpose of measuring 714e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 715e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 716e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 7170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private float handleText(TextPaint wp, int start, int end, 7180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextStart, int contextEnd, boolean runIsRtl, 7190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt Canvas c, float x, int top, int y, int bottom, 720909c7bca570b6f50650d0872e2037389b29252e3Raph Levien FontMetricsInt fmi, boolean needWidth, int offset) { 721e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 72209da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka wp.setWordSpacing(mAddedWidth); 723850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio // Get metrics first (even for empty strings or "0" width runs) 724850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio if (fmi != null) { 725850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio expandMetricsFromPaint(fmi, wp); 726850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio } 727e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 7280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int runLen = end - start; 729850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio // No need to do anything if the run width is "0" 730850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio if (runLen == 0) { 731850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio return 0f; 732850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio } 733850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio 734850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio float ret = 0; 735850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio 736c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) { 737e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 738ea2b4a40f7236172398ebcaa273612e00340d847Raph Levien ret = wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, 739909c7bca570b6f50650d0872e2037389b29252e3Raph Levien runIsRtl, offset); 740e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 7410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int delta = mStart; 742ea2b4a40f7236172398ebcaa273612e00340d847Raph Levien ret = wp.getRunAdvance(mText, delta + start, delta + end, 743909c7bca570b6f50650d0872e2037389b29252e3Raph Levien delta + contextStart, delta + contextEnd, runIsRtl, delta + offset); 744e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 745e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 746e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 747e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (c != null) { 748e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runIsRtl) { 749e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt x -= ret; 750e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 751e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 752e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (wp.bgColor != 0) { 753dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne int previousColor = wp.getColor(); 754dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne Paint.Style previousStyle = wp.getStyle(); 755dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 756e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.setColor(wp.bgColor); 757e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.setStyle(Paint.Style.FILL); 758e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt c.drawRect(x, top, x + ret, bottom, wp); 759e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 760dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setStyle(previousStyle); 761dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setColor(previousColor); 762dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne } 763dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 764c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne if (wp.underlineColor != 0) { 765dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h 766e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize(); 767dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 768dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne int previousColor = wp.getColor(); 769dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne Paint.Style previousStyle = wp.getStyle(); 770e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin boolean previousAntiAlias = wp.isAntiAlias(); 771dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 772dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setStyle(Paint.Style.FILL); 773e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin wp.setAntiAlias(true); 774e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin 775c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne wp.setColor(wp.underlineColor); 776c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThickness, wp); 777dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 778dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setStyle(previousStyle); 779dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setColor(previousColor); 780e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin wp.setAntiAlias(previousAntiAlias); 781e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 782e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 783da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl, 784da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio x, y + wp.baselineShift); 785e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 786e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 787e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return runIsRtl ? -ret : ret; 788e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 789e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 791e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for measuring and rendering a replacement. 792e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 793bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 794e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param replacement the replacement 795e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the work paint 796e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the run 797e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the run 798e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 799e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null if not rendering 800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the edge of the replacement closest to the leading margin 801e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 802e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 803e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 804e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 805e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width of the replacement is needed 806e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 807e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 808e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 809e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private float handleReplacement(ReplacementSpan replacement, TextPaint wp, 810bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy int start, int limit, boolean runIsRtl, Canvas c, 811e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float x, int top, int y, int bottom, FontMetricsInt fmi, 8120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean needWidth) { 813e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 814e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float ret = 0; 815e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int textStart = mStart + start; 8170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int textLimit = mStart + limit; 818e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (needWidth || (c != null && runIsRtl)) { 8208a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousTop = 0; 8218a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousAscent = 0; 8228a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousDescent = 0; 8238a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousBottom = 0; 8248a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousLeading = 0; 8258a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8268a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio boolean needUpdateMetrics = (fmi != null); 8278a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8288a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio if (needUpdateMetrics) { 8298a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousTop = fmi.top; 8308a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousAscent = fmi.ascent; 8318a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousDescent = fmi.descent; 8328a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousBottom = fmi.bottom; 8338a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousLeading = fmi.leading; 8348a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio } 8358a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ret = replacement.getSize(wp, mText, textStart, textLimit, fmi); 8378a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8388a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio if (needUpdateMetrics) { 8398a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom, 8408a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousLeading); 8418a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio } 8420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 843e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (c != null) { 8450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (runIsRtl) { 8460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x -= ret; 847e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 8480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt replacement.draw(c, mText, textStart, textLimit, 8490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x, top, y, bottom, wp); 850e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 851e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 852e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return runIsRtl ? -ret : ret; 853e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 854945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 855e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 856e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for handling a unidirectional run. The run must not 857112d9c7f116bec0a52badde81bd778e59e88cb63Roozbeh Pournader * contain tabs but can contain styles. 858e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 859bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 860e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start of the run 8610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param measureLimit the offset to measure to, between start and limit inclusive 862e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the run 863e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 864e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null 865e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the end of the run closest to the leading margin 866e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 867e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 868e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 869e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 870e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width is required 871e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 872e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 873e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 874bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private float handleRun(int start, int measureLimit, 875e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int limit, boolean runIsRtl, Canvas c, float x, int top, int y, 8760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int bottom, FontMetricsInt fmi, boolean needWidth) { 877e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8789f3958cc2ba3da1406caac64650620d226bf8562Siyamed Sinir if (measureLimit < start || measureLimit > limit) { 8799f3958cc2ba3da1406caac64650620d226bf8562Siyamed Sinir throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of " 8809f3958cc2ba3da1406caac64650620d226bf8562Siyamed Sinir + "start (" + start + ") and limit (" + limit + ") bounds"); 8819f3958cc2ba3da1406caac64650620d226bf8562Siyamed Sinir } 8829f3958cc2ba3da1406caac64650620d226bf8562Siyamed Sinir 883f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne // Case of an empty line, make sure we update fmi according to mPaint 884f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne if (start == measureLimit) { 885f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne TextPaint wp = mWorkPaint; 886f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne wp.set(mPaint); 88715c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio if (fmi != null) { 88815c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio expandMetricsFromPaint(fmi, wp); 88915c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio } 89015c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio return 0f; 891f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne } 892f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne 893945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (mSpanned == null) { 894945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne TextPaint wp = mWorkPaint; 895945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne wp.set(mPaint); 896945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final int mlimit = measureLimit; 897909c7bca570b6f50650d0872e2037389b29252e3Raph Levien return handleText(wp, start, limit, start, limit, runIsRtl, c, x, top, 898909c7bca570b6f50650d0872e2037389b29252e3Raph Levien y, bottom, fmi, needWidth || mlimit < measureLimit, mlimit); 899945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 900945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 901c1f44830809f0a8526855f13822702ea756214faGilles Debunne mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); 902c1f44830809f0a8526855f13822702ea756214faGilles Debunne mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); 903945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 904e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Shaping needs to take into account context up to metric boundaries, 905e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // but rendering needs to take into account character style boundaries. 9060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // So we iterate through metric runs to get metric bounds, 9070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // then within each metric run iterate through character style runs 9080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // for the run bounds. 909945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final float originalX = x; 9100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int i = start, inext; i < measureLimit; i = inext) { 911e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextPaint wp = mWorkPaint; 912e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.set(mPaint); 913e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 914c1f44830809f0a8526855f13822702ea756214faGilles Debunne inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) - 915c1f44830809f0a8526855f13822702ea756214faGilles Debunne mStart; 916945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne int mlimit = Math.min(inext, measureLimit); 917945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 918945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne ReplacementSpan replacement = null; 919945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 920c1f44830809f0a8526855f13822702ea756214faGilles Debunne for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) { 921945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT 922945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // empty by construction. This special case in getSpans() explains the >= & <= tests 923c1f44830809f0a8526855f13822702ea756214faGilles Debunne if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) || 924c1f44830809f0a8526855f13822702ea756214faGilles Debunne (mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue; 925c1f44830809f0a8526855f13822702ea756214faGilles Debunne MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j]; 926945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (span instanceof ReplacementSpan) { 927945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne replacement = (ReplacementSpan)span; 928945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } else { 929945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // We might have a replacement that uses the draw 930945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // state, otherwise measure state would suffice. 931945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne span.updateDrawState(wp); 932e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 933e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 934e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 935945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (replacement != null) { 936945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y, 937945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne bottom, fmi, needWidth || mlimit < measureLimit); 938945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne continue; 939945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 940945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 94142ef515d185d4fc038d602172789cc264f1d9960Raph Levien for (int j = i, jnext; j < mlimit; j = jnext) { 942909c7bca570b6f50650d0872e2037389b29252e3Raph Levien jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) - 94342ef515d185d4fc038d602172789cc264f1d9960Raph Levien mStart; 944909c7bca570b6f50650d0872e2037389b29252e3Raph Levien int offset = Math.min(jnext, mlimit); 94542ef515d185d4fc038d602172789cc264f1d9960Raph Levien 94642ef515d185d4fc038d602172789cc264f1d9960Raph Levien wp.set(mPaint); 94742ef515d185d4fc038d602172789cc264f1d9960Raph Levien for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) { 94842ef515d185d4fc038d602172789cc264f1d9960Raph Levien // Intentionally using >= and <= as explained above 949909c7bca570b6f50650d0872e2037389b29252e3Raph Levien if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) || 95042ef515d185d4fc038d602172789cc264f1d9960Raph Levien (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue; 9510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 95242ef515d185d4fc038d602172789cc264f1d9960Raph Levien CharacterStyle span = mCharacterStyleSpanSet.spans[k]; 95342ef515d185d4fc038d602172789cc264f1d9960Raph Levien span.updateDrawState(wp); 954e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 95542ef515d185d4fc038d602172789cc264f1d9960Raph Levien 95626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien // Only draw hyphen on last run in line 95726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien if (jnext < mLen) { 95826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien wp.setHyphenEdit(0); 95926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien } 96042ef515d185d4fc038d602172789cc264f1d9960Raph Levien x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x, 961909c7bca570b6f50650d0872e2037389b29252e3Raph Levien top, y, bottom, fmi, needWidth || jnext < measureLimit, offset); 962e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 963e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 964e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 965945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne return x - originalX; 966e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 967e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 968e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 969e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Render a text run with the set-up paint. 970e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 971e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas 972e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the paint used to render the text 9730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param start the start of the run 9740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param end the end of the run 9750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param contextStart the start of context for the run 9760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param contextEnd the end of the context for the run 977da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio * @param runIsRtl true if the run is right-to-left 978e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the x position of the left edge of the run 979e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline of the run 980e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 9810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private void drawTextRun(Canvas c, TextPaint wp, int start, int end, 982da12f389eb4be0c08ca3fa9ca7663f4977858df5Fabrice Di Meglio int contextStart, int contextEnd, boolean runIsRtl, float x, int y) { 983e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 984e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 9850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int count = end - start; 9860c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextCount = contextEnd - contextStart; 9870c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt c.drawTextRun(mChars, start, count, contextStart, contextCount, 988051910b9f998030dacb8a0722588cc715813fde1Raph Levien x, y, runIsRtl, wp); 989e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 9900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int delta = mStart; 9910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt c.drawTextRun(mText, delta + start, delta + end, 992051910b9f998030dacb8a0722588cc715813fde1Raph Levien delta + contextStart, delta + contextEnd, x, y, runIsRtl, wp); 993e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 994e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 995e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 996e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 997e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the next tab position. 998e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 999e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param h the (unsigned) offset from the leading margin 1000e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the (unsigned) tab position after this offset 1001e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 1002e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float nextTab(float h) { 1003c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mTabs != null) { 1004c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return mTabs.nextTab(h); 1005e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1006c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return TabStops.nextDefaultStop(h, TAB_INCREMENT); 1007e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1008e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 100909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka private boolean isStretchableWhitespace(int ch) { 101009da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka // TODO: Support other stretchable whitespace. (Bug: 34013491) 101109da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka return ch == 0x0020 || ch == 0x00A0; 101209da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka } 101309da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka 101409da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka private int nextStretchableSpace(int start, int end) { 101509da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka for (int i = start; i < end; i++) { 101609da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka final char c = mCharsValid ? mChars[i] : mText.charAt(i + mStart); 101709da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka if (isStretchableWhitespace(c)) return i; 101809da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka } 101909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka return end; 102009da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka } 102109da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka 102209da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka /* Return the number of spaces in the text line, for the purpose of justification */ 102309da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka private int countStretchableSpaces(int start, int end) { 102409da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka int count = 0; 102509da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka for (int i = start; i < end; i = nextStretchableSpace(i + 1, end)) { 102609da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka count++; 102709da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka } 102809da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka return count; 102909da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka } 103009da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka 103109da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace() 103209da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka public static boolean isLineEndSpace(char ch) { 103309da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka return ch == ' ' || ch == '\t' || ch == 0x1680 103409da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka || (0x2000 <= ch && ch <= 0x200A && ch != 0x2007) 103509da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka || ch == 0x205F || ch == 0x3000; 103609da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka } 103709da71a6dcfe07e0efdc35933322fba16091f555Seigo Nonaka 1038e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private static final int TAB_INCREMENT = 20; 1039e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt} 1040