Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 88da97f

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

File tree

Expand file treeCollapse file tree

1 file changed

+136
-5
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+136
-5
lines changed

‎lib/matplotlib/font_manager.py

Copy file name to clipboardExpand all lines: lib/matplotlib/font_manager.py
+136-5Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.