diff options
| author | Po Lu | 2023-09-14 09:57:09 +0800 |
|---|---|---|
| committer | Po Lu | 2023-09-14 09:59:45 +0800 |
| commit | 921c0a16ce20828043b3a9df3e0fb6c3a4ecbd74 (patch) | |
| tree | 47849b6044689807759a949cd6472d0226e5a0a7 /java | |
| parent | ef2bd5e48751ac3b1abe9a0a4865a62a1c51a419 (diff) | |
| download | emacs-921c0a16ce20828043b3a9df3e0fb6c3a4ecbd74.tar.gz emacs-921c0a16ce20828043b3a9df3e0fb6c3a4ecbd74.zip | |
Improve the Android last resort font driver
* java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7Typeface):
Rename fileName to familyName and cease attempting to infer a
style from it.
(EmacsSdk7FontDriver): Employ preset typefaces rather than
enumerating each typeface within the system fonts directory.
(draw): Circumvent kerning difficulties by advancing past each
character individually.
Diffstat (limited to 'java')
| -rw-r--r-- | java/org/gnu/emacs/EmacsSdk7FontDriver.java | 194 |
1 files changed, 71 insertions, 123 deletions
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java index 97969585d16..b8aecbe7c2d 100644 --- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java +++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java | |||
| @@ -31,6 +31,19 @@ import android.graphics.Canvas; | |||
| 31 | 31 | ||
| 32 | import android.util.Log; | 32 | import android.util.Log; |
| 33 | 33 | ||
| 34 | |||
| 35 | |||
| 36 | /* EmacsSdk7FontDriver implements a fallback font driver under | ||
| 37 | Android. This font driver is enabled when the SFNT font driver (in | ||
| 38 | sfntfont-android.c) proves incapable of locating any fonts, which | ||
| 39 | has hitherto not been observed in practice. | ||
| 40 | |||
| 41 | This font driver does not supply each font installed on the system, | ||
| 42 | in lieu of which it provides a list of fonts for each conceivable | ||
| 43 | style and sub-type of the system's own Typefaces, which arises from | ||
| 44 | Android's absence of suitable APIs for loading individual font | ||
| 45 | files. */ | ||
| 46 | |||
| 34 | public class EmacsSdk7FontDriver extends EmacsFontDriver | 47 | public class EmacsSdk7FontDriver extends EmacsFontDriver |
| 35 | { | 48 | { |
| 36 | private static final String TOFU_STRING = "\uDB3F\uDFFD"; | 49 | private static final String TOFU_STRING = "\uDB3F\uDFFD"; |
| @@ -46,106 +59,26 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver | |||
| 46 | public int slant, width, weight, spacing; | 59 | public int slant, width, weight, spacing; |
| 47 | 60 | ||
| 48 | public | 61 | public |
| 49 | Sdk7Typeface (String fileName, Typeface typeface) | 62 | Sdk7Typeface (String familyName, Typeface typeface) |
| 50 | { | 63 | { |
| 51 | String style, testString; | 64 | String style, testString; |
| 52 | int index, measured, i; | 65 | int index, measured, i; |
| 53 | float[] widths; | 66 | float[] widths; |
| 54 | 67 | ||
| 68 | /* Initialize the font style fields and create a paint object | ||
| 69 | linked with that typeface. */ | ||
| 70 | |||
| 55 | slant = NORMAL; | 71 | slant = NORMAL; |
| 56 | weight = REGULAR; | 72 | weight = REGULAR; |
| 57 | width = UNSPECIFIED; | 73 | width = UNSPECIFIED; |
| 58 | spacing = PROPORTIONAL; | 74 | spacing = PROPORTIONAL; |
| 59 | 75 | ||
| 60 | this.typeface = typeface; | 76 | this.typeface = typeface; |
| 77 | this.familyName = familyName; | ||
| 61 | 78 | ||
| 62 | typefacePaint = new Paint (); | 79 | typefacePaint = new Paint (); |
| 63 | typefacePaint.setAntiAlias (true); | 80 | typefacePaint.setAntiAlias (true); |
| 64 | typefacePaint.setTypeface (typeface); | 81 | typefacePaint.setTypeface (typeface); |
| 65 | |||
| 66 | /* For the calls to measureText below. */ | ||
| 67 | typefacePaint.setTextSize (10.0f); | ||
| 68 | |||
| 69 | /* Parse the file name into some useful data. First, strip off | ||
| 70 | the extension. */ | ||
| 71 | fileName = fileName.split ("\\.", 2)[0]; | ||
| 72 | |||
| 73 | /* Next, split the file name by dashes. Everything before the | ||
| 74 | last dash is part of the family name. */ | ||
| 75 | index = fileName.lastIndexOf ("-"); | ||
| 76 | |||
| 77 | if (index > 0) | ||
| 78 | { | ||
| 79 | style = fileName.substring (index + 1, fileName.length ()); | ||
| 80 | familyName = fileName.substring (0, index); | ||
| 81 | |||
| 82 | /* Look for something describing the weight. */ | ||
| 83 | if (style.contains ("Thin")) | ||
| 84 | weight = THIN; | ||
| 85 | else if (style.contains ("UltraLight")) | ||
| 86 | weight = ULTRA_LIGHT; | ||
| 87 | else if (style.contains ("SemiLight")) | ||
| 88 | weight = SEMI_LIGHT; | ||
| 89 | else if (style.contains ("Light")) | ||
| 90 | weight = LIGHT; | ||
| 91 | else if (style.contains ("Medium")) | ||
| 92 | weight = MEDIUM; | ||
| 93 | else if (style.contains ("SemiBold")) | ||
| 94 | weight = SEMI_BOLD; | ||
| 95 | else if (style.contains ("ExtraBold")) | ||
| 96 | weight = EXTRA_BOLD; | ||
| 97 | else if (style.contains ("Bold")) | ||
| 98 | weight = BOLD; | ||
| 99 | else if (style.contains ("Black")) | ||
| 100 | weight = BLACK; | ||
| 101 | else if (style.contains ("UltraHeavy")) | ||
| 102 | weight = ULTRA_HEAVY; | ||
| 103 | |||
| 104 | /* And the slant. */ | ||
| 105 | if (style.contains ("ReverseOblique")) | ||
| 106 | slant = OBLIQUE; | ||
| 107 | else if (style.contains ("ReverseItalic")) | ||
| 108 | slant = REVERSE_ITALIC; | ||
| 109 | else if (style.contains ("Italic")) | ||
| 110 | slant = ITALIC; | ||
| 111 | else if (style.contains ("Oblique")) | ||
| 112 | slant = OBLIQUE; | ||
| 113 | |||
| 114 | /* Finally, the width. */ | ||
| 115 | if (style.contains ("UltraCondensed")) | ||
| 116 | width = ULTRA_CONDENSED; | ||
| 117 | else if (style.contains ("ExtraCondensed")) | ||
| 118 | width = EXTRA_CONDENSED; | ||
| 119 | else if (style.contains ("SemiCondensed")) | ||
| 120 | width = SEMI_CONDENSED; | ||
| 121 | else if (style.contains ("Condensed")) | ||
| 122 | width = CONDENSED; | ||
| 123 | else if (style.contains ("SemiExpanded")) | ||
| 124 | width = SEMI_EXPANDED; | ||
| 125 | else if (style.contains ("ExtraExpanded")) | ||
| 126 | width = EXTRA_EXPANDED; | ||
| 127 | else if (style.contains ("UltraExpanded")) | ||
| 128 | width = ULTRA_EXPANDED; | ||
| 129 | else if (style.contains ("Expanded")) | ||
| 130 | width = EXPANDED; | ||
| 131 | |||
| 132 | /* Guess the spacing information. */ | ||
| 133 | testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
| 134 | widths = new float[testString.length ()]; | ||
| 135 | |||
| 136 | measured = typefacePaint.getTextWidths (testString, | ||
| 137 | 0, testString.length (), | ||
| 138 | widths); | ||
| 139 | spacing = MONO; | ||
| 140 | for (i = 0; i < measured; ++i) | ||
| 141 | { | ||
| 142 | if (i != 0 && widths[i - 1] != widths[i]) | ||
| 143 | /* This isn't a monospace font. */ | ||
| 144 | spacing = PROPORTIONAL; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | else | ||
| 148 | familyName = fileName; | ||
| 149 | } | 82 | } |
| 150 | 83 | ||
| 151 | @Override | 84 | @Override |
| @@ -244,43 +177,42 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver | |||
| 244 | EmacsSdk7FontDriver () | 177 | EmacsSdk7FontDriver () |
| 245 | { | 178 | { |
| 246 | int i; | 179 | int i; |
| 247 | File systemFontsDirectory, fontFile; | ||
| 248 | Typeface typeface; | 180 | Typeface typeface; |
| 249 | 181 | ||
| 250 | systemFontsDirectory = new File ("/system/fonts"); | 182 | typefaceList = new Sdk7Typeface[5]; |
| 251 | |||
| 252 | fontFamilyList = systemFontsDirectory.list (); | ||
| 253 | |||
| 254 | /* If that returned null, replace it with an empty array. */ | ||
| 255 | fontFamilyList = new String[0]; | ||
| 256 | 183 | ||
| 257 | typefaceList = new Sdk7Typeface[fontFamilyList.length + 3]; | 184 | /* Initialize the default monospace and Sans Serif typefaces. |
| 258 | 185 | Initialize the same typeface with various distinct styles. */ | |
| 259 | /* It would be nice to avoid opening each and every font upon | 186 | fallbackTypeface = new Sdk7Typeface ("Sans Serif", |
| 260 | startup. But that doesn't seem to be possible on | 187 | Typeface.DEFAULT); |
| 261 | Android. */ | 188 | typefaceList[1] = fallbackTypeface; |
| 262 | 189 | ||
| 263 | for (i = 0; i < fontFamilyList.length; ++i) | 190 | fallbackTypeface = new Sdk7Typeface ("Sans Serif", |
| 264 | { | 191 | Typeface.create (Typeface.DEFAULT, |
| 265 | fontFile = new File (systemFontsDirectory, | 192 | Typeface.BOLD)); |
| 266 | fontFamilyList[i]); | 193 | fallbackTypeface.weight = BOLD; |
| 267 | typeface = Typeface.createFromFile (fontFile); | 194 | typefaceList[2] = fallbackTypeface; |
| 268 | typefaceList[i] = new Sdk7Typeface (fontFile.getName (), | ||
| 269 | typeface); | ||
| 270 | } | ||
| 271 | 195 | ||
| 272 | /* Initialize the default monospace and serif typefaces. */ | 196 | fallbackTypeface = new Sdk7Typeface ("Sans Serif", |
| 273 | fallbackTypeface = new Sdk7Typeface ("monospace", | 197 | Typeface.create (Typeface.DEFAULT, |
| 274 | Typeface.MONOSPACE); | 198 | Typeface.ITALIC)); |
| 275 | typefaceList[fontFamilyList.length] = fallbackTypeface; | 199 | fallbackTypeface.slant = ITALIC; |
| 200 | typefaceList[3] = fallbackTypeface; | ||
| 201 | |||
| 202 | fallbackTypeface | ||
| 203 | = new Sdk7Typeface ("Sans Serif", | ||
| 204 | Typeface.create (Typeface.DEFAULT, | ||
| 205 | Typeface.BOLD_ITALIC)); | ||
| 206 | fallbackTypeface.weight = BOLD; | ||
| 207 | fallbackTypeface.slant = ITALIC; | ||
| 208 | typefaceList[4] = fallbackTypeface; | ||
| 276 | 209 | ||
| 277 | fallbackTypeface = new Sdk7Typeface ("Monospace", | 210 | fallbackTypeface = new Sdk7Typeface ("Monospace", |
| 278 | Typeface.MONOSPACE); | 211 | Typeface.MONOSPACE); |
| 279 | typefaceList[fontFamilyList.length + 1] = fallbackTypeface; | 212 | fallbackTypeface.spacing = MONO; |
| 213 | typefaceList[0] = fallbackTypeface; | ||
| 280 | 214 | ||
| 281 | fallbackTypeface = new Sdk7Typeface ("Sans Serif", | 215 | fontFamilyList = new String[] { "Monospace", "Sans Serif", }; |
| 282 | Typeface.DEFAULT); | ||
| 283 | typefaceList[fontFamilyList.length + 2] = fallbackTypeface; | ||
| 284 | } | 216 | } |
| 285 | 217 | ||
| 286 | private boolean | 218 | private boolean |
| @@ -491,16 +423,12 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver | |||
| 491 | { | 423 | { |
| 492 | Rect backgroundRect, bounds; | 424 | Rect backgroundRect, bounds; |
| 493 | Sdk7FontObject sdk7FontObject; | 425 | Sdk7FontObject sdk7FontObject; |
| 494 | char[] charsArray; | ||
| 495 | int i; | 426 | int i; |
| 496 | Canvas canvas; | 427 | Canvas canvas; |
| 497 | Paint paint; | 428 | Paint paint; |
| 429 | char[] array; | ||
| 498 | 430 | ||
| 499 | sdk7FontObject = (Sdk7FontObject) fontObject; | 431 | sdk7FontObject = (Sdk7FontObject) fontObject; |
| 500 | charsArray = new char[chars.length]; | ||
| 501 | |||
| 502 | for (i = 0; i < chars.length; ++i) | ||
| 503 | charsArray[i] = (char) chars[i]; | ||
| 504 | 432 | ||
| 505 | backgroundRect = new Rect (); | 433 | backgroundRect = new Rect (); |
| 506 | backgroundRect.top = y - sdk7FontObject.ascent; | 434 | backgroundRect.top = y - sdk7FontObject.ascent; |
| @@ -526,13 +454,33 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver | |||
| 526 | paint.setTextSize (sdk7FontObject.pixelSize); | 454 | paint.setTextSize (sdk7FontObject.pixelSize); |
| 527 | paint.setTypeface (sdk7FontObject.typeface.typeface); | 455 | paint.setTypeface (sdk7FontObject.typeface.typeface); |
| 528 | paint.setAntiAlias (true); | 456 | paint.setAntiAlias (true); |
| 529 | canvas.drawText (charsArray, 0, chars.length, x, y, paint); | 457 | |
| 458 | /* Android applies kerning to non-monospaced fonts by default, | ||
| 459 | which brings the dimensions of strings drawn via `drawText' out | ||
| 460 | of agreement with measurements previously provided to redisplay | ||
| 461 | by textExtents. To avert such disaster, draw each character | ||
| 462 | individually, advancing the origin point by hand. */ | ||
| 530 | 463 | ||
| 531 | bounds = new Rect (); | 464 | bounds = new Rect (); |
| 532 | paint.getTextBounds (charsArray, 0, chars.length, bounds); | 465 | array = new char[1]; |
| 533 | bounds.offset (x, y); | 466 | |
| 534 | bounds.union (backgroundRect); | 467 | for (i = 0; i < chars.length; ++i) |
| 535 | drawable.damageRect (bounds); | 468 | { |
| 469 | /* Retrieve the text bounds for this character so as to | ||
| 470 | compute the damage rectangle. */ | ||
| 471 | array[0] = (char) chars[i]; | ||
| 472 | paint.getTextBounds (array, 0, 1, bounds); | ||
| 473 | bounds.offset (x, y); | ||
| 474 | backgroundRect.union (bounds); | ||
| 475 | |||
| 476 | /* Draw this character. */ | ||
| 477 | canvas.drawText (array, 0, 1, x, y, paint); | ||
| 478 | |||
| 479 | /* Advance the origin point by that much. */ | ||
| 480 | x += paint.measureText ("" + array[0]); | ||
| 481 | } | ||
| 482 | |||
| 483 | drawable.damageRect (backgroundRect); | ||
| 536 | paint.setAntiAlias (false); | 484 | paint.setAntiAlias (false); |
| 537 | return 1; | 485 | return 1; |
| 538 | } | 486 | } |