chromatic-python 0.1.0__py3-none-any.whl
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.
- chromatic/__init__.py +26 -0
- chromatic/_typing.py +168 -0
- chromatic/ascii/__init__.py +5 -0
- chromatic/ascii/_array.py +1059 -0
- chromatic/ascii/_curses.py +153 -0
- chromatic/ascii/_glyph_proc.py +107 -0
- chromatic/color/__init__.py +6 -0
- chromatic/color/colorconv.py +316 -0
- chromatic/color/core.py +1677 -0
- chromatic/color/core.pyi +421 -0
- chromatic/color/palette.py +693 -0
- chromatic/color/palette.pyi +330 -0
- chromatic/data/__init__.py +189 -0
- chromatic/data/__init__.pyi +15 -0
- chromatic/data/fonts/IBM_VGA_437_8x16.ttf +0 -0
- chromatic/data/fonts/consolas.ttf +0 -0
- chromatic/data/images/butterfly.jpg +0 -0
- chromatic/data/images/escher.png +0 -0
- chromatic/data/images/goblin_virus.png +0 -0
- chromatic/data/images/hotdog.jpg +0 -0
- chromatic/demo.py +417 -0
- chromatic_python-0.1.0.dist-info/LICENSE +21 -0
- chromatic_python-0.1.0.dist-info/METADATA +39 -0
- chromatic_python-0.1.0.dist-info/RECORD +26 -0
- chromatic_python-0.1.0.dist-info/WHEEL +5 -0
- chromatic_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'Back',
|
|
3
|
+
'ColorNamespace',
|
|
4
|
+
'Fore',
|
|
5
|
+
'Style',
|
|
6
|
+
'color_str_wrapper',
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
from types import MappingProxyType
|
|
11
|
+
from typing import Callable, Iterator, overload, TypedDict, Union, Unpack
|
|
12
|
+
|
|
13
|
+
from chromatic._typing import AnsiColorAlias, Int3Tuple
|
|
14
|
+
from chromatic.color.core import AnsiColorFormat, Color, ColorStr, SgrParameter, SgrSequence
|
|
15
|
+
|
|
16
|
+
type _ColorLike = Union[Color, int, tuple[int, int, int]]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def display_ansi256_color_range() -> list[list[ColorStr]]: ...
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def display_named_colors() -> list[ColorStr]: ...
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AnsiBack(ColorNamespace[color_str_wrapper]):
|
|
26
|
+
RESET: color_str_wrapper
|
|
27
|
+
|
|
28
|
+
def __call__(self, bg: _ColorLike) -> color_str_wrapper: ...
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AnsiFore(ColorNamespace[color_str_wrapper]):
|
|
32
|
+
RESET: color_str_wrapper
|
|
33
|
+
|
|
34
|
+
def __call__(self, fg: _ColorLike) -> color_str_wrapper: ...
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AnsiStyle[StyleStr: color_str_wrapper](DynamicNamespace[StyleStr]):
|
|
38
|
+
RESET: StyleStr
|
|
39
|
+
BOLD: StyleStr
|
|
40
|
+
FAINT: StyleStr
|
|
41
|
+
ITALICS: StyleStr
|
|
42
|
+
SINGLE_UNDERLINE: StyleStr
|
|
43
|
+
SLOW_BLINK: StyleStr
|
|
44
|
+
RAPID_BLINK: StyleStr
|
|
45
|
+
NEGATIVE: StyleStr
|
|
46
|
+
CONCEALED_CHARS: StyleStr
|
|
47
|
+
CROSSED_OUT: StyleStr
|
|
48
|
+
PRIMARY: StyleStr
|
|
49
|
+
FIRST_ALT: StyleStr
|
|
50
|
+
SECOND_ALT: StyleStr
|
|
51
|
+
THIRD_ALT: StyleStr
|
|
52
|
+
FOURTH_ALT: StyleStr
|
|
53
|
+
FIFTH_ALT: StyleStr
|
|
54
|
+
SIXTH_ALT: StyleStr
|
|
55
|
+
SEVENTH_ALT: StyleStr
|
|
56
|
+
EIGHTH_ALT: StyleStr
|
|
57
|
+
NINTH_ALT: StyleStr
|
|
58
|
+
GOTHIC: StyleStr
|
|
59
|
+
DOUBLE_UNDERLINE: StyleStr
|
|
60
|
+
RESET_BOLD_AND_FAINT: StyleStr
|
|
61
|
+
RESET_ITALIC_AND_GOTHIC: StyleStr
|
|
62
|
+
RESET_UNDERLINES: StyleStr
|
|
63
|
+
RESET_BLINKING: StyleStr
|
|
64
|
+
POSITIVE: StyleStr
|
|
65
|
+
REVEALED_CHARS: StyleStr
|
|
66
|
+
RESET_CROSSED_OUT: StyleStr
|
|
67
|
+
BLACK_FG: StyleStr
|
|
68
|
+
RED_FG: StyleStr
|
|
69
|
+
GREEN_FG: StyleStr
|
|
70
|
+
YELLOW_FG: StyleStr
|
|
71
|
+
BLUE_FG: StyleStr
|
|
72
|
+
MAGENTA_FG: StyleStr
|
|
73
|
+
CYAN_FG: StyleStr
|
|
74
|
+
WHITE_FG: StyleStr
|
|
75
|
+
ANSI_256_SET_FG: StyleStr
|
|
76
|
+
DEFAULT_FG_COLOR: StyleStr
|
|
77
|
+
BLACK_BG: StyleStr
|
|
78
|
+
RED_BG: StyleStr
|
|
79
|
+
GREEN_BG: StyleStr
|
|
80
|
+
YELLOW_BG: StyleStr
|
|
81
|
+
BLUE_BG: StyleStr
|
|
82
|
+
MAGENTA_BG: StyleStr
|
|
83
|
+
CYAN_BG: StyleStr
|
|
84
|
+
WHITE_BG: StyleStr
|
|
85
|
+
ANSI_256_SET_BG: StyleStr
|
|
86
|
+
DEFAULT_BG_COLOR: StyleStr
|
|
87
|
+
FRAMED: StyleStr
|
|
88
|
+
ENCIRCLED: StyleStr
|
|
89
|
+
OVERLINED: StyleStr
|
|
90
|
+
NOT_FRAMED_OR_CIRCLED: StyleStr
|
|
91
|
+
IDEOGRAM_UNDER_OR_RIGHT: StyleStr
|
|
92
|
+
IDEOGRAM_2UNDER_OR_2RIGHT: StyleStr
|
|
93
|
+
IDEOGRAM_OVER_OR_LEFT: StyleStr
|
|
94
|
+
IDEOGRAM_2OVER_OR_2LEFT: StyleStr
|
|
95
|
+
CANCEL: StyleStr
|
|
96
|
+
BLACK_BRIGHT_FG: StyleStr
|
|
97
|
+
RED_BRIGHT_FG: StyleStr
|
|
98
|
+
GREEN_BRIGHT_FG: StyleStr
|
|
99
|
+
YELLOW_BRIGHT_FG: StyleStr
|
|
100
|
+
BLUE_BRIGHT_FG: StyleStr
|
|
101
|
+
MAGENTA_BRIGHT_FG: StyleStr
|
|
102
|
+
CYAN_BRIGHT_FG: StyleStr
|
|
103
|
+
WHITE_BRIGHT_FG: StyleStr
|
|
104
|
+
BLACK_BRIGHT_BG: StyleStr
|
|
105
|
+
RED_BRIGHT_BG: StyleStr
|
|
106
|
+
GREEN_BRIGHT_BG: StyleStr
|
|
107
|
+
YELLOW_BRIGHT_BG: StyleStr
|
|
108
|
+
BLUE_BRIGHT_BG: StyleStr
|
|
109
|
+
MAGENTA_BRIGHT_BG: StyleStr
|
|
110
|
+
CYAN_BRIGHT_BG: StyleStr
|
|
111
|
+
WHITE_BRIGHT_BG: StyleStr
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class color_str_wrapper:
|
|
115
|
+
|
|
116
|
+
def __add__(self, other) -> ColorStr: ...
|
|
117
|
+
|
|
118
|
+
def __call__(self, __obj=None) -> color_str_wrapper | ColorStr: ...
|
|
119
|
+
|
|
120
|
+
def __init__(self, **kwargs: Unpack[_ColorStrWrapperKwargs]) -> None: ...
|
|
121
|
+
|
|
122
|
+
def __repr__(self) -> str: ...
|
|
123
|
+
|
|
124
|
+
def __str__(self) -> str: ...
|
|
125
|
+
|
|
126
|
+
_ansi_type_: type[AnsiColorFormat]
|
|
127
|
+
_sgr_: SgrSequence
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class DynamicNamespace[_VT](metaclass=DynamicNSMeta[_VT]):
|
|
131
|
+
|
|
132
|
+
def as_dict(self) -> dict[str, _VT]: ...
|
|
133
|
+
|
|
134
|
+
def __init__[_KT](self, **kwargs: dict[_KT, _VT]) -> None: ...
|
|
135
|
+
|
|
136
|
+
def __init_subclass__(cls, **kwargs) -> DynamicNamespace[_VT]: ...
|
|
137
|
+
|
|
138
|
+
def __iter__(self) -> Iterator[_VT]: ...
|
|
139
|
+
|
|
140
|
+
def __new__(cls, *args, **kwargs) -> DynamicNamespace[_VT]: ...
|
|
141
|
+
|
|
142
|
+
def __setattr__(self, name, value) -> None: ...
|
|
143
|
+
|
|
144
|
+
__members__: list[_VT]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class DynamicNSMeta[_VT](type):
|
|
148
|
+
|
|
149
|
+
def __new__(
|
|
150
|
+
mcls,
|
|
151
|
+
clsname: str,
|
|
152
|
+
bases: tuple[type, ...],
|
|
153
|
+
mapping: dict[str, ...],
|
|
154
|
+
**kwargs
|
|
155
|
+
) -> DynamicNSMeta[_VT]: ...
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class ColorNamespace[NamedColor: Color](DynamicNamespace[NamedColor]):
|
|
159
|
+
BLACK: NamedColor
|
|
160
|
+
DIM_GREY: NamedColor
|
|
161
|
+
GREY: NamedColor
|
|
162
|
+
DARK_GREY: NamedColor
|
|
163
|
+
SILVER: NamedColor
|
|
164
|
+
LIGHT_GREY: NamedColor
|
|
165
|
+
WHITE_SMOKE: NamedColor
|
|
166
|
+
WHITE: NamedColor
|
|
167
|
+
ROSY_BROWN: NamedColor
|
|
168
|
+
INDIAN_RED: NamedColor
|
|
169
|
+
BROWN: NamedColor
|
|
170
|
+
FIREBRICK: NamedColor
|
|
171
|
+
LIGHT_CORAL: NamedColor
|
|
172
|
+
MAROON: NamedColor
|
|
173
|
+
DARK_RED: NamedColor
|
|
174
|
+
RED: NamedColor
|
|
175
|
+
SNOW: NamedColor
|
|
176
|
+
MISTY_ROSE: NamedColor
|
|
177
|
+
SALMON: NamedColor
|
|
178
|
+
TOMATO: NamedColor
|
|
179
|
+
BURNT_SIENNA: NamedColor
|
|
180
|
+
DARK_SALMON: NamedColor
|
|
181
|
+
CORAL: NamedColor
|
|
182
|
+
ORANGE_RED: NamedColor
|
|
183
|
+
LIGHT_SALMON: NamedColor
|
|
184
|
+
SIENNA: NamedColor
|
|
185
|
+
SEASHELL: NamedColor
|
|
186
|
+
CHOCOLATE: NamedColor
|
|
187
|
+
SADDLE_BROWN: NamedColor
|
|
188
|
+
SANDY_BROWN: NamedColor
|
|
189
|
+
PEACH_PUFF: NamedColor
|
|
190
|
+
PERU: NamedColor
|
|
191
|
+
LINEN: NamedColor
|
|
192
|
+
BISQUE: NamedColor
|
|
193
|
+
DARK_ORANGE: NamedColor
|
|
194
|
+
BURLY_WOOD: NamedColor
|
|
195
|
+
ANTIQUE_WHITE: NamedColor
|
|
196
|
+
TAN: NamedColor
|
|
197
|
+
NAVAJO_WHITE: NamedColor
|
|
198
|
+
BLANCHED_ALMOND: NamedColor
|
|
199
|
+
PAPAYA_WHIP: NamedColor
|
|
200
|
+
MOCCASIN: NamedColor
|
|
201
|
+
ORANGE: NamedColor
|
|
202
|
+
WHEAT: NamedColor
|
|
203
|
+
OLD_LACE: NamedColor
|
|
204
|
+
FLORAL_WHITE: NamedColor
|
|
205
|
+
DARK_GOLDENROD: NamedColor
|
|
206
|
+
GOLDENROD: NamedColor
|
|
207
|
+
CORNSILK: NamedColor
|
|
208
|
+
GOLD: NamedColor
|
|
209
|
+
LEMON_CHIFFON: NamedColor
|
|
210
|
+
KHAKI: NamedColor
|
|
211
|
+
PALE_GOLDENROD: NamedColor
|
|
212
|
+
DARK_KHAKI: NamedColor
|
|
213
|
+
BEIGE: NamedColor
|
|
214
|
+
LIGHT_GOLDENROD_YELLOW: NamedColor
|
|
215
|
+
OLIVE: NamedColor
|
|
216
|
+
YELLOW: NamedColor
|
|
217
|
+
LIGHT_YELLOW: NamedColor
|
|
218
|
+
IVORY: NamedColor
|
|
219
|
+
OLIVE_DRAB: NamedColor
|
|
220
|
+
YELLOW_GREEN: NamedColor
|
|
221
|
+
DARK_OLIVE_GREEN: NamedColor
|
|
222
|
+
GREEN_YELLOW: NamedColor
|
|
223
|
+
CHARTREUSE: NamedColor
|
|
224
|
+
LAWN_GREEN: NamedColor
|
|
225
|
+
DARK_SEA_GREEN: NamedColor
|
|
226
|
+
FOREST_GREEN: NamedColor
|
|
227
|
+
LIME_GREEN: NamedColor
|
|
228
|
+
LIGHT_GREEN: NamedColor
|
|
229
|
+
PALE_GREEN: NamedColor
|
|
230
|
+
DARK_GREEN: NamedColor
|
|
231
|
+
GREEN: NamedColor
|
|
232
|
+
LIME: NamedColor
|
|
233
|
+
HONEYDEW: NamedColor
|
|
234
|
+
SEA_GREEN: NamedColor
|
|
235
|
+
MEDIUM_SEA_GREEN: NamedColor
|
|
236
|
+
SPRING_GREEN: NamedColor
|
|
237
|
+
MINT_CREAM: NamedColor
|
|
238
|
+
MEDIUM_SPRING_GREEN: NamedColor
|
|
239
|
+
MEDIUM_AQUAMARINE: NamedColor
|
|
240
|
+
AQUAMARINE: NamedColor
|
|
241
|
+
TURQUOISE: NamedColor
|
|
242
|
+
LIGHT_SEA_GREEN: NamedColor
|
|
243
|
+
MEDIUM_TURQUOISE: NamedColor
|
|
244
|
+
DARK_SLATE_GREY: NamedColor
|
|
245
|
+
PALE_TURQUOISE: NamedColor
|
|
246
|
+
TEAL: NamedColor
|
|
247
|
+
DARK_CYAN: NamedColor
|
|
248
|
+
CYAN: NamedColor
|
|
249
|
+
LIGHT_CYAN: NamedColor
|
|
250
|
+
AZURE: NamedColor
|
|
251
|
+
DARK_TURQUOISE: NamedColor
|
|
252
|
+
CADET_BLUE: NamedColor
|
|
253
|
+
POWDER_BLUE: NamedColor
|
|
254
|
+
LIGHT_BLUE: NamedColor
|
|
255
|
+
DEEP_SKY_BLUE: NamedColor
|
|
256
|
+
SKY_BLUE: NamedColor
|
|
257
|
+
LIGHT_SKY_BLUE: NamedColor
|
|
258
|
+
STEEL_BLUE: NamedColor
|
|
259
|
+
ALICE_BLUE: NamedColor
|
|
260
|
+
DODGER_BLUE: NamedColor
|
|
261
|
+
SLATE_GREY: NamedColor
|
|
262
|
+
LIGHT_SLATE_GREY: NamedColor
|
|
263
|
+
LIGHT_STEEL_BLUE: NamedColor
|
|
264
|
+
CORNFLOWER_BLUE: NamedColor
|
|
265
|
+
ROYAL_BLUE: NamedColor
|
|
266
|
+
MIDNIGHT_BLUE: NamedColor
|
|
267
|
+
LAVENDER: NamedColor
|
|
268
|
+
NAVY: NamedColor
|
|
269
|
+
DARK_BLUE: NamedColor
|
|
270
|
+
MEDIUM_BLUE: NamedColor
|
|
271
|
+
BLUE: NamedColor
|
|
272
|
+
GHOST_WHITE: NamedColor
|
|
273
|
+
SLATE_BLUE: NamedColor
|
|
274
|
+
DARK_SLATE_BLUE: NamedColor
|
|
275
|
+
MEDIUM_SLATE_BLUE: NamedColor
|
|
276
|
+
MEDIUM_PURPLE: NamedColor
|
|
277
|
+
REBECCA_PURPLE: NamedColor
|
|
278
|
+
BLUE_VIOLET: NamedColor
|
|
279
|
+
INDIGO: NamedColor
|
|
280
|
+
DARK_ORCHID: NamedColor
|
|
281
|
+
DARK_VIOLET: NamedColor
|
|
282
|
+
MEDIUM_ORCHID: NamedColor
|
|
283
|
+
THISTLE: NamedColor
|
|
284
|
+
PLUM: NamedColor
|
|
285
|
+
VIOLET: NamedColor
|
|
286
|
+
PURPLE: NamedColor
|
|
287
|
+
DARK_MAGENTA: NamedColor
|
|
288
|
+
FUCHSIA: NamedColor
|
|
289
|
+
ORCHID: NamedColor
|
|
290
|
+
MEDIUM_VIOLET_RED: NamedColor
|
|
291
|
+
DEEP_PINK: NamedColor
|
|
292
|
+
HOT_PINK: NamedColor
|
|
293
|
+
LAVENDER_BLUSH: NamedColor
|
|
294
|
+
PALE_VIOLET_RED: NamedColor
|
|
295
|
+
CRIMSON: NamedColor
|
|
296
|
+
PINK: NamedColor
|
|
297
|
+
LIGHT_PINK: NamedColor
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
# noinspection PyTypedDict
|
|
301
|
+
class _ColorStrWrapperKwargs(TypedDict, total=False):
|
|
302
|
+
ansi_type: Union[AnsiColorAlias, type[AnsiColorFormat]]
|
|
303
|
+
bg: _ColorLike
|
|
304
|
+
fg: _ColorLike
|
|
305
|
+
sgr_params: Sequence[Union[int, SgrParameter]]
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class rgb_dispatch[** P, R]:
|
|
309
|
+
color_ns: MappingProxyType[str, Int3Tuple]
|
|
310
|
+
|
|
311
|
+
@overload
|
|
312
|
+
def __new__(
|
|
313
|
+
cls,
|
|
314
|
+
func: Callable[P, R], /, *, args: Sequence[str] = ()
|
|
315
|
+
):
|
|
316
|
+
return func
|
|
317
|
+
|
|
318
|
+
@overload
|
|
319
|
+
def __new__(
|
|
320
|
+
cls,
|
|
321
|
+
func: None = None, /, *, args: Sequence[str] = ()
|
|
322
|
+
) -> type[rgb_dispatch]:
|
|
323
|
+
...
|
|
324
|
+
|
|
325
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R: ...
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
Back = AnsiBack()
|
|
329
|
+
Fore = AnsiFore()
|
|
330
|
+
Style = AnsiStyle()
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from enum import IntEnum
|
|
4
|
+
from functools import partial
|
|
5
|
+
from hashlib import sha256
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Iterable, Literal, TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from PIL import Image
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from _typeshed import SupportsWrite
|
|
13
|
+
|
|
14
|
+
data_root = Path(os.path.dirname(__file__))
|
|
15
|
+
stub = data_root / (__file__ + 'i')
|
|
16
|
+
config = data_root / 'config.json'
|
|
17
|
+
fonts = data_root / 'fonts'
|
|
18
|
+
images = data_root / 'images'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_checksum(__fp_iter: Iterable[str]) -> str:
|
|
22
|
+
return sha256(';'.join(sorted(__fp_iter)).encode()).hexdigest()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _is_img_ext(__fp: str):
|
|
26
|
+
return __fp.endswith(('.png', '.jpg', '.jpeg', '.webp'))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _is_ttf_ext(__fp: str):
|
|
30
|
+
return __fp.endswith(('.ttf', '.ttc'))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _build_config():
|
|
34
|
+
from string import printable
|
|
35
|
+
|
|
36
|
+
f: SupportsWrite
|
|
37
|
+
d: dict[Literal['fonts', 'images'] | Literal['__hash__'], dict[str, str] | str]
|
|
38
|
+
d = {
|
|
39
|
+
'fonts': {},
|
|
40
|
+
'images': {},
|
|
41
|
+
'__hash__': ''
|
|
42
|
+
}
|
|
43
|
+
printable = ''.join(c for c in printable if any((c.isalnum(), c.isidentifier(), c == '.')))
|
|
44
|
+
for font_fp in ((Path(fonts) / x).absolute() for x in
|
|
45
|
+
filter(_is_ttf_ext, os.listdir(fonts))):
|
|
46
|
+
font_name = font_fp.stem
|
|
47
|
+
for c in set(font_name):
|
|
48
|
+
if c not in printable:
|
|
49
|
+
font_name = font_name.replace(c, '_')
|
|
50
|
+
d['fonts'][font_name.upper()] = str(font_fp.relative_to(data_root))
|
|
51
|
+
for image_fp in ((Path(images) / x).absolute() for x in
|
|
52
|
+
filter(_is_img_ext, os.listdir(images))):
|
|
53
|
+
img_name = image_fp.stem
|
|
54
|
+
for c in set(img_name):
|
|
55
|
+
if c not in printable:
|
|
56
|
+
img_name = img_name.replace(c, '_')
|
|
57
|
+
d['images'][img_name.lower()] = str(image_fp.relative_to(data_root))
|
|
58
|
+
d['__hash__'] = _get_checksum((d['fonts'] | d['images']).values())
|
|
59
|
+
with config.open('w', encoding='utf-8') as f:
|
|
60
|
+
json.dump(d, f, indent='\t')
|
|
61
|
+
return d
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _validate(**kwargs):
|
|
65
|
+
need_new_stub = kwargs.get('update_stub', False)
|
|
66
|
+
if need_new_stub or not config.exists():
|
|
67
|
+
json_data = _build_config()
|
|
68
|
+
need_new_stub = True
|
|
69
|
+
else:
|
|
70
|
+
json_data = json.load(config.open('r'))
|
|
71
|
+
s1, s2 = (set(
|
|
72
|
+
str(fp.relative_to(data_root)) for fp in
|
|
73
|
+
((Path(subdir) / x).absolute() for x in filter(f, os.listdir(subdir))))
|
|
74
|
+
for f, subdir in zip((_is_ttf_ext, _is_img_ext), (fonts, images)))
|
|
75
|
+
if json_data['__hash__'] != _get_checksum(s1 | s2):
|
|
76
|
+
json_data = _build_config()
|
|
77
|
+
need_new_stub = True
|
|
78
|
+
font_data, img_data = json_data['fonts'], json_data['images']
|
|
79
|
+
if need_new_stub or not stub.exists():
|
|
80
|
+
exports = ['register_user_font', 'UserFont']
|
|
81
|
+
img_funcdefs = []
|
|
82
|
+
for img_name in img_data.keys():
|
|
83
|
+
exports.append(img_name)
|
|
84
|
+
img_funcdefs.append(f"def {img_name}() -> ImageFile: ...")
|
|
85
|
+
body = [
|
|
86
|
+
f"__all__ = [{', '.join(map(repr, exports))}]",
|
|
87
|
+
'from PIL.ImageFile import ImageFile',
|
|
88
|
+
'from enum import IntEnum',
|
|
89
|
+
'from os import PathLike',
|
|
90
|
+
'from pathlib import Path',
|
|
91
|
+
'def register_user_font[AnyStr: (str, bytes)]'
|
|
92
|
+
'(__path: AnyStr | PathLike[AnyStr]) -> None: ...',
|
|
93
|
+
] + img_funcdefs
|
|
94
|
+
body.extend(
|
|
95
|
+
'\n\t'.join(
|
|
96
|
+
('class UserFont(IntEnum):',
|
|
97
|
+
*(f'{x} = {i}' for i, x in enumerate(sorted(font_data))),
|
|
98
|
+
'@property',
|
|
99
|
+
'def path(self) -> Path: ...')).replace('\t', ' ' * 4).splitlines())
|
|
100
|
+
stub.open('w', encoding='utf-8').write('\n'.join(body))
|
|
101
|
+
return font_data, img_data
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
_font_data_, _img_data_ = _validate()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _create_font_enum() -> type['UserFont']:
|
|
108
|
+
def path(self):
|
|
109
|
+
return data_root / Path(_font_data_[self.name])
|
|
110
|
+
|
|
111
|
+
enum_cls = IntEnum(
|
|
112
|
+
'UserFont',
|
|
113
|
+
{k: i for (i, k) in enumerate(sorted(_font_data_.keys()))})
|
|
114
|
+
enum_cls.path = property(path)
|
|
115
|
+
return enum_cls
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
UserFont = _create_font_enum()
|
|
119
|
+
|
|
120
|
+
def register_user_font[AnyStr: (str, bytes)](
|
|
121
|
+
__path: AnyStr | os.PathLike[AnyStr]
|
|
122
|
+
):
|
|
123
|
+
"""Register a .ttf font file as a new :class:`UserFont` enum member.
|
|
124
|
+
|
|
125
|
+
If the source file is on the same drive :mod:`chromatic.data`,
|
|
126
|
+
a symlink to it is added to the ``data/fonts`` package subdirectory.
|
|
127
|
+
|
|
128
|
+
Parameters
|
|
129
|
+
----------
|
|
130
|
+
__path : AnyStr | PathLike[AnyStr]
|
|
131
|
+
Path to the .ttf font file being registered.
|
|
132
|
+
|
|
133
|
+
Raises
|
|
134
|
+
------
|
|
135
|
+
FileNotFoundError
|
|
136
|
+
If the specified path does not exist.
|
|
137
|
+
ValueError
|
|
138
|
+
If the file is not a .ttf font.
|
|
139
|
+
OSError
|
|
140
|
+
If the font file is invalid or cannot be loaded.
|
|
141
|
+
"""
|
|
142
|
+
path = os.fspath(__path)
|
|
143
|
+
if not os.path.exists(path):
|
|
144
|
+
raise FileNotFoundError(
|
|
145
|
+
f"{__path!r}")
|
|
146
|
+
path_obj = Path(path)
|
|
147
|
+
if path_obj.is_symlink():
|
|
148
|
+
path_obj = path_obj.readlink()
|
|
149
|
+
if path_obj.suffix != '.ttf':
|
|
150
|
+
raise ValueError(
|
|
151
|
+
f"Expected '.ttf' file, "
|
|
152
|
+
f"got filetype {path_obj.suffix!r} instead")
|
|
153
|
+
from PIL.ImageFont import FreeTypeFont
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
_ = FreeTypeFont(path_obj)
|
|
157
|
+
except OSError as err:
|
|
158
|
+
if path_obj.exists():
|
|
159
|
+
err.add_note(f"{path_obj.resolve()!r}")
|
|
160
|
+
raise err
|
|
161
|
+
src = path_obj.absolute().relative_to(data_root)
|
|
162
|
+
src_pardir = src.parent if not src.is_dir() else src
|
|
163
|
+
if fonts.samefile(src_pardir):
|
|
164
|
+
is_link = False
|
|
165
|
+
else:
|
|
166
|
+
is_link = src.drive == fonts.drive
|
|
167
|
+
path_obj = Path(fonts / (src.stem + ('.ttf' if not is_link else '.lnk')))
|
|
168
|
+
ttf_obj = FreeTypeFont(path_obj)
|
|
169
|
+
ttf_name = ttf_obj.getname()
|
|
170
|
+
if any(ttf_name == FreeTypeFont(v.path).getname() for v in UserFont):
|
|
171
|
+
return print(f"{ttf_name} is already a UserFont member")
|
|
172
|
+
if is_link:
|
|
173
|
+
if path_obj.exists():
|
|
174
|
+
path_obj.unlink()
|
|
175
|
+
path_obj.symlink_to(src)
|
|
176
|
+
else:
|
|
177
|
+
path_obj.write_bytes(src.read_bytes())
|
|
178
|
+
return print(f"Successfully registered new UserFont: {ttf_name}")
|
|
179
|
+
|
|
180
|
+
def __getattr__(name) -> ...:
|
|
181
|
+
if name.startswith('_'):
|
|
182
|
+
pass
|
|
183
|
+
elif name in _img_data_:
|
|
184
|
+
return partial(Image.open, fp=data_root / Path(_img_data_[name]))
|
|
185
|
+
raise AttributeError(
|
|
186
|
+
f"Module '{__name__}' has no attribute '{name}'")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
__all__ = ['UserFont', *_img_data_, 'register_user_font']
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
__all__ = ['register_user_font', 'UserFont', 'butterfly', 'escher', 'goblin_virus', 'hotdog']
|
|
2
|
+
from PIL.ImageFile import ImageFile
|
|
3
|
+
from enum import IntEnum
|
|
4
|
+
from os import PathLike
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
def register_user_font[AnyStr: (str, bytes)](__path: AnyStr | PathLike[AnyStr]) -> None: ...
|
|
7
|
+
def butterfly() -> ImageFile: ...
|
|
8
|
+
def escher() -> ImageFile: ...
|
|
9
|
+
def goblin_virus() -> ImageFile: ...
|
|
10
|
+
def hotdog() -> ImageFile: ...
|
|
11
|
+
class UserFont(IntEnum):
|
|
12
|
+
CONSOLAS = 0
|
|
13
|
+
IBM_VGA_437_8X16 = 1
|
|
14
|
+
@property
|
|
15
|
+
def path(self) -> Path: ...
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|