8000 Steal fontconfig's font weight guessing algorithm, using fc weights. · matplotlib/matplotlib@5205320 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5205320

Browse files
committed
Steal fontconfig's font weight guessing algorithm, using fc weights.
1 parent 60b3f54 commit 5205320

File tree

1 file changed

+114
-4
lines changed

1 file changed

+114
-4
lines changed

lib/matplotlib/font_manager.py

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

26+
from enum import IntEnum
2627
from functools import lru_cache
2728
import json
2829
import logging
2930
from numbers import Number
3031
import os
3132
from pathlib import Path
33+
import re
3234
import subprocess
3335
import sys
3436
try:
3537
from threading import Timer
3638
except ImportError:
3739
from dummy_threading import Timer
3840

41+
import numpy as np
42+
3943
import matplotlib as mpl
4044
from matplotlib import afm, cbook, ft2font, rcParams
4145
from matplotlib.fontconfig_pattern import (
@@ -86,6 +90,61 @@
8690
'extra bold': 800,
8791
'black': 900,
8892
}
93+
94+
95+
class _Weight(IntEnum):
96+
Thin = 0
97+
Extralight = Ultralight = 40
98+
Light = 50
99+
Demilight = Semilight = 55
100+
Book = 75
101+
Regular = Normal = 80
102+
Medium = 100
103+
Demibold = Semibold = 180
104+
Bold = 200
105+
Extrabold = Ultrabold = 205
106+
Black = Heavy = 210
107+
Extrablack = Ultrablack = 215
108+
109+
@classmethod
110+
def from_opentype(cls, ot_weight):
111+
fc_weights = [0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210, 215]
112+
ot_wei 8000 ghts = [
113+
100, 200, 300, 350, 380, 400, 500, 600, 700, 800, 900, 1000]
114+
weight = int(np.interp(ot_weight, ot_weights, fc_weights) + .5)
115+
try:
116+
return _Weight(weight)
117+
except ValueError:
118+
return weight
119+
120+
121+
_weight_regexes = [
122+
("thin", _Weight.Thin),
123+
("extralight", _Weight.Extralight),
124+
("ultralight", _Weight.Ultralight),
125+
("demilight", _Weight.Demilight),
126+
("semilight", _Weight.Semilight),
127+
("light", _Weight.Light),
128+
("book", _Weight.Book),
129+
("regular", _Weight.Regular),
130+
("normal", _Weight.Normal),
131+
("medium", _Weight.Medium),
132+
("demibold", _Weight.Demibold),
133+
("demi", _Weight.Demibold),
134+
("semibold", _Weight.Semibold),
135+
("extrabold", _Weight.Extrabold),
136+
("superbold", _Weight.Extrabold),
137+
("ultrabold", _Weight.Ultrabold),
138+
("bold", _Weight.Bold),
139+
("ultrablack", _Weight.Ultrablack),
140+
("superblack", _Weight.Extrablack),
141+
("extrablack", _Weight.Extrablack),
142+
(r"\bultra", _Weight.Ultrabold),
143+
("black", _Weight.Black),
144+
("heavy", _Weight.Heavy),
145+
]
146+
147+
89148
font_family_aliases = {
90149
'serif',
91150
'sans-serif',
@@ -95,6 +154,8 @@
95154
'monospace',
96155
'sans',
97156
}
157+
158+
98159
# OS Font paths
99160
MSFolders = \
100161
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
@@ -403,10 +464,59 @@ def ttfFontProperty(font):
403464
else:
404465
variant = 'normal'
405466

406-
if font.style_flags & ft2font.BOLD:
407-
weight = 700
408-
else:
409-
weight = next((w for w in weight_dict if w in sfnt4), 400)
467+
# The weight-guessing algorithm is directly translated from fontconfig
468+
# 2.13.1's FcFreeTypeQueryFaceInternal (fcfreetype.c).
469+
platform_macintosh = 1
470+
mac_id_roman = 0
471+
mac_langid_english = 0
472+
mac_key = (platform_macintosh, mac_id_roman, mac_langid_english)
473+
platform_microsoft = 3
474+
ms_id_unicode_cs = 1
475+
ms_langid_english_united_states = 0x0409
476+
ms_key = (platform_microsoft, ms_id_unicode_cs,
477+
ms_langid_english_united_states)
478+
wws_subfamily = 22
479+
typographic_subfamily = 16
480+
font_subfamily = 2
481+
styles = ([*filter(None, (sfnt.get((*mac_key, wws_subfamily), b'')
482+
.decode('latin-1'),
483+
sfnt.get((*mac_key, typographic_subfamily), b'')
484+
.decode('latin-1'),
485+
sfnt.get((*mac_key, font_subfamily), b'')
486+
.decode('latin-1'),
487+
sfnt.get((*ms_key, wws_subfamily), b'')
488+
.decode('utf-16-be'),
489+
sfnt.get((*ms_key, typographic_subfamily), b'')
490+
.decode('utf-16-be'),
491+
sfnt.get((*ms_key, font_subfamily), b'')
492+
.decode('utf-16-be'),
493+
))]
494+
or [font.style_name])
495+
496+
def get_weight():
497+
# OS/2 table weight.
498+
os2 = font.get_sfnt_table("OS/2")
499+
if os2 and os2["version"] != 0xffff:
500+
return _Weight.from_opentype(os2["usWeightClass"])
501+
# PostScript font info weight.
502+
try:
503+
ps_font_info = font.get_ps_font_info()
504+
except ValueError:
505+
pass
506+
else:
507+
for regex, weight in _weight_regexes:
508+
if re.fullmatch(regex, ps_font_info["weight"] or "", re.I):
509+
return weight
510+
# Style name weight.
511+
for style in styles:
512+
for regex, weight in _weight_regexes:
513+
if re.search(regex, style, re.I):
514+
return weight
515+
if font.style_flags & ft2font.BOLD:
516+
return _Weight.BOLD
517+
return _Weight.REGULAR
518+
519+
weight = int(get_weight())
410520

411521
# Stretch can be absolute and relative
412522
# Absolute stretches are: ultra-condensed, extra-condensed, condensed,

0 commit comments

Comments
 (0)
0