23
23
# - setWeights function needs improvement
24
24
# - 'light' is an invalid weight value, remove it.
25
25
26
+ from collections import OrderedDict
26
27
import dataclasses
27
28
from functools import lru_cache
28
29
import json
@@ -1019,6 +1020,19 @@ def json_load(filename):
1019
1020
return json .load (fh , object_hook = _json_decode )
1020
1021
1021
1022
1023
+ class FontsPath :
1024
+ """Class to hold the result of findfont"""
1025
+ def __init__ (self , file_paths ):
1026
+ self ._filepaths = None
1027
+ self .set_filepaths (file_paths )
1028
+
1029
+ def set_filepaths (self , file_paths ):
1030
+ self ._filepaths = file_paths
1031
+
1032
+ def get_filepaths (self ):
1033
+ return self ._filepaths
1034
+
1035
+
1022
1036
def _normalize_font_family (family ):
1023
1037
if isinstance (family , str ):
1024
1038
family = [family ]
@@ -1304,16 +1318,39 @@ def findfont(self, prop, fontext='ttf', directory=None,
1304
1318
rc_params = tuple (tuple (rcParams [key ]) for key in [
1305
1319
"font.serif" , "font.sans-serif" , "font.cursive" , "font.fantasy" ,
1306
1320
"font.monospace" ])
1307
- return self ._findfont_cached (
1308
- prop , fontext , directory , fallback_to_default , rebuild_if_missing ,
1309
- rc_params )
1321
+
1322
+ prop = FontProperties ._from_any (prop )
1323
+ ffamily = prop .get_family ()
1324
+
1325
+ # maintain two dicts, one for available paths,
1326
+ # the other for fallback paths
1327
+ fpaths , fbpaths = OrderedDict (), OrderedDict ()
1328
+ for fidx in range (len (ffamily )):
1329
+ prop = prop .copy ()
1330
+
1331
+ # set current prop's family
1332
+ prop .set_family (ffamily [fidx ])
1333
+
1334
+ fpath = self ._findfont_cached (
1335 + FontProperties ._from_any (prop ), fontext , directory ,
1336
+ fallback_to_default , rebuild_if_missing , rc_params )
1337
+
1338
+ # if fontfile isn't found, fpath will be a FontsPath object
1339
+ if isinstance (fpath , FontsPath ):
1340
+ fbpaths .update (fpath .get_filepaths ())
1341
+ else :
1342
+ fpaths [ffamily [fidx ]] = fpath
1343
+
1344
+ # append fallback font(s) to the very end
1345
+ fpaths .update (fbpaths )
1346
+
1347
+ return FontsPath (fpaths )
1348
+
1310
1349
1311
1350
@lru_cache ()
1312
1351
def _findfont_cached (self , prop , fontext , directory , fallback_to_default ,
1313
1352
rebuild_if_missing , rc_params ):
1314
1353
1315
- prop = FontProperties ._from_any (prop )
1316
-
1317
1354
fname = prop .get_file ()
1318
1355
if fname is not None :
1319
1356
return fname
@@ -1401,7 +1438,10 @@ def is_opentype_cff_font(filename):
1401
1438
1402
1439
1403
1440
@lru_cache (64 )
1404
- def _get_font (filename , hinting_factor , * , _kerning_factor , thread_id ):
1441
+ def _get_font (filenames , hinting_factor , * , _kerning_factor , thread_id ):
1442
+ # TODO: allow multiple files (future PR)
1443
+ # for now just pass the first element
1444
+ filename = filenames [0 ]
1405
1445
return ft2font .FT2Font (
1406
1446
filename , hinting_factor , _kerning_factor = _kerning_factor )
1407
1447
@@ -1417,11 +1457,20 @@ def _get_font(filename, hinting_factor, *, _kerning_factor, thread_id):
1417
1457
def get_font (filename , hinting_factor = None ):
1418
1458
# Resolving the path avoids embedding the font twice in pdf/ps output if a
1419
1459
# single font is selected using two different relative paths.
1420
- filename = _cached_realpath (filename )
1460
+ if isinstance (filename , FontsPath ):
1461
+ filenames = []
1462
+ for fname in filename .get_filepaths ().values ():
1463
+ filenames .append (_cached_realpath (fname ))
1464
+ else :
1465
+ filenames = [_cached_realpath (filename )]
1421
1466
if hinting_factor is None :
1422
1467
hinting_factor = rcParams ['text.hinting_factor' ]
1468
+
1469
+ # convert to tuple so its hashable
1470
+ filenames = tuple (filenames )
1471
+
1423
1472
# also key on the thread ID to prevent segfaults with multi-threading
1424
- return _get_font (filename , hinting_factor ,
1473
+ return _get_font (filenames , hinting_factor ,
1425
1474
_kerning_factor = rcParams ['text.kerning_factor' ],
1426
1475
thread_id = threading .get_ident ())
1427
1476
0 commit comments