-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Rework mapping of dvi glyph indices to freetype indices. #29829
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,7 @@ | |
|
||
import numpy as np | ||
|
||
from matplotlib import _api, cbook | ||
from matplotlib import _api, cbook, font_manager | ||
|
||
_log = logging.getLogger(__name__) | ||
|
||
|
@@ -71,8 +71,8 @@ | |
*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`. | ||
effects specified by `font_effects`, and load the glyph at the FreeType | ||
glyph `index`. | ||
""" | ||
|
||
def _get_pdftexmap_entry(self): | ||
|
@@ -105,6 +105,14 @@ | |
return self._get_pdftexmap_entry().effects | ||
|
||
@property | ||
def index(self): | ||
""" | ||
The FreeType index of this glyph (that can be passed to FT_Load_Glyph). | ||
""" | ||
# See DviFont._index_dvi_to_freetype for details on the index mapping. | ||
return self.font._index_dvi_to_freetype(self.glyph) | ||
|
||
@property # To be deprecated together with font_size, font_effects. | ||
def glyph_name_or_index(self): | ||
""" | ||
Either the glyph name or the native charmap glyph index. | ||
|
@@ -579,7 +587,7 @@ | |
Size of the font in Adobe points, converted from the slightly | ||
smaller TeX points. | ||
""" | ||
__slots__ = ('texname', 'size', '_scale', '_vf', '_tfm') | ||
__slots__ = ('texname', 'size', '_scale', '_vf', '_tfm', '_encoding') | ||
|
||
def __init__(self, scale, tfm, texname, vf): | ||
_api.check_isinstance(bytes, texname=texname) | ||
|
@@ -588,6 +596,7 @@ | |
self.texname = texname | ||
self._vf = vf | ||
self.size = scale * (72.0 / (72.27 * 2**16)) | ||
self._encoding = None | ||
|
||
widths = _api.deprecated("3.11")(property(lambda self: [ | ||
(1000 * self._tfm.width.get(char, 0)) >> 20 | ||
|
@@ -630,6 +639,33 @@ | |
hd[-1] = 0 | ||
return hd | ||
|
||
def _index_dvi_to_freetype(self, idx): | ||
"""Convert dvi glyph indices to FreeType ones.""" | ||
# Glyphs indices stored in the dvi file map to FreeType glyph indices | ||
# (i.e., which can be passed to FT_Load_Glyph) in various ways: | ||
# - if pdftex.map specifies an ".enc" file for the font, that file maps | ||
# dvi indices to Adobe glyph names, which can then be converted to | ||
# FreeType glyph indices with FT_Get_Name_Index. | ||
# - if no ".enc" file is specified, then the font must be a Type 1 | ||
# font, and dvi indices directly index into the font's CharStrings | ||
# vector. | ||
# - (xetex & luatex, currently unsupported, can also declare "native | ||
# fonts", for which dvi indices are equal to FreeType indices.) | ||
if self._encoding is None: | ||
psfont = PsfontsMap(find_tex_file("pdftex.map"))[self.texname] | ||
QuLogic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if psfont.filename is None: | ||
raise ValueError("No usable font file found for {} ({}); " | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please add a test for coverage? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This gets further refactored in the followup PR #29939 into DviFont.path, which does have test coverage (because it is also used elsewhere -- see https://app.codecov.io/gh/matplotlib/matplotlib/pull/29939#4c703021fcf83cd901550e2c525d78ae-R733). I can move code around so that the intermediate stage also has full coverage if you prefer, but this split seems more logical to me. |
||
"the font may lack a Type-1 version" | ||
.format(psfont.psname.decode("ascii"), | ||
psfont.texname.decode("ascii"))) | ||
face = font_manager.get_font(psfont.filename) | ||
if psfont.encoding: | ||
self._encoding = [face.get_name_index(name) | ||
for name in _parse_enc(psfont.encoding)] | ||
else: | ||
self._encoding = face._get_type1_encoding_vector() | ||
return self._encoding[idx] | ||
|
||
|
||
class Vf(Dvi): | ||
r""" | ||
|
@@ -1023,8 +1059,7 @@ | |
Returns | ||
------- | ||
list | ||
The nth entry of the list is the PostScript glyph name of the nth | ||
glyph. | ||
The nth list item is the PostScript glyph name of the nth glyph. | ||
""" | ||
no_comments = re.sub("%.*", "", Path(path).read_text(encoding="ascii")) | ||
array = re.search(r"(?s)\[(.*)\]", no_comments).group(1) | ||
|
@@ -1156,12 +1191,7 @@ | |
face = FT2Font(fontpath) | ||
_print_fields("x", "y", "glyph", "chr", "w") | ||
for text in group: | ||
if psfont.encoding: | ||
glyph_name = _parse_enc(psfont.encoding)[text.glyph] | ||
else: | ||
encoding_vector = face._get_type1_encoding_vector() | ||
glyph_name = face.get_glyph_name(encoding_vector[text.glyph]) | ||
glyph_str = fontTools.agl.toUnicode(glyph_name) | ||
glyph_str = fontTools.agl.toUnicode(face.get_glyph_name(text.index)) | ||
_print_fields(text.x, text.y, text.glyph, glyph_str, text.width) | ||
if page.boxes: | ||
print("--- BOXES ---") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just checking but is this why this step is no longer needed:
index = t1_encodings[font][glyph_name_or_index]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still happens at the bottom:
self._encoding = face._get_t1_encoding_vector()
(likewise caching the value of the encoding vector, but this now occurs on the DviFont object) thenreturn self._encoding[idx]
.