Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Cache paths of fonts shipped with mpl relative to the mpl data path. #10245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ per-file-ignores =
matplotlib/backends/qt_editor/formlayout.py: E301, E501
matplotlib/backends/tkagg.py: E231, E302, E701
matplotlib/backends/windowing.py: E301, E302
matplotlib/font_manager.py: E203, E221, E225, E251, E261, E262, E302, E501
matplotlib/font_manager.py: E203, E221, E251, E261, E262, E302, E501
matplotlib/fontconfig_pattern.py: E201, E203, E221, E222, E225, E302
matplotlib/legend_handler.py: E201, E501
matplotlib/mathtext.py: E201, E202, E203, E211, E221, E222, E225, E231, E251, E261, E301, E302, E303, E402, E501
Expand Down
76 changes: 49 additions & 27 deletions lib/matplotlib/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from dummy_threading import Timer
import warnings

import matplotlib as mpl
from matplotlib import afm, cbook, ft2font, rcParams, get_cachedir
from matplotlib.fontconfig_pattern import (
parse_fontconfig_pattern, generate_fontconfig_pattern)
Expand Down Expand Up @@ -828,15 +829,23 @@ def copy(self):
class JSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, FontManager):
return dict(o.__dict__, _class='FontManager')
return dict(o.__dict__, __class__='FontManager')
elif isinstance(o, FontEntry):
return dict(o.__dict__, _class='FontEntry')
d = dict(o.__dict__, __class__='FontEntry')
try:
# Cache paths of fonts shipped with mpl relative to the mpl
# data path, which helps in the presence of venvs.
d["fname"] = str(
Path(d["fname"]).relative_to(mpl.get_data_path()))
except ValueError:
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One concern is that are we sure every path we see here will be absolute?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fontconfig returns absolute paths, and we call os.path.abspath in findSystemFonts.

return d
else:
return super().default(o)


def _json_decode(o):
cls = o.pop('_class', None)
cls = o.pop('__class__', None)
if cls is None:
return o
elif cls == 'FontManager':
Expand All @@ -846,15 +855,21 @@ def _json_decode(o):
elif cls == 'FontEntry':
r = FontEntry.__new__(FontEntry)
r.__dict__.update(o)
if not os.path.isabs(r.fname):
r.fname = os.path.join(mpl.get_data_path(), r.fname)
return r
else:
raise ValueError("don't know how to deserialize _class=%s" % cls)
raise ValueError("don't know how to deserialize __class__=%s" % cls)


def json_dump(data, filename):
"""Dumps a data structure as JSON in the named file.
Handles FontManager and its fields."""
"""
Dumps a data structure as JSON in the named file.

Handles FontManager and its fields. File paths that are children of the
Matplotlib data path (typically, fonts shipped with Matplotlib) are stored
relative to that data path (to remain valid across virtualenvs).
"""
with open(filename, 'w') as fh:
try:
json.dump(data, fh, cls=JSONEncoder, indent=2)
Expand All @@ -863,9 +878,13 @@ def json_dump(data, filename):


def json_load(filename):
"""Loads a data structure as JSON from the named file.
Handles FontManager and its fields."""
"""
Loads a data structure as JSON from the named file.

Handles FontManager and its fields. Relative file paths are interpreted
as being relative to the Matplotlib data path, and transformed into
absolute paths.
"""
with open(filename, 'r') as fh:
return json.load(fh, object_hook=_json_decode)

Expand Down Expand Up @@ -926,7 +945,7 @@ class FontManager(object):
# Increment this version number whenever the font cache data
# format or behavior has changed and requires a existing font
# cache files to be rebuilt.
__version__ = 201
__version__ = 300

def __init__(self, size=None, weight='normal'):
self._version = self.__version__
Expand All @@ -951,30 +970,32 @@ def __init__(self, size=None, weight='normal'):
_log.debug('font search path %s', str(paths))
# Load TrueType fonts and create font dictionary.

self.ttffiles = findSystemFonts(paths) + findSystemFonts()
self.defaultFamily = {
'ttf': 'DejaVu Sans',
'afm': 'Helvetica'}
self.defaultFont = {}

for fname in self.ttffiles:
_log.debug('trying fontname %s', fname)
if fname.lower().find('DejaVuSans.ttf')>=0:
self.defaultFont['ttf'] = fname
break
else:
# use anything
self.defaultFont['ttf'] = self.ttffiles[0]
ttffiles = findSystemFonts(paths) + findSystemFonts()
self.defaultFont['ttf'] = next(
(fname for fname in ttffiles
if fname.lower().endswith("dejavusans.ttf")),
ttffiles[0])
self.ttflist = createFontList(ttffiles)

self.ttflist = createFontList(self.ttffiles)
afmfiles = (findSystemFonts(paths, fontext='afm')
+ findSystemFonts(fontext='afm'))
self.afmlist = createFontList(afmfiles, fontext='afm')
self.defaultFont['afm'] = afmfiles[0] if afmfiles else None

self.afmfiles = (findSystemFonts(paths, fontext='afm')
+ findSystemFonts(fontext='afm'))
self.afmlist = createFontList(self.afmfiles, fontext='afm')
if len(self.afmfiles):
self.defaultFont['afm'] = self.afmfiles[0]
else:
self.defaultFont['afm'] = None
@property
@cbook.deprecated("3.0")
def ttffiles(self):
return [font.fname for font in self.ttflist]

@property
@cbook.deprecated("3.0")
def afmfiles(self):
return [font.fname for font in self.afmlist]

def get_default_weight(self):
"""
Expand Down Expand Up @@ -1312,7 +1333,8 @@ def findfont(prop, fontext='ttf'):

cachedir = get_cachedir()
if cachedir is not None:
_fmcache = os.path.join(cachedir, 'fontList.json')
_fmcache = os.path.join(
cachedir, 'fontlist-v{}.json'.format(FontManager.__version__))

fontManager = None

Expand Down