Skip to content

FT2Font extension improvements #28842

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions doc/api/next_api_changes/deprecations/28842-ES.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
ft2font module-level constants replaced by enums
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The `.ft2font`-level constants have been converted to `enum` classes, and all API using
them now take/return the new types.

The following constants are now part of `.ft2font.Kerning` (without the ``KERNING_``
prefix):

- ``KERNING_DEFAULT``
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps note that the new name doesn't include KERNING_; ditto for LOAD_.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

- ``KERNING_UNFITTED``
- ``KERNING_UNSCALED``

The following constants are now part of `.ft2font.LoadFlags` (without the ``LOAD_``
prefix):

- ``LOAD_DEFAULT``
- ``LOAD_NO_SCALE``
- ``LOAD_NO_HINTING``
- ``LOAD_RENDER``
- ``LOAD_NO_BITMAP``
- ``LOAD_VERTICAL_LAYOUT``
- ``LOAD_FORCE_AUTOHINT``
- ``LOAD_CROP_BITMAP``
- ``LOAD_PEDANTIC``
- ``LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH``
- ``LOAD_NO_RECURSE``
- ``LOAD_IGNORE_TRANSFORM``
- ``LOAD_MONOCHROME``
- ``LOAD_LINEAR_DESIGN``
- ``LOAD_NO_AUTOHINT``
- ``LOAD_TARGET_NORMAL``
- ``LOAD_TARGET_LIGHT``
- ``LOAD_TARGET_MONO``
- ``LOAD_TARGET_LCD``
- ``LOAD_TARGET_LCD_V``

The following constants are now part of `.ft2font.FaceFlags`:

- ``EXTERNAL_STREAM``
- ``FAST_GLYPHS``
- ``FIXED_SIZES``
- ``FIXED_WIDTH``
- ``GLYPH_NAMES``
- ``HORIZONTAL``
- ``KERNING``
- ``MULTIPLE_MASTERS``
- ``SCALABLE``
- ``SFNT``
- ``VERTICAL``

The following constants are now part of `.ft2font.StyleFlags`:

- ``ITALIC``
- ``BOLD``
11 changes: 5 additions & 6 deletions galleries/examples/misc/font_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
import os

import matplotlib
from matplotlib.ft2font import (KERNING_DEFAULT, KERNING_UNFITTED,
KERNING_UNSCALED, FT2Font)
from matplotlib.ft2font import FT2Font, Kerning

font = FT2Font(
os.path.join(matplotlib.get_data_path(), 'fonts/ttf/DejaVuSans.ttf'))
Expand All @@ -31,7 +30,7 @@
glyph = font.load_char(code)
print(glyph.bbox)
print(glyphd['A'], glyphd['V'], coded['A'], coded['V'])
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], KERNING_DEFAULT))
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], KERNING_UNFITTED))
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], KERNING_UNSCALED))
print('AT', font.get_kerning(glyphd['A'], glyphd['T'], KERNING_UNSCALED))
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], Kerning.DEFAULT))
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], Kerning.UNFITTED))
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], Kerning.UNSCALED))
print('AT', font.get_kerning(glyphd['A'], glyphd['T'], Kerning.UNSCALED))
22 changes: 7 additions & 15 deletions galleries/examples/misc/ftface_props.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,10 @@
# vertical thickness of the underline
print('Underline thickness:', font.underline_thickness)

for style in ('Italic',
'Bold',
'Scalable',
'Fixed sizes',
'Fixed width',
'SFNT',
'Horizontal',
'Vertical',
'Kerning',
'Fast glyphs',
'Multiple masters',
'Glyph names',
'External stream'):
bitpos = getattr(ft, style.replace(' ', '_').upper()) - 1
print(f"{style+':':17}", bool(font.style_flags & (1 << bitpos)))
for flag in ft.StyleFlags:
name = flag.name.replace('_', ' ').title() + ':'
print(f"{name:17}", flag in font.style_flags)

for flag in ft.FaceFlags:
name = flag.name.replace('_', ' ').title() + ':'
print(f"{name:17}", flag in font.face_flags)
18 changes: 9 additions & 9 deletions lib/matplotlib/_mathtext.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from ._mathtext_data import (
latex_to_bakoma, stix_glyph_fixes, stix_virtual_fonts, tex2uni)
from .font_manager import FontProperties, findfont, get_font
from .ft2font import FT2Font, FT2Image, KERNING_DEFAULT
from .ft2font import FT2Font, FT2Image, Kerning, LoadFlags

from packaging.version import parse as parse_version
from pyparsing import __version__ as pyparsing_version
Expand Down Expand Up @@ -227,14 +227,14 @@ class Fonts(abc.ABC):
to do the actual drawing.
"""

def __init__(self, default_font_prop: FontProperties, load_glyph_flags: int):
def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlags):
"""
Parameters
----------
default_font_prop : `~.font_manager.FontProperties`
The default non-math font, or the base font for Unicode (generic)
font rendering.
load_glyph_flags : int
load_glyph_flags : `.ft2font.LoadFlags`
Flags passed to the glyph loader (e.g. ``FT_Load_Glyph`` and
``FT_Load_Char`` for FreeType-based fonts).
"""
Expand Down Expand Up @@ -332,7 +332,7 @@ class TruetypeFonts(Fonts, metaclass=abc.ABCMeta):
(through FT2Font).
"""

def __init__(self, default_font_prop: FontProperties, load_glyph_flags: int):
def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlags):
super().__init__(default_font_prop, load_glyph_flags)
# Per-instance cache.
self._get_info = functools.cache(self._get_info) # type: ignore[method-assign]
Expand Down Expand Up @@ -426,7 +426,7 @@ def get_kern(self, font1: str, fontclass1: str, sym1: str, fontsize1: float,
info1 = self._get_info(font1, fontclass1, sym1, fontsize1, dpi)
info2 = self._get_info(font2, fontclass2, sym2, fontsize2, dpi)
font = info1.font
return font.get_kerning(info1.num, info2.num, KERNING_DEFAULT) / 64
return font.get_kerning(info1.num, info2.num, Kerning.DEFAULT) / 64
return super().get_kern(font1, fontclass1, sym1, fontsize1,
font2, fontclass2, sym2, fontsize2, dpi)

Expand All @@ -448,7 +448,7 @@ class BakomaFonts(TruetypeFonts):
'ex': 'cmex10',
}

def __init__(self, default_font_prop: FontProperties, load_glyph_flags: int):
def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlags):
self._stix_fallback = StixFonts(default_font_prop, load_glyph_flags)

super().__init__(default_font_prop, load_glyph_flags)
Expand Down Expand Up @@ -557,7 +557,7 @@ class UnicodeFonts(TruetypeFonts):
0x2212: 0x00A1, # Minus sign.
}

def __init__(self, default_font_prop: FontProperties, load_glyph_flags: int):
def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlags):
# This must come first so the backend's owner is set correctly
fallback_rc = mpl.rcParams['mathtext.fallback']
font_cls: type[TruetypeFonts] | None = {
Expand Down Expand Up @@ -672,7 +672,7 @@ def get_sized_alternatives_for_symbol(self, fontname: str,
class DejaVuFonts(UnicodeFonts, metaclass=abc.ABCMeta):
_fontmap: dict[str | int, str] = {}

def __init__(self, default_font_prop: FontProperties, load_glyph_flags: int):
def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlags):
# This must come first so the backend's owner is set correctly
if isinstance(self, DejaVuSerifFonts):
self._fallback_font = StixFonts(default_font_prop, load_glyph_flags)
Expand Down Expand Up @@ -776,7 +776,7 @@ class StixFonts(UnicodeFonts):
_fallback_font = None
_sans = False

def __init__(self, default_font_prop: FontProperties, load_glyph_flags: int):
def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlags):
TruetypeFonts.__init__(self, default_font_prop, load_glyph_flags)
for key, name in self._fontmap.items():
fullpath = findfont(name)
Expand Down
8 changes: 4 additions & 4 deletions lib/matplotlib/_text_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import dataclasses

from . import _api
from .ft2font import KERNING_DEFAULT, LOAD_NO_HINTING, FT2Font
from .ft2font import FT2Font, Kerning, LoadFlags


@dataclasses.dataclass(frozen=True)
Expand Down Expand Up @@ -43,7 +43,7 @@ def warn_on_missing_glyph(codepoint, fontnames):
f"Matplotlib currently does not support {block} natively.")


def layout(string, font, *, kern_mode=KERNING_DEFAULT):
def layout(string, font, *, kern_mode=Kerning.DEFAULT):
"""
Render *string* with *font*.

Expand All @@ -56,7 +56,7 @@ def layout(string, font, *, kern_mode=KERNING_DEFAULT):
The string to be rendered.
font : FT2Font
The font.
kern_mode : int
kern_mode : Kerning
A FreeType kerning mode.

Yields
Expand All @@ -76,7 +76,7 @@ def layout(string, font, *, kern_mode=KERNING_DEFAULT):
if prev_glyph_idx is not None else 0.
)
x += kern
glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING)
glyph = font.load_glyph(glyph_idx, flags=LoadFlags.NO_HINTING)
yield LayoutItem(font, char, glyph_idx, x, kern)
x += glyph.linearHoriAdvance / 65536
prev_glyph_idx = glyph_idx
2 changes: 1 addition & 1 deletion lib/matplotlib/backends/_backend_pdf_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def get_text_width_height_descent(self, s, prop, ismath):
return w, h, d
else:
font = self._get_font_ttf(prop)
font.set_text(s, 0.0, flags=ft2font.LOAD_NO_HINTING)
font.set_text(s, 0.0, flags=ft2font.LoadFlags.NO_HINTING)
w, h = font.get_width_height()
d = font.get_descent()
scale = 1 / 64
Expand Down
23 changes: 11 additions & 12 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@
from matplotlib.backend_bases import (
_Backend, FigureCanvasBase, FigureManagerBase, RendererBase)
from matplotlib.font_manager import fontManager as _fontManager, get_font
from matplotlib.ft2font import (LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING,
LOAD_DEFAULT, LOAD_NO_AUTOHINT)
from matplotlib.ft2font import LoadFlags
from matplotlib.mathtext import MathTextParser
from matplotlib.path import Path
from matplotlib.transforms import Bbox, BboxBase
Expand All @@ -41,16 +40,16 @@

def get_hinting_flag():
mapping = {
'default': LOAD_DEFAULT,
'no_autohint': LOAD_NO_AUTOHINT,
'force_autohint': LOAD_FORCE_AUTOHINT,
'no_hinting': LOAD_NO_HINTING,
True: LOAD_FORCE_AUTOHINT,
False: LOAD_NO_HINTING,
'either': LOAD_DEFAULT,
'native': LOAD_NO_AUTOHINT,
'auto': LOAD_FORCE_AUTOHINT,
'none': LOAD_NO_HINTING,
'default': LoadFlags.DEFAULT,
'no_autohint': LoadFlags.NO_AUTOHINT,
'force_autohint': LoadFlags.FORCE_AUTOHINT,
'no_hinting': LoadFlags.NO_HINTING,
True: LoadFlags.FORCE_AUTOHINT,
False: LoadFlags.NO_HINTING,
'either': LoadFlags.DEFAULT,
'native': LoadFlags.NO_AUTOHINT,
'auto': LoadFlags.FORCE_AUTOHINT,
'none': LoadFlags.NO_HINTING,
}
return mapping[mpl.rcParams['text.hinting']]

Expand Down
15 changes: 7 additions & 8 deletions lib/matplotlib/backends/backend_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@
from matplotlib.figure import Figure
from matplotlib.font_manager import get_font, fontManager as _fontManager
from matplotlib._afm import AFM
from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE,
LOAD_NO_HINTING, KERNING_UNFITTED, FT2Font)
from matplotlib.ft2font import FT2Font, FaceFlags, Kerning, LoadFlags, StyleFlags
from matplotlib.transforms import Affine2D, BboxBase
from matplotlib.path import Path
from matplotlib.dates import UTC
Expand Down Expand Up @@ -617,7 +616,7 @@ def _get_pdf_charprocs(font_path, glyph_ids):
conv = 1000 / font.units_per_EM # Conversion to PS units (1/1000's).
procs = {}
for glyph_id in glyph_ids:
g = font.load_glyph(glyph_id, LOAD_NO_SCALE)
g = font.load_glyph(glyph_id, LoadFlags.NO_SCALE)
# NOTE: We should be using round(), but instead use
# "(x+.5).astype(int)" to keep backcompat with the old ttconv code
# (this is different for negative x's).
Expand Down Expand Up @@ -1185,7 +1184,7 @@ def embedTTFType3(font, characters, descriptor):
def get_char_width(charcode):
s = ord(cp1252.decoding_table[charcode])
width = font.load_char(
s, flags=LOAD_NO_SCALE | LOAD_NO_HINTING).horiAdvance
s, flags=LoadFlags.NO_SCALE | LoadFlags.NO_HINTING).horiAdvance
return cvt(width)
with warnings.catch_warnings():
# Ignore 'Required glyph missing from current font' warning
Expand Down Expand Up @@ -1322,7 +1321,7 @@ def embedTTFType42(font, characters, descriptor):
ccode = c
gind = font.get_char_index(ccode)
glyph = font.load_char(ccode,
flags=LOAD_NO_SCALE | LOAD_NO_HINTING)
flags=LoadFlags.NO_SCALE | LoadFlags.NO_HINTING)
widths.append((ccode, cvt(glyph.horiAdvance)))
if ccode < 65536:
cid_to_gid_map[ccode] = chr(gind)
Expand Down Expand Up @@ -1418,15 +1417,15 @@ def embedTTFType42(font, characters, descriptor):

flags = 0
symbolic = False # ps_name.name in ('Cmsy10', 'Cmmi10', 'Cmex10')
if ff & FIXED_WIDTH:
if FaceFlags.FIXED_WIDTH in ff:
flags |= 1 << 0
if 0: # TODO: serif
flags |= 1 << 1
if symbolic:
flags |= 1 << 2
else:
flags |= 1 << 5
if sf & ITALIC:
if StyleFlags.ITALIC in sf:
flags |= 1 << 6
if 0: # TODO: all caps
flags |= 1 << 16
Expand Down Expand Up @@ -2379,7 +2378,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
multibyte_glyphs = []
prev_was_multibyte = True
prev_font = font
for item in _text_helpers.layout(s, font, kern_mode=KERNING_UNFITTED):
for item in _text_helpers.layout(s, font, kern_mode=Kerning.UNFITTED):
if _font_supports_glyph(fonttype, ord(item.char)):
if prev_was_multibyte or item.ft_object != prev_font:
singlebyte_chunks.append((item.ft_object, item.x, []))
Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/backends/backend_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
_Backend, FigureCanvasBase, FigureManagerBase, RendererBase)
from matplotlib.cbook import is_writable_file_like, file_requires_unicode
from matplotlib.font_manager import get_font
from matplotlib.ft2font import LOAD_NO_SCALE
from matplotlib.ft2font import LoadFlags
from matplotlib._mathtext_data import uni2type1
from matplotlib.path import Path
from matplotlib.texmanager import TexManager
Expand Down Expand Up @@ -148,7 +148,7 @@ def _font_to_ps_type3(font_path, chars):

entries = []
for glyph_id in glyph_ids:
g = font.load_glyph(glyph_id, LOAD_NO_SCALE)
g = font.load_glyph(glyph_id, LoadFlags.NO_SCALE)
v, c = font.get_path()
entries.append(
"/%(name)s{%(bbox)s sc\n" % {
Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ def ttfFontProperty(font):
style = 'italic'
elif sfnt2.find('regular') >= 0:
style = 'normal'
elif font.style_flags & ft2font.ITALIC:
elif ft2font.StyleFlags.ITALIC in font.style_flags:
style = 'italic'
else:
style = 'normal'
Expand Down Expand Up @@ -428,7 +428,7 @@ def get_weight(): # From fontconfig's FcFreeTypeQueryFaceInternal.
for regex, weight in _weight_regexes:
if re.search(regex, style, re.I):
return weight
if font.style_flags & ft2font.BOLD:
if ft2font.StyleFlags.BOLD in font.style_flags:
return 700 # "bold"
return 500 # "medium", not "regular"!

Expand Down
Loading
Loading