Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 99185ed

Browse filesBrowse files
committed
Rework mapping of dvi glyph indices to freetype indices.
In 89a7e19, an API for converting "dvi glyph indices" (as stored in a dvi file) to FreeType-compatible keys (either "indices into the native charmap" or "glyph names") was introduced. It was intended that end users (i.e., backends) would check the type of `text.glyph_name_or_index` ((A) int or (B) str) and load the glyph accordingly ((A) `FT_Set_Charmap(native_cmap); FT_Load_Char(index);` or (B) `FT_Load_Glyph(FT_Get_Name_Index(name));`); however, with the future introduction of {xe,lua}tex support, this kind of type checking becomes inconvenient, because {xe,lua}tex's "dvi glyph indices", which are directly equal to FreeType glyph indices (i.e. they would be loaded with `FT_Load_Glyph(index);`), would normally also be converted to ints. This PR introduces a new API (`Text.index`) that performs this mapping (via the new `DviFont._index_dvi_to_freetype`), always mapping to FreeType glyph indices (i.e. one can always just call `FT_Load_Glyph` on the result). To do so, in case (A) it loads itself the native charmap (something the end user needed to do by themselves previously) and performs the cmap-to-index conversion (`FT_Get_Char_Index`) previously implicit in `FT_Load_Char`; in case (B) it performs itself the name-to-index conversion (`FT_Get_Name_Index`). When {xe,lua}tex support is introduced in the future, `_index_dvi_to_freetype` will just return the index as is. The old APIs are not deprecated yet, as other changes will occur for {xe,lua}tex support (e.g. font_effects will go away and be replaced by `.font.effects`, as {xe,lua}tex don't store that info in the pdftexmap entry), and grouping all API changes together seems nicer (to only require a single adaptation step by the API consumer).
1 parent b7e7663 commit 99185ed
Copy full SHA for 99185ed

File tree

3 files changed

+45
-23
lines changed
Filter options

3 files changed

+45
-23
lines changed

‎lib/matplotlib/dviread.py

Copy file name to clipboardExpand all lines: lib/matplotlib/dviread.py
+42-12Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
import numpy as np
3333

34-
from matplotlib import _api, cbook
34+
from matplotlib import _api, cbook, font_manager
3535

3636
_log = logging.getLogger(__name__)
3737

@@ -71,8 +71,8 @@ class Text(namedtuple('Text', 'x y font glyph width')):
7171
*glyph*, and *width* attributes are kept public for back-compatibility,
7272
but users wanting to draw the glyph themselves are encouraged to instead
7373
load the font specified by `font_path` at `font_size`, warp it with the
74-
effects specified by `font_effects`, and load the glyph specified by
75-
`glyph_name_or_index`.
74+
effects specified by `font_effects`, and load the glyph at the FreeType
75+
glyph `index`.
7676
"""
7777

7878
def _get_pdftexmap_entry(self):
@@ -105,6 +105,14 @@ def font_effects(self):
105105
return self._get_pdftexmap_entry().effects
106106

107107
@property
108+
def index(self):
109+
"""
110+
The FreeType index of this glyph (that can be passed to FT_Load_Glyph).
111+
"""
112+
# See DviFont._index_dvi_to_freetype for details on the index mapping.
113+
return self.font._index_dvi_to_freetype(self.glyph)
114+
115+
@property # To be deprecated together with font_size, font_effects.
108116
def glyph_name_or_index(self):
109117
"""
110118
Either the glyph name or the native charmap glyph index.
@@ -579,7 +587,7 @@ class DviFont:
579587
Size of the font in Adobe points, converted from the slightly
580588
smaller TeX points.
581589
"""
582-
__slots__ = ('texname', 'size', '_scale', '_vf', '_tfm')
590+
__slots__ = ('texname', 'size', '_scale', '_vf', '_tfm', '_encoding')
583591

584592
def __init__(self, scale, tfm, texname, vf):
585593
_api.check_isinstance(bytes, texname=texname)
@@ -588,6 +596,7 @@ def __init__(self, scale, tfm, texname, vf):
588596
self.texname = texname
589597
self._vf = vf
590598
self.size = scale * (72.0 / (72.27 * 2**16))
599+
self._encoding = None
591600

592601
widths = _api.deprecated("3.11")(property(lambda self: [
593602
(1000 * self._tfm.width.get(char, 0)) >> 20
@@ -630,6 +639,33 @@ def _height_depth_of(self, char):
630639
hd[-1] = 0
631640
return hd
632641

642+
def _index_dvi_to_freetype(self, idx):
643+
"""Convert dvi glyph indices to FreeType ones."""
644+
# Glyphs indices stored in the dvi file map to FreeType glyph indices
645+
# (i.e., which can be passed to FT_Load_Glyph) in various ways:
646+
# - if pdftex.map specifies an ".enc" file for the font, that file maps
647+
# dvi indices to Adobe glyph names, which can then be converted to
648+
# FreeType glyph indices with FT_Get_Name_Index.
649+
# - if no ".enc" file is specified, then the font must be a Type 1
650+
# font, and dvi indices directly index into the font's CharStrings
651+
# vector.
652+
# - (xetex & luatex, currently unsupported, can also declare "native
653+
# fonts", for which dvi indices are equal to FreeType indices.)
654+
if self._encoding is None:
655+
psfont = PsfontsMap(find_tex_file("pdftex.map"))[self.texname]
656+
if psfont.filename is None:
657+
raise ValueError("No usable font file found for {} ({}); "
658+
"the font may lack a Type-1 version"
659+
.format(psfont.psname.decode("ascii"),
660+
psfont.texname.decode("ascii")))
661+
face = font_manager.get_font(psfont.filename)
662+
if psfont.encoding:
663+
self._encoding = [face.get_name_index(name)
664+
for name in _parse_enc(psfont.encoding)]
665+
else:
666+
self._encoding = face._get_type1_encoding_vector()
667+
return self._encoding[idx]
668+
633669

634670
class Vf(Dvi):
635671
r"""
@@ -1023,8 +1059,7 @@ def _parse_enc(path):
10231059
Returns
10241060
-------
10251061
list
1026-
The nth entry of the list is the PostScript glyph name of the nth
1027-
glyph.
1062+
The nth list item is the PostScript glyph name of the nth glyph.
10281063
"""
10291064
no_comments = re.sub("%.*", "", Path(path).read_text(encoding="ascii"))
10301065
array = re.search(r"(?s)\[(.*)\]", no_comments).group(1)
@@ -1156,12 +1191,7 @@ def _print_fields(*args):
11561191
face = FT2Font(fontpath)
11571192
_print_fields("x", "y", "glyph", "chr", "w")
11581193
for text in group:
1159-
if psfont.encoding:
1160-
glyph_name = _parse_enc(psfont.encoding)[text.glyph]
1161-
else:
1162-
encoding_vector = face._get_type1_encoding_vector()
1163-
glyph_name = face.get_glyph_name(encoding_vector[text.glyph])
1164-
glyph_str = fontTools.agl.toUnicode(glyph_name)
1194+
glyph_str = fontTools.agl.toUnicode(face.get_glyph_name(text.index))
11651195
_print_fields(text.x, text.y, text.glyph, glyph_str, text.width)
11661196
if page.boxes:
11671197
print("--- BOXES ---")

‎lib/matplotlib/dviread.pyi

Copy file name to clipboardExpand all lines: lib/matplotlib/dviread.pyi
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class Text(NamedTuple):
4141
@property
4242
def font_effects(self) -> dict[str, float]: ...
4343
@property
44+
def index(self) -> int: ...
45+
@property
4446
def glyph_name_or_index(self) -> int | str: ...
4547

4648
class Dvi:

‎lib/matplotlib/textpath.py

Copy file name to clipboardExpand all lines: lib/matplotlib/textpath.py
+1-11Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -239,17 +239,7 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,
239239
if char_id not in glyph_map:
240240
font.clear()
241241
font.set_size(self.FONT_SCALE, self.DPI)
242-
glyph_name_or_index = text.glyph_name_or_index
243-
if isinstance(glyph_name_or_index, str):
244-
index = font.get_name_index(glyph_name_or_index)
245-
elif isinstance(glyph_name_or_index, int):
246-
if font not in t1_encodings:
247-
t1_encodings[font] = font._get_type1_encoding_vector()
248-
index = t1_encodings[font][glyph_name_or_index]
249-
else: # Should not occur.
250-
raise TypeError(f"Glyph spec of unexpected type: "
251-
f"{glyph_name_or_index!r}")
252-
font.load_glyph(index, flags=LoadFlags.TARGET_LIGHT)
242+
font.load_glyph(text.index, flags=LoadFlags.TARGET_LIGHT)
253243
glyph_map_new[char_id] = font.get_path()
254244

255245
glyph_ids.append(char_id)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.