StaticLayout.java revision a6a082862b9e2ea4c9e9a1a945927c4040993f6e
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19531c30c62b14881aab31a5133920a971b1fbb50eRaph Levienimport android.annotation.Nullable; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Paint; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.LeadingMarginSpan; 226611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunneimport android.text.style.LeadingMarginSpan.LeadingMarginSpan2; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.LineHeightSpan; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.MetricAffectingSpan; 25c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.style.TabStopSpan; 268059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglioimport android.util.Log; 2739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levienimport android.util.Pools.SynchronizedPool; 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 29cb379120456d8065d742021fc5c66748fc8a11a8Doug Feltimport com.android.internal.util.ArrayUtils; 30776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinskiimport com.android.internal.util.GrowingArrayUtils; 31cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 32c8f9e6218681640d5f384c12edf06619be56a583Anish Athalyeimport java.util.Arrays; 334c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levienimport java.util.Locale; 34c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * StaticLayout is a Layout for text that will not be edited after it 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * is laid out. Use {@link DynamicLayout} for text that may change. 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>This is used by widgets to control text layout. You should not need 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * to use this class directly unless you are implementing your own widget 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * or custom display object, or would be tempted to call 414e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, 424e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * float, float, android.graphics.Paint) 434e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * Canvas.drawText()} directly.</p> 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 45121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Megliopublic class StaticLayout extends Layout { 46121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio 478059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio static final String TAG = "StaticLayout"; 488059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio 49d3ab692d28018825578ff05832644cfad60233fbRaph Levien /** 50531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Builder for static layouts. The builder is a newer pattern for constructing 51531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * StaticLayout objects and should be preferred over the constructors, 52531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * particularly to access newer features. To build a static layout, first 53531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * call {@link #obtain} with the required arguments (text, paint, and width), 54531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * then call setters for optional parameters, and finally {@link #build} 55531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * to build the StaticLayout object. Parameters not explicitly set will get 56531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * default values. 57d3ab692d28018825578ff05832644cfad60233fbRaph Levien */ 58d3ab692d28018825578ff05832644cfad60233fbRaph Levien public final static class Builder { 594c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien private Builder() { 604c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien mNativePtr = nNewBuilder(); 614c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien } 624c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien 63531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 64531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Obtain a builder for constructing StaticLayout objects 65531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 66531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param source The text to be laid out, optionally with spans 67531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param start The index of the start of the text 68531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param end The index + 1 of the end of the text 69531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param paint The base paint used for layout 70531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param width The width in pixels 71531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return a builder object used for constructing the StaticLayout 72531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 73ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien public static Builder obtain(CharSequence source, int start, int end, TextPaint paint, 74ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien int width) { 7539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien Builder b = sPool.acquire(); 76d3ab692d28018825578ff05832644cfad60233fbRaph Levien if (b == null) { 77d3ab692d28018825578ff05832644cfad60233fbRaph Levien b = new Builder(); 78d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 79d3ab692d28018825578ff05832644cfad60233fbRaph Levien 80d3ab692d28018825578ff05832644cfad60233fbRaph Levien // set default initial values 8139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien b.mText = source; 8239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien b.mStart = start; 8339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien b.mEnd = end; 84ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien b.mPaint = paint; 8539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien b.mWidth = width; 8639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien b.mAlignment = Alignment.ALIGN_NORMAL; 87d3ab692d28018825578ff05832644cfad60233fbRaph Levien b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; 88d3ab692d28018825578ff05832644cfad60233fbRaph Levien b.mSpacingMult = 1.0f; 89d3ab692d28018825578ff05832644cfad60233fbRaph Levien b.mSpacingAdd = 0.0f; 90d3ab692d28018825578ff05832644cfad60233fbRaph Levien b.mIncludePad = true; 9139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien b.mEllipsizedWidth = width; 92d3ab692d28018825578ff05832644cfad60233fbRaph Levien b.mEllipsize = null; 93d3ab692d28018825578ff05832644cfad60233fbRaph Levien b.mMaxLines = Integer.MAX_VALUE; 943bd60c7b1125ee42cd7e8746aeaebdb43e8211d8Raph Levien b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; 9595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; 96d3ab692d28018825578ff05832644cfad60233fbRaph Levien 97d3ab692d28018825578ff05832644cfad60233fbRaph Levien b.mMeasuredText = MeasuredText.obtain(); 98d3ab692d28018825578ff05832644cfad60233fbRaph Levien return b; 99d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 100d3ab692d28018825578ff05832644cfad60233fbRaph Levien 10139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien private static void recycle(Builder b) { 102d3ab692d28018825578ff05832644cfad60233fbRaph Levien b.mPaint = null; 103d3ab692d28018825578ff05832644cfad60233fbRaph Levien b.mText = null; 104d3ab692d28018825578ff05832644cfad60233fbRaph Levien MeasuredText.recycle(b.mMeasuredText); 1053bd60c7b1125ee42cd7e8746aeaebdb43e8211d8Raph Levien b.mMeasuredText = null; 1063bd60c7b1125ee42cd7e8746aeaebdb43e8211d8Raph Levien nFinishBuilder(b.mNativePtr); 10739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien sPool.release(b); 108d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 109d3ab692d28018825578ff05832644cfad60233fbRaph Levien 110d3ab692d28018825578ff05832644cfad60233fbRaph Levien // release any expensive state 111d3ab692d28018825578ff05832644cfad60233fbRaph Levien /* package */ void finish() { 1124c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien nFinishBuilder(mNativePtr); 113d3ab692d28018825578ff05832644cfad60233fbRaph Levien mMeasuredText.finish(); 114d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 115d3ab692d28018825578ff05832644cfad60233fbRaph Levien 116d3ab692d28018825578ff05832644cfad60233fbRaph Levien public Builder setText(CharSequence source) { 117d3ab692d28018825578ff05832644cfad60233fbRaph Levien return setText(source, 0, source.length()); 118d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 119d3ab692d28018825578ff05832644cfad60233fbRaph Levien 120531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 121531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set the text. Only useful when re-using the builder, which is done for 122531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * the internal implementation of {@link DynamicLayout} but not as part 123531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * of normal {@link StaticLayout} usage. 124531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 125531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param source The text to be laid out, optionally with spans 126531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param start The index of the start of the text 127531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param end The index + 1 of the end of the text 128531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 129531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 130531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @hide 131531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 132d3ab692d28018825578ff05832644cfad60233fbRaph Levien public Builder setText(CharSequence source, int start, int end) { 133d3ab692d28018825578ff05832644cfad60233fbRaph Levien mText = source; 134d3ab692d28018825578ff05832644cfad60233fbRaph Levien mStart = start; 135d3ab692d28018825578ff05832644cfad60233fbRaph Levien mEnd = end; 136d3ab692d28018825578ff05832644cfad60233fbRaph Levien return this; 137d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 138d3ab692d28018825578ff05832644cfad60233fbRaph Levien 139531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 140531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set the paint. Internal for reuse cases only. 141531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 142531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param paint The base paint used for layout 143531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 144531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 145531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @hide 146531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 147d3ab692d28018825578ff05832644cfad60233fbRaph Levien public Builder setPaint(TextPaint paint) { 148d3ab692d28018825578ff05832644cfad60233fbRaph Levien mPaint = paint; 149d3ab692d28018825578ff05832644cfad60233fbRaph Levien return this; 150d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 151d3ab692d28018825578ff05832644cfad60233fbRaph Levien 152531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 153531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set the width. Internal for reuse cases only. 154531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 155531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param width The width in pixels 156531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 157531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 158531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @hide 159531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 160d3ab692d28018825578ff05832644cfad60233fbRaph Levien public Builder setWidth(int width) { 161d3ab692d28018825578ff05832644cfad60233fbRaph Levien mWidth = width; 162d3ab692d28018825578ff05832644cfad60233fbRaph Levien if (mEllipsize == null) { 163d3ab692d28018825578ff05832644cfad60233fbRaph Levien mEllipsizedWidth = width; 164d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 165d3ab692d28018825578ff05832644cfad60233fbRaph Levien return this; 166d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 167d3ab692d28018825578ff05832644cfad60233fbRaph Levien 168531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 169531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set the alignment. The default is {@link Layout.Alignment#ALIGN_NORMAL}. 170531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 171531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param alignment Alignment for the resulting {@link StaticLayout} 172531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 173531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 17439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien public Builder setAlignment(Alignment alignment) { 17539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien mAlignment = alignment; 17639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien return this; 17739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien } 17839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 179531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 180531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set the text direction heuristic. The text direction heuristic is used to 181531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * resolve text direction based per-paragraph based on the input text. The default is 182531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}. 183531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 184531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param textDir text direction heuristic for resolving BiDi behavior. 185531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 186531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 187a6a082862b9e2ea4c9e9a1a945927c4040993f6eRaph Levien public Builder setTextDirection(TextDirectionHeuristic textDir) { 188d3ab692d28018825578ff05832644cfad60233fbRaph Levien mTextDir = textDir; 189d3ab692d28018825578ff05832644cfad60233fbRaph Levien return this; 190d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 191d3ab692d28018825578ff05832644cfad60233fbRaph Levien 192531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 193531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set line spacing parameters. The default is 0.0 for {@code spacingAdd} 194531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * and 1.0 for {@code spacingMult}. 195531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 196531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param spacingAdd line spacing add 197531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param spacingMult line spacing multiplier 198531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 199531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @see android.widget.TextView#setLineSpacing 200531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 201531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien public Builder setLineSpacing(float spacingAdd, float spacingMult) { 202d3ab692d28018825578ff05832644cfad60233fbRaph Levien mSpacingAdd = spacingAdd; 203531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien mSpacingMult = spacingMult; 204d3ab692d28018825578ff05832644cfad60233fbRaph Levien return this; 205d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 206d3ab692d28018825578ff05832644cfad60233fbRaph Levien 207531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 208531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set whether to include extra space beyond font ascent and descent (which is 209531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * needed to avoid clipping in some languages, such as Arabic and Kannada). The 210531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * default is {@code true}. 211531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 212531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param includePad whether to include padding 213531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 214531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @see android.widget.TextView#setIncludeFontPadding 215531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 216d3ab692d28018825578ff05832644cfad60233fbRaph Levien public Builder setIncludePad(boolean includePad) { 217d3ab692d28018825578ff05832644cfad60233fbRaph Levien mIncludePad = includePad; 218d3ab692d28018825578ff05832644cfad60233fbRaph Levien return this; 219d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 220d3ab692d28018825578ff05832644cfad60233fbRaph Levien 221531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 222531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set the width as used for ellipsizing purposes, if it differs from the 223531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * normal layout width. The default is the {@code width} 224531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * passed to {@link #obtain}. 225531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 226531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param ellipsizedWidth width used for ellipsizing, in pixels 227531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 228531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @see android.widget.TextView#setEllipsize 229531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 230d3ab692d28018825578ff05832644cfad60233fbRaph Levien public Builder setEllipsizedWidth(int ellipsizedWidth) { 231d3ab692d28018825578ff05832644cfad60233fbRaph Levien mEllipsizedWidth = ellipsizedWidth; 232d3ab692d28018825578ff05832644cfad60233fbRaph Levien return this; 233d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 234d3ab692d28018825578ff05832644cfad60233fbRaph Levien 235531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 236531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set ellipsizing on the layout. Causes words that are longer than the view 237531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * is wide, or exceeding the number of lines (see #setMaxLines) in the case 238531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * of {@link android.text.TextUtils.TruncateAt#END} or 239531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * {@link android.text.TextUtils.TruncateAt#MARQUEE}, to be ellipsized instead 240531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * of broken. The default is 241531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * {@code null}, indicating no ellipsis is to be applied. 242531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 243531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param ellipsize type of ellipsis behavior 244531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 245531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @see android.widget.TextView#setEllipsize 246531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 247531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) { 248d3ab692d28018825578ff05832644cfad60233fbRaph Levien mEllipsize = ellipsize; 249d3ab692d28018825578ff05832644cfad60233fbRaph Levien return this; 250d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 251d3ab692d28018825578ff05832644cfad60233fbRaph Levien 252531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 253531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set maximum number of lines. This is particularly useful in the case of 254531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * ellipsizing, where it changes the layout of the last line. The default is 255531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * unlimited. 256531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 257531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param maxLines maximum number of lines in the layout 258531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 259531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @see android.widget.TextView#setMaxLines 260531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 261d3ab692d28018825578ff05832644cfad60233fbRaph Levien public Builder setMaxLines(int maxLines) { 262d3ab692d28018825578ff05832644cfad60233fbRaph Levien mMaxLines = maxLines; 263d3ab692d28018825578ff05832644cfad60233fbRaph Levien return this; 264d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 265d3ab692d28018825578ff05832644cfad60233fbRaph Levien 266531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 267531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set break strategy, useful for selecting high quality or balanced paragraph 268531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * layout options. The default is {@link Layout#BREAK_STRATEGY_SIMPLE}. 269531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 270531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param breakStrategy break strategy for paragraph layout 271531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 272531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @see android.widget.TextView#setBreakStrategy 273531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 27439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien public Builder setBreakStrategy(@BreakStrategy int breakStrategy) { 27539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien mBreakStrategy = breakStrategy; 27639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien return this; 27739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien } 27839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 279531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 28095c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * Set hyphenation frequency, to control the amount of automatic hyphenation used. The 28195c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * default is {@link Layout#HYPHENATION_FREQUENCY_NONE}. 28295c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * 28395c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * @param hyphenationFrequency hyphenation frequency for the paragraph 28495c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * @return this builder, useful for chaining 28595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * @see android.widget.TextView#setHyphenationFrequency 28695c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader */ 28795c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) { 28895c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader mHyphenationFrequency = hyphenationFrequency; 28995c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader return this; 29095c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader } 29195c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader 29295c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader /** 293531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Set indents. Arguments are arrays holding an indent amount, one per line, measured in 294531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * pixels. For lines past the last element in the array, the last element repeats. 295531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 296531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param leftIndents array of indent values for left margin, in pixels 297531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @param rightIndents array of indent values for right margin, in pixels 298531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return this builder, useful for chaining 299531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @see android.widget.TextView#setIndents 300531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 301e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien public Builder setIndents(int[] leftIndents, int[] rightIndents) { 302e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien int leftLen = leftIndents == null ? 0 : leftIndents.length; 303e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien int rightLen = rightIndents == null ? 0 : rightIndents.length; 304e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien int[] indents = new int[Math.max(leftLen, rightLen)]; 305e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien for (int i = 0; i < indents.length; i++) { 306e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien int leftMargin = i < leftLen ? leftIndents[i] : 0; 307e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien int rightMargin = i < rightLen ? rightIndents[i] : 0; 308e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien indents[i] = leftMargin + rightMargin; 309e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien } 310e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien nSetIndents(mNativePtr, indents); 311e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien return this; 312e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien } 313e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien 31470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien /** 31570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * Measurement and break iteration is done in native code. The protocol for using 31670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * the native code is as follows. 31770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * 31826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab 31995c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * stops, break strategy, and hyphenation frequency (and possibly other parameters in the 32095c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader * future). 321c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien * 322c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien * Then, for each run within the paragraph: 32370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * - setLocale (this must be done at least for the first run, optional afterwards) 32470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * - one of the following, depending on the type of run: 32570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * + addStyleRun (a text run, to be measured in native code) 32670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * + addMeasuredRun (a run already measured in Java, passed into native code) 32770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * + addReplacementRun (a replacement run, width is given) 32870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * 32970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * After measurement, nGetWidths() is valid if the widths are needed (eg for ellipsis). 33070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * Run nComputeLineBreaks() to obtain line breaks for the paragraph. 33170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * 33270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien * After all paragraphs, call finish() to release expensive buffers. 33370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien */ 33470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien 33570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien private void setLocale(Locale locale) { 3364c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien if (!locale.equals(mLocale)) { 33726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale)); 3384c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien mLocale = locale; 3394c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien } 3404c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien } 3414c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien 34270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien /* package */ float addStyleRun(TextPaint paint, int start, int end, boolean isRtl) { 34370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien return nAddStyleRun(mNativePtr, paint.getNativeInstance(), paint.mNativeTypeface, 34470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien start, end, isRtl); 34570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien } 34670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien 34770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien /* package */ void addMeasuredRun(int start, int end, float[] widths) { 34870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien nAddMeasuredRun(mNativePtr, start, end, widths); 34970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien } 35070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien 35170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien /* package */ void addReplacementRun(int start, int end, float width) { 35270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien nAddReplacementRun(mNativePtr, start, end, width); 35370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien } 35470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien 355531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien /** 356531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * Build the {@link StaticLayout} after options have been set. 357531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 358531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * <p>Note: the builder object must not be reused in any way after calling this 359531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * method. Setting parameters after calling this method, or calling it a second 360531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * time on the same builder object, will likely lead to unexpected results. 361531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * 362531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien * @return the newly constructed {@link StaticLayout} object 363531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien */ 364d3ab692d28018825578ff05832644cfad60233fbRaph Levien public StaticLayout build() { 36539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien StaticLayout result = new StaticLayout(this); 36639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien Builder.recycle(this); 367d3ab692d28018825578ff05832644cfad60233fbRaph Levien return result; 368d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 369d3ab692d28018825578ff05832644cfad60233fbRaph Levien 3704c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien @Override 3714c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien protected void finalize() throws Throwable { 3724c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien try { 3734c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien nFreeBuilder(mNativePtr); 3744c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien } finally { 3754c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien super.finalize(); 3764c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien } 3774c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien } 3784c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien 3794c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien /* package */ long mNativePtr; 3804c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien 381d3ab692d28018825578ff05832644cfad60233fbRaph Levien CharSequence mText; 382d3ab692d28018825578ff05832644cfad60233fbRaph Levien int mStart; 383d3ab692d28018825578ff05832644cfad60233fbRaph Levien int mEnd; 384d3ab692d28018825578ff05832644cfad60233fbRaph Levien TextPaint mPaint; 385d3ab692d28018825578ff05832644cfad60233fbRaph Levien int mWidth; 38639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien Alignment mAlignment; 387d3ab692d28018825578ff05832644cfad60233fbRaph Levien TextDirectionHeuristic mTextDir; 388d3ab692d28018825578ff05832644cfad60233fbRaph Levien float mSpacingMult; 389d3ab692d28018825578ff05832644cfad60233fbRaph Levien float mSpacingAdd; 390d3ab692d28018825578ff05832644cfad60233fbRaph Levien boolean mIncludePad; 391d3ab692d28018825578ff05832644cfad60233fbRaph Levien int mEllipsizedWidth; 392d3ab692d28018825578ff05832644cfad60233fbRaph Levien TextUtils.TruncateAt mEllipsize; 393d3ab692d28018825578ff05832644cfad60233fbRaph Levien int mMaxLines; 39439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien int mBreakStrategy; 39595c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader int mHyphenationFrequency; 396d3ab692d28018825578ff05832644cfad60233fbRaph Levien 397d3ab692d28018825578ff05832644cfad60233fbRaph Levien Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); 398d3ab692d28018825578ff05832644cfad60233fbRaph Levien 399d3ab692d28018825578ff05832644cfad60233fbRaph Levien // This will go away and be subsumed by native builder code 400d3ab692d28018825578ff05832644cfad60233fbRaph Levien MeasuredText mMeasuredText; 401d3ab692d28018825578ff05832644cfad60233fbRaph Levien 4024c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien Locale mLocale; 4034c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien 40439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3); 405d3ab692d28018825578ff05832644cfad60233fbRaph Levien } 406d3ab692d28018825578ff05832644cfad60233fbRaph Levien 4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public StaticLayout(CharSequence source, TextPaint paint, 4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int width, 4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align, float spacingmult, float spacingadd, 4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean includepad) { 4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this(source, 0, source.length(), paint, width, align, 4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project spacingmult, spacingadd, includepad); 4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 415cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 416cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @hide 417cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 418cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public StaticLayout(CharSequence source, TextPaint paint, 419cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt int width, Alignment align, TextDirectionHeuristic textDir, 420cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt float spacingmult, float spacingadd, 421cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt boolean includepad) { 422cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt this(source, 0, source.length(), paint, width, align, textDir, 423cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt spacingmult, spacingadd, includepad); 424cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 425cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public StaticLayout(CharSequence source, int bufstart, int bufend, 4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextPaint paint, int outerwidth, 4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Alignment align, 4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float spacingmult, float spacingadd, 4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean includepad) { 4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this(source, bufstart, bufend, paint, outerwidth, align, 4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project spacingmult, spacingadd, includepad, null, 0); 4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 435cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 436cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @hide 437cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 438cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public StaticLayout(CharSequence source, int bufstart, int bufend, 439cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt TextPaint paint, int outerwidth, 440cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt Alignment align, TextDirectionHeuristic textDir, 441cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt float spacingmult, float spacingadd, 442cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt boolean includepad) { 443cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt this(source, bufstart, bufend, paint, outerwidth, align, textDir, 4448059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio spacingmult, spacingadd, includepad, null, 0, Integer.MAX_VALUE); 445cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt} 446cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 447cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public StaticLayout(CharSequence source, int bufstart, int bufend, 448cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt TextPaint paint, int outerwidth, 449cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt Alignment align, 450cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt float spacingmult, float spacingadd, 451cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt boolean includepad, 452cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { 453cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt this(source, bufstart, bufend, paint, outerwidth, align, 454cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt TextDirectionHeuristics.FIRSTSTRONG_LTR, 4558059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE); 456cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 457cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 458cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 459cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * @hide 460cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public StaticLayout(CharSequence source, int bufstart, int bufend, 4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TextPaint paint, int outerwidth, 463cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt Alignment align, TextDirectionHeuristic textDir, 4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float spacingmult, float spacingadd, 4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean includepad, 4668059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) { 4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super((ellipsize == null) 4684e0c5e55e171532760d5f51e0165563827129d4eDoug Felt ? source 4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project : (source instanceof Spanned) 4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ? new SpannedEllipsizer(source) 4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project : new Ellipsizer(source), 472cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt paint, outerwidth, align, textDir, spacingmult, spacingadd); 4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 474ebd66ca600dc2c43edb0830bcf1a92fafec30a5aRaph Levien Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth) 47539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien .setAlignment(align) 476a6a082862b9e2ea4c9e9a1a945927c4040993f6eRaph Levien .setTextDirection(textDir) 477531c30c62b14881aab31a5133920a971b1fbb50eRaph Levien .setLineSpacing(spacingadd, spacingmult) 478d3ab692d28018825578ff05832644cfad60233fbRaph Levien .setIncludePad(includepad) 479d3ab692d28018825578ff05832644cfad60233fbRaph Levien .setEllipsizedWidth(ellipsizedWidth) 480d3ab692d28018825578ff05832644cfad60233fbRaph Levien .setEllipsize(ellipsize) 481d3ab692d28018825578ff05832644cfad60233fbRaph Levien .setMaxLines(maxLines); 4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* 4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is annoying, but we can't refer to the layout until 4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * superclass construction is finished, and the superclass 4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * constructor wants the reference to the display text. 4864e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * 4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This will break if the superclass constructor ever actually 4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * cares about the content instead of just holding the reference. 4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ellipsize != null) { 4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Ellipsizer e = (Ellipsizer) getText(); 4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project e.mLayout = this; 4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project e.mWidth = ellipsizedWidth; 4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project e.mMethod = ellipsize; 4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mEllipsizedWidth = ellipsizedWidth; 4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mColumns = COLUMNS_ELLIPSIZE; 4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mColumns = COLUMNS_NORMAL; 5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mEllipsizedWidth = outerwidth; 5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 504776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns); 505776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski mLines = new int[mLineDirections.length]; 5068059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio mMaximumVisibleLineCount = maxLines; 5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 50870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien generate(b, b.mIncludePad, b.mIncludePad); 5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 510d3ab692d28018825578ff05832644cfad60233fbRaph Levien Builder.recycle(b); 5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5138059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio /* package */ StaticLayout(CharSequence text) { 5148059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio super(text, null, 0, null, 0, 0); 5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mColumns = COLUMNS_ELLIPSIZE; 517776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns); 518776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski mLines = new int[mLineDirections.length]; 5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 52139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien private StaticLayout(Builder b) { 52239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien super((b.mEllipsize == null) 52339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien ? b.mText 52439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien : (b.mText instanceof Spanned) 52539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien ? new SpannedEllipsizer(b.mText) 52639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien : new Ellipsizer(b.mText), 52739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien b.mPaint, b.mWidth, b.mAlignment, b.mSpacingMult, b.mSpacingAdd); 52839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 52939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien if (b.mEllipsize != null) { 53039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien Ellipsizer e = (Ellipsizer) getText(); 53139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 53239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien e.mLayout = this; 53339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien e.mWidth = b.mEllipsizedWidth; 53439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien e.mMethod = b.mEllipsize; 53539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien mEllipsizedWidth = b.mEllipsizedWidth; 53639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 53739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien mColumns = COLUMNS_ELLIPSIZE; 53839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien } else { 53939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien mColumns = COLUMNS_NORMAL; 54039b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien mEllipsizedWidth = b.mWidth; 54139b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien } 54239b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 54339b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns); 54439b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien mLines = new int[mLineDirections.length]; 54539b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien mMaximumVisibleLineCount = b.mMaxLines; 54639b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 54739b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien generate(b, b.mIncludePad, b.mIncludePad); 54839b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien } 54939b4db73c3340ff955f67e4e5318159d19d1ab3aRaph Levien 550d3ab692d28018825578ff05832644cfad60233fbRaph Levien /* package */ void generate(Builder b, boolean includepad, boolean trackpad) { 551d3ab692d28018825578ff05832644cfad60233fbRaph Levien CharSequence source = b.mText; 552d3ab692d28018825578ff05832644cfad60233fbRaph Levien int bufStart = b.mStart; 553d3ab692d28018825578ff05832644cfad60233fbRaph Levien int bufEnd = b.mEnd; 554d3ab692d28018825578ff05832644cfad60233fbRaph Levien TextPaint paint = b.mPaint; 555d3ab692d28018825578ff05832644cfad60233fbRaph Levien int outerWidth = b.mWidth; 556d3ab692d28018825578ff05832644cfad60233fbRaph Levien TextDirectionHeuristic textDir = b.mTextDir; 557d3ab692d28018825578ff05832644cfad60233fbRaph Levien float spacingmult = b.mSpacingMult; 558d3ab692d28018825578ff05832644cfad60233fbRaph Levien float spacingadd = b.mSpacingAdd; 559d3ab692d28018825578ff05832644cfad60233fbRaph Levien float ellipsizedWidth = b.mEllipsizedWidth; 560d3ab692d28018825578ff05832644cfad60233fbRaph Levien TextUtils.TruncateAt ellipsize = b.mEllipsize; 5614c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien LineBreaks lineBreaks = new LineBreaks(); // TODO: move to builder to avoid allocation costs 562c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // store span end locations 563c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int[] spanEndCache = new int[4]; 564c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // store fontMetrics per span range 565c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range) 566c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int[] fmCache = new int[4 * 4]; 5674c1f12efcf24e404df40f19075eb95a148a9d6a1Raph Levien b.setLocale(paint.getTextLocale()); // TODO: also respect LocaleSpan within the text 56888b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye 5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLineCount = 0; 5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int v = 0; 5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean needMultiply = (spacingmult != 1 || spacingadd != 0); 5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 574d3ab692d28018825578ff05832644cfad60233fbRaph Levien Paint.FontMetricsInt fm = b.mFontMetricsInt; 575121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio int[] chooseHtv = null; 5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 577d3ab692d28018825578ff05832644cfad60233fbRaph Levien MeasuredText measured = b.mMeasuredText; 5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Spanned spanned = null; 5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (source instanceof Spanned) 5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project spanned = (Spanned) source; 5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int paraEnd; 584121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) { 585121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd); 586e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (paraEnd < 0) 587121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio paraEnd = bufEnd; 5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project else 589e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt paraEnd++; 5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 591c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int firstWidthLineCount = 1; 592121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio int firstWidth = outerWidth; 593121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio int restWidth = outerWidth; 5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 595121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio LineHeightSpan[] chooseHt = null; 5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (spanned != null) { 59874d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, 599e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt LeadingMarginSpan.class); 6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < sp.length; i++) { 6017b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner LeadingMarginSpan lms = sp[i]; 602121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio firstWidth -= sp[i].getLeadingMargin(true); 603121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio restWidth -= sp[i].getLeadingMargin(false); 604cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 605c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt // LeadingMarginSpan2 is odd. The count affects all 606ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye // leading margin spans, not just this particular one 607c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (lms instanceof LeadingMarginSpan2) { 608c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms; 609c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye firstWidthLineCount = Math.max(firstWidthLineCount, 610c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye lms2.getLeadingMarginLineCount()); 6117b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner } 6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 614121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); 6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 616121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio if (chooseHt.length != 0) { 617121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio if (chooseHtv == null || 618121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio chooseHtv.length < chooseHt.length) { 619776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length); 6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 622121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio for (int i = 0; i < chooseHt.length; i++) { 623121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio int o = spanned.getSpanStart(chooseHt[i]); 6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 625e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (o < paraStart) { 6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // starts in this layout, before the 6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // current paragraph 6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 629121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio chooseHtv[i] = getLineTop(getLineForOffset(o)); 6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // starts in this paragraph 6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 633121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio chooseHtv[i] = v; 6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 63970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien measured.setPara(source, paraStart, paraEnd, textDir, b); 640e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt char[] chs = measured.mChars; 641e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float[] widths = measured.mWidths; 642e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt byte[] chdirs = measured.mLevels; 643e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int dir = measured.mDir; 644e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean easy = measured.mEasy; 645c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien 646c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien // tab stop locations 647c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien int[] variableTabStops = null; 648c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien if (spanned != null) { 649c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, 650c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien paraEnd, TabStopSpan.class); 651c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien if (spans.length > 0) { 652c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien int[] stops = new int[spans.length]; 653c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien for (int i = 0; i < spans.length; i++) { 654c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien stops[i] = spans[i].getTabStop(); 655c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien } 656c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien Arrays.sort(stops, 0, stops.length); 657c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien variableTabStops = stops; 658c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien } 659c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien } 660c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien 661c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, 662c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien firstWidth, firstWidthLineCount, restWidth, 66395c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency); 6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 665c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // measurement has to be done before performing line breaking 666c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // but we don't want to recompute fontmetrics or span ranges the 667c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // second time, so we cache those and then use those stored values 668c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int fmCacheCount = 0; 669c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int spanEndCacheCount = 0; 670cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { 671c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye if (fmCacheCount * 4 >= fmCache.length) { 672c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int[] grow = new int[fmCacheCount * 4 * 2]; 673c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4); 674c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmCache = grow; 675c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } 676c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye 677c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye if (spanEndCacheCount >= spanEndCache.length) { 678c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int[] grow = new int[spanEndCacheCount * 2]; 679c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount); 680c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye spanEndCache = grow; 681c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } 68223241887515ed77687c23e29a4a3ffff671666bdDoug Felt 683cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne if (spanned == null) { 684cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne spanEnd = paraEnd; 68523241887515ed77687c23e29a4a3ffff671666bdDoug Felt int spanLen = spanEnd - spanStart; 686cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne measured.addStyleRun(paint, spanLen, fm); 687cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne } else { 688cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne spanEnd = spanned.nextSpanTransition(spanStart, paraEnd, 689cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne MetricAffectingSpan.class); 690cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne int spanLen = spanEnd - spanStart; 691cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne MetricAffectingSpan[] spans = 69223241887515ed77687c23e29a4a3ffff671666bdDoug Felt spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class); 693cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class); 694cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne measured.addStyleRun(paint, spans, spanLen, fm); 69523241887515ed77687c23e29a4a3ffff671666bdDoug Felt } 69623241887515ed77687c23e29a4a3ffff671666bdDoug Felt 697c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // the order of storage here (top, bottom, ascent, descent) has to match the code below 698c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // where these values are retrieved 699c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmCache[fmCacheCount * 4 + 0] = fm.top; 700c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmCache[fmCacheCount * 4 + 1] = fm.bottom; 701c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmCache[fmCacheCount * 4 + 2] = fm.ascent; 702c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmCache[fmCacheCount * 4 + 3] = fm.descent; 703c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmCacheCount++; 704c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye 705c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye spanEndCache[spanEndCacheCount] = spanEnd; 706c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye spanEndCacheCount++; 707c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } 708c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye 70970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien nGetWidths(b.mNativePtr, widths); 710c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks, 711c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length); 71281541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne 713c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int[] breaks = lineBreaks.breaks; 714c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye float[] lineWidths = lineBreaks.widths; 71526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien int[] flags = lineBreaks.flags; 7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 717c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // here is the offset of the starting character of the line we are currently measuring 718c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int here = paraStart; 719d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne 720c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0; 721c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int fmCacheIndex = 0; 722c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int spanEndCacheIndex = 0; 723c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int breakIndex = 0; 724c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { 725c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // retrieve end of span 726c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye spanEnd = spanEndCache[spanEndCacheIndex++]; 727c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye 728c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // retrieve cached metrics, order matches above 729c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fm.top = fmCache[fmCacheIndex * 4 + 0]; 730c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fm.bottom = fmCache[fmCacheIndex * 4 + 1]; 731c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fm.ascent = fmCache[fmCacheIndex * 4 + 2]; 732c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fm.descent = fmCache[fmCacheIndex * 4 + 3]; 733c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmCacheIndex++; 734c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye 735c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye if (fm.top < fmTop) { 736c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmTop = fm.top; 737c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } 738c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye if (fm.ascent < fmAscent) { 739c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmAscent = fm.ascent; 740c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } 741c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye if (fm.descent > fmDescent) { 742c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmDescent = fm.descent; 743c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } 744c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye if (fm.bottom > fmBottom) { 745c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmBottom = fm.bottom; 746c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } 747cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne 748c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // skip breaks ending before current span range 749c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) { 750c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye breakIndex++; 751c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } 75281541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne 753c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) { 754c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye int endPos = paraStart + breaks[breakIndex]; 755c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye 756ce4155a204144ae5462f547f7738af24be5a1f77Raph Levien boolean moreChars = (endPos < bufEnd); 7574c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien 758c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye v = out(source, here, endPos, 759c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmAscent, fmDescent, fmTop, fmBottom, 760c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex], 761c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, 762c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye chs, widths, paraStart, ellipsize, ellipsizedWidth, 7634c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien lineWidths[breakIndex], paint, moreChars); 764c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye 765c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye if (endPos < spanEnd) { 766c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // preserve metrics for current span 767c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmTop = fm.top; 768c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmBottom = fm.bottom; 769c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmAscent = fm.ascent; 770c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmDescent = fm.descent; 771c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } else { 772c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye fmTop = fmBottom = fmAscent = fmDescent = 0; 7738059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 775c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye here = endPos; 776c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye breakIndex++; 7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 778c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye if (mLineCount >= mMaximumVisibleLineCount) { 779c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye return; 780c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } 7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 784121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio if (paraEnd == bufEnd) 7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7888059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) && 789ad0b051b133baf92f199c96a8ac1e81b3393190cFabrice Di Meglio mLineCount < mMaximumVisibleLineCount) { 790121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio // Log.e("text", "output last " + bufEnd); 7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 79270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien measured.setPara(source, bufEnd, bufEnd, textDir, b); 793e631889e1ae7edc6a2fae495ba504f85820b6a4bFabrice Di Meglio 7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project paint.getFontMetricsInt(fm); 7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project v = out(source, 797121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio bufEnd, bufEnd, fm.ascent, fm.descent, 7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project fm.top, fm.bottom, 7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project v, 8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project spacingmult, spacingadd, null, 80126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien null, fm, 0, 802e631889e1ae7edc6a2fae495ba504f85820b6a4bFabrice Di Meglio needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd, 803d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne includepad, trackpad, null, 804d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne null, bufStart, ellipsize, 805d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne ellipsizedWidth, 0, paint, false); 8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int out(CharSequence text, int start, int end, 8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int above, int below, int top, int bottom, int v, 8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float spacingmult, float spacingadd, 812121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio LineHeightSpan[] chooseHt, int[] chooseHtv, 81326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien Paint.FontMetricsInt fm, int flags, 814d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne boolean needMultiply, byte[] chdirs, int dir, 815d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne boolean easy, int bufEnd, boolean includePad, 816d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne boolean trackPad, char[] chs, 817d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne float[] widths, int widthStart, TextUtils.TruncateAt ellipsize, 818d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne float ellipsisWidth, float textWidth, 819d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne TextPaint paint, boolean moreChars) { 8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int j = mLineCount; 8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int off = j * mColumns; 8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int want = off + mColumns + TOP; 8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int[] lines = mLines; 8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (want >= lines.length) { 826776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski Directions[] grow2 = ArrayUtils.newUnpaddedArray( 827776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski Directions.class, GrowingArrayUtils.growSize(want)); 8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project System.arraycopy(mLineDirections, 0, grow2, 0, 8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLineDirections.length); 8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLineDirections = grow2; 831776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski 832776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski int[] grow = new int[grow2.length]; 833776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski System.arraycopy(lines, 0, grow, 0, lines.length); 834776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski mLines = grow; 835776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski lines = grow; 8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 838121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio if (chooseHt != null) { 8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project fm.ascent = above; 8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project fm.descent = below; 8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project fm.top = top; 8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project fm.bottom = bottom; 8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 844121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio for (int i = 0; i < chooseHt.length; i++) { 845121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio if (chooseHt[i] instanceof LineHeightSpan.WithDensity) { 846121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio ((LineHeightSpan.WithDensity) chooseHt[i]). 847121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio chooseHeight(text, start, end, chooseHtv[i], v, fm, paint); 848a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer 849a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer } else { 850121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm); 851a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer } 8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project above = fm.ascent; 8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project below = fm.descent; 8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project top = fm.top; 8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bottom = fm.bottom; 8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 860d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien boolean firstLine = (j == 0); 861d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount); 862d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd); 863d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien 864d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien if (firstLine) { 865121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio if (trackPad) { 8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mTopPadding = top - above; 8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 869121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio if (includePad) { 8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project above = top; 8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 873d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien 874d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien int extra; 875d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien 876d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien if (lastLine) { 877121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio if (trackPad) { 8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBottomPadding = bottom - below; 8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 881121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio if (includePad) { 8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project below = bottom; 8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 887d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien if (needMultiply && !lastLine) { 8881065758a0f8966a8597a61492112f7859a7050a4Doug Felt double ex = (below - above) * (spacingmult - 1) + spacingadd; 8891065758a0f8966a8597a61492112f7859a7050a4Doug Felt if (ex >= 0) { 890121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio extra = (int)(ex + EXTRA_ROUNDING); 8911065758a0f8966a8597a61492112f7859a7050a4Doug Felt } else { 892121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio extra = -(int)(-ex + EXTRA_ROUNDING); 8931065758a0f8966a8597a61492112f7859a7050a4Doug Felt } 8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project extra = 0; 8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lines[off + START] = start; 8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lines[off + TOP] = v; 9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lines[off + DESCENT] = below + extra; 9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project v += (below - above) + extra; 9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lines[off + mColumns + START] = end; 9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lines[off + mColumns + TOP] = v; 9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 90626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining 90726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien // one bit for start field 90826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien lines[off + TAB] |= flags & TAB_MASK; 90926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien lines[off + HYPHEN] = flags; 9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt lines[off + DIR] |= dir << DIR_SHIFT; 9129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT; 9139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // easy means all chars < the first RTL, so no emoji, no nothing 9144e0c5e55e171532760d5f51e0165563827129d4eDoug Felt // XXX a run with no text or all spaces is easy but might be an empty 9159f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt // RTL paragraph. Make sure easy is false if this is the case. 9169f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt if (easy) { 9179f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt mLineDirections[j] = linedirs; 9189f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt } else { 919f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs, 920f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne start - widthStart, end - start); 9210a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne } 9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 923aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio if (ellipsize != null) { 924aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio // If there is only one line, then do any type of ellipsis except when it is MARQUEE 925aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio // if there are multiple lines, just allow END ellipsis on the last line 9268059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount); 927aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio 92834a126e51aaf22e32c7af808ec6b5a0c41ae3311Fabrice Di Meglio boolean doEllipsis = 92934a126e51aaf22e32c7af808ec6b5a0c41ae3311Fabrice Di Meglio (((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) && 930aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio ellipsize != TextUtils.TruncateAt.MARQUEE) || 931aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) && 932aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio ellipsize == TextUtils.TruncateAt.END); 933aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio if (doEllipsis) { 934aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio calculateEllipsis(start, end, widths, widthStart, 935aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio ellipsisWidth, ellipsize, j, 936aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio textWidth, paint, forceEllipsis); 937aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio } 9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLineCount++; 9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return v; 9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 944121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio private void calculateEllipsis(int lineStart, int lineEnd, 945121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio float[] widths, int widthStart, 9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float avail, TextUtils.TruncateAt where, 9478059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio int line, float textWidth, TextPaint paint, 9488059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio boolean forceEllipsis) { 9498059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (textWidth <= avail && !forceEllipsis) { 9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Everything fits! 9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLines[mColumns * line + ELLIPSIS_START] = 0; 9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLines[mColumns * line + ELLIPSIS_COUNT] = 0; 9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 956cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio float ellipsisWidth = paint.measureText( 9578d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio (where == TextUtils.TruncateAt.END_SMALL) ? 958d29bdb266d54b4551f42776bb790e80147a279d0Neil Fuller TextUtils.ELLIPSIS_TWO_DOTS : TextUtils.ELLIPSIS_NORMAL, 0, 1); 9598059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio int ellipsisStart = 0; 9608059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio int ellipsisCount = 0; 961121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio int len = lineEnd - lineStart; 9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9638059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio // We only support start ellipsis on a single line 9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (where == TextUtils.TruncateAt.START) { 9658059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (mMaximumVisibleLineCount == 1) { 9668059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio float sum = 0; 9678059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio int i; 9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 969ed2eea1bfc1fb57d3b34f2c1e6062b541737e73eKeisuke Kuroyanagi for (i = len; i > 0; i--) { 9708059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio float w = widths[i - 1 + lineStart - widthStart]; 9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9728059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (w + sum + ellipsisWidth > avail) { 9738059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio break; 9748059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 9758059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio 9768059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio sum += w; 9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9798059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio ellipsisStart = 0; 9808059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio ellipsisCount = i; 9818059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } else { 9828059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 9838059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio Log.w(TAG, "Start Ellipsis only supported with one line"); 9848059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 986cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio } else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE || 987cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio where == TextUtils.TruncateAt.END_SMALL) { 9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float sum = 0; 9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int i; 9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (i = 0; i < len; i++) { 992121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio float w = widths[i + lineStart - widthStart]; 9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 994121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio if (w + sum + ellipsisWidth > avail) { 9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sum += w; 9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ellipsisStart = i; 10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ellipsisCount = len - i; 1003aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio if (forceEllipsis && ellipsisCount == 0 && len > 0) { 1004aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio ellipsisStart = len - 1; 10058059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio ellipsisCount = 1; 10068059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 10078059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } else { 10088059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio // where = TextUtils.TruncateAt.MIDDLE We only support middle ellipsis on a single line 10098059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (mMaximumVisibleLineCount == 1) { 10108059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio float lsum = 0, rsum = 0; 10118059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio int left = 0, right = len; 10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10138059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio float ravail = (avail - ellipsisWidth) / 2; 10140e3c5e827235911d33312e431975533f046421e7Raph Levien for (right = len; right > 0; right--) { 10158059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio float w = widths[right - 1 + lineStart - widthStart]; 10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10178059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (w + rsum > ravail) { 10188059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio break; 10198059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 10208059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio 10218059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio rsum += w; 10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10248059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio float lavail = avail - ellipsisWidth - rsum; 10258059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio for (left = 0; left < right; left++) { 10268059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio float w = widths[left + lineStart - widthStart]; 10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10288059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (w + lsum > lavail) { 10298059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio break; 10308059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10328059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio lsum += w; 10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10358059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio ellipsisStart = left; 10368059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio ellipsisCount = right - left; 10378059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } else { 10388059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 10398059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio Log.w(TAG, "Middle Ellipsis only supported with one line"); 10408059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart; 10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount; 10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1048e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Override the base class so we can directly access our members, 10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // rather than relying on member functions. 10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // The logic mirrors that of Layout.getLineForVertical 10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // FIXME: It may be faster to do a linear search for layouts without many lines. 10526611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne @Override 10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineForVertical(int vertical) { 10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int high = mLineCount; 10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int low = -1; 10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int guess; 10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int[] lines = mLines; 10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (high - low > 1) { 10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project guess = (high + low) >> 1; 10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (lines[mColumns * guess + TOP] > vertical){ 10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project high = guess; 10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project low = guess; 10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (low < 0) { 10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return low; 10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10736611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne @Override 10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineCount() { 10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mLineCount; 10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10786611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne @Override 10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineTop(int line) { 10800a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne int top = mLines[mColumns * line + TOP]; 10810a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount && 10820a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne line != mLineCount) { 10830a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne top += getBottomPadding(); 10840a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne } 10850a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne return top; 10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10886611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne @Override 10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineDescent(int line) { 10900a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne int descent = mLines[mColumns * line + DESCENT]; 1091f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount - 1 && // -1 intended 10920a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne line != mLineCount) { 10930a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne descent += getBottomPadding(); 10940a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne } 10950a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne return descent; 10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10986611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne @Override 10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getLineStart(int line) { 11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mLines[mColumns * line + START] & START_MASK; 11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11036611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne @Override 11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getParagraphDirection(int line) { 11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mLines[mColumns * line + DIR] >> DIR_SHIFT; 11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11086611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne @Override 11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean getLineContainsTab(int line) { 11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return (mLines[mColumns * line + TAB] & TAB_MASK) != 0; 11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11136611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne @Override 11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public final Directions getLineDirections(int line) { 11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mLineDirections[line]; 11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11186611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne @Override 11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getTopPadding() { 11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mTopPadding; 11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11236611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne @Override 11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getBottomPadding() { 11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mBottomPadding; 11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 112826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien /** 112926d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien * @hide 113026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien */ 113126d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien @Override 113226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien public int getHyphen(int line) { 113326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien return mLines[mColumns * line + HYPHEN] & 0xff; 113426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien } 113526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien 11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getEllipsisCount(int line) { 11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mColumns < COLUMNS_ELLIPSIZE) { 11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mLines[mColumns * line + ELLIPSIS_COUNT]; 11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getEllipsisStart(int line) { 11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mColumns < COLUMNS_ELLIPSIZE) { 11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return 0; 11499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mLines[mColumns * line + ELLIPSIS_START]; 11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getEllipsizedWidth() { 11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mEllipsizedWidth; 11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 115970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien private static native long nNewBuilder(); 116070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien private static native void nFreeBuilder(long nativePtr); 116170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien private static native void nFinishBuilder(long nativePtr); 116226d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien 116326d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien /* package */ static native long nLoadHyphenator(String patternData); 116426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien 116526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator); 116670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien 1167e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien private static native void nSetIndents(long nativePtr, int[] indents); 1168e319d5a3627aa3cd73c6ec0c76f8593ddefbab9dRaph Levien 1169c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien // Set up paragraph text and settings; done as one big method to minimize jni crossings 1170c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien private static native void nSetupParagraph(long nativePtr, char[] text, int length, 1171c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien float firstWidth, int firstWidthLineCount, float restWidth, 117295c7a13f2ac4f31ed3aaec9b47b9a29a3dbca978Roozbeh Pournader int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency); 117370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien 117470616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien private static native float nAddStyleRun(long nativePtr, long nativePaint, 117570616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien long nativeTypeface, int start, int end, boolean isRtl); 117670616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien 117770616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien private static native void nAddMeasuredRun(long nativePtr, 117870616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien int start, int end, float[] widths); 117970616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien 118070616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien private static native void nAddReplacementRun(long nativePtr, int start, int end, float width); 118170616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien 118270616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien private static native void nGetWidths(long nativePtr, float[] widths); 118370616ecd22fafccf2fab7565ccfbb3b5f91c5580Raph Levien 1184c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // populates LineBreaks and returns the number of breaks found 1185c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // 1186c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // the arrays inside the LineBreaks objects are passed in as well 1187c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // to reduce the number of JNI calls in the common case where the 1188c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // arrays do not have to be resized 1189c94f742f7e07a3b86c8f603836c19638472b3e83Raph Levien private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle, 119026d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien int[] recycleBreaks, float[] recycleWidths, int[] recycleFlags, int recycleLength); 119188b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye 11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mLineCount; 11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mTopPadding, mBottomPadding; 11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mColumns; 11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mEllipsizedWidth; 11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 119726d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien private static final int COLUMNS_NORMAL = 4; 119826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien private static final int COLUMNS_ELLIPSIZE = 6; 11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int START = 0; 12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int DIR = START; 12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int TAB = START; 12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int TOP = 1; 12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int DESCENT = 2; 120426d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien private static final int HYPHEN = 3; 120526d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien private static final int ELLIPSIS_START = 4; 120626d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien private static final int ELLIPSIS_COUNT = 5; 12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int[] mLines; 12099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Directions[] mLineDirections; 12108059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio private int mMaximumVisibleLineCount = Integer.MAX_VALUE; 12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int START_MASK = 0x1FFFFFFF; 12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int DIR_SHIFT = 30; 12149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int TAB_MASK = 0x20000000; 12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1216c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private static final int TAB_INCREMENT = 20; // same as Layout, but that's private 12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1218121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio private static final char CHAR_NEW_LINE = '\n'; 1219121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio 1220121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio private static final double EXTRA_ROUNDING = 0.5; 1221cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio 1222c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // This is used to return three arrays from a single JNI call when 1223c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // performing line breaking 12247053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta /*package*/ static class LineBreaks { 1225c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye private static final int INITIAL_SIZE = 16; 1226c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye public int[] breaks = new int[INITIAL_SIZE]; 1227c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye public float[] widths = new float[INITIAL_SIZE]; 122826d443aee4ee5a8791417b4ca09e8c78ba8dc78bRaph Levien public int[] flags = new int[INITIAL_SIZE]; // hasTabOrEmoji 1229c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye // breaks, widths, and flags should all have the same length 1230c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye } 1231c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye 12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1233