-
-
Notifications
You must be signed in to change notification settings - Fork 687
/
Copy pathfonts.py
108 lines (89 loc) · 3.27 KB
/
fonts.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
from concurrent.futures import ThreadPoolExecutor
from android.graphics import Typeface
from android.graphics.fonts import FontFamily
from android.util import TypedValue
from fontTools.ttLib import TTFont
from java import jint
from java.lang import Integer, Long
from toga.fonts import (
BOLD,
ITALIC,
MESSAGE,
NORMAL,
OBLIQUE,
SMALL_CAPS,
SYSTEM,
SYSTEM_DEFAULT_FONT_SIZE,
)
SYSTEM_FONTS = {}
nativeGetFamily = new_FontFamily = None
def load_fontmap():
field = Typeface.getClass().getDeclaredField("sSystemFontMap")
field.setAccessible(True)
fontmap = field.get(None)
for name in fontmap.keySet().toArray():
typeface = fontmap.get(name)
SYSTEM_FONTS[typeface] = name
for native_style in [
Typeface.BOLD,
Typeface.ITALIC,
Typeface.BOLD | Typeface.ITALIC,
]:
SYSTEM_FONTS[Typeface.create(typeface, native_style)] = name
def reflect_font_methods():
global nativeGetFamily, new_FontFamily
# Bypass non-SDK interface restrictions by looking them up on a background thread
# with no Java stack frames (https://stackoverflow.com/a/61600526).
with ThreadPoolExecutor() as executor:
nativeGetFamily = executor.submit(
Typeface.getClass().getDeclaredMethod,
"nativeGetFamily",
Long.TYPE,
Integer.TYPE,
).result()
nativeGetFamily.setAccessible(True)
new_FontFamily = executor.submit(
FontFamily.getClass().getConstructor, Long.TYPE
).result()
class FontMixin:
supports_custom_fonts = True
supports_custom_variable_fonts = True
def assert_font_options(self, weight=NORMAL, style=NORMAL, variant=NORMAL):
assert (BOLD if self.typeface.isBold() else NORMAL) == weight
if style == OBLIQUE:
print("Interpreting OBLIQUE font as ITALIC")
assert self.typeface.isItalic()
else:
assert (ITALIC if self.typeface.isItalic() else NORMAL) == style
if variant == SMALL_CAPS:
print("Ignoring SMALL CAPS font test")
else:
assert NORMAL == variant
def assert_font_size(self, expected):
if expected == SYSTEM_DEFAULT_FONT_SIZE:
expected = self.default_font_size * (72 / 96)
assert round(self.text_size) == round(
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
expected * (96 / 72),
self.native.getResources().getDisplayMetrics(),
)
)
def assert_font_family(self, expected):
if not SYSTEM_FONTS:
load_fontmap()
if actual := SYSTEM_FONTS.get(self.typeface):
assert actual == {
SYSTEM: self.default_font_family,
MESSAGE: "sans-serif",
}.get(expected, expected)
else:
if not nativeGetFamily:
reflect_font_methods()
family_ptr = nativeGetFamily.invoke(
None, self.typeface.native_instance, jint(0)
)
family = new_FontFamily.newInstance(family_ptr)
assert family.getSize() == 1
font = TTFont(family.getFont(0).getFile().getPath())
assert font["name"].getDebugName(1) == expected