@nativescript-community/text 1.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +317 -0
- package/LICENSE +201 -0
- package/README.md +14 -0
- package/index-common.d.ts +41 -0
- package/index-common.js +211 -0
- package/index-common.js.map +1 -0
- package/index.android.d.ts +28 -0
- package/index.android.js +352 -0
- package/index.android.js.map +1 -0
- package/index.d.ts +1 -0
- package/index.ios.d.ts +15 -0
- package/index.ios.js +231 -0
- package/index.ios.js.map +1 -0
- package/index.js +25 -0
- package/index.js.map +1 -0
- package/package.json +34 -0
- package/platforms/android/AndroidManifest.xml +3 -0
- package/platforms/android/include.gradle +4 -0
- package/platforms/android/java/com/nativescript/text/BaselineAdjustedSpan.java +71 -0
- package/platforms/android/java/com/nativescript/text/CustomBackgroundSpan.java +70 -0
- package/platforms/android/java/com/nativescript/text/CustomTypefaceSpan.java +37 -0
- package/platforms/android/java/com/nativescript/text/Font.java +406 -0
- package/platforms/android/java/com/nativescript/text/HeightSpan.java +81 -0
- package/platforms/android/java/com/nativescript/text/HtmlToSpannedConverter.java +692 -0
- package/platforms/android/java/com/nativescript/text/URLSpanNoUnderline.java +24 -0
- package/platforms/android/text.aar +0 -0
- package/typings/DTCoreText.ios.d.ts +22 -0
- package/typings/android.d.ts +34 -0
@@ -0,0 +1,406 @@
|
|
1
|
+
package com.nativescript.text;
|
2
|
+
|
3
|
+
import android.content.Context;
|
4
|
+
import android.content.res.AssetManager;
|
5
|
+
import android.graphics.Typeface;
|
6
|
+
import android.os.Build;
|
7
|
+
import android.text.SpannableStringBuilder;
|
8
|
+
import android.text.style.AbsoluteSizeSpan;
|
9
|
+
import android.text.style.RelativeSizeSpan;
|
10
|
+
import android.text.style.BackgroundColorSpan;
|
11
|
+
import android.text.style.ForegroundColorSpan;
|
12
|
+
import android.text.style.TypefaceSpan;
|
13
|
+
import android.util.Log;
|
14
|
+
|
15
|
+
import org.json.JSONException;
|
16
|
+
import org.json.JSONObject;
|
17
|
+
import org.json.JSONArray;
|
18
|
+
|
19
|
+
import org.xml.sax.InputSource;
|
20
|
+
|
21
|
+
import java.io.File;
|
22
|
+
import java.io.StringReader;
|
23
|
+
import java.util.ArrayList;
|
24
|
+
import java.util.HashMap;
|
25
|
+
import java.util.StringTokenizer;
|
26
|
+
|
27
|
+
import javax.xml.parsers.SAXParser;
|
28
|
+
import javax.xml.parsers.SAXParserFactory;
|
29
|
+
|
30
|
+
public class Font {
|
31
|
+
static AssetManager appAssets;
|
32
|
+
static HashMap<String, Typeface> typefaceCache = new HashMap<String, Typeface>();
|
33
|
+
static HashMap<String, Typeface> typefaceCreatedCache = new HashMap<String, Typeface>();
|
34
|
+
|
35
|
+
static final String TAG = "Font";
|
36
|
+
|
37
|
+
public static Typeface loadFontFromFile(Context context, String fontFolder, String fontFamily) {
|
38
|
+
if (typefaceCache.containsKey(fontFamily)) {
|
39
|
+
return typefaceCache.get(fontFamily);
|
40
|
+
}
|
41
|
+
if (fontFamily.startsWith("res/")) {
|
42
|
+
int fontID = context.getResources().getIdentifier(fontFamily.substring(4), "font",
|
43
|
+
context.getPackageName());
|
44
|
+
Typeface result = androidx.core.content.res.ResourcesCompat.getFont(context, fontID);
|
45
|
+
if (result != null) {
|
46
|
+
typefaceCache.put(fontFamily, result);
|
47
|
+
}
|
48
|
+
return result;
|
49
|
+
}
|
50
|
+
|
51
|
+
if (appAssets == null) {
|
52
|
+
appAssets = context.getAssets();
|
53
|
+
}
|
54
|
+
if (appAssets == null) {
|
55
|
+
return null;
|
56
|
+
}
|
57
|
+
|
58
|
+
Typeface result = typefaceCache.get(fontFamily);
|
59
|
+
// Check for undefined explicitly as null mean we tried to load the font, but
|
60
|
+
// failed.
|
61
|
+
File file = new File(fontFolder, fontFamily + ".ttf");
|
62
|
+
// const basePath = fs.path.join(fs.knownFolders.currentApp().path, "fonts",
|
63
|
+
// fontFamily);
|
64
|
+
|
65
|
+
if (!file.exists()) {
|
66
|
+
file = new File(fontFolder, fontFamily + ".otf");
|
67
|
+
if (!file.exists()) {
|
68
|
+
Log.w(TAG, "Could not find font file for " + fontFamily + " in folder " + fontFolder);
|
69
|
+
return null;
|
70
|
+
}
|
71
|
+
|
72
|
+
}
|
73
|
+
|
74
|
+
try {
|
75
|
+
result = Typeface.createFromFile(file.getAbsolutePath());
|
76
|
+
} catch (Exception e) {
|
77
|
+
Log.w(TAG, "\"Error loading font asset: " + file.getAbsolutePath() + "," + e.getLocalizedMessage());
|
78
|
+
}
|
79
|
+
typefaceCache.put(fontFamily, result);
|
80
|
+
|
81
|
+
return result;
|
82
|
+
}
|
83
|
+
|
84
|
+
public interface FontWeight {
|
85
|
+
String THIN = "thin";
|
86
|
+
String EXTRA_LIGHT = "extralight";
|
87
|
+
String LIGHT = "light";
|
88
|
+
String NORMAL = "normal";
|
89
|
+
String MEDIUM = "medium";
|
90
|
+
String SEMI_BOLD = "semibold";
|
91
|
+
String BOLD = "bold";
|
92
|
+
String EXTRA_BOLD = "extrabold";
|
93
|
+
String BLACK = "black";
|
94
|
+
}
|
95
|
+
|
96
|
+
public interface genericFontFamilies {
|
97
|
+
String serif = "serif";
|
98
|
+
String sansSerif = "sans-serif";
|
99
|
+
String monospace = "monospace";
|
100
|
+
String system = "system";
|
101
|
+
}
|
102
|
+
|
103
|
+
public static int getIntFontWeight(String fontWeight) {
|
104
|
+
if (fontWeight == null) {
|
105
|
+
return 400;
|
106
|
+
}
|
107
|
+
switch (fontWeight) {
|
108
|
+
case FontWeight.THIN:
|
109
|
+
return 100;
|
110
|
+
case FontWeight.EXTRA_LIGHT:
|
111
|
+
return 200;
|
112
|
+
case FontWeight.LIGHT:
|
113
|
+
return 300;
|
114
|
+
case FontWeight.NORMAL:
|
115
|
+
return 400;
|
116
|
+
case FontWeight.MEDIUM:
|
117
|
+
return 500;
|
118
|
+
case FontWeight.SEMI_BOLD:
|
119
|
+
return 600;
|
120
|
+
case FontWeight.BOLD:
|
121
|
+
return 700;
|
122
|
+
case FontWeight.EXTRA_BOLD:
|
123
|
+
return 800;
|
124
|
+
case FontWeight.BLACK:
|
125
|
+
return 900;
|
126
|
+
default:
|
127
|
+
return Integer.parseInt(fontWeight, 10);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
public static String getFontWeightSuffix(int fontWeight) {
|
132
|
+
|
133
|
+
switch (fontWeight) {
|
134
|
+
case 100:
|
135
|
+
return Build.VERSION.SDK_INT >= 16 ? "-thin" : "";
|
136
|
+
case 200:
|
137
|
+
case 300:
|
138
|
+
return Build.VERSION.SDK_INT >= 16 ? "-light" : "";
|
139
|
+
case 400:
|
140
|
+
return "";
|
141
|
+
case 500:
|
142
|
+
case 600:
|
143
|
+
return Build.VERSION.SDK_INT >= 21 ? "-medium" : "";
|
144
|
+
case 700:
|
145
|
+
case 800:
|
146
|
+
return Build.VERSION.SDK_INT >= 21 ? "-bold" : "";
|
147
|
+
case 900:
|
148
|
+
return Build.VERSION.SDK_INT >= 21 ? "-black" : "";
|
149
|
+
default:
|
150
|
+
throw new Error("Invalid font weight:" + fontWeight);
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
public static ArrayList<String> parseFontFamily(String value) {
|
155
|
+
ArrayList<String> result = new ArrayList<String>();
|
156
|
+
if (value == null) {
|
157
|
+
return result;
|
158
|
+
}
|
159
|
+
if (!value.contains(",")) {
|
160
|
+
result.add(value.replace("'", "").replace("\"", ""));
|
161
|
+
return result;
|
162
|
+
}
|
163
|
+
|
164
|
+
// not removing the "['\"]+" and not trimming make the parseFontFamily much
|
165
|
+
// faster!
|
166
|
+
// should be done in span/text properties
|
167
|
+
StringTokenizer st = new StringTokenizer(value, ",");
|
168
|
+
while (st.hasMoreTokens()) {
|
169
|
+
result.add(st.nextToken().replace("'", "").replace("\"", "").trim());
|
170
|
+
}
|
171
|
+
return result;
|
172
|
+
}
|
173
|
+
|
174
|
+
public static Typeface createTypeface(Context context, String fontFolder, String fontFamily, String fontWeight,
|
175
|
+
boolean isBold, boolean isItalic) {
|
176
|
+
|
177
|
+
int fontStyle = 0;
|
178
|
+
if (isBold) {
|
179
|
+
fontStyle |= Typeface.BOLD;
|
180
|
+
}
|
181
|
+
if (isItalic) {
|
182
|
+
fontStyle |= Typeface.ITALIC;
|
183
|
+
}
|
184
|
+
int fontWeightInt = getIntFontWeight(fontWeight);
|
185
|
+
final String cacheKey = fontFamily + fontWeightInt + isItalic;
|
186
|
+
// Log.d("JS", "Font createTypeface: " + fontFamily + ",fontFolder " +
|
187
|
+
// fontFolder + ",fontWeight " + fontWeight
|
188
|
+
// + ",fontWeightInt " + fontWeightInt);
|
189
|
+
if (typefaceCreatedCache.containsKey(cacheKey)) {
|
190
|
+
return typefaceCreatedCache.get(cacheKey);
|
191
|
+
}
|
192
|
+
// http://stackoverflow.com/questions/19691530/valid-values-for-androidfontfamily-and-what-they-map-to
|
193
|
+
ArrayList<String> fonts = parseFontFamily(fontFamily);
|
194
|
+
// Log.d(TAG, "createTypeface1: " + fonts.toString());
|
195
|
+
Typeface result = null;
|
196
|
+
for (int i = 0; i < fonts.size(); i++) {
|
197
|
+
switch (fonts.get(i).toLowerCase()) {
|
198
|
+
case genericFontFamilies.serif:
|
199
|
+
result = Typeface.create("serif" + getFontWeightSuffix(fontWeightInt), fontStyle);
|
200
|
+
break;
|
201
|
+
|
202
|
+
case genericFontFamilies.sansSerif:
|
203
|
+
case genericFontFamilies.system:
|
204
|
+
result = Typeface.create("sans-serif" + getFontWeightSuffix(fontWeightInt), fontStyle);
|
205
|
+
break;
|
206
|
+
|
207
|
+
case genericFontFamilies.monospace:
|
208
|
+
result = Typeface.create("monospace" + getFontWeightSuffix(fontWeightInt), fontStyle);
|
209
|
+
break;
|
210
|
+
|
211
|
+
default:
|
212
|
+
result = loadFontFromFile(context, fontFolder, fonts.get(i));
|
213
|
+
|
214
|
+
if (result != null && (fontStyle != 0 || isItalic || fontWeightInt != 400)) {
|
215
|
+
if (Build.VERSION.SDK_INT >= 28) {
|
216
|
+
result = Typeface.create(result, fontWeightInt, isItalic);
|
217
|
+
} else {
|
218
|
+
// Log.d("JS", "Font loading font style found: " + fonts.get(i) + ",fontStyle "
|
219
|
+
// + fontStyle + ",fontWeightInt " + fontWeightInt);
|
220
|
+
result = Typeface.create(result, fontStyle);
|
221
|
+
}
|
222
|
+
}
|
223
|
+
break;
|
224
|
+
}
|
225
|
+
|
226
|
+
if (result != null) {
|
227
|
+
// Found the font!
|
228
|
+
// Log.d("JS", "Font found: " + fonts.get(i) + ",fontStyle " + fontStyle +
|
229
|
+
// ",fontWeightInt " + fontWeightInt);
|
230
|
+
break;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
if (result == null) {
|
235
|
+
result = Typeface.create("sans-serif" + getFontWeightSuffix(fontWeightInt), fontStyle);
|
236
|
+
}
|
237
|
+
typefaceCreatedCache.put(cacheKey, result);
|
238
|
+
return result;
|
239
|
+
}
|
240
|
+
|
241
|
+
public static SpannableStringBuilder stringBuilderFromHtmlString(Context context, String fontFolder, String parentFontFamily,
|
242
|
+
String htmlString) {
|
243
|
+
if (htmlString == null) {
|
244
|
+
return null;
|
245
|
+
}
|
246
|
+
CharSequence spannedString = fromHtml(htmlString, context, fontFolder, parentFontFamily, false);
|
247
|
+
SpannableStringBuilder builder = new SpannableStringBuilder(spannedString);
|
248
|
+
|
249
|
+
return builder;
|
250
|
+
}
|
251
|
+
|
252
|
+
public static void setSpanModifiers(Context context, String fontFolder, String parentFontFamily, SpannableStringBuilder ssb,
|
253
|
+
JSONObject span, int start, int end) {
|
254
|
+
String fontWeight = span.optString("fontWeight", null);
|
255
|
+
String fontStyle = span.optString("fontStyle", null);
|
256
|
+
boolean bold = fontWeight != null && (fontWeight.equals("bold") || fontWeight.equals("700"));
|
257
|
+
boolean italic = fontStyle != null && fontStyle.equals("italic");
|
258
|
+
|
259
|
+
// if (android.os.Build.VERSION.SDK_INT < 28) {
|
260
|
+
if (bold && italic) {
|
261
|
+
ssb.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD_ITALIC), start, end,
|
262
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
263
|
+
} else if (bold) {
|
264
|
+
ssb.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD), start, end,
|
265
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
266
|
+
} else if (italic) {
|
267
|
+
ssb.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.ITALIC), start, end,
|
268
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
269
|
+
}
|
270
|
+
// }
|
271
|
+
|
272
|
+
String fontFamily = span.optString("fontFamily", null);
|
273
|
+
if (fontFamily == null && parentFontFamily != null) {
|
274
|
+
fontFamily = parentFontFamily;
|
275
|
+
}
|
276
|
+
if (fontFamily != null) {
|
277
|
+
Typeface typeface = createTypeface(context, fontFolder, fontFamily,fontWeight, bold, italic);
|
278
|
+
TypefaceSpan typefaceSpan = new CustomTypefaceSpan(fontFamily, typeface);
|
279
|
+
ssb.setSpan(typefaceSpan, start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
280
|
+
}
|
281
|
+
Double fontSize = span.optDouble("fontSize");
|
282
|
+
Double maxFontSize = span.optDouble("maxFontSize");
|
283
|
+
String verticalTextAlignment = span.optString("verticalTextAlignment", null);
|
284
|
+
if (verticalTextAlignment != null && !verticalTextAlignment.equals("initial") && !verticalTextAlignment.equals("stretch")) {
|
285
|
+
ssb.setSpan(new BaselineAdjustedSpan(fontSize.intValue(), verticalTextAlignment, maxFontSize.intValue()), start, end,
|
286
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
287
|
+
}
|
288
|
+
if (!Double.isNaN(fontSize)) {
|
289
|
+
ssb.setSpan(new AbsoluteSizeSpan(fontSize.intValue()), start, end,
|
290
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
291
|
+
}
|
292
|
+
Double relativeSize = span.optDouble("relativeSize");
|
293
|
+
if (!Double.isNaN(relativeSize)) {
|
294
|
+
ssb.setSpan(new RelativeSizeSpan(relativeSize.floatValue()), start, end,
|
295
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
296
|
+
}
|
297
|
+
|
298
|
+
Double letterSpacing = span.optDouble("letterSpacing");
|
299
|
+
if (!Double.isNaN(letterSpacing)) {
|
300
|
+
ssb.setSpan(new android.text.style.ScaleXSpan((letterSpacing.floatValue() + 1) / 10), start, end,
|
301
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
302
|
+
}
|
303
|
+
Double lineHeight = span.optDouble("lineHeight");
|
304
|
+
if (!Double.isNaN(lineHeight)) {
|
305
|
+
ssb.setSpan(new HeightSpan(lineHeight.intValue()), start, end,
|
306
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
307
|
+
}
|
308
|
+
|
309
|
+
int color = span.optInt("color", -1);
|
310
|
+
if (color != -1) {
|
311
|
+
ssb.setSpan(new ForegroundColorSpan(color), start, end,
|
312
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
313
|
+
}
|
314
|
+
|
315
|
+
int backgroundColor = span.optInt("backgroundColor", -1);
|
316
|
+
if (backgroundColor != -1) {
|
317
|
+
ssb.setSpan(new BackgroundColorSpan(backgroundColor), start, end,
|
318
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
319
|
+
}
|
320
|
+
|
321
|
+
String textDecoration = span.optString("textDecoration", null);
|
322
|
+
if (textDecoration != null) {
|
323
|
+
if (textDecoration.contains("underline")) {
|
324
|
+
ssb.setSpan(new android.text.style.UnderlineSpan(), start, end,
|
325
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
326
|
+
}
|
327
|
+
|
328
|
+
if (textDecoration.contains("line-through")) {
|
329
|
+
ssb.setSpan(new android.text.style.StrikethroughSpan(), start, end,
|
330
|
+
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
331
|
+
}
|
332
|
+
}
|
333
|
+
}
|
334
|
+
|
335
|
+
public static SpannableStringBuilder stringBuilderFromFormattedString(Context context, String fontFolder, String parentFontFamily,
|
336
|
+
String options) {
|
337
|
+
if (options == null) {
|
338
|
+
return null;
|
339
|
+
}
|
340
|
+
try {
|
341
|
+
JSONArray arrayOptions = new JSONArray(options);
|
342
|
+
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
343
|
+
for (int i = 0, spanStart = 0, spanLength = 0, length = arrayOptions.length(); i < length; i++) {
|
344
|
+
JSONObject span = (JSONObject)arrayOptions.get(i);
|
345
|
+
String text = span.optString("text", "");
|
346
|
+
spanLength = text.length();
|
347
|
+
if (spanLength > 0) {
|
348
|
+
ssb.insert(spanStart, text);
|
349
|
+
setSpanModifiers(context, fontFolder, parentFontFamily, ssb, span, spanStart, spanStart + spanLength);
|
350
|
+
spanStart += spanLength;
|
351
|
+
}
|
352
|
+
}
|
353
|
+
return ssb;
|
354
|
+
|
355
|
+
} catch (JSONException e) {
|
356
|
+
Log.e("TEXT", "parse error", e);
|
357
|
+
return null;
|
358
|
+
}
|
359
|
+
// ArrayList<ArrayList<String>> parsedFormattedString = parseFormattedString(formattedString);
|
360
|
+
// SpannableStringBuilder ssb = new SpannableStringBuilder();
|
361
|
+
// for (int i = 0, spanStart = 0, spanLength = 0, length = parsedFormattedString.size(); i < length; i++) {
|
362
|
+
// ArrayList<String> span = parsedFormattedString.get(i);
|
363
|
+
// String text = span.get(11);
|
364
|
+
// spanLength = text.length();
|
365
|
+
// if (spanLength > 0) {
|
366
|
+
// ssb.insert(spanStart, text);
|
367
|
+
// setSpanModifiers(context, fontFolder, parentFontFamily, ssb, span, spanStart, spanStart + spanLength);
|
368
|
+
// spanStart += spanLength;
|
369
|
+
// }
|
370
|
+
// }
|
371
|
+
|
372
|
+
}
|
373
|
+
|
374
|
+
static SAXParser saxParser = null;
|
375
|
+
static HtmlToSpannedConverter converter = null;
|
376
|
+
|
377
|
+
public static CharSequence fromHtml(CharSequence html, Context context, String fontFolder, String parentFontFamily,
|
378
|
+
final boolean disableLinkStyle) {
|
379
|
+
// long startTime = System.nanoTime();
|
380
|
+
// XMLReader xmlReader;
|
381
|
+
try {
|
382
|
+
if (saxParser == null) {
|
383
|
+
SAXParserFactory factory = SAXParserFactory.newInstance();
|
384
|
+
saxParser = factory.newSAXParser();
|
385
|
+
}
|
386
|
+
if (converter == null) {
|
387
|
+
converter = new HtmlToSpannedConverter(context, fontFolder, parentFontFamily, null, null, disableLinkStyle);
|
388
|
+
} else {
|
389
|
+
converter.reset();
|
390
|
+
converter.disableLinkStyle = disableLinkStyle;
|
391
|
+
}
|
392
|
+
// Log.d(TAG, "parse: " +html);
|
393
|
+
final String toParse = "<doc>" + ((String) html).replaceAll("<br>", "<br></br>") + "</doc>";
|
394
|
+
saxParser.parse(new InputSource(new StringReader(toParse)), converter);
|
395
|
+
// Log.d(TAG, "fromHtml: " + ((System.nanoTime() - startTime)/1000000) + "ms");
|
396
|
+
return converter.spannable();
|
397
|
+
} catch (Exception e) {
|
398
|
+
e.printStackTrace();
|
399
|
+
}
|
400
|
+
return html;
|
401
|
+
}
|
402
|
+
|
403
|
+
public static CharSequence fromHtml(Context context, String fontFolder, String parentFontFamily, CharSequence html) {
|
404
|
+
return fromHtml(html, context, fontFolder, parentFontFamily, false);
|
405
|
+
}
|
406
|
+
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
package com.nativescript.text;
|
2
|
+
|
3
|
+
import android.annotation.SuppressLint;
|
4
|
+
import android.graphics.Paint;
|
5
|
+
import android.graphics.Rect;
|
6
|
+
import android.text.TextPaint;
|
7
|
+
import android.text.style.LineHeightSpan;
|
8
|
+
import java.lang.CharSequence;
|
9
|
+
|
10
|
+
@SuppressLint("ParcelCreator")
|
11
|
+
public class HeightSpan implements LineHeightSpan.WithDensity {
|
12
|
+
private int mSize;
|
13
|
+
private static float sProportion = 0;
|
14
|
+
|
15
|
+
public HeightSpan(int size) {
|
16
|
+
mSize = size;
|
17
|
+
}
|
18
|
+
|
19
|
+
public void chooseHeight(CharSequence text, int start, int end,
|
20
|
+
int spanstartv, int v,
|
21
|
+
Paint.FontMetricsInt fm) {
|
22
|
+
// Should not get called, at least not by StaticLayout.
|
23
|
+
chooseHeight(text, start, end, spanstartv, v, fm, null);
|
24
|
+
}
|
25
|
+
|
26
|
+
public void chooseHeight(CharSequence text, int start, int end,
|
27
|
+
int spanstartv, int v,
|
28
|
+
Paint.FontMetricsInt fm, TextPaint paint) {
|
29
|
+
int size = mSize;
|
30
|
+
if (paint != null) {
|
31
|
+
size *= paint.density;
|
32
|
+
}
|
33
|
+
|
34
|
+
if (fm.bottom - fm.top < size) {
|
35
|
+
fm.top = fm.bottom - size;
|
36
|
+
fm.ascent = fm.ascent - size;
|
37
|
+
} else {
|
38
|
+
if (sProportion == 0) {
|
39
|
+
/*
|
40
|
+
* Calculate what fraction of the nominal ascent
|
41
|
+
* the height of a capital letter actually is,
|
42
|
+
* so that we won't reduce the ascent to less than
|
43
|
+
* that unless we absolutely have to.
|
44
|
+
*/
|
45
|
+
|
46
|
+
Paint p = new Paint();
|
47
|
+
p.setTextSize(100);
|
48
|
+
Rect r = new Rect();
|
49
|
+
p.getTextBounds("ABCDEFG", 0, 7, r);
|
50
|
+
|
51
|
+
sProportion = (r.top) / p.ascent();
|
52
|
+
}
|
53
|
+
|
54
|
+
int need = (int) Math.ceil(-fm.top * sProportion);
|
55
|
+
|
56
|
+
if (size - fm.descent >= need) {
|
57
|
+
/*
|
58
|
+
* It is safe to shrink the ascent this much.
|
59
|
+
*/
|
60
|
+
|
61
|
+
fm.top = fm.bottom - size;
|
62
|
+
fm.ascent = fm.descent - size;
|
63
|
+
} else if (size >= need) {
|
64
|
+
/*
|
65
|
+
* We can't show all the descent, but we can at least
|
66
|
+
* show all the ascent.
|
67
|
+
*/
|
68
|
+
|
69
|
+
fm.top = fm.ascent = -need;
|
70
|
+
fm.bottom = fm.descent = fm.top + size;
|
71
|
+
} else {
|
72
|
+
/*
|
73
|
+
* Show as much of the ascent as we can, and no descent.
|
74
|
+
*/
|
75
|
+
|
76
|
+
fm.top = fm.ascent = -size;
|
77
|
+
fm.bottom = fm.descent = 0;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|