File size: 2,545 Bytes
3c2cd23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from __future__ import annotations

import base64
from io import BytesIO
from dataclasses import dataclass
from functools import lru_cache
from importlib.resources import files
from pathlib import Path


@dataclass(frozen=True, slots=True)
class FontFace:
    family: str
    weight: int
    italic: bool
    filename: str

    @property
    def path(self) -> Path:
        return Path(str(files("editable_image").joinpath("fonts", self.filename)))


FACES = (
    FontFace("Lato", 400, False, "Lato-Regular.ttf"),
    FontFace("Lato", 400, True, "Lato-Italic.ttf"),
    FontFace("Lato", 700, False, "Lato-Bold.ttf"),
    FontFace("Lato", 700, True, "Lato-BoldItalic.ttf"),
    FontFace("Liberation Sans", 400, False, "LiberationSans-Regular.ttf"),
    FontFace("Liberation Sans", 400, True, "LiberationSans-Italic.ttf"),
    FontFace("Liberation Sans", 700, False, "LiberationSans-Bold.ttf"),
    FontFace("Liberation Sans", 700, True, "LiberationSans-BoldItalic.ttf"),
    FontFace("Liberation Serif", 400, False, "LiberationSerif-Regular.ttf"),
    FontFace("Liberation Serif", 400, True, "LiberationSerif-Italic.ttf"),
    FontFace("Liberation Serif", 700, False, "LiberationSerif-Bold.ttf"),
    FontFace("Liberation Serif", 700, True, "LiberationSerif-BoldItalic.ttf"),
    FontFace("Liberation Mono", 400, False, "LiberationMono-Regular.ttf"),
    FontFace("Liberation Mono", 400, True, "LiberationMono-Italic.ttf"),
    FontFace("Liberation Mono", 700, False, "LiberationMono-Bold.ttf"),
    FontFace("Liberation Mono", 700, True, "LiberationMono-BoldItalic.ttf"),
)


def families() -> list[str]:
    return list(dict.fromkeys(face.family for face in FACES if face.path.exists()))


def get_face(family: str, weight: int, italic: bool) -> FontFace:
    candidates = [face for face in FACES if face.family == family and face.path.exists()]
    if not candidates:
        candidates = [face for face in FACES if face.path.exists()]
    if not candidates:
        raise FileNotFoundError("No bundled fonts are installed")
    return min(
        candidates,
        key=lambda face: abs(face.weight - weight) + (1000 if face.italic != italic else 0),
    )


@lru_cache(maxsize=32)
def font_data_uri(filename: str) -> str:
    path = Path(str(files("editable_image").joinpath("fonts", filename)))
    from fontTools.ttLib import TTFont

    font = TTFont(path)
    font.flavor = "woff2"
    output = BytesIO()
    font.save(output)
    data = base64.b64encode(output.getvalue()).decode("ascii")
    return f"data:font/woff2;base64,{data}"