Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Improve usability of dviread.Text by third parties. #22609

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 1 commit into from
Apr 19, 2022
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
63 changes: 62 additions & 1 deletion lib/matplotlib/dviread.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,71 @@

# The marks on a page consist of text and boxes. A page also has dimensions.
Page = namedtuple('Page', 'text boxes height width descent')
Text = namedtuple('Text', 'x y font glyph width')
Box = namedtuple('Box', 'x y height width')


# Also a namedtuple, for backcompat.
class Text(namedtuple('Text', 'x y font glyph width')):
"""
A glyph in the dvi file.

The *x* and *y* attributes directly position the glyph. The *font*,
*glyph*, and *width* attributes are kept public for back-compatibility,
but users wanting to draw the glyph themselves are encouraged to instead
load the font specified by `font_path` at `font_size`, warp it with the
effects specified by `font_effects`, and load the glyph specified by
`glyph_name_or_index`.
"""

def _get_pdftexmap_entry(self):
return PsfontsMap(find_tex_file("pdftex.map"))[self.font.texname]

@property
def font_path(self):
"""The `~pathlib.Path` to the font for this glyph."""
psfont = self._get_pdftexmap_entry()
if psfont.filename is None:
raise ValueError("No usable font file found for {} ({}); "
"the font may lack a Type-1 version"
.format(psfont.psname.decode("ascii"),
psfont.texname.decode("ascii")))
return Path(psfont.filename)

@property
def font_size(self):
"""The font size."""
return self.font.size

@property
def font_effects(self):
"""
The "font effects" dict for this glyph.

This dict contains the values for this glyph of SlantFont and
ExtendFont (if any), read off :file:`pdftex.map`.
"""
return self._get_pdftexmap_entry().effects

@property
def glyph_name_or_index(self):
"""
Either the glyph name or the native charmap glyph index.

If :file:`pdftex.map` specifies an encoding for this glyph's font, that
is a mapping of glyph indices to Adobe glyph names; use it to convert
dvi indices to glyph names. Callers can then convert glyph names to
glyph indices (with FT_Get_Name_Index/get_name_index), and load the
glyph using FT_Load_Glyph/load_glyph.

If :file:`pdftex.map` specifies no encoding, the indices directly map
to the font's "native" charmap; glyphs should directly loaded using
FT_Load_Char/load_char after selecting the native charmap.
"""
entry = self._get_pdftexmap_entry()
return (_parse_enc(entry.encoding)[self.glyph]
if entry.encoding is not None else self.glyph)


# Opcode argument parsing
#
# Each of the following functions takes a Dvi object and delta,
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/tests/test_usetex.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def test_missing_psfont(fmt, monkeypatch):
monkeypatch.setattr(
dviread.PsfontsMap, '__getitem__',
lambda self, k: dviread.PsFont(
texname='texfont', psname='Some Font',
texname=b'texfont', psname=b'Some Font',
effects=None, encoding=None, filename=None))
mpl.rcParams['text.usetex'] = True
fig, ax = plt.subplots()
Expand Down
84 changes: 30 additions & 54 deletions lib/matplotlib/textpath.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from collections import OrderedDict
import functools
import logging
import urllib.parse

Expand Down Expand Up @@ -243,25 +242,29 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,

# Gather font information and do some setup for combining
# characters into strings.
for x1, y1, dvifont, glyph, width in page.text:
font, enc = self._get_ps_font_and_encoding(dvifont.texname)
char_id = self._get_char_id(font, glyph)

for text in page.text:
font = get_font(text.font_path)
char_id = self._get_char_id(font, text.glyph)
if char_id not in glyph_map:
font.clear()
font.set_size(self.FONT_SCALE, self.DPI)
# See comments in _get_ps_font_and_encoding.
if enc is not None:
index = font.get_name_index(enc[glyph])
glyph_name_or_index = text.glyph_name_or_index
if isinstance(glyph_name_or_index, str):
index = font.get_name_index(glyph_name_or_index)
font.load_glyph(index, flags=LOAD_TARGET_LIGHT)
else:
font.load_char(glyph, flags=LOAD_TARGET_LIGHT)
elif isinstance(glyph_name_or_index, int):
self._select_native_charmap(font)
font.load_char(
glyph_name_or_index, flags=LOAD_TARGET_LIGHT)
else: # Should not occur.
raise TypeError(f"Glyph spec of unexpected type: "
f"{glyph_name_or_index!r}")
glyph_map_new[char_id] = font.get_path()

glyph_ids.append(char_id)
xpositions.append(x1)
ypositions.append(y1)
sizes.append(dvifont.size / self.FONT_SCALE)
xpositions.append(text.x)
ypositions.append(text.y)
sizes.append(text.font_size / self.FONT_SCALE)

myrects = []

Expand All @@ -277,48 +280,21 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,
glyph_map_new, myrects)

@staticmethod
@functools.lru_cache(50)
def _get_ps_font_and_encoding(texname):
tex_font_map = dviread.PsfontsMap(dviread._find_tex_file('pdftex.map'))
psfont = tex_font_map[texname]
if psfont.filename is None:
raise ValueError(
f"No usable font file found for {psfont.psname} ({texname}). "
f"The font may lack a Type-1 version.")

font = get_font(psfont.filename)

if psfont.encoding:
# If psfonts.map specifies an encoding, use it: it gives us a
# mapping of glyph indices to Adobe glyph names; use it to convert
# dvi indices to glyph names and use the FreeType-synthesized
# Unicode charmap to convert glyph names to glyph indices (with
# FT_Get_Name_Index/get_name_index), and load the glyph using
# FT_Load_Glyph/load_glyph. (That charmap has a coverage at least
# as good as, and possibly better than, the native charmaps.)
enc = dviread._parse_enc(psfont.encoding)
else:
# If psfonts.map specifies no encoding, the indices directly
# map to the font's "native" charmap; so don't use the
# FreeType-synthesized charmap but the native ones (we can't
# directly identify it but it's typically an Adobe charmap), and
# directly load the dvi glyph indices using FT_Load_Char/load_char.
for charmap_code in [
1094992451, # ADOBE_CUSTOM.
1094995778, # ADOBE_STANDARD.
]:
try:
font.select_charmap(charmap_code)
except (ValueError, RuntimeError):
pass
else:
break
def _select_native_charmap(font):
# Select the native charmap. (we can't directly identify it but it's
# typically an Adobe charmap).
for charmap_code in [
1094992451, # ADOBE_CUSTOM.
1094995778, # ADOBE_STANDARD.
]:
try:
font.select_charmap(charmap_code)
except (ValueError, RuntimeError):
pass
else:
_log.warning("No supported encoding in font (%s).",
psfont.filename)
enc = None

return font, enc
break
else:
_log.warning("No supported encoding in font (%s).", font.fname)


text_to_path = TextToPath()
Expand Down