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

Skip to content

Py3fy font_manager. #10522

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
Mar 5, 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-02-18-AL-removals.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Removal of deprecated functions
```````````````````````````````
The following previously deprecated functions have been removed:
- ``matplotlib.font_manager.ttfdict_to_fnames``
- ``matplotlib.font_manager.weight_as_number``
133 changes: 50 additions & 83 deletions lib/matplotlib/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,16 @@
found.
"""

import six

"""
KNOWN ISSUES

- documentation
- font variant is untested
- font stretch is incomplete
- font size is incomplete
- default font algorithm needs improvement and testing
- setWeights function needs improvement
- 'light' is an invalid weight value, remove it.
- update_fonts not implemented

Authors : John Hunter <[email protected]>
Paul Barrett <[email protected]>
Michael Droettboom <[email protected]>
Copyright : John Hunter (2004,2005), Paul Barrett (2004,2005)
License : matplotlib license (PSF compatible)
The font directory code is from ttfquery,
see license/LICENSE_TTFQUERY.
"""
# KNOWN ISSUES
#
# - documentation
# - font variant is untested
# - font stretch is incomplete
# - font size is incomplete
# - default font algorithm needs improvement and testing
# - setWeights function needs improvement
# - 'light' is an invalid weight value, remove it.
# - update_fonts not implemented

from collections import Iterable
from functools import lru_cache
Expand Down Expand Up @@ -179,22 +167,17 @@ def win32FontDirectory():

If the key is not found, $WINDIR/Fonts will be returned.
"""
import winreg
try:
from six.moves import winreg
except ImportError:
pass # Fall through to default
else:
user = winreg.OpenKey(winreg.HKEY_CURRENT_USER, MSFolders)
try:
user = winreg.OpenKey(winreg.HKEY_CURRENT_USER, MSFolders)
try:
try:
return winreg.QueryValueEx(user, 'Fonts')[0]
except OSError:
pass # Fall through to default
finally:
winreg.CloseKey(user)
return winreg.QueryValueEx(user, 'Fonts')[0]
except OSError:
pass # Fall through to default
finally:
winreg.CloseKey(user)
except OSError:
pass # Fall through to default
return os.path.join(os.environ['WINDIR'], 'Fonts')


Expand All @@ -206,7 +189,8 @@ def win32InstalledFonts(directory=None, fontext='ttf'):
'afm'.
"""

from six.moves import winreg
import winreg

if directory is None:
directory = win32FontDirectory()

Expand All @@ -224,7 +208,7 @@ def win32InstalledFonts(directory=None, fontext='ttf'):
for j in range(winreg.QueryInfoKey(local)[1]):
try:
key, direc, tp = winreg.EnumValue(local, j)
if not isinstance(direc, six.string_types):
if not isinstance(direc, str):
continue
# Work around for https://bugs.python.org/issue25778, which
# is fixed in Py>=3.6.1.
Expand Down Expand Up @@ -274,19 +258,12 @@ def _call_fc_list():
'This may take a moment.'))
timer.start()
try:
out = subprocess.check_output([str('fc-list'), '--format=%{file}\\n'])
out = subprocess.check_output(['fc-list', '--format=%{file}\\n'])
except (OSError, subprocess.CalledProcessError):
return []
finally:
timer.cancel()
fnames = []
for fname in out.split(b'\n'):
try:
fname = six.text_type(fname, sys.getfilesystemencoding())
except UnicodeDecodeError:
continue
fnames.append(fname)
return fnames
return [os.fsdecode(fname) for fname in out.split(b'\n')]


def get_fontconfig_fonts(fontext='ttf'):
Expand Down Expand Up @@ -328,7 +305,7 @@ def findSystemFonts(fontpaths=None, fontext='ttf'):
for f in get_fontconfig_fonts(fontext):
fontfiles.add(f)

elif isinstance(fontpaths, six.string_types):
elif isinstance(fontpaths, str):
fontpaths = [fontpaths]

for path in fontpaths:
Expand Down Expand Up @@ -478,9 +455,9 @@ def afmFontProperty(fontpath, font):

# Styles are: italic, oblique, and normal (default)

if font.get_angle() != 0 or name.lower().find('italic') >= 0:
if font.get_angle() != 0 or 'italic' in name.lower():
style = 'italic'
elif name.lower().find('oblique') >= 0:
elif 'oblique' in name.lower():
style = 'oblique'
else:
style = 'normal'
Expand All @@ -501,12 +478,11 @@ def afmFontProperty(fontpath, font):
# and ultra-expanded.
# Relative stretches are: wider, narrower
# Child value is: inherit
if fontname.find('narrow') >= 0 or fontname.find('condensed') >= 0 or \
fontname.find('cond') >= 0:
stretch = 'condensed'
elif fontname.find('demi cond') >= 0:
if 'demi cond' in fontname:
stretch = 'semi-condensed'
elif fontname.find('wide') >= 0 or fontname.find('expanded') >= 0:
elif 'narrow' in fontname or 'cond' in fontname:
Copy link
Member

Choose a reason for hiding this comment

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

Why have these if statements swapped order?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the search for "demi cond" has to go before the search for "cond" (otherwise the latter always happens before the former...).

stretch = 'condensed'
elif 'wide' in fontname or 'expanded' in fontname:
stretch = 'expanded'
else:
stretch = 'normal'
Expand Down Expand Up @@ -568,7 +544,7 @@ def createFontList(fontfiles, fontext='ttf'):
except UnicodeError:
_log.info("Cannot handle unicode filenames")
continue
except IOError:
except OSError:
_log.info("IO error - cannot open font file %s", fpath)
Copy link
Member

Choose a reason for hiding this comment

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

If its now an OSError should this message be changed, or is it obvious enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, from the POV of the user it is indeed an input/output error... (and IOError == OSError anyways on Py3)

continue
try:
Expand Down Expand Up @@ -646,7 +622,7 @@ def __init__(self,
weight = None,
stretch= None,
size = None,
fname = None, # if this is set, it's a hardcoded filename to use
fname = None, # if set, it's a hardcoded filename to use
_init = None # used only by copy()
):
self._family = _normalize_font_family(rcParams['font.family'])
Expand All @@ -662,7 +638,7 @@ def __init__(self,
self.__dict__.update(_init.__dict__)
return

if isinstance(family, six.string_types):
if isinstance(family, str):
# Treat family as a fontconfig pattern if it is the only
# parameter provided.
if (style is None and
Expand Down Expand Up @@ -712,23 +688,20 @@ def get_family(self):

def get_name(self):
"""
Return the name of the font that best matches the font
properties.
Return the name of the font that best matches the font properties.
"""
return get_font(findfont(self)).family_name

def get_style(self):
"""
Return the font style. Values are: 'normal', 'italic' or
'oblique'.
Return the font style. Values are: 'normal', 'italic' or 'oblique'.
"""
return self._slant
get_slant = get_style

def get_variant(self):
"""
Return the font variant. Values are: 'normal' or
'small-caps'.
Return the font variant. Values are: 'normal' or 'small-caps'.
"""
return self._variant

Expand Down Expand Up @@ -793,8 +766,7 @@ def set_family(self, family):

def set_style(self, style):
"""
Set the font style. Values are: 'normal', 'italic' or
'oblique'.
Set the font style. Values are: 'normal', 'italic' or 'oblique'.
"""
if style is None:
style = rcParams['font.style']
Expand Down Expand Up @@ -892,7 +864,7 @@ def set_fontconfig_pattern(self, pattern):
support for it to be enabled. We are merely borrowing its
pattern syntax for use here.
"""
for key, val in six.iteritems(self._parse_fontconfig_pattern(pattern)):
for key, val in self._parse_fontconfig_pattern(pattern).items():
if type(val) == list:
getattr(self, "set_" + key)(val[0])
else:
Expand Down Expand Up @@ -936,9 +908,10 @@ def json_dump(data, filename):
with open(filename, 'w') as fh:
try:
json.dump(data, fh, cls=JSONEncoder, indent=2)
except IOError as e:
except OSError as e:
warnings.warn('Could not save font_manager cache ', e)


def json_load(filename):
"""Loads a data structure as JSON from the named file.
Handles FontManager and its fields."""
Expand All @@ -948,10 +921,8 @@ def json_load(filename):


def _normalize_font_family(family):
if isinstance(family, six.string_types):
family = [six.text_type(family)]
elif isinstance(family, Iterable):
family = [six.text_type(f) for f in family]
if isinstance(family, str):
family = [family]
return family


Expand Down Expand Up @@ -1170,14 +1141,14 @@ def score_weight(self, weight1, weight2):
The result is 0.0 if both weight1 and weight 2 are given as strings
and have the same value.

Otherwise, the result is the absolute value of the difference between the
CSS numeric values of *weight1* and *weight2*, normalized
between 0.05 and 1.0.
Otherwise, the result is the absolute value of the difference between
the CSS numeric values of *weight1* and *weight2*, normalized between
0.05 and 1.0.
"""

# exact match of the weight names (e.g. weight1 == weight2 == "regular")
if (isinstance(weight1, six.string_types) and
isinstance(weight2, six.string_types) and
# exact match of the weight names, e.g. weight1 == weight2 == "regular"
if (isinstance(weight1, str) and
isinstance(weight2, str) and
weight1 == weight2):
return 0.0
try:
Expand Down Expand Up @@ -1364,26 +1335,22 @@ def fc_match(pattern, fontext):
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
output = pipe.communicate()[0]
except (OSError, IOError):
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 output.split(b'\n'):
try:
fname = six.text_type(fname, sys.getfilesystemencoding())
except UnicodeDecodeError:
continue
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, six.string_types):
if not isinstance(prop, str):
prop = prop.get_fontconfig_pattern()
cached = _fc_match_cache.get(prop)
if cached is not None:
Expand Down