ktrk115 commited on
Commit
0a2f23b
·
verified ·
1 Parent(s): d3e05e9

Upload fonts.py

Browse files
Files changed (1) hide show
  1. fonts.py +154 -0
fonts.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import pickle
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ import fsspec # type: ignore
6
+ import huggingface_hub # type: ignore
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ FontDict = Dict[str, List[Dict[str, Any]]]
11
+
12
+
13
+ FONT_WEIGHTS = {
14
+ "thin",
15
+ "light",
16
+ "extralight",
17
+ "regular",
18
+ "medium",
19
+ "semibold",
20
+ "bold",
21
+ "extrabold",
22
+ "black",
23
+ }
24
+
25
+ FONT_STYLES = {"regular", "bold", "italic", "bolditalic"}
26
+
27
+ _OPENCOLE_REPOSITORY = "cyberagent/opencole"
28
+ _OPENCOLE_FONTS_PATH = "resources/fonts.pickle"
29
+
30
+
31
+ class FontManager(object):
32
+ """Font face manager.
33
+
34
+ Example::
35
+
36
+ # Use the font manager to lookup a font face.
37
+ fm = FontManager("/path/to/fonts.pickle")
38
+ typeface = skia.Typeface.MakeFromData(fm.lookup("Montserrat"))
39
+ """
40
+
41
+ def __init__(self, input_path: Optional[str] = None) -> None:
42
+ self._fonts: Optional[FontDict] = None
43
+
44
+ if input_path is None:
45
+ input_path = huggingface_hub.hf_hub_download(
46
+ repo_id=_OPENCOLE_REPOSITORY,
47
+ filename=_OPENCOLE_FONTS_PATH,
48
+ repo_type="dataset",
49
+ )
50
+
51
+ self.load(input_path)
52
+
53
+ def save(self, output_path: str) -> None:
54
+ """Save fonts to a pickle file."""
55
+ assert self._fonts is not None, "Fonts not loaded yet."
56
+ logger.info("Saving fonts to %s", output_path)
57
+ with fsspec.open(output_path, "wb") as f:
58
+ pickle.dump(self._fonts, f)
59
+
60
+ def load(self, input_path: str) -> None:
61
+ """Load fonts from a pickle file."""
62
+ logger.info("Loading fonts from %s", input_path)
63
+ with fsspec.open(input_path, "rb") as f:
64
+ self._fonts = pickle.load(f)
65
+ assert self._fonts is not None, "No font loaded."
66
+ logger.info("Loaded %d font families", len(self._fonts))
67
+
68
+ def lookup(
69
+ self,
70
+ font_family: str,
71
+ font_weight: str = "regular",
72
+ font_style: str = "regular",
73
+ ) -> bytes:
74
+ """Lookup the specified font face."""
75
+ assert self._fonts is not None, "Fonts not loaded yet."
76
+ assert font_weight in FONT_WEIGHTS, f"Invalid font weight: {font_weight}"
77
+ assert font_style in FONT_STYLES, f"Invalid font style: {font_style}"
78
+
79
+ family = []
80
+ for i, family_name in enumerate([font_family, "Montserrat"]):
81
+ try:
82
+ family = self._fonts[normalize_family(family_name)]
83
+ if i > 0:
84
+ logger.warning(
85
+ f"Font family fallback to {family[0]['fontFamily']}."
86
+ )
87
+ break
88
+ except KeyError:
89
+ logger.warning(f"Font family not found: {font_family}")
90
+
91
+ if not family:
92
+ family = next(iter(self._fonts.values()))
93
+ logger.warning(f"Font family fallback to {family[0]['fontFamily']}.")
94
+
95
+ font_weight = get_font_weight(font_family, font_weight)
96
+ font_style = get_font_style(font_family, font_style)
97
+ try:
98
+ font = next(
99
+ font
100
+ for font in family
101
+ if font.get("fontWeight", "regular") == font_weight
102
+ and font.get("fontStyle", "regular") == font_style
103
+ )
104
+ except StopIteration:
105
+ font = family[0]
106
+ logger.warning(
107
+ f"Font style for {font['fontFamily']} not found: {font_weight} "
108
+ f"{font_style}, fallback to {font.get('fontWeight', 'regular')} "
109
+ f"{font.get('fontStyle', 'regular')}"
110
+ )
111
+ return font["bytes"]
112
+
113
+
114
+ def normalize_family(name: str) -> str:
115
+ """Normalize font name."""
116
+ name = name.replace("_", " ").title()
117
+ name = name.replace(" Bold", "")
118
+ name = name.replace(" Regular", "")
119
+ name = name.replace(" Light", "")
120
+ name = name.replace(" Italic", "")
121
+ name = name.replace(" Medium", "")
122
+
123
+ FONT_MAP = {
124
+ "Arkana Script": "Arkana",
125
+ "Blogger": "Blogger Sans",
126
+ "Delius Swash": "Delius Swash Caps",
127
+ "Elsie Swash": "Elsie Swash Caps",
128
+ "Gluk Glametrix": "Gluk Foglihtenno06",
129
+ "Gluk Znikomitno25": "Gluk Foglihtenno06",
130
+ "Im Fell": "Im Fell Dw Pica Sc",
131
+ "Medieval Sharp": "Medievalsharp",
132
+ "Playlist Caps": "Playlist",
133
+ "Rissa Typeface": "Rissatypeface",
134
+ "Selima": "Selima Script",
135
+ "Six": "Six Caps",
136
+ "V T323": "Vt323",
137
+ # The rest is unknown.
138
+ "Different Summer": "Montserrat",
139
+ "Dukomdesign Constantine": "Montserrat",
140
+ "Sunday": "Montserrat",
141
+ }
142
+ return FONT_MAP.get(name, name)
143
+
144
+
145
+ def get_font_weight(name: str, default: str) -> str:
146
+ """Get font weight from the font name."""
147
+ key = name.replace("_", " ").lower().split(" ")[-1]
148
+ return key if key in FONT_WEIGHTS else default
149
+
150
+
151
+ def get_font_style(name: str, default: str) -> str:
152
+ """Get font style from the font name."""
153
+ key = name.replace("_", " ").lower().split(" ")[-1]
154
+ return key if key in FONT_STYLES else default