Skip to content

Commit b7e7663

Browse files
authored
Merge pull request #29843 from anntzer/t1ev
Fix loading of Type1 "native" charmap.
2 parents f101e49 + d31d674 commit b7e7663

File tree

3 files changed

+45
-35
lines changed

3 files changed

+45
-35
lines changed

lib/matplotlib/dviread.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -1132,7 +1132,6 @@ def _fontfile(cls, suffix, texname):
11321132
import fontTools.agl
11331133

11341134
from matplotlib.ft2font import FT2Font
1135-
from matplotlib.textpath import TextToPath
11361135

11371136
parser = ArgumentParser()
11381137
parser.add_argument("filename")
@@ -1155,14 +1154,13 @@ def _print_fields(*args):
11551154
print(f"font: {font.texname.decode('latin-1')} "
11561155
f"(scale: {font._scale / 2 ** 20}) at {fontpath}")
11571156
face = FT2Font(fontpath)
1158-
TextToPath._select_native_charmap(face)
11591157
_print_fields("x", "y", "glyph", "chr", "w")
11601158
for text in group:
11611159
if psfont.encoding:
11621160
glyph_name = _parse_enc(psfont.encoding)[text.glyph]
11631161
else:
1164-
glyph_name = face.get_glyph_name(
1165-
face.get_char_index(text.glyph))
1162+
encoding_vector = face._get_type1_encoding_vector()
1163+
glyph_name = face.get_glyph_name(encoding_vector[text.glyph])
11661164
glyph_str = fontTools.agl.toUnicode(glyph_name)
11671165
_print_fields(text.x, text.y, text.glyph, glyph_str, text.width)
11681166
if page.boxes:

lib/matplotlib/textpath.py

+5-21
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,
232232

233233
# Gather font information and do some setup for combining
234234
# characters into strings.
235+
t1_encodings = {}
235236
for text in page.text:
236237
font = get_font(text.font_path)
237238
char_id = self._get_char_id(font, text.glyph)
@@ -241,14 +242,14 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,
241242
glyph_name_or_index = text.glyph_name_or_index
242243
if isinstance(glyph_name_or_index, str):
243244
index = font.get_name_index(glyph_name_or_index)
244-
font.load_glyph(index, flags=LoadFlags.TARGET_LIGHT)
245245
elif isinstance(glyph_name_or_index, int):
246-
self._select_native_charmap(font)
247-
font.load_char(
248-
glyph_name_or_index, flags=LoadFlags.TARGET_LIGHT)
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]
249249
else: # Should not occur.
250250
raise TypeError(f"Glyph spec of unexpected type: "
251251
f"{glyph_name_or_index!r}")
252+
font.load_glyph(index, flags=LoadFlags.TARGET_LIGHT)
252253
glyph_map_new[char_id] = font.get_path()
253254

254255
glyph_ids.append(char_id)
@@ -269,23 +270,6 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,
269270
return (list(zip(glyph_ids, xpositions, ypositions, sizes)),
270271
glyph_map_new, myrects)
271272

272-
@staticmethod
273-
def _select_native_charmap(font):
274-
# Select the native charmap. (we can't directly identify it but it's
275-
# typically an Adobe charmap).
276-
for charmap_code in [
277-
1094992451, # ADOBE_CUSTOM.
278-
1094995778, # ADOBE_STANDARD.
279-
]:
280-
try:
281-
font.select_charmap(charmap_code)
282-
except (ValueError, RuntimeError):
283-
pass
284-
else:
285-
break
286-
else:
287-
_log.warning("No supported encoding in font (%s).", font.fname)
288-
289273

290274
text_to_path = TextToPath()
291275

src/ft2font_wrapper.cpp

+38-10
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,16 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8,
498498
return self;
499499
}
500500

501+
static py::str
502+
PyFT2Font_fname(PyFT2Font *self)
503+
{
504+
if (self->stream.close) { // Called passed a filename to the constructor.
505+
return self->py_file.attr("name");
506+
} else {
507+
return py::cast<py::str>(self->py_file);
508+
}
509+
}
510+
501511
const char *PyFT2Font_clear__doc__ =
502512
"Clear all the glyphs, reset for a new call to `.set_text`.";
503513

@@ -1431,6 +1441,32 @@ PyFT2Font_get_image(PyFT2Font *self)
14311441
return py::array_t<unsigned char>(dims, im.get_buffer());
14321442
}
14331443

1444+
const char *PyFT2Font__get_type1_encoding_vector__doc__ = R"""(
1445+
Return a list mapping CharString indices of a Type 1 font to FreeType glyph indices.
1446+
1447+
Returns
1448+
-------
1449+
list[int]
1450+
)""";
1451+
1452+
static std::array<FT_UInt, 256>
1453+
PyFT2Font__get_type1_encoding_vector(PyFT2Font *self)
1454+
{
1455+
auto face = self->x->get_face();
1456+
auto indices = std::array<FT_UInt, 256>{};
1457+
for (auto i = 0u; i < indices.size(); ++i) {
1458+
auto len = FT_Get_PS_Font_Value(face, PS_DICT_ENCODING_ENTRY, i, nullptr, 0);
1459+
if (len == -1) {
1460+
// Explicitly ignore missing entries (mapped to glyph 0 = .notdef).
1461+
continue;
1462+
}
1463+
auto buf = std::make_unique<char[]>(len);
1464+
FT_Get_PS_Font_Value(face, PS_DICT_ENCODING_ENTRY, i, buf.get(), len);
1465+
indices[i] = FT_Get_Name_Index(face, buf.get());
1466+
}
1467+
return indices;
1468+
}
1469+
14341470
static const char *
14351471
PyFT2Font_postscript_name(PyFT2Font *self)
14361472
{
@@ -1569,16 +1605,6 @@ PyFT2Font_underline_thickness(PyFT2Font *self)
15691605
return self->x->get_face()->underline_thickness;
15701606
}
15711607

1572-
static py::str
1573-
PyFT2Font_fname(PyFT2Font *self)
1574-
{
1575-
if (self->stream.close) { // Called passed a filename to the constructor.
1576-
return self->py_file.attr("name");
1577-
} else {
1578-
return py::cast<py::str>(self->py_file);
1579-
}
1580-
}
1581-
15821608
static py::object
15831609
ft2font__getattr__(std::string name) {
15841610
auto api = py::module_::import("matplotlib._api");
@@ -1761,6 +1787,8 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used())
17611787
PyFT2Font_get_sfnt_table__doc__)
17621788
.def("get_path", &PyFT2Font_get_path, PyFT2Font_get_path__doc__)
17631789
.def("get_image", &PyFT2Font_get_image, PyFT2Font_get_image__doc__)
1790+
.def("_get_type1_encoding_vector", &PyFT2Font__get_type1_encoding_vector,
1791+
PyFT2Font__get_type1_encoding_vector__doc__)
17641792

17651793
.def_property_readonly("postscript_name", &PyFT2Font_postscript_name,
17661794
"PostScript name of the font.")

0 commit comments

Comments
 (0)