|
23 | 23 | # - setWeights function needs improvement
|
24 | 24 | # - 'light' is an invalid weight value, remove it.
|
25 | 25 |
|
| 26 | +from enum import IntEnum |
26 | 27 | from functools import lru_cache
|
27 | 28 | import json
|
28 | 29 | import logging
|
29 | 30 | from numbers import Number
|
30 | 31 | import os
|
31 | 32 | from pathlib import Path
|
| 33 | +import re |
32 | 34 | import subprocess
|
33 | 35 | import sys
|
34 | 36 | try:
|
35 | 37 | from threading import Timer
|
36 | 38 | except ImportError:
|
37 | 39 | from dummy_threading import Timer
|
38 | 40 |
|
| 41 | +import numpy as np |
| 42 | + |
39 | 43 | import matplotlib as mpl
|
40 | 44 | from matplotlib import afm, cbook, ft2font, rcParams
|
41 | 45 | from matplotlib.fontconfig_pattern import (
|
|
86 | 90 | 'extra bold': 800,
|
87 | 91 | 'black': 900,
|
88 | 92 | }
|
| 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 | + |
89 | 148 | font_family_aliases = {
|
90 | 149 | 'serif',
|
91 | 150 | 'sans-serif',
|
|
95 | 154 | 'monospace',
|
96 | 155 | 'sans',
|
97 | 156 | }
|
| 157 | + |
| 158 | + |
98 | 159 | # OS Font paths
|
99 | 160 | MSFolders = \
|
100 | 161 | r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
|
@@ -403,10 +464,59 @@ def ttfFontProperty(font):
|
403 | 464 | else:
|
404 | 465 | variant = 'normal'
|
405 | 466 |
|
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()) |
410 | 520 |
|
411 | 521 | # Stretch can be absolute and relative
|
412 | 522 | # Absolute stretches are: ultra-condensed, extra-condensed, condensed,
|
|
0 commit comments