8000 FIX: overhaul how font metadata is extracted · matplotlib/matplotlib@75aa38b · GitHub
[go: up one dir, main page]

Skip to content

Commit 75aa38b

Browse files
committed
FIX: overhaul how font metadata is extracted
1 parent c1fef83 commit 75aa38b

File tree

1 file changed

+64
-72
lines changed

1 file changed

+64
-72
lines changed

lib/matplotlib/font_manager.py

Lines changed: 64 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@
2525
import six
2626
from six.moves import cPickle as pickle
2727

28+
from collections import Iterable
29+
import json
30+
import os
31+
import sys
32+
from threading import Timer
33+
import warnings
34+
35+
import matplotlib
36+
from matplotlib import afm, cbook, ft2font, rcParams, get_cachedir
37+
from matplotlib.compat import subprocess
38+
from matplotlib.fontconfig_pattern import (
39+
parse_fontconfig_pattern, generate_fontconfig_pattern)
40+
from matplotlib._ttf_header_data import get_all_style_strings
41+
try:
42+
from functools import lru_cache
43+
except ImportError:
44+
from backports.functools_lru_cache import lru_cache
45+
2846
"""
2947
KNOWN ISSUES
3048
@@ -47,24 +65,6 @@
4765
see license/LICENSE_TTFQUERY.
4866
"""
4967

50-
from collections import Iterable
51-
import json
52-
import os
53-
import sys
54-
from threading import Timer
55-
import warnings
56-
57-
import matplotlib
58-
from matplotlib import afm, cbook, ft2font, rcParams, get_cachedir
59-
from matplotlib.compat import subprocess
60-
from matplotlib.fontconfig_pattern import (
61-
parse_fontconfig_pattern, generate_fontconfig_pattern)
62-
63-
try:
64-
from functools import lru_cache
65-
except ImportError:
66-
from backports.functools_lru_cache import lru_cache
67-
6868

6969
USE_FONTCONFIG = False
7070
verbose = matplotlib.verbose
@@ -414,72 +414,64 @@ def ttfFontProperty(font):
414414
# Styles are: italic, oblique, and normal (default)
415415

416416
sfnt = font.get_sfnt()
417-
sfnt2 = sfnt.get((1,0,0,2))
418-
sfnt2_3 = sfnt.get((3,1,1033,2))
419-
sfnt4 = sfnt.get((1,0,0,4))
420-
sfnt4_3 = sfnt.get((3,1,1033,4))
421-
if sfnt2:
422-
sfnt2 = sfnt2.decode('macroman').lower()
423-
elif sfnt2_3:
424-
sfnt2 = sfnt2_3.decode('utf_16_be').lower()
417+
sfnt_strings = set(s
418+
for v in get_all_style_strings(sfnt)
419+
for s in v.lower().split())
420+
# this may not be needed, to sure where how ft2font is computing this
421+
# under the hood
422+
sfnt_strings.update(set(style_name.lower().split()))
423+
# discard regular because it appears in both style and weight and is
424+
# the default value (patched up below)
425+
sfnt_strings.discard('regular')
426+
if name == 'Ubuntu':
427+
# the platform 1 and platform 3 information is inconsistent for
428+
# some of the meta data
429+
sfnt_strings.discard('medium')
430+
known_style = {'oblique', 'italic', 'regular'}
431+
style_candidates = sfnt_strings & known_style
432+
weight_candidates = sfnt_strings & set(weight_dict)
433+
stretch_candidates = sfnt_strings & {'ultra-condensed',
434+
'extra-condensed',
435+
'condensed',
436+
'semi-condensed', 'normal',
437+
'semi-expanded', 'expanded',
438+
'extra-expanded',
439+
'ultra-expanded'}
440+
441+
if style_candidates:
442+
if len(style_candidates) > 1:
443+
raise RuntimeError('{} font is weird has too many styles')
444+
style = style_candidates.pop()
425445
else:
426-
sfnt2 = ''
446+
if font.style_flags & ft2font.ITALIC:
447+
style = 'italic'
448+
else:
449+
style = 'regular'
427450

428-
if sfnt4:
429-
sfnt4 = sfnt4.decode('macroman').lower()
430-
elif sfnt4_3:
431-
sfnt4 = sfnt4_3.decode('utf_16_be').lower()
451+
if weight_candidates:
452+
if len(weight_candidates) > 1:
453+
raise RuntimeError('{} font is weird has too many weights')
454+
weight = weight_candidates.pop()
432455
else:
433-
sfnt4 = ''
434-
if sfnt4.find('oblique') >= 0:
435-
style = 'oblique'
436-
elif sfnt4.find('italic') >= 0:
437-
style = 'italic'
438-
elif sfnt2.find('regular') >= 0:
439-
style = 'normal'
440-
elif font.style_flags & ft2font.ITALIC:
441-
style = 'italic'
456+
if font.style_flags & ft2font.BOLD:
457+
weight = 'bold'
458+
else:
459+
weight = 'regular'
460+
461+
if stretch_candidates:
462+
if len(stretch_candidates) > 1:
463+
raise RuntimeError('{} font is weird has too many stretches')
464+
stretch = stretch_candidates.pop()
442465
else:
443-
style = 'normal'
466+
stretch = 'normal'
444467

445468
# Variants are: small-caps and normal (default)
446-
447469
# !!!! Untested
448470
if name.lower() in ['capitals', 'small-caps']:
449471
variant = 'small-caps'
450472
else:
451473
variant = 'normal'
452474

453-
sorted_weights = sorted(weight_dict.keys(), key=lambda k: len(k),
454-
reverse=True)
455-
weight = next((w for w in sorted_weights if sfnt4.find(w) >= 0), None)
456-
if not weight:
457-
weight = next((w for w in weight_dict if style_name.find(w) >= 0), None)
458-
if weight:
459-
pass
460-
elif style_name in ["italic", "oblique", "condensed"]:
461-
weight = 400
462-
else:
463-
# give up rather than risk making mistakes (reason: #8550)
464-
raise KeyError("unknown weight: {!r}".format(font.family_name))
465-
466-
# Stretch can be absolute and relative
467-
# Absolute stretches are: ultra-condensed, extra-condensed, condensed,
468-
# semi-condensed, normal, semi-expanded, expanded, extra-expanded,
469-
# and ultra-expanded.
470-
# Relative stretches are: wider, narrower
471-
# Child value is: inherit
472-
473-
if (sfnt4.find('narrow') >= 0 or sfnt4.find('condensed') >= 0 or
474-
sfnt4.find('cond') >= 0):
475-
stretch = 'condensed'
476-
elif sfnt4.find('demi cond') >= 0:
477-
stretch = 'semi-condensed'
478-
elif sfnt4.find('wide') >= 0 or sfnt4.find('expanded') >= 0:
479-
stretch = 'expanded'
480-
else:
481-
stretch = 'normal'
482-
483475
# Sizes can be absolute and relative.
484476
# Absolute sizes are: xx-small, x-small, small, medium, large, x-large,
485477
# and xx-large.

0 commit comments

Comments
 (0)
0