]> git.immae.eu Git - perso/Immae/Projets/Python/MusicSampler.git/blob - helpers/sysfont.py
Remove fonts from directory
[perso/Immae/Projets/Python/MusicSampler.git] / helpers / sysfont.py
1 # This file was imported from
2 # https://bitbucket.org/marcusva/python-utils/overview
3 # And slightly adapted
4
5 """OS-specific font detection."""
6 import os
7 import sys
8 from subprocess import Popen, PIPE
9
10 if sys.platform in ("win32", "cli"):
11 import winreg
12
13 __all__ = ["STYLE_NORMAL", "STYLE_BOLD", "STYLE_ITALIC", "STYLE_LIGHT",
14 "init", "list_fonts", "get_fonts", "get_font"
15 ]
16
17 # Font cache entries:
18 # { family : [...,
19 # (name, styles, fonttype, filename)
20 # ...
21 # ]
22 # }
23 __FONTCACHE = None
24
25
26 STYLE_NONE = 0x00
27 STYLE_NORMAL = 0x01
28 STYLE_BOLD = 0x02
29 STYLE_ITALIC = 0x04
30 STYLE_LIGHT = 0x08
31 STYLE_MEDIUM = 0x10
32
33 def _add_font(family, name, styles, fonttype, filename):
34 """Adds a font to the internal font cache."""
35 global __FONTCACHE
36
37 if family not in __FONTCACHE:
38 __FONTCACHE[family] = []
39 __FONTCACHE[family].append((name, styles, fonttype, filename))
40
41
42 def _cache_fonts_win32():
43 """Caches fonts on a Win32 platform."""
44 key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
45 regfonts = []
46 try:
47 with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key) as fontkey:
48 idx = 0
49 enumval = winreg.EnumValue
50 rappend = regfonts.append
51 while True:
52 rappend(enumval(fontkey, idx)[:2])
53 idx += 1
54 except WindowsError:
55 pass
56
57 # TODO: integrate alias handling for fonts within the registry.
58 # TODO: Scan and index fonts from %SystemRoot%\\Fonts that are not in the
59 # registry
60
61 # Received all fonts from the registry.
62 for name, filename in regfonts:
63 fonttype = os.path.splitext(filename)[1][1:].lower()
64 if name.endswith("(TrueType)"):
65 name = name[:-10].strip()
66 if name.endswith("(All Res)"):
67 name = name[:-9].strip()
68 style = STYLE_NORMAL
69 if name.find(" Bold") >= 0:
70 style |= STYLE_BOLD
71 if name.find(" Italic") >= 0 or name.find(" Oblique") >= 0:
72 style |= STYLE_ITALIC
73
74 family = name
75 for rm in ("Bold", "Italic", "Oblique"):
76 family = family.replace(rm, "")
77 family = family.lower().strip()
78
79 fontpath = os.environ.get("SystemRoot", "C:\\Windows")
80 fontpath = os.path.join(fontpath, "Fonts")
81 if filename.find("\\") == -1:
82 # No path delimiter is given; we assume it to be a font in
83 # %SystemRoot%\Fonts
84 filename = os.path.join(fontpath, filename)
85 _add_font(family, name, style, fonttype, filename)
86
87
88 def _cache_fonts_darwin():
89 """Caches fonts on Mac OS."""
90 raise NotImplementedError("Mac OS X support is not given yet")
91
92
93 def _cache_fonts_fontconfig():
94 """Caches font on POSIX-alike platforms."""
95 try:
96 command = "fc-list : file family style fullname fullnamelang"
97 proc = Popen(command, stdout=PIPE, shell=True, stderr=PIPE)
98 pout = proc.communicate()[0]
99 output = pout.decode("utf-8")
100 except OSError:
101 return
102
103 for entry in output.split(os.linesep):
104 if entry.strip() == "":
105 continue
106 values = entry.split(":")
107 filename = values[0]
108
109 # get the font type
110 fname, fonttype = os.path.splitext(filename)
111 if fonttype == ".gz":
112 fonttype = os.path.splitext(fname)[1][1:].lower()
113 else:
114 fonttype = fonttype.lstrip(".").lower()
115
116 # get the font name
117 name = None
118 if len(values) > 3:
119 fullnames, fullnamelangs = values[3:]
120 langs = fullnamelangs.split(",")
121 try:
122 offset = langs.index("fullnamelang=en")
123 except ValueError:
124 offset = -1
125 if offset == -1:
126 try:
127 offset = langs.index("en")
128 except ValueError:
129 offset = -1
130 if offset != -1:
131 # got an english name, use that one
132 name = fullnames.split(",")[offset]
133 if name.startswith("fullname="):
134 name = name[9:]
135 if name is None:
136 if fname.endswith(".pcf") or fname.endswith(".bdf"):
137 name = os.path.basename(fname[:-4])
138 else:
139 name = os.path.basename(fname)
140 name = name.lower()
141
142 # family and styles
143 family = values[1].strip().lower()
144 stylevals = values[2].strip()
145 style = STYLE_NONE
146
147 if stylevals.find("Bold") >= 0:
148 style |= STYLE_BOLD
149 if stylevals.find("Light") >= 0:
150 style |= STYLE_LIGHT
151 if stylevals.find("Italic") >= 0 or stylevals.find("Oblique") >= 0:
152 style |= STYLE_ITALIC
153 if stylevals.find("Medium") >= 0:
154 style |= STYLE_MEDIUM
155 if style == STYLE_NONE:
156 style = STYLE_NORMAL
157 _add_font(family, name, style, fonttype, filename)
158
159
160 def init():
161 """Initialises the internal font cache.
162
163 It does not need to be called explicitly.
164 """
165 global __FONTCACHE
166 if __FONTCACHE is not None:
167 return
168 __FONTCACHE = {}
169 if sys.platform in ("win32", "cli"):
170 _cache_fonts_win32()
171 elif sys.platform == "darwin":
172 _cache_fonts_darwin()
173 else:
174 _cache_fonts_fontconfig()
175
176
177 def list_fonts():
178 """Returns an iterator over the cached fonts."""
179 if __FONTCACHE is None:
180 init()
181 if len(__FONTCACHE) == 0:
182 yield None
183 for family, entries in __FONTCACHE.items():
184 for fname, styles, fonttype, filename in entries:
185 yield (family, fname, styles, fonttype, filename)
186
187
188 def get_fonts(name, style=STYLE_NONE, ftype=None):
189 """Retrieves all fonts matching the given family or font name."""
190 if __FONTCACHE is None:
191 init()
192 if len(__FONTCACHE) == 0:
193 return None
194
195 results = []
196 rappend = results.append
197
198 name = name.lower()
199 if ftype:
200 ftype = ftype.lower()
201
202 fonts = __FONTCACHE.get(name, [])
203 for fname, fstyles, fonttype, filename in fonts:
204 if ftype and fonttype != ftype:
205 # ignore font filetype mismatches
206 continue
207 if style == STYLE_NONE or fstyles == style:
208 rappend((name, fname, fstyles, fonttype, filename))
209
210 for family, fonts in __FONTCACHE.items():
211 for fname, fstyles, fonttype, filename in fonts:
212 if fname.lower() == name and filename not in results:
213 rappend((family, fname, fstyles, fonttype, filename))
214 return results
215
216
217 def get_font(name, style=STYLE_NONE, ftype=None):
218 """Retrieves the best matching font file for the given name and
219 criteria.
220 """
221 retvals = get_fonts(name, style, ftype)
222 if len(retvals) > 0:
223 return retvals[0]
224 return None