4747 see license/LICENSE_TTFQUERY.
4848"""
4949
50- import json
51- import os , sys , warnings
5250from collections import Iterable
51+ import json
52+ import os
53+ import sys
54+ from threading import Timer
55+ import warnings
56+
5357import matplotlib
54- from matplotlib import afm
55- from matplotlib import ft2font
56- from matplotlib import rcParams , get_cachedir
58+ from matplotlib import afm , cbook , ft2font , rcParams , get_cachedir
5759from matplotlib .cbook import is_string_like
58- import matplotlib .cbook as cbook
5960from matplotlib .compat import subprocess
60- from matplotlib .fontconfig_pattern import \
61- parse_fontconfig_pattern , generate_fontconfig_pattern
61+ from matplotlib .fontconfig_pattern import (
62+ parse_fontconfig_pattern , generate_fontconfig_pattern )
6263
6364try :
6465 from functools import lru_cache
@@ -262,39 +263,39 @@ def OSXInstalledFonts(directories=None, fontext='ttf'):
262263 files .extend (list_fonts (path , fontext ))
263264 return files
264265
265- def get_fontconfig_fonts (fontext = 'ttf' ):
266+
267+ @lru_cache ()
268+ def _call_fc_list ():
269+ """Cache and list the font filenames known to `fc-list`.
266270 """
267- Grab a list of all the fonts that are being tracked by fontconfig
268- by making a system call to ``fc-list``. This is an easy way to
269- grab all of the fonts the user wants to be made available to
270- applications, without needing knowing where all of them reside.
271+ # Delay the warning by 5s.
272+ timer = Timer (5 , lambda : warnings .warn (
273+ 'Matplotlib is building the font cache using fc-list. '
274+ 'This may take a moment.' ))
275+ timer .start ()
276+ try :
277+ out = subprocess .check_output (['fc-list' , '--format=%{file}' ])
278+ except (OSError , subprocess .CalledProcessError ):
279+ return []
280+ finally :
281+ timer .cancel ()
282+ fnames = []
283+ for fname in out .split (b'\n ' ):
284+ try :
285+ fname = six .text_type (fname , sys .getfilesystemencoding ())
286+ except UnicodeDecodeError :
287+ continue
288+ fnames .append (fname )
289+ return fnames
290+
291+
292+ def get_fontconfig_fonts (fontext = 'ttf' ):
293+ """List the font filenames known to `fc-list` having the given extension.
271294 """
272295 fontext = get_fontext_synonyms (fontext )
296+ return [fname for fname in _call_fc_list ()
297+ if os .path .splitext (fname )[1 ][1 :] in fontext ]
273298
274- fontfiles = {}
275- try :
276- warnings .warn ('Matplotlib is building the font cache using fc-list. This may take a moment.' )
277- pipe = subprocess .Popen (['fc-list' , '--format=%{file}\\ n' ],
278- stdout = subprocess .PIPE ,
279- stderr = subprocess .PIPE )
280- output = pipe .communicate ()[0 ]
281- except (OSError , IOError ):
282- # Calling fc-list did not work, so we'll just return nothing
283- return fontfiles
284-
285- if pipe .returncode == 0 :
286- # The line breaks between results are in ascii, but each entry
287- # is in in sys.filesystemencoding().
288- for fname in output .split (b'\n ' ):
289- try :
290- fname = six .text_type (fname , sys .getfilesystemencoding ())
291- except UnicodeDecodeError :
292- continue
293- if (os .path .splitext (fname )[1 ][1 :] in fontext and
294- os .path .exists (fname )):
295- fontfiles [fname ] = 1
296-
297- return fontfiles
298299
299300def findSystemFonts (fontpaths = None , fontext = 'ttf' ):
300301 """
@@ -304,7 +305,7 @@ def findSystemFonts(fontpaths=None, fontext='ttf'):
304305 available. A list of TrueType fonts are returned by default with
305306 AFM fonts as an option.
306307 """
307- fontfiles = {}
308+ fontfiles = set ()
308309 fontexts = get_fontext_synonyms (fontext )
309310
310311 if fontpaths is None :
@@ -316,26 +317,26 @@ def findSystemFonts(fontpaths=None, fontext='ttf'):
316317 for f in win32InstalledFonts (fontdir ):
317318 base , ext = os .path .splitext (f )
318319 if len (ext )> 1 and ext [1 :].lower () in fontexts :
319- fontfiles [ f ] = 1
320+ fontfiles . add ( f )
320321 else :
321322 fontpaths = X11FontDirectories
322323 # check for OS X & load its fonts if present
323324 if sys .platform == 'darwin' :
324325 for f in OSXInstalledFonts (fontext = fontext ):
325- fontfiles [ f ] = 1
326+ fontfiles . add ( f )
326327
327328 for f in get_fontconfig_fonts (fontext ):
328- fontfiles [ f ] = 1
329+ fontfiles . add ( f )
329330
330331 elif isinstance (fontpaths , six .string_types ):
331332 fontpaths = [fontpaths ]
332333
333334 for path in fontpaths :
334335 files = list_fonts (path , fontexts )
335336 for fname in files :
336- fontfiles [ os .path .abspath (fname )] = 1
337+ fontfiles . add ( os .path .abspath (fname ))
337338
338- return [fname for fname in six . iterkeys ( fontfiles ) if os .path .exists (fname )]
339+ return [fname for fname in fontfiles if os .path .exists (fname )]
339340
340341def weight_as_number (weight ):
341342 """
@@ -833,7 +834,7 @@ def set_family(self, family):
833834 family = rcParams ['font.family' ]
834835 if is_string_like (family ):
835836 family = [six .text_type (family )]
836- elif ( not is_string_like (family ) and isinstance (family , Iterable ) ):
837+ elif not is_string_like (family ) and isinstance (family , Iterable ):
837838 family = [six .text_type (f ) for f in family ]
838839 self ._family = family
839840 set_name = set_family
0 commit comments