1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.text.style;
18 
19 import android.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.annotation.Px;
22 import android.graphics.Paint;
23 import android.os.Parcel;
24 import android.text.ParcelableSpan;
25 import android.text.TextPaint;
26 import android.text.TextUtils;
27 
28 import com.android.internal.util.Preconditions;
29 
30 /**
31  * The classes that affect the line height of paragraph should implement this interface.
32  */
33 public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
34     /**
35      * Classes that implement this should define how the height is being calculated.
36      *
37      * @param text       the text
38      * @param start      the start of the line
39      * @param end        the end of the line
40      * @param spanstartv the start of the span
41      * @param lineHeight the line height
42      * @param fm         font metrics of the paint, in integers
43      */
chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm)44     public void chooseHeight(CharSequence text, int start, int end,
45             int spanstartv, int lineHeight,
46             Paint.FontMetricsInt fm);
47 
48     /**
49      * The classes that affect the line height of paragraph with respect to density,
50      * should implement this interface.
51      */
52     public interface WithDensity extends LineHeightSpan {
53 
54         /**
55          * Classes that implement this should define how the height is being calculated.
56          *
57          * @param text       the text
58          * @param start      the start of the line
59          * @param end        the end of the line
60          * @param spanstartv the start of the span
61          * @param lineHeight the line height
62          * @param paint      the paint
63          */
chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm, TextPaint paint)64         public void chooseHeight(CharSequence text, int start, int end,
65                 int spanstartv, int lineHeight,
66                 Paint.FontMetricsInt fm, TextPaint paint);
67     }
68 
69     /**
70      * Default implementation of the {@link LineHeightSpan}, which changes the line height of the
71      * attached paragraph.
72      * <p>
73      * For example, a paragraph with its line height equal to 100px can be set like this:
74      * <pre>
75      * SpannableString string = new SpannableString("This is a multiline paragraph. This is a multiline paragraph.");
76      * string.setSpan(new LineHeightSpan.Standard(100), 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
77      * </pre>
78      * <img src="{@docRoot}reference/android/images/text/style/lineheightspan.png" />
79      * <figcaption>Text with line height set to 100 pixels.</figcaption>
80      * <p>
81      * Notice that LineHeightSpan will change the line height of the entire paragraph, even though it
82      * covers only part of the paragraph.
83      * </p>
84      */
85     class Standard implements LineHeightSpan, ParcelableSpan {
86 
87         private final @Px int mHeight;
88         /**
89          * Set the line height of the paragraph to <code>height</code> physical pixels.
90          */
Standard(@x @ntRangefrom = 1) int height)91         public Standard(@Px @IntRange(from = 1) int height) {
92             Preconditions.checkArgument(height > 0, "Height:" + height + "must be positive");
93             mHeight = height;
94         }
95 
96         /**
97          * Constructor called from {@link TextUtils} to restore the span from a parcel
98          */
Standard(@onNull Parcel src)99         public Standard(@NonNull Parcel src) {
100             mHeight = src.readInt();
101         }
102 
103         /**
104          * Returns the line height specified by this span.
105          */
106         @Px
getHeight()107         public int getHeight() {
108             return mHeight;
109         }
110 
111         @Override
getSpanTypeId()112         public int getSpanTypeId() {
113             return getSpanTypeIdInternal();
114         }
115 
116         /** @hide */
117         @Override
getSpanTypeIdInternal()118         public int getSpanTypeIdInternal() {
119             return TextUtils.LINE_HEIGHT_SPAN;
120         }
121 
122         @Override
describeContents()123         public int describeContents() {
124             return 0;
125         }
126 
127         @Override
writeToParcel(Parcel dest, int flags)128         public void writeToParcel(Parcel dest, int flags) {
129             writeToParcelInternal(dest, flags);
130         }
131 
132         /** @hide */
133         @Override
writeToParcelInternal(@onNull Parcel dest, int flags)134         public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
135             dest.writeInt(mHeight);
136         }
137 
138         @Override
chooseHeight(@onNull CharSequence text, int start, int end, int spanstartv, int lineHeight, @NonNull Paint.FontMetricsInt fm)139         public void chooseHeight(@NonNull CharSequence text, int start, int end,
140                 int spanstartv, int lineHeight,
141                 @NonNull Paint.FontMetricsInt fm) {
142             final int originHeight = fm.descent - fm.ascent;
143             // If original height is not positive, do nothing.
144             if (originHeight <= 0) {
145                 return;
146             }
147             final float ratio = mHeight * 1.0f / originHeight;
148             fm.descent = Math.round(fm.descent * ratio);
149             fm.ascent = fm.descent - mHeight;
150         }
151     }
152 }
153