Skip to content

Remove "experimental" fontconfig font_manager backend. #10933

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 1 commit into from
Oct 25, 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
5 changes: 5 additions & 0 deletions doc/api/next_api_changes/2018-07-16-AL-removals.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
API removals
````````````
The following API elements have been removed:

- ``font_manager.USE_FONTCONFIG``, ``font_manager.cachedir``,
6 changes: 3 additions & 3 deletions lib/matplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,9 +555,9 @@ def get_home():
return None


def _create_tmp_config_dir():
def _create_tmp_config_or_cache_dir():
"""
If the config directory can not be created, create a temporary directory.
If the config or cache directory cannot be created, create a temporary one.
"""
configdir = os.environ['MPLCONFIGDIR'] = (
tempfile.mkdtemp(prefix='matplotlib-'))
Expand Down Expand Up @@ -609,7 +609,7 @@ def _get_config_or_cache_dir(xdg_base):
if os.access(str(configdir), os.W_OK) and configdir.is_dir():
return str(configdir)

return _create_tmp_config_dir()
return _create_tmp_config_or_cache_dir()


@_logged_cached('CONFIGDIR=%s')
Expand Down
125 changes: 25 additions & 100 deletions lib/matplotlib/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@
The design is based on the `W3C Cascading Style Sheet, Level 1 (CSS1)
font specification <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_.
Future versions may implement the Level 2 or 2.1 specifications.

Experimental support is included for using `fontconfig` on Unix
variant platforms (Linux, OS X, Solaris). To enable it, set the
constant ``USE_FONTCONFIG`` in this file to ``True``. Fontconfig has
the advantage that it is the standard way to look up fonts on X11
platforms, so if a font is installed, it is much more likely to be
found.
"""

# KNOWN ISSUES
Expand All @@ -44,14 +37,12 @@
import warnings

import matplotlib as mpl
from matplotlib import afm, cbook, ft2font, rcParams, get_cachedir
from matplotlib import afm, cbook, ft2font, rcParams
from matplotlib.fontconfig_pattern import (
parse_fontconfig_pattern, generate_fontconfig_pattern)

_log = logging.getLogger(__name__)

USE_FONTCONFIG = False

font_scalings = {
'xx-small' : 0.579,
'x-small' : 0.694,
Expand Down Expand Up @@ -104,12 +95,10 @@
MSFolders = \
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'


MSFontDirectories = [
r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts',
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Fonts']


X11FontDirectories = [
# an old standard installation point
"/usr/X11R6/lib/X11/fonts/TTF/",
Expand All @@ -120,20 +109,20 @@
"/usr/local/share/fonts/",
# common application, not really useful
"/usr/lib/openoffice/share/fonts/truetype/",
]
# user fonts
str(Path.home() / ".fonts"),
]

OSXFontDirectories = [
"/Library/Fonts/",
"/Network/Library/Fonts/",
"/System/Library/Fonts/",
# fonts installed via MacPorts
"/opt/local/share/fonts",
# user fonts
str(Path.home() / "Library/Fonts"),
]

if not USE_FONTCONFIG and sys.platform != 'win32':
OSXFontDirectories.append(str(Path.home() / "Library/Fonts"))
X11FontDirectories.append(str(Path.home() / ".fonts"))


def get_fontext_synonyms(fontext):
"""
Expand Down Expand Up @@ -1149,7 +1138,7 @@ def score_size(self, size1, size2):
sizeval2 = float(size2)
except ValueError:
return 1.0
return abs(sizeval1 - sizeval2) / 72.0
return abs(sizeval1 - sizeval2) / 72

def findfont(self, prop, fontext='ttf', directory=None,
fallback_to_default=True, rebuild_if_missing=True):
Expand Down Expand Up @@ -1273,11 +1262,10 @@ def is_opentype_cff_font(filename):
return False


fontManager = None
_fmcache = None


_get_font = lru_cache(64)(ft2font.FT2Font)
_fmcache = os.path.join(
mpl.get_cachedir(), 'fontlist-v{}.json'.format(FontManager.__version__))
fontManager = None


def get_font(filename, hinting_factor=None):
Expand All @@ -1286,86 +1274,23 @@ def get_font(filename, hinting_factor=None):
return _get_font(filename, hinting_factor)


# The experimental fontconfig-based backend.
if USE_FONTCONFIG and sys.platform != 'win32':
def _rebuild():
global fontManager
fontManager = FontManager()
with cbook._lock_path(_fmcache):
json_dump(fontManager, _fmcache)
_log.info("generated new fontManager")

def fc_match(pattern, fontext):
fontexts = get_fontext_synonyms(fontext)
ext = "." + fontext
try:
pipe = subprocess.Popen(
['fc-match', '-s', '--format=%{file}\\n', pattern],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
output = pipe.communicate()[0]
except OSError:
return None

# The bulk of the output from fc-list is ascii, so we keep the
# result in bytes and parse it as bytes, until we extract the
# filename, which is in sys.filesystemencoding().
if pipe.returncode == 0:
for fname in map(os.fsdecode, output.split(b'\n')):
if os.path.splitext(fname)[1][1:] in fontexts:
return fname
return None

_fc_match_cache = {}

def findfont(prop, fontext='ttf'):
if not isinstance(prop, str):
prop = prop.get_fontconfig_pattern()
cached = _fc_match_cache.get(prop)
if cached is not None:
return cached

result = fc_match(prop, fontext)
if result is None:
result = fc_match(':', fontext)

_fc_match_cache[prop] = result
return result

try:
fontManager = json_load(_fmcache)
except Exception:
_rebuild()
else:
_fmcache = None

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

fontManager = None

def _rebuild():
global fontManager

fontManager = FontManager()

if _fmcache:
with cbook._lock_path(_fmcache):
json_dump(fontManager, _fmcache)
_log.debug("generated new fontManager")

if _fmcache:
try:
fontManager = json_load(_fmcache)
except FileNotFoundError:
_log.debug("No font cache found %s", _fmcache)
except json.JSONDecodeError:
_log.warning("Font cache parsing failed %s", _fmcache)
else:
if (not hasattr(fontManager, '_version') or
fontManager._version != FontManager.__version__):
_log.debug("Font cache needs rebuild (version mismatch)")
fontManager = None
else:
fontManager.default_size = None
_log.debug("Using fontManager instance from %s", _fmcache)

if fontManager is None:
if getattr(fontManager, '_version', object()) != FontManager.__version__:
_rebuild()
else:
_log.debug("Using fontManager instance from %s", _fmcache)


def findfont(prop, **kw):
global fontManager
font = fontManager.findfont(prop, **kw)
return font
findfont = fontManager.findfont