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

Skip to content

Commit 88da97f

Browse files
committed
Parse multiple fonts for a single FT2Font object
1 parent 5199773 commit 88da97f

File tree

1 file changed

+136
-5
lines changed

1 file changed

+136
-5
lines changed

lib/matplotlib/font_manager.py

Lines changed: 136 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
# - setWeights function needs improvement
2424
# - 'light' is an invalid weight value, remove it.
2525

26+
from collections import OrderedDict
2627
import dataclasses
2728
from functools import lru_cache
2829
import json
@@ -1308,6 +1309,118 @@ def findfont(self, prop, fontext='ttf', directory=None,
13081309
prop, fontext, directory, fallback_to_default, rebuild_if_missing,
13091310
rc_params)
13101311

1312+
def find_fontsprop(self, prop, fontext='ttf', directory=None,
1313+
fallback_to_default=True, rebuild_if_missing=True):
1314+
"""
1315+
Find font families that most closely matches the given properties.
1316+
1317+
Parameters
1318+
----------
1319+
prop : str or `~matplotlib.font_manager.FontProperties`
1320+
The font properties to search for. This can be either a
1321+
`.FontProperties` object or a string defining a
1322+
`fontconfig patterns`_.
1323+
1324+
fontext : {'ttf', 'afm'}, default: 'ttf'
1325+
The extension of the font file:
1326+
1327+
- 'ttf': TrueType and OpenType fonts (.ttf, .ttc, .otf)
1328+
- 'afm': Adobe Font Metrics (.afm)
1329+
1330+
directory : str, optional
1331+
If given, only search this directory and its subdirectories.
1332+
1333+
fallback_to_default : bool
1334+
If True, will fallback to the default font family (usually
1335+
"DejaVu Sans" or "Helvetica") if none of the families were found.
1336+
1337+
rebuild_if_missing : bool
1338+
Whether to rebuild the font cache and search again if the first
1339+
match appears to point to a nonexisting font (i.e., the font cache
1340+
contains outdated entries).
1341+
1342+
Returns
1343+
-------
1344+
OrderedDict
1345+
key, value pair of families and their corresponding filepaths.
1346+
1347+
Notes
1348+
-----
1349+
This is a plugin to original findfont API, which only returns a
1350+
single font for given font properties. Instead, this API returns
1351+
an OrderedDict containing multiple fonts and their filepaths which
1352+
closely match the given font properties.
1353+
Since this internally uses original API, there's no change
1354+
to the logic of performing the nearest neighbor search.
1355+
See `findfont` for more details.
1356+
1357+
"""
1358+
1359+
rc_params = tuple(tuple(rcParams[key]) for key in [
1360+
"font.serif", "font.sans-serif", "font.cursive", "font.fantasy",
1361+
"font.monospace"])
1362+
1363+
prop = FontProperties._from_any(prop)
1364+
ffamily = prop.get_family()
1365+
1366+
fpaths = OrderedDict()
1367+
for fidx in range(len(ffamily)):
1368+
cprop = prop.copy()
1369+
1370+
# set current prop's family
1371+
cprop.set_family(ffamily[fidx])
1372+
1373+
# do not fall back to default font
1374+
fpath = self._findfontsprop_cached(
1375+
ffamily[fidx], cprop, fontext, directory,
1376+
False, rebuild_if_missing, rc_params
1377+
)
1378+
if fpath:
1379+
fpaths[ffamily[fidx]] = fpath
1380+
1381+
# only add default family if no other font was found
1382+
# and fallback_to_default is enabled
1383+
if not fpaths:
1384+
if fallback_to_default:
1385+
dfamily = self.defaultFamily[fontext]
1386+
cprop = prop.copy().set_family(dfamily)
1387+
fpath = self._findfontsprop_cached(
1388+
dfamily, cprop, fontext, directory,
1389+
True, rebuild_if_missing, rc_params
1390+
)
1391+
fpaths[dfamily] = fpath
1392+
else:
1393+
raise ValueError("Failed to find any font, and fallback "
1394+
"to the default font was disabled.")
1395+
1396+
return fpaths
1397+
1398+
1399+
@lru_cache()
1400+
def _findfontsprop_cached(
1401+
self, family, prop, fontext, directory,
1402+
fallback_to_default, rebuild_if_missing, rc_params
1403+
):
1404+
try:
1405+
return self._findfont_cached(
1406+
prop, fontext, directory, fallback_to_default,
1407+
rebuild_if_missing, rc_params
1408+
)
1409+
except ValueError:
1410+
if not fallback_to_default:
1411+
if family.lower() in font_family_aliases:
1412+
_log.warning(
1413+
"findfont: Generic family %r not found because "
1414+
"none of the following families were found: %s",
1415+
family,
1416+
", ".join(self._expand_aliases(family))
1417+
)
1418+
else:
1419+
_log.warning(
1420+
'findfont: Font family \'%s\' not found.', family
1421+
)
1422+
1423+
13111424
@lru_cache()
13121425
def _findfont_cached(self, prop, fontext, directory, fallback_to_default,
13131426
rebuild_if_missing, rc_params):
@@ -1401,9 +1514,23 @@ def is_opentype_cff_font(filename):
14011514

14021515

14031516
@lru_cache(64)
1404-
def _get_font(filename, hinting_factor, *, _kerning_factor, thread_id):
1405-
return ft2font.FT2Font(
1406-
filename, hinting_factor, _kerning_factor=_kerning_factor)
1517+
def _get_font(fpaths, hinting_factor, *, _kerning_factor, thread_id):
1518+
ftobjects = []
1519+
for fpath in fpaths[1:]:
1520+
ftobject = ft2font.FT2Font(
1521+
fpath, hinting_factor,
1522+
_kerning_factor=_kerning_factor
1523+
)
1524+
ftobjects.append(ftobject)
1525+
1526+
print("\nIn Python:", ftobjects, "\n")
1527+
ft2font_object = ft2font.FT2Font(
1528+
fpaths[0], hinting_factor,
1529+
_fallback_list=ftobjects,
1530+
_kerning_factor=_kerning_factor
1531+
)
1532+
print("\nBack to Python!\n")
1533+
return ft2font_object
14071534

14081535

14091536
# FT2Font objects cannot be used across fork()s because they reference the same
@@ -1417,11 +1544,14 @@ def _get_font(filename, hinting_factor, *, _kerning_factor, thread_id):
14171544
def get_font(filename, hinting_factor=None):
14181545
# Resolving the path avoids embedding the font twice in pdf/ps output if a
14191546
# single font is selected using two different relative paths.
1420-
filename = _cached_realpath(filename)
1547+
if isinstance(filename, OrderedDict):
1548+
paths = tuple(_cached_realpath(fname) for fname in filename.values())
1549+
else:
1550+
paths = (_cached_realpath(filename),)
14211551
if hinting_factor is None:
14221552
hinting_factor = rcParams['text.hinting_factor']
14231553
# also key on the thread ID to prevent segfaults with multi-threading
1424-
return _get_font(filename, hinting_factor,
1554+
return _get_font(paths, hinting_factor,
14251555
_kerning_factor=rcParams['text.kerning_factor'],
14261556
thread_id=threading.get_ident())
14271557

@@ -1446,3 +1576,4 @@ def _load_fontmanager(*, try_read_cache=True):
14461576

14471577
fontManager = _load_fontmanager()
14481578
findfont = fontManager.findfont
1579+
find_fontsprop = fontManager.find_fontsprop

0 commit comments

Comments
 (0)