🌐 AI搜索 & 代理 主页
Skip to content
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
15 changes: 15 additions & 0 deletions doc/api/next_api_changes/behavior/30335-ES.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
``mathtext.VectorParse`` now includes glyph indices
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For a *path*-outputting `.MathTextParser`, in the return value of
`~.MathTextParser.parse`, (a `.VectorParse`), the *glyphs* field is now a list
containing tuples of:

- font: `.FT2Font`
- fontsize: `float`
- character code: `int`
- glyph index: `int`
- x: `float`
- y: `float`

Specifically, the glyph index was added after the character code.
15 changes: 9 additions & 6 deletions lib/matplotlib/_mathtext.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

if T.TYPE_CHECKING:
from collections.abc import Iterable
from .ft2font import CharacterCodeType, Glyph
from .ft2font import CharacterCodeType, Glyph, GlyphIndexType


ParserElement.enable_packrat()
Expand Down Expand Up @@ -87,7 +87,7 @@ class VectorParse(NamedTuple):
width: float
height: float
depth: float
glyphs: list[tuple[FT2Font, float, CharacterCodeType, float, float]]
glyphs: list[tuple[FT2Font, float, CharacterCodeType, GlyphIndexType, float, float]]
rects: list[tuple[float, float, float, float]]

VectorParse.__module__ = "matplotlib.mathtext"
Expand Down Expand Up @@ -132,7 +132,8 @@ def __init__(self, box: Box):
def to_vector(self) -> VectorParse:
w, h, d = map(
np.ceil, [self.box.width, self.box.height, self.box.depth])
gs = [(info.font, info.fontsize, info.num, ox, h - oy + info.offset)
gs = [(info.font, info.fontsize, info.num, info.glyph_index,
ox, h - oy + info.offset)
for ox, oy, info in self.glyphs]
rs = [(x1, h - y2, x2 - x1, y2 - y1)
for x1, y1, x2, y2 in self.rects]
Expand Down Expand Up @@ -215,6 +216,7 @@ class FontInfo(NamedTuple):
postscript_name: str
metrics: FontMetrics
num: CharacterCodeType
glyph_index: GlyphIndexType
glyph: Glyph
offset: float

Expand Down Expand Up @@ -375,7 +377,8 @@ def _get_info(self, fontname: str, font_class: str, sym: str, fontsize: float,
dpi: float) -> FontInfo:
font, num, slanted = self._get_glyph(fontname, font_class, sym)
font.set_size(fontsize, dpi)
glyph = font.load_char(num, flags=self.load_glyph_flags)
glyph_index = font.get_char_index(num)
glyph = font.load_glyph(glyph_index, flags=self.load_glyph_flags)

xmin, ymin, xmax, ymax = (val / 64 for val in glyph.bbox)
offset = self._get_offset(font, glyph, fontsize, dpi)
Expand All @@ -398,6 +401,7 @@ def _get_info(self, fontname: str, font_class: str, sym: str, fontsize: float,
postscript_name=font.postscript_name,
metrics=metrics,
num=num,
glyph_index=glyph_index,
glyph=glyph,
offset=offset
)
Expand Down Expand Up @@ -427,8 +431,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(font.get_char_index(info1.num),
font.get_char_index(info2.num),
return font.get_kerning(info1.glyph_index, info2.glyph_index,
Kerning.DEFAULT) / 64
return super().get_kern(font1, fontclass1, sym1, fontsize1,
font2, fontclass2, sym2, fontsize2, dpi)
Expand Down
16 changes: 8 additions & 8 deletions lib/matplotlib/_text_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
class LayoutItem:
ft_object: FT2Font
char: str
glyph_idx: GlyphIndexType
glyph_index: GlyphIndexType
x: float
prev_kern: float

Expand Down Expand Up @@ -47,19 +47,19 @@ def layout(string, font, *, kern_mode=Kerning.DEFAULT):
LayoutItem
"""
x = 0
prev_glyph_idx = None
prev_glyph_index = None
char_to_font = font._get_fontmap(string)
base_font = font
for char in string:
# This has done the fallback logic
font = char_to_font.get(char, base_font)
glyph_idx = font.get_char_index(ord(char))
glyph_index = font.get_char_index(ord(char))
kern = (
base_font.get_kerning(prev_glyph_idx, glyph_idx, kern_mode) / 64
if prev_glyph_idx is not None else 0.
base_font.get_kerning(prev_glyph_index, glyph_index, kern_mode) / 64
if prev_glyph_index is not None else 0.
)
x += kern
glyph = font.load_glyph(glyph_idx, flags=LoadFlags.NO_HINTING)
yield LayoutItem(font, char, glyph_idx, x, kern)
glyph = font.load_glyph(glyph_index, flags=LoadFlags.NO_HINTING)
yield LayoutItem(font, char, glyph_index, x, kern)
x += glyph.linearHoriAdvance / 65536
prev_glyph_idx = glyph_idx
prev_glyph_index = glyph_index
45 changes: 27 additions & 18 deletions lib/matplotlib/backends/_backend_pdf_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
Common functionality between the PDF and PS backends.
"""

from __future__ import annotations

from io import BytesIO
import functools
import logging
import typing

from fontTools import subset

Expand All @@ -14,33 +17,38 @@
from ..backend_bases import RendererBase


if typing.TYPE_CHECKING:
from .ft2font import FT2Font, GlyphIndexType
from fontTools.ttLib import TTFont


@functools.lru_cache(50)
def _cached_get_afm_from_fname(fname):
with open(fname, "rb") as fh:
return AFM(fh)


def get_glyphs_subset(fontfile, characters):
def get_glyphs_subset(fontfile: str, glyphs: set[GlyphIndexType]) -> TTFont:
"""
Subset a TTF font
Subset a TTF font.

Reads the named fontfile and restricts the font to the characters.
Reads the named fontfile and restricts the font to the glyphs.

Parameters
----------
fontfile : str
Path to the font file
characters : str
Continuous set of characters to include in subset
glyphs : set[GlyphIndexType]
Set of glyph indices to include in subset.

Returns
-------
fontTools.ttLib.ttFont.TTFont
An open font object representing the subset, which needs to
be closed by the caller.
"""

options = subset.Options(glyph_names=True, recommended_glyphs=True)
options = subset.Options(glyph_names=True, recommended_glyphs=True,
retain_gids=True)

# Prevent subsetting extra tables.
options.drop_tables += [
Expand Down Expand Up @@ -71,7 +79,7 @@ def get_glyphs_subset(fontfile, characters):

font = subset.load_font(fontfile, options)
subsetter = subset.Subsetter(options=options)
subsetter.populate(text=characters)
subsetter.populate(gids=glyphs)
subsetter.subset(font)
return font

Expand All @@ -97,24 +105,25 @@ def font_as_file(font):

class CharacterTracker:
"""
Helper for font subsetting by the pdf and ps backends.
Helper for font subsetting by the PDF and PS backends.

Maintains a mapping of font paths to the set of character codepoints that
are being used from that font.
Maintains a mapping of font paths to the set of glyphs that are being used from that
font.
"""

def __init__(self):
self.used = {}
def __init__(self) -> None:
self.used: dict[str, set[GlyphIndexType]] = {}

def track(self, font, s):
def track(self, font: FT2Font, s: str) -> None:
"""Record that string *s* is being typeset using font *font*."""
char_to_font = font._get_fontmap(s)
for _c, _f in char_to_font.items():
self.used.setdefault(_f.fname, set()).add(ord(_c))
glyph_index = _f.get_char_index(ord(_c))
self.used.setdefault(_f.fname, set()).add(glyph_index)

def track_glyph(self, font, glyph):
"""Record that codepoint *glyph* is being typeset using font *font*."""
self.used.setdefault(font.fname, set()).add(glyph)
def track_glyph(self, font: FT2Font, glyph_index: GlyphIndexType) -> None:
"""Record that glyph index *glyph_index* is being typeset using font *font*."""
self.used.setdefault(font.fname, set()).add(glyph_index)


class RendererPDFPSBase(RendererBase):
Expand Down
10 changes: 5 additions & 5 deletions lib/matplotlib/backends/backend_cairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import functools
import gzip
import itertools
import math

import numpy as np
Expand Down Expand Up @@ -248,13 +249,12 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle):
if angle:
ctx.rotate(np.deg2rad(-angle))

for font, fontsize, idx, ox, oy in glyphs:
for (font, fontsize), font_glyphs in itertools.groupby(
glyphs, key=lambda info: (info[0], info[1])):
ctx.new_path()
ctx.move_to(ox, -oy)
ctx.select_font_face(
*_cairo_font_args_from_font_prop(ttfFontProperty(font)))
ctx.select_font_face(*_cairo_font_args_from_font_prop(ttfFontProperty(font)))
ctx.set_font_size(self.points_to_pixels(fontsize))
ctx.show_text(chr(idx))
ctx.show_glyphs([(idx, ox, -oy) for _, _, idx, ox, oy in font_glyphs])

for ox, oy, w, h in rects:
ctx.new_path()
Expand Down
Loading
Loading