From 8a26ff9527047665233aa22d921d9580a0694f6d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 7 Aug 2022 16:55:50 -0400 Subject: [PATCH 01/10] ENH: add c++ code + Python API to get char -> font mapping --- lib/matplotlib/tests/test_ft2font.py | 27 ++++++++++ src/ft2font.cpp | 25 ++++++++++ src/ft2font.h | 1 + src/ft2font_wrapper.cpp | 74 ++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+) diff --git a/lib/matplotlib/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py index eee47f5b03f8..cd173e99283a 100644 --- a/lib/matplotlib/tests/test_ft2font.py +++ b/lib/matplotlib/tests/test_ft2font.py @@ -76,3 +76,30 @@ def test_font_fallback_chinese(fig_test, fig_ref, family_name, file_name): ): fig_ref.text(0.05, .85 - 0.15*j, txt, family=ref_font) fig_test.text(0.05, .85 - 0.15*j, txt, family=test_font) + + +@pytest.mark.parametrize( + "family_name, file_name", + [ + ("WenQuanYi Zen Hei", "wqy-zenhei.ttc"), + ("Noto Sans CJK JP", "NotoSansCJK-Regular.ttc"), + ], +) +def test__get_fontmap(family_name, file_name): + fp = fm.FontProperties(family=[family_name]) + if Path(fm.findfont(fp)).name != file_name: + pytest.skip(f"Font {family_name} ({file_name}) is missing") + + text = "There are 几个汉字 in between!" + ft = fm.get_font( + fm.fontManager._find_fonts_by_props( + fm.FontProperties(family=["DejaVu Sans", family_name]) + ) + ) + + fontmap = ft._get_fontmap(text) + for char, font in fontmap.items(): + if ord(char) > 127: + assert Path(font.fname).name == file_name + else: + assert Path(font.fname).name == "DejaVuSans.ttf" diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 4454a4a51ac6..1dc831545554 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -603,6 +603,31 @@ void FT2Font::load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool } } + +bool FT2Font::get_char_fallback_index(FT_ULong charcode, int& index) const +{ + FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); + if (glyph_index) { + // -1 means the host has the char and we do not need to fallback + index = -1; + return true; + } else { + int inner_index = 0; + bool was_found; + + for (size_t i = 0; i < fallbacks.size(); ++i) { + // TODO handle recursion somehow! + was_found = fallbacks[i]->get_char_fallback_index(charcode, inner_index); + if (was_found) { + index = i; + return true; + } + } + } + return false; +} + + bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, FT_UInt &final_glyph_index, std::vector &parent_glyphs, diff --git a/src/ft2font.h b/src/ft2font.h index cdcb979bdf3c..dc157f0e2887 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -108,6 +108,7 @@ class FT2Font FT_UInt get_char_index(FT_ULong charcode, bool fallback); void get_cbox(FT_BBox &bbox); PyObject* get_path(); + bool get_char_fallback_index(FT_ULong charcode, int& index) const; FT_Face const &get_face() const { diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 63d21184cc4c..7d1b48992963 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -7,6 +7,9 @@ // From Python #include +#include +#include + #define STRINGIFY(s) XSTRINGIFY(s) #define XSTRINGIFY(s) #s @@ -552,6 +555,76 @@ static PyObject *PyFT2Font_get_kerning(PyFT2Font *self, PyObject *args) return PyLong_FromLong(result); } +const char *PyFT2Font_get_fontmap__doc__ = + "_get_fontmap(self, string)\n" + "--\n\n" + "Get a mapping between characters and the font that includes them.\n" + "A dictionary mapping unicode characters to PyFT2Font objects."; +static PyObject *PyFT2Font_get_fontmap(PyFT2Font *self, PyObject *args, PyObject *kwds) +{ + PyObject *textobj; + const char *names[] = { "string", NULL }; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "O:_get_fontmap", (char **)names, &textobj)) { + return NULL; + } + + std::set codepoints; + size_t size; + + if (PyUnicode_Check(textobj)) { + size = PyUnicode_GET_LENGTH(textobj); +#if defined(PYPY_VERSION) && (PYPY_VERSION_NUM < 0x07040000) + // PyUnicode_ReadChar is available from PyPy 7.3.2, but wheels do not + // specify the micro-release version, so put the version bound at 7.4 + // to prevent generating wheels unusable on PyPy 7.3.{0,1}. + Py_UNICODE *unistr = PyUnicode_AsUnicode(textobj); + for (size_t i = 0; i < size; ++i) { + codepoints.insert(unistr[i]); + } +#else + for (size_t i = 0; i < size; ++i) { + codepoints.insert(PyUnicode_ReadChar(textobj, i)); + } +#endif + } else { + PyErr_SetString(PyExc_TypeError, "String must be str"); + return NULL; + } + PyObject *char_to_font; + if (!(char_to_font = PyDict_New())) { + return NULL; + } + for (auto it = codepoints.begin(); it != codepoints.end(); ++it) { + auto x = *it; + PyObject* target_font; + int index; + if (self->x->get_char_fallback_index(x, index)) { + if (index >= 0) { + target_font = self->fallbacks[index]; + } else { + target_font = (PyObject *)self; + } + } else { + // TODO Handle recursion! + target_font = (PyObject *)self; + } + + PyObject *key = NULL; + bool error = (!(key = PyUnicode_FromFormat("%c", x)) + || (PyDict_SetItem(char_to_font, key, target_font) == -1)); + Py_XDECREF(key); + if (error) { + Py_DECREF(char_to_font); + PyErr_SetString(PyExc_ValueError, "Something went very wrong"); + return NULL; + } + } + return char_to_font; +} + + const char *PyFT2Font_set_text__doc__ = "set_text(self, string, angle, flags=32)\n" "--\n\n" @@ -1525,6 +1598,7 @@ static PyTypeObject *PyFT2Font_init_type() {"select_charmap", (PyCFunction)PyFT2Font_select_charmap, METH_VARARGS, PyFT2Font_select_charmap__doc__}, {"get_kerning", (PyCFunction)PyFT2Font_get_kerning, METH_VARARGS, PyFT2Font_get_kerning__doc__}, {"set_text", (PyCFunction)PyFT2Font_set_text, METH_VARARGS|METH_KEYWORDS, PyFT2Font_set_text__doc__}, + {"_get_fontmap", (PyCFunction)PyFT2Font_get_fontmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_get_fontmap__doc__}, {"get_num_glyphs", (PyCFunction)PyFT2Font_get_num_glyphs, METH_NOARGS, PyFT2Font_get_num_glyphs__doc__}, {"load_char", (PyCFunction)PyFT2Font_load_char, METH_VARARGS|METH_KEYWORDS, PyFT2Font_load_char__doc__}, {"load_glyph", (PyCFunction)PyFT2Font_load_glyph, METH_VARARGS|METH_KEYWORDS, PyFT2Font_load_glyph__doc__}, From 7fcde53564f78c26fd7102a16f2e727933a65e63 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 9 Aug 2022 16:50:13 -0400 Subject: [PATCH 02/10] MNT: move font fallback handling into CharacterTracker --- lib/matplotlib/backends/_backend_pdf_ps.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/_backend_pdf_ps.py b/lib/matplotlib/backends/_backend_pdf_ps.py index 65d38eb5a542..365188060a2b 100644 --- a/lib/matplotlib/backends/_backend_pdf_ps.py +++ b/lib/matplotlib/backends/_backend_pdf_ps.py @@ -65,7 +65,9 @@ def __init__(self): def track(self, font, s): """Record that string *s* is being typeset using font *font*.""" - self.used.setdefault(font.fname, set()).update(map(ord, s)) + char_to_font = font._get_fontmap(s) + for _c, _f in char_to_font.items(): + self.used.setdefault(_f.fname, set()).add(ord(_c)) def track_glyph(self, font, glyph): """Record that codepoint *glyph* is being typeset using font *font*.""" From c5fd8804204ee715ee008c35f96d6e95f8dfcc29 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Wed, 28 Jul 2021 20:05:31 +0530 Subject: [PATCH 03/10] ENH: implement font fallback for PDF --- lib/matplotlib/_text_helpers.py | 14 +++- lib/matplotlib/backends/_backend_pdf_ps.py | 4 +- lib/matplotlib/backends/backend_pdf.py | 70 +++++++++++------- .../test_backend_pdf/multi_font_type3.pdf | Bin 0 -> 9486 bytes .../test_backend_pdf/multi_font_type42.pdf | Bin 0 -> 9565 bytes lib/matplotlib/tests/test_backend_pdf.py | 30 +++++++- 6 files changed, 85 insertions(+), 33 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type3.pdf create mode 100644 lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type42.pdf diff --git a/lib/matplotlib/_text_helpers.py b/lib/matplotlib/_text_helpers.py index 75d84997be9f..18bfb550c90b 100644 --- a/lib/matplotlib/_text_helpers.py +++ b/lib/matplotlib/_text_helpers.py @@ -9,7 +9,7 @@ LayoutItem = dataclasses.make_dataclass( - "LayoutItem", ["char", "glyph_idx", "x", "prev_kern"]) + "LayoutItem", ["ft_object", "char", "glyph_idx", "x", "prev_kern"]) def warn_on_missing_glyph(codepoint): @@ -57,12 +57,18 @@ def layout(string, font, *, kern_mode=KERNING_DEFAULT): """ x = 0 prev_glyph_idx = 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)) - kern = (font.get_kerning(prev_glyph_idx, glyph_idx, kern_mode) / 64 - if prev_glyph_idx is not None else 0.) + kern = ( + base_font.get_kerning(prev_glyph_idx, glyph_idx, kern_mode) / 64 + if prev_glyph_idx is not None else 0. + ) x += kern glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING) - yield LayoutItem(char, glyph_idx, x, kern) + yield LayoutItem(font, char, glyph_idx, x, kern) x += glyph.linearHoriAdvance / 65536 prev_glyph_idx = glyph_idx diff --git a/lib/matplotlib/backends/_backend_pdf_ps.py b/lib/matplotlib/backends/_backend_pdf_ps.py index 365188060a2b..4ab23915e9e2 100644 --- a/lib/matplotlib/backends/_backend_pdf_ps.py +++ b/lib/matplotlib/backends/_backend_pdf_ps.py @@ -137,8 +137,8 @@ def _get_font_afm(self, prop): return _cached_get_afm_from_fname(fname) def _get_font_ttf(self, prop): - fname = font_manager.findfont(prop) - font = font_manager.get_font(fname) + fnames = font_manager.fontManager._find_fonts_by_props(prop) + font = font_manager.get_font(fnames) font.clear() font.set_size(prop.get_size_in_points(), 72) return font diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 904665bf2b36..c7955d409587 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -32,7 +32,9 @@ RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.figure import Figure -from matplotlib.font_manager import findfont, get_font +from matplotlib.font_manager import ( + findfont, get_font, fontManager as _fontManager +) from matplotlib._afm import AFM from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, LOAD_NO_HINTING, KERNING_UNFITTED, FT2Font) @@ -925,20 +927,28 @@ def fontName(self, fontprop): """ if isinstance(fontprop, str): - filename = fontprop + filenames = [fontprop] elif mpl.rcParams['pdf.use14corefonts']: - filename = findfont( - fontprop, fontext='afm', directory=RendererPdf._afm_font_dir) + filenames = _fontManager._find_fonts_by_props( + fontprop, fontext='afm', directory=RendererPdf._afm_font_dir + ) else: - filename = findfont(fontprop) - - Fx = self.fontNames.get(filename) - if Fx is None: - Fx = next(self._internal_font_seq) - self.fontNames[filename] = Fx - _log.debug('Assigning font %s = %r', Fx, filename) - - return Fx + filenames = _fontManager._find_fonts_by_props(fontprop) + first_Fx = None + for fname in filenames: + Fx = self.fontNames.get(fname) + if not first_Fx: + first_Fx = Fx + if Fx is None: + Fx = next(self._internal_font_seq) + self.fontNames[fname] = Fx + _log.debug('Assigning font %s = %r', Fx, fname) + if not first_Fx: + first_Fx = Fx + + # find_fontsprop's first value always adheres to + # findfont's value, so technically no behaviour change + return first_Fx def dviFontName(self, dvifont): """ @@ -1204,7 +1214,6 @@ def get_char_width(charcode): width = font.load_char( s, flags=LOAD_NO_SCALE | LOAD_NO_HINTING).horiAdvance return cvt(width) - with warnings.catch_warnings(): # Ignore 'Required glyph missing from current font' warning # from ft2font: here we're just building the widths table, but @@ -2389,22 +2398,27 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # the regular text show command (TJ) with appropriate kerning between # chunks, whereas multibyte characters use the XObject command (Do). else: - # List of (start_x, [prev_kern, char, char, ...]), w/o zero kerns. + # List of (ft_object, start_x, [prev_kern, char, char, ...]), + # w/o zero kerns. singlebyte_chunks = [] - # List of (start_x, glyph_index). + # List of (ft_object, start_x, glyph_index). multibyte_glyphs = [] prev_was_multibyte = True + prev_font = font for item in _text_helpers.layout( s, font, kern_mode=KERNING_UNFITTED): if _font_supports_glyph(fonttype, ord(item.char)): - if prev_was_multibyte: - singlebyte_chunks.append((item.x, [])) + if prev_was_multibyte or item.ft_object != prev_font: + singlebyte_chunks.append((item.ft_object, item.x, [])) + prev_font = item.ft_object if item.prev_kern: - singlebyte_chunks[-1][1].append(item.prev_kern) - singlebyte_chunks[-1][1].append(item.char) + singlebyte_chunks[-1][2].append(item.prev_kern) + singlebyte_chunks[-1][2].append(item.char) prev_was_multibyte = False else: - multibyte_glyphs.append((item.x, item.glyph_idx)) + multibyte_glyphs.append( + (item.ft_object, item.x, item.glyph_idx) + ) prev_was_multibyte = True # Do the rotation and global translation as a single matrix # concatenation up front @@ -2414,10 +2428,12 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): -math.sin(a), math.cos(a), x, y, Op.concat_matrix) # Emit all the 1-byte characters in a BT/ET group. - self.file.output(Op.begin_text, - self.file.fontName(prop), fontsize, Op.selectfont) + + self.file.output(Op.begin_text) prev_start_x = 0 - for start_x, kerns_or_chars in singlebyte_chunks: + for ft_object, start_x, kerns_or_chars in singlebyte_chunks: + ft_name = self.file.fontName(ft_object.fname) + self.file.output(ft_name, fontsize, Op.selectfont) self._setup_textpos(start_x, 0, 0, prev_start_x, 0, 0) self.file.output( # See pdf spec "Text space details" for the 1000/fontsize @@ -2429,8 +2445,10 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): prev_start_x = start_x self.file.output(Op.end_text) # Then emit all the multibyte characters, one at a time. - for start_x, glyph_idx in multibyte_glyphs: - self._draw_xobject_glyph(font, fontsize, glyph_idx, start_x, 0) + for ft_object, start_x, glyph_idx in multibyte_glyphs: + self._draw_xobject_glyph( + ft_object, fontsize, glyph_idx, start_x, 0 + ) self.file.output(Op.grestore) def _draw_xobject_glyph(self, font, fontsize, glyph_idx, x, y): diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type3.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type3.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a148a7d571b187691eaa4d7492cc6aecbff8bb01 GIT binary patch literal 9486 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 literal 0 HcmV?d00001 diff --git a/lib/matplotlib/tests/test_backend_pdf.py b/lib/matplotlib/tests/test_backend_pdf.py index 80f63133ce7d..fdfbc1277882 100644 --- a/lib/matplotlib/tests/test_backend_pdf.py +++ b/lib/matplotlib/tests/test_backend_pdf.py @@ -9,7 +9,9 @@ import pytest import matplotlib as mpl -from matplotlib import pyplot as plt, rcParams +from matplotlib import ( + pyplot as plt, rcParams, font_manager as fm +) from matplotlib.cbook import _get_data_path from matplotlib.ft2font import FT2Font from matplotlib.font_manager import findfont, FontProperties @@ -383,3 +385,29 @@ def test_glyphs_subset(): # since both objects are assigned same characters assert subfont.get_num_glyphs() == nosubfont.get_num_glyphs() + + +@image_comparison(["multi_font_type3.pdf"]) +def test_multi_font_type3(): + fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) + if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": + pytest.skip("Font may be missing") + + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + plt.rc('pdf', fonttype=3) + + fig = plt.figure() + fig.text(0.15, 0.475, "There are 几个汉字 in between!") + + +@image_comparison(["multi_font_type42.pdf"]) +def test_multi_font_type42(): + fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) + if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": + pytest.skip("Font may be missing") + + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + plt.rc('pdf', fonttype=42) + + fig = plt.figure() + fig.text(0.15, 0.475, "There are 几个汉字 in between!") From 09a229976fddb3e97d37575dba7d310af9abcd76 Mon Sep 17 00:00:00 2001 From: Aitik Gupta Date: Fri, 13 Aug 2021 17:53:21 +0530 Subject: [PATCH 04/10] ENH: implement fontfallback for eps backen --- lib/matplotlib/backends/backend_ps.py | 35 +- .../test_backend_ps/multi_font_type3.eps | 521 ++++++++++++++++++ .../test_backend_ps/multi_font_type42.eps | 361 ++++++++++++ lib/matplotlib/tests/test_backend_ps.py | 30 +- 4 files changed, 935 insertions(+), 12 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps create mode 100644 lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 0ce2a20360da..c30267fd18a9 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -631,7 +631,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): if mpl.rcParams['ps.useafm']: font = self._get_font_afm(prop) scale = 0.001 * prop.get_size_in_points() - + stream = [] thisx = 0 last_name = None # kerns returns 0 for None. xs_names = [] @@ -647,21 +647,36 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): thisx += kern * scale xs_names.append((thisx, name)) thisx += width * scale + ps_name = (font.postscript_name + .encode("ascii", "replace").decode("ascii")) + stream.append((ps_name, xs_names)) else: font = self._get_font_ttf(prop) - font.set_text(s, 0, flags=LOAD_NO_HINTING) self._character_tracker.track(font, s) - xs_names = [(item.x, font.get_glyph_name(item.glyph_idx)) - for item in _text_helpers.layout(s, font)] + stream = [] + prev_font = curr_stream = None + for item in _text_helpers.layout(s, font): + ps_name = (item.ft_object.postscript_name + .encode("ascii", "replace").decode("ascii")) + if item.ft_object is not prev_font: + if curr_stream: + stream.append(curr_stream) + prev_font = item.ft_object + curr_stream = [ps_name, []] + curr_stream[1].append( + (item.x, item.ft_object.get_glyph_name(item.glyph_idx)) + ) + # append the last entry + stream.append(curr_stream) self.set_color(*gc.get_rgb()) - ps_name = (font.postscript_name - .encode("ascii", "replace").decode("ascii")) - self.set_font(ps_name, prop.get_size_in_points()) - thetext = "\n".join(f"{x:g} 0 m /{name:s} glyphshow" - for x, name in xs_names) - self._pswriter.write(f"""\ + + for ps_name, xs_names in stream: + self.set_font(ps_name, prop.get_size_in_points(), False) + thetext = "\n".join(f"{x:g} 0 m /{name:s} glyphshow" + for x, name in xs_names) + self._pswriter.write(f"""\ gsave {self._get_clip_cmd(gc)} {x:g} {y:g} translate diff --git a/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps new file mode 100644 index 000000000000..efc8fc9416a7 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type3.eps @@ -0,0 +1,521 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Title: multi_font_type3.eps +%%Creator: Matplotlib v3.6.0.dev2856+g4848cedd6d.d20220807, https://matplotlib.org/ +%%CreationDate: Sun Aug 7 16:45:01 2022 +%%Orientation: portrait +%%BoundingBox: 18 180 594 612 +%%HiResBoundingBox: 18.000000 180.000000 594.000000 612.000000 +%%EndComments +%%BeginProlog +/mpldict 12 dict def +mpldict begin +/_d { bind def } bind def +/m { moveto } _d +/l { lineto } _d +/r { rlineto } _d +/c { curveto } _d +/cl { closepath } _d +/ce { closepath eofill } _d +/box { + m + 1 index 0 r + 0 exch r + neg 0 r + cl + } _d +/clipbox { + box + clip + newpath + } _d +/sc { setcachedevice } _d +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /DejaVuSans def +/PaintType 0 def +/FontMatrix [0.00048828125 0 0 0.00048828125 0 0] def +/FontBBox [-2090 -948 3673 2524] def +/FontType 3 def +/Encoding [/space /exclam /b /a /e /h /i /n /r /T /t /w] def +/CharStrings 13 dict dup begin +/.notdef 0 def +/space{651 0 0 0 0 0 sc +ce} _d +/exclam{821 0 309 0 512 1493 sc +309 254 m +512 254 l +512 0 l +309 0 l +309 254 l + +309 1493 m +512 1493 l +512 838 l +492 481 l +330 481 l +309 838 l +309 1493 l + +ce} _d +/b{1300 0 186 -29 1188 1556 sc +997 559 m +997 694 969 800 913 877 c +858 954 781 993 684 993 c +587 993 510 954 454 877 c +399 800 371 694 371 559 c +371 424 399 317 454 240 c +510 163 587 125 684 125 c +781 125 858 163 913 240 c +969 317 997 424 997 559 c + +371 950 m +410 1017 458 1066 517 1098 c +576 1131 647 1147 729 1147 c +865 1147 975 1093 1060 985 c +1145 877 1188 735 1188 559 c +1188 383 1145 241 1060 133 c +975 25 865 -29 729 -29 c +647 -29 576 -13 517 19 c +458 52 410 101 371 168 c +371 0 l +186 0 l +186 1556 l +371 1556 l +371 950 l + +ce} _d +/a{1255 0 123 -29 1069 1147 sc +702 563 m +553 563 450 546 393 512 c +336 478 307 420 307 338 c +307 273 328 221 371 182 c +414 144 473 125 547 125 c +649 125 731 161 792 233 c +854 306 885 402 885 522 c +885 563 l +702 563 l + +1069 639 m +1069 0 l +885 0 l +885 170 l +843 102 791 52 728 19 c +665 -13 589 -29 498 -29 c +383 -29 292 3 224 67 c +157 132 123 218 123 326 c +123 452 165 547 249 611 c +334 675 460 707 627 707 c +885 707 l +885 725 l +885 810 857 875 801 921 c +746 968 668 991 567 991 c +503 991 441 983 380 968 c +319 953 261 930 205 899 c +205 1069 l +272 1095 338 1114 401 1127 c +464 1140 526 1147 586 1147 c +748 1147 869 1105 949 1021 c +1029 937 1069 810 1069 639 c + +ce} _d +/e{1260 0 113 -29 1151 1147 sc +1151 606 m +1151 516 l +305 516 l +313 389 351 293 419 226 c +488 160 583 127 705 127 c +776 127 844 136 910 153 c +977 170 1043 196 1108 231 c +1108 57 l +1042 29 974 8 905 -7 c +836 -22 765 -29 694 -29 c +515 -29 374 23 269 127 c +165 231 113 372 113 549 c +113 732 162 878 261 985 c +360 1093 494 1147 662 1147 c +813 1147 932 1098 1019 1001 c +1107 904 1151 773 1151 606 c + +967 660 m +966 761 937 841 882 901 c +827 961 755 991 664 991 c +561 991 479 962 417 904 c +356 846 320 764 311 659 c +967 660 l + +ce} _d +/h{1298 0 186 0 1124 1556 sc +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1556 l +371 1556 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/i{569 0 193 0 377 1556 sc +193 1120 m +377 1120 l +377 0 l +193 0 l +193 1120 l + +193 1556 m +377 1556 l +377 1323 l +193 1323 l +193 1556 l + +ce} _d +/n{1298 0 186 0 1124 1147 sc +1124 676 m +1124 0 l +940 0 l +940 670 l +940 776 919 855 878 908 c +837 961 775 987 692 987 c +593 987 514 955 457 892 c +400 829 371 742 371 633 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +415 1013 467 1064 526 1097 c +586 1130 655 1147 733 1147 c +862 1147 959 1107 1025 1027 c +1091 948 1124 831 1124 676 c + +ce} _d +/r{842 0 186 0 842 1147 sc +842 948 m +821 960 799 969 774 974 c +750 980 723 983 694 983 c +590 983 510 949 454 881 c +399 814 371 717 371 590 c +371 0 l +186 0 l +186 1120 l +371 1120 l +371 946 l +410 1014 460 1064 522 1097 c +584 1130 659 1147 748 1147 c +761 1147 775 1146 790 1144 c +805 1143 822 1140 841 1137 c +842 948 l + +ce} _d +/T{1251 0 -6 0 1257 1493 sc +-6 1493 m +1257 1493 l +1257 1323 l +727 1323 l +727 0 l +524 0 l +524 1323 l +-6 1323 l +-6 1493 l + +ce} _d +/t{803 0 55 0 754 1438 sc +375 1438 m +375 1120 l +754 1120 l +754 977 l +375 977 l +375 369 l +375 278 387 219 412 193 c +437 167 488 154 565 154 c +754 154 l +754 0 l +565 0 l +423 0 325 26 271 79 c +217 132 190 229 190 369 c +190 977 l +55 977 l +55 1120 l +190 1120 l +190 1438 l +375 1438 l + +ce} _d +/w{1675 0 86 0 1589 1120 sc +86 1120 m +270 1120 l +500 246 l +729 1120 l +946 1120 l +1176 246 l +1405 1120 l +1589 1120 l +1296 0 l +1079 0 l +838 918 l +596 0 l +379 0 l +86 1120 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +%!PS-Adobe-3.0 Resource-Font +%%Creator: Converted from TrueType to Type 3 by Matplotlib. +10 dict begin +/FontName /WenQuanYiZenHei def +/PaintType 0 def +/FontMatrix [0.0009765625 0 0 0.0009765625 0 0] def +/FontBBox [-129 -304 1076 986] def +/FontType 3 def +/Encoding [/uni51E0 /uni6C49 /uni4E2A /uni5B57] def +/CharStrings 5 dict dup begin +/.notdef 0 def +/uni51E0{1024 0 34 -126 1004 818 sc +935 257 m +947 232 970 219 1004 217 c +974 -26 l +973 -40 968 -51 960 -60 c +955 -63 950 -65 943 -65 c +729 -65 l +697 -65 670 -55 649 -34 c +628 -14 617 12 615 43 c +614 75 613 193 613 397 c +613 602 614 719 616 748 c +393 748 l +394 395 l +391 256 361 144 302 58 c +250 -21 184 -82 104 -126 c +89 -96 66 -77 34 -68 c +119 -34 188 23 240 103 c +292 184 318 281 318 395 c +317 818 l +692 818 l +692 55 l +692 42 695 30 702 20 c +709 11 719 6 730 6 c +886 6 l +898 7 905 12 908 23 c +911 42 912 62 913 81 c +935 257 l + +ce} _d +/uni6C49{1024 0 17 -119 990 820 sc +612 211 m +536 349 488 512 468 699 c +447 699 426 698 405 697 c +407 719 407 741 405 763 c +445 761 485 760 526 760 c +871 760 l +851 534 793 348 696 202 c +769 91 867 2 990 -65 c +963 -76 942 -94 928 -119 c +819 -56 727 31 653 143 c +561 27 446 -58 307 -112 c +289 -89 268 -69 243 -53 c +392 -2 515 86 612 211 c + +535 700 m +552 534 592 391 655 271 c +735 396 782 539 796 700 c +535 700 l + +151 -118 m +123 -102 88 -93 47 -92 c +76 -38 107 24 138 93 c +169 162 215 283 274 454 c +315 433 l +199 54 l +151 -118 l + +230 457 m +166 408 l +17 544 l +80 594 l +230 457 l + +248 626 m +202 677 152 724 97 768 c +157 820 l +214 773 268 723 317 670 c +248 626 l + +ce} _d +/uni4E2A{1024 0 14 -123 980 833 sc +547 -123 m +520 -120 492 -120 464 -123 c +467 -72 468 -21 468 30 c +468 362 l +468 413 467 465 464 516 c +492 513 520 513 547 516 c +545 465 544 413 544 362 c +544 30 l +544 -21 545 -72 547 -123 c + +980 427 m +955 410 939 387 931 358 c +846 384 767 429 695 494 c +624 559 563 631 514 711 c +383 520 236 378 71 285 c +59 314 40 337 14 354 c +113 405 204 471 285 550 c +367 630 433 724 484 833 c +499 822 515 813 531 805 c +537 808 l +542 800 l +549 796 557 792 564 789 c +555 775 l +614 672 682 590 759 531 c +824 484 898 450 980 427 c + +ce} _d +/uni5B57{1024 0 32 -132 982 872 sc +982 285 m +980 264 980 243 982 222 c +943 224 904 225 865 225 c +555 225 l +555 -29 l +555 -54 545 -76 525 -95 c +496 -120 444 -132 368 -131 c +376 -98 368 -68 344 -43 c +366 -46 392 -47 422 -47 c +452 -48 470 -46 475 -42 c +480 -38 483 -27 483 -9 c +483 225 l +148 225 l +109 225 71 224 32 222 c +34 243 34 264 32 285 c +71 283 109 282 148 282 c +483 282 l +483 355 l +648 506 l +317 506 l +278 506 239 505 200 503 c +203 524 203 545 200 566 c +239 564 278 563 317 563 c +761 563 l +769 498 l +748 493 730 483 714 469 c +555 323 l +555 282 l +865 282 l +904 282 943 283 982 285 c + +131 562 m +59 562 l +59 753 l +468 752 l +390 807 l +435 872 l +542 798 l +510 752 l +925 752 l +925 562 l +852 562 l +852 695 l +131 695 l +131 562 l + +ce} _d +end readonly def + +/BuildGlyph { + exch begin + CharStrings exch + 2 copy known not {pop /.notdef} if + true 3 1 roll get exec + end +} _d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +} _d + +FontName currentdict end definefont pop +end +%%EndProlog +mpldict begin +18 180 translate +576 432 0 0 clipbox +gsave +0 0 m +576 0 l +576 432 l +0 432 l +cl +1.000 setgray +fill +grestore +0.000 setgray +/DejaVuSans 27.000 selectfont +gsave + +86.4 205.2 translate +0 rotate +0.000000 0 m /T glyphshow +16.492676 0 m /h glyphshow +33.604980 0 m /e glyphshow +50.216309 0 m /r glyphshow +61.316895 0 m /e glyphshow +77.928223 0 m /space glyphshow +86.510742 0 m /a glyphshow +103.056152 0 m /r glyphshow +114.156738 0 m /e glyphshow +130.768066 0 m /space glyphshow +grestore +/WenQuanYiZenHei 27.000 selectfont +gsave + +86.4 205.2 translate +0 rotate +139.350586 0 m /uni51E0 glyphshow +166.350586 0 m /uni4E2A glyphshow +193.350586 0 m /uni6C49 glyphshow +220.350586 0 m /uni5B57 glyphshow +grestore +/DejaVuSans 27.000 selectfont +gsave + +86.4 205.2 translate +0 rotate +247.350586 0 m /space glyphshow +255.933105 0 m /i glyphshow +263.434570 0 m /n glyphshow +280.546875 0 m /space glyphshow +289.129395 0 m /b glyphshow +306.268066 0 m /e glyphshow +322.879395 0 m /t glyphshow +333.465820 0 m /w glyphshow +355.548340 0 m /e glyphshow +372.159668 0 m /e glyphshow +388.770996 0 m /n glyphshow +405.883301 0 m /exclam glyphshow +grestore + +end +showpage diff --git a/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps new file mode 100644 index 000000000000..472824330da4 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_backend_ps/multi_font_type42.eps @@ -0,0 +1,361 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Title: multi_font_type42.eps +%%Creator: Matplotlib v3.6.0.dev2856+g4848cedd6d.d20220807, https://matplotlib.org/ +%%CreationDate: Sun Aug 7 16:45:01 2022 +%%Orientation: portrait +%%BoundingBox: 18 180 594 612 +%%HiResBoundingBox: 18.000000 180.000000 594.000000 612.000000 +%%EndComments +%%BeginProlog +/mpldict 12 dict def +mpldict begin +/_d { bind def } bind def +/m { moveto } _d +/l { lineto } _d +/r { rlineto } _d +/c { curveto } _d +/cl { closepath } _d +/ce { closepath eofill } _d +/box { + m + 1 index 0 r + 0 exch r + neg 0 r + cl + } _d +/clipbox { + box + clip + newpath + } _d +/sc { setcachedevice } _d +%!PS-TrueTypeFont-1.0-2.22937 +%%Title: unknown +%%Creator: Converted from TrueType to type 42 by PPR +15 dict begin +/FontName /DejaVuSans def +/PaintType 0 def +/FontMatrix[1 0 0 1 0 0]def +/FontBBox[-1021 -463 1793 1232]def +/FontType 42 def +/Encoding StandardEncoding def +/FontInfo 10 dict dup begin +/FamilyName (unknown) def +/FullName (unknown) def +/Weight (unknown) def +/Version (unknown) def +/ItalicAngle 0.0 def +/isFixedPitch false def +/UnderlinePosition -130 def +/UnderlineThickness 90 def +end readonly def +/sfnts[<0001000000090080000300106376742000691D390000009C000001FE6670676D +7134766A0000029C000000AB676C7966118399D500000348000007B668656164085DC286 +00000B0000000036686865610D9F077C00000B3800000024686D74783A5706B700000B5C +0000003C6C6F636108C30B6500000B98000000206D617870047C067100000BB800000020 +707265703B07F10000000BD800000568013500B800CB00CB00C100AA009C01A600B80066 +0000007100CB00A002B20085007500B800C301CB0189022D00CB00A600F000D300AA0087 +00CB03AA0400014A003300CB000000D9050200F4015400B4009C01390114013907060400 +044E04B4045204B804E704CD0037047304CD04600473013303A2055605A60556053903C5 +021200C9001F00B801DF007300BA03E9033303BC0444040E00DF03CD03AA00E503AA0404 +000000CB008F00A4007B00B80014016F007F027B0252008F00C705CD009A009A006F00CB +00CD019E01D300F000BA018300D5009803040248009E01D500C100CB00F600830354027F +00000333026600D300C700A400CD008F009A0073040005D5010A00FE022B00A400B4009C +00000062009C0000001D032D05D505D505D505F0007F007B005400A406B80614072301D3 +00B800CB00A601C301EC069300A000D3035C037103DB0185042304A80448008F01390114 +01390360008F05D5019A0614072306660179046004600460047B009C00000277046001AA +00E904600762007B00C5007F027B000000B4025205CD006600BC00660077061000CD013B +01850389008F007B0000001D00CD074A042F009C009C0000077D006F0000006F0335006A +006F007B00AE00B2002D0396008F027B00F600830354063705F6008F009C04E10266008F +018D02F600CD03440029006604EE00730000140000960000B707060504030201002C2010 +B002254964B040515820C859212D2CB002254964B040515820C859212D2C20100720B000 +50B00D7920B8FFFF5058041B0559B0051CB0032508B0042523E120B00050B00D7920B8FF +FF5058041B0559B0051CB0032508E12D2C4B505820B0FD454459212D2CB002254560442D +2C4B5358B00225B0022545445921212D2C45442D2CB00225B0022549B00525B005254960 +B0206368208A108A233A8A10653A2D00000201350000020005D5000300090035400F0700 +8304810208070501030400000A10FC4BB00B5458B90000FFC038593CEC32393931002FE4 +FCCC3001B6000B200B500B035D253315231133110323030135CBCBCB14A215FEFE05D5FD +71FE9B0165000001FFFA000004E905D50007004A400E0602950081040140031C00400508 +10D4E4FCE431002FF4EC3230014BB00A5458BD00080040000100080008FFC03811373859 +401300091F00100110021F071009400970099F09095D03211521112311210604EFFDEECB +FDEE05D5AAFAD5052B000002007BFFE3042D047B000A002500BC4027191F0B17090E00A9 +1706B90E1120861FBA1CB923B8118C170C001703180D09080B1F030814452610FCECCCD4 +EC323211393931002FC4E4F4FCF4EC10C6EE10EE11391139123930406E301D301E301F30 +20302130223F27401D401E401F402040214022501D501E501F5020502150225027702785 +1D871E871F8720872185229027A027F0271E301E301F30203021401E401F40204021501E +501F50205021601E601F60206021701E701F70207021801E801F80208021185D015D0122 +061514163332363D01371123350E01232226353436332135342623220607353E01333216 +02BEDFAC816F99B9B8B83FBC88ACCBFDFB0102A79760B65465BE5AF3F00233667B6273D9 +B4294CFD81AA6661C1A2BDC0127F8B2E2EAA2727FC00000200BAFFE304A40614000B001C +0038401903B90C0F09B918158C0FB81B971900121247180C06081A461D10FCEC3232F4EC +31002FECE4F4C4EC10C6EE30B6601E801EA01E03015D013426232206151416333236013E +01333200111002232226271523113303E5A79292A7A79292A7FD8E3AB17BCC00FFFFCC7B +B13AB9B9022FCBE7E7CBCBE7E702526461FEBCFEF8FEF8FEBC6164A8061400020071FFE3 +047F047B0014001B00704024001501098608880515A90105B90C01BB18B912B80C8C1C1B +1502081508004B02120F451C10FCECF4ECC4111239310010E4F4ECE410EE10EE10F4EE11 +12393040293F1D701DA01DD01DF01D053F003F013F023F153F1B052C072F082F092C0A6F +006F016F026F156F1B095D71015D0115211E0133323637150E0123200011100033320007 +2E0123220607047FFCB20CCDB76AC76263D06BFEF4FEC70129FCE20107B802A5889AB90E +025E5ABEC73434AE2A2C0138010A01130143FEDDC497B4AE9E00000100BA000004640614 +001300344019030900030E0106870E11B80C970A010208004E0D09080B461410FCEC32F4 +EC31002F3CECF4C4EC1112173930B2601501015D0111231134262322061511231133113E +013332160464B87C7C95ACB9B942B375C1C602A4FD5C029E9F9EBEA4FD870614FD9E6564 +EF00000200C100000179061400030007002B400E06BE04B100BC020501080400460810FC +3CEC3231002FE4FCEC30400B1009400950096009700905015D1333112311331523C1B8B8 +B8B80460FBA00614E900000100BA00000464047B001300364019030900030E0106870E11 +B80CBC0A010208004E0D09080B461410FCEC32F4EC31002F3CE4F4C4EC1112173930B460 +15CF1502015D0111231134262322061511231133153E013332160464B87C7C95ACB9B942 +B375C1C602A4FD5C029E9F9EBEA4FD870460AE6564EF000100BA0000034A047B00110030 +4014060B0700110B03870EB809BC070A06080008461210FCC4EC3231002FE4F4ECC4D4CC +11123930B450139F1302015D012E012322061511231133153E0133321617034A1F492C9C +A7B9B93ABA85132E1C03B41211CBBEFDB20460AE6663050500010037000002F2059E0013 +003840190E05080F03A9001101BC08870A0B08090204000810120E461410FC3CC4FC3CC4 +32393931002FECF43CC4EC3211393930B2AF1501015D01112115211114163B0115232226 +3511233533110177017BFE854B73BDBDD5A28787059EFEC28FFDA0894E9A9FD202608F01 +3E0000010056000006350460000C01EB404905550605090A0904550A0903550A0B0A0255 +01020B0B0A061107080705110405080807021103020C000C011100000C420A0502030603 +00BF0B080C0B0A09080605040302010B07000D10D44BB00A544BB011545B4BB012545B4B +B013545B4BB00B545B58B9000000403859014BB00C544BB00D545B4BB010545B58B90000 +FFC03859CC173931002F3CEC32321739304B5358071005ED071008ED071008ED071005ED +071008ED071005ED0705ED071008ED59220140FF050216021605220A350A49024905460A +400A5B025B05550A500A6E026E05660A79027F0279057F05870299029805940ABC02BC05 +CE02C703CF051D0502090306040B050A080B09040B050C1502190316041A051B081B0914 +0B150C2500250123022703210425052206220725082709240A210B230C39033604360839 +0C300E460248034604400442054006400740084409440A440B400E400E56005601560250 +0451055206520750085309540A550B6300640165026A0365046A056A066A076E09610B67 +0C6F0E7500750179027D0378047D057A067F067A077F07780879097F097B0A760B7D0C87 +0288058F0E97009701940293039C049B05980698079908402F960C9F0EA600A601A402A4 +03AB04AB05A906A907AB08A40CAF0EB502B103BD04BB05B809BF0EC402C303CC04CA0579 +5D005D13331B01331B013301230B012356B8E6E5D9E6E5B8FEDBD9F1F2D90460FC96036A +FC96036AFBA00396FC6A00000001000000025999D203C60C5F0F3CF5001F080000000000 +D17E0EE400000000D17E0EE4F7D6FC4C0E5909DC00000008000000000000000000010000 +076DFE1D00000EFEF7D6FA510E5900010000000000000000000000000000000F04CD0066 +0000000002AA0000028B00000335013504E3FFFA04E7007B051400BA04EC0071051200BA +023900C1051200BA034A00BA03230037068B0056000000000000000000000031006900FF +014B01B501F102190255028C02C903DB00010000000F0354002B0068000C000200100099 +000800000415021600080004B8028040FFFBFE03FA1403F92503F83203F79603F60E03F5 +FE03F4FE03F32503F20E03F19603F02503EF8A4105EFFE03EE9603ED9603ECFA03EBFA03 +EAFE03E93A03E84203E7FE03E63203E5E45305E59603E48A4105E45303E3E22F05E3FA03 +E22F03E1FE03E0FE03DF3203DE1403DD9603DCFE03DB1203DA7D03D9BB03D8FE03D68A41 +05D67D03D5D44705D57D03D44703D3D21B05D3FE03D21B03D1FE03D0FE03CFFE03CEFE03 +CD9603CCCB1E05CCFE03CB1E03CA3203C9FE03C6851105C61C03C51603C4FE03C3FE03C2 +FE03C1FE03C0FE03BFFE03BEFE03BDFE03BCFE03BBFE03BA1103B9862505B9FE03B8B7BB +05B8FE03B7B65D05B7BB03B78004B6B52505B65D40FF03B64004B52503B4FE03B39603B2 +FE03B1FE03B0FE03AFFE03AE6403AD0E03ACAB2505AC6403ABAA1205AB2503AA1203A98A +4105A9FA03A8FE03A7FE03A6FE03A51203A4FE03A3A20E05A33203A20E03A16403A08A41 +05A096039FFE039E9D0C059EFE039D0C039C9B19059C64039B9A10059B19039A1003990A +0398FE0397960D0597FE03960D03958A410595960394930E05942803930E0392FA039190 +BB0591FE03908F5D0590BB039080048F8E25058F5D038F40048E25038DFE038C8B2E058C +FE038B2E038A8625058A410389880B05891403880B038786250587640386851105862503 +85110384FE038382110583FE0382110381FE0380FE037FFE0340FF7E7D7D057EFE037D7D +037C64037B5415057B25037AFE0379FE03780E03770C03760A0375FE0374FA0373FA0372 +FA0371FA0370FE036FFE036EFE036C21036BFE036A1142056A530369FE03687D03671142 +0566FE0365FE0364FE0363FE0362FE03613A0360FA035E0C035DFE035BFE035AFE035958 +0A0559FA03580A035716190557320356FE03555415055542035415035301100553180352 +1403514A130551FE03500B034FFE034E4D10054EFE034D10034CFE034B4A13054BFE034A +4910054A1303491D0D05491003480D0347FE0346960345960344FE0343022D0543FA0342 +BB03414B0340FE033FFE033E3D12053E14033D3C0F053D12033C3B0D053C40FF0F033B0D +033AFE0339FE033837140538FA033736100537140336350B05361003350B03341E03330D +0332310B0532FE03310B03302F0B05300D032F0B032E2D09052E10032D09032C32032B2A +25052B64032A2912052A25032912032827250528410327250326250B05260F03250B0324 +FE0323FE03220F03210110052112032064031FFA031E1D0D051E64031D0D031C1142051C +FE031BFA031A42031911420519FE031864031716190517FE031601100516190315FE0314 +FE0313FE031211420512FE0311022D05114203107D030F64030EFE030D0C16050DFE030C +0110050C16030BFE030A100309FE0308022D0508FE030714030664030401100504FE0340 +1503022D0503FE0302011005022D0301100300FE0301B80164858D012B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B002B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B +2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B1D00>]def +/CharStrings 13 dict dup begin +/.notdef 0 def +/space 3 def +/exclam 4 def +/b 7 def +/a 6 def +/e 8 def +/h 9 def +/i 10 def +/n 11 def +/r 12 def +/T 5 def +/t 13 def +/w 14 def +end readonly def + +systemdict/resourcestatus known + {42 /FontType resourcestatus + {pop pop false}{true}ifelse} + {true}ifelse +{/TrueDict where{pop}{(%%[ Error: no TrueType rasterizer ]%%)= flush}ifelse +/FontType 3 def + /TrueState 271 string def + TrueDict begin sfnts save + 72 0 matrix defaultmatrix dtransform dup + mul exch dup mul add sqrt cvi 0 72 matrix + defaultmatrix dtransform dup mul exch dup + mul add sqrt cvi 3 -1 roll restore + TrueState initer end + /BuildGlyph{exch begin + CharStrings dup 2 index known + {exch}{exch pop /.notdef}ifelse + get dup xcheck + {currentdict systemdict begin begin exec end end} + {TrueDict begin /bander load cvlit exch TrueState render end} + ifelse + end}bind def + /BuildChar{ + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec + }bind def +}if + +FontName currentdict end definefont pop +%!PS-TrueTypeFont-1.0-0.58982 +%%Title: unknown +%%Creator: Converted from TrueType to type 42 by PPR +15 dict begin +/FontName /WenQuanYiZenHei def +/PaintType 0 def +/FontMatrix[1 0 0 1 0 0]def +/FontBBox[-126 -297 1051 963]def +/FontType 42 def +/Encoding StandardEncoding def +/FontInfo 10 dict dup begin +/FamilyName (unknown) def +/FullName (unknown) def +/Weight (unknown) def +/Version (unknown) def +/ItalicAngle 0.0 def +/isFixedPitch false def +/UnderlinePosition -230 def +/UnderlineThickness 51 def +end readonly def +/sfnts[<00010000000700400002003063767420002202880000007C00000004676C7966 +AC20EA39000000800000024A68656164F2831BDF000002CC000000366868656107EC01A3 +0000030400000024686D747805A7004E000003280000001A6C6F636101A0011100000344 +000000126D617870008A02690000035800000020002202880002000DFF8503D50341000D +0024000005260736351134271637061511140106072E0127020726273E01371617371716 +17071617160223292A04042A290301B4250C80D74AC4F7122795F54C171806050B0B0958 +74627B04044D4C014C4D4D04044D4DFEB44C01D91A2B27C278FEE18B2C194DEFA3100C03 +0806050E9B59460000010021FF8203EC033300230000011617070607062B01222E011037 +23130607060726273E013503211114163B013637363703A712331E020C070AD6303F0503 +DF0104584E781630809C01017715119C1204040101012503F3150D053D5F02652CFE9FD0 +8176422D0D33F1AB01A7FD05141D01101D1D0002001FFF7C03D60369002A003700000106 +17262321151407062736271E01363D012122073627163321353721220736271633211706 +0F011521320123350527371707211523352103D603033A3BFECA1E2B720C24215A10FEB1 +3A3A03033A3A014FA5FEB53A3B04043B3A01BC081F189F01363BFCE74801994E2D6B2001 +9F49FD2F011D1F2003FE261C2501322604010C1BEA03201F03499703201F034108159229 +0118BF0137414A2EBE8500050010FF8803DF03350016001B00230027002D000025260322 +0736271633210207161706072627060726273613161736370126273E011317031307273F +0126273716170264721E201F03033C3D01591E916EB82915A46F8AD01B25E0441A5E7815 +FD7B2A3E2C5E5929741F40953FA845523C564AD3CF011902212103FEADDBA66510265EA8 +AE5123184D02A4F9B4BBF2FCCE180251D0010115FE850193318832204C42344650000000 +000100000000E666EDAC36235F0F3CF5003F040000000000C7BE78E900000000C7BE78E9 +FF7FFED0043403DA0000000800020000000000000001000003DAFED0005C0455FF7FFE78 +043400010000000000000000000000000000000501760022000000000000000000000000 +0400000D0021001F00100000000000000000000000000040007B00D10125000000010000 +00080165002800D10012000200000001000100000040002E0006000200>]def +/CharStrings 5 dict dup begin +/.notdef 0 def +/uni51E0 5 def +/uni6C49 7 def +/uni4E2A 4 def +/uni5B57 6 def +end readonly def + +systemdict/resourcestatus known + {42 /FontType resourcestatus + {pop pop false}{true}ifelse} + {true}ifelse +{/TrueDict where{pop}{(%%[ Error: no TrueType rasterizer ]%%)= flush}ifelse +/FontType 3 def + /TrueState 271 string def + TrueDict begin sfnts save + 72 0 matrix defaultmatrix dtransform dup + mul exch dup mul add sqrt cvi 0 72 matrix + defaultmatrix dtransform dup mul exch dup + mul add sqrt cvi 3 -1 roll restore + TrueState initer end + /BuildGlyph{exch begin + CharStrings dup 2 index known + {exch}{exch pop /.notdef}ifelse + get dup xcheck + {currentdict systemdict begin begin exec end end} + {TrueDict begin /bander load cvlit exch TrueState render end} + ifelse + end}bind def + /BuildChar{ + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec + }bind def +}if + +FontName currentdict end definefont pop +end +%%EndProlog +mpldict begin +18 180 translate +576 432 0 0 clipbox +gsave +0 0 m +576 0 l +576 432 l +0 432 l +cl +1.000 setgray +fill +grestore +0.000 setgray +/DejaVuSans 27.000 selectfont +gsave + +86.4 205.2 translate +0 rotate +0.000000 0 m /T glyphshow +16.492676 0 m /h glyphshow +33.604980 0 m /e glyphshow +50.216309 0 m /r glyphshow +61.316895 0 m /e glyphshow +77.928223 0 m /space glyphshow +86.510742 0 m /a glyphshow +103.056152 0 m /r glyphshow +114.156738 0 m /e glyphshow +130.768066 0 m /space glyphshow +grestore +/WenQuanYiZenHei 27.000 selectfont +gsave + +86.4 205.2 translate +0 rotate +139.350586 0 m /uni51E0 glyphshow +166.350586 0 m /uni4E2A glyphshow +193.350586 0 m /uni6C49 glyphshow +220.350586 0 m /uni5B57 glyphshow +grestore +/DejaVuSans 27.000 selectfont +gsave + +86.4 205.2 translate +0 rotate +247.350586 0 m /space glyphshow +255.933105 0 m /i glyphshow +263.434570 0 m /n glyphshow +280.546875 0 m /space glyphshow +289.129395 0 m /b glyphshow +306.268066 0 m /e glyphshow +322.879395 0 m /t glyphshow +333.465820 0 m /w glyphshow +355.548340 0 m /e glyphshow +372.159668 0 m /e glyphshow +388.770996 0 m /n glyphshow +405.883301 0 m /exclam glyphshow +grestore + +end +showpage diff --git a/lib/matplotlib/tests/test_backend_ps.py b/lib/matplotlib/tests/test_backend_ps.py index 5737b6fddbca..52f854e97e5f 100644 --- a/lib/matplotlib/tests/test_backend_ps.py +++ b/lib/matplotlib/tests/test_backend_ps.py @@ -6,12 +6,12 @@ import pytest -from matplotlib import cbook, patheffects +from matplotlib import cbook, patheffects, font_manager as fm from matplotlib._api import MatplotlibDeprecationWarning from matplotlib.figure import Figure from matplotlib.patches import Ellipse -from matplotlib.testing.decorators import check_figures_equal, image_comparison from matplotlib.testing._markers import needs_ghostscript, needs_usetex +from matplotlib.testing.decorators import check_figures_equal, image_comparison import matplotlib as mpl import matplotlib.pyplot as plt @@ -272,3 +272,29 @@ def test_no_duplicate_definition(): if ln.startswith('/')] assert max(Counter(wds).values()) == 1 + + +@image_comparison(["multi_font_type3.eps"]) +def test_multi_font_type3(): + fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) + if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": + pytest.skip("Font may be missing") + + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + plt.rc('ps', fonttype=3) + + fig = plt.figure() + fig.text(0.15, 0.475, "There are 几个汉字 in between!") + + +@image_comparison(["multi_font_type42.eps"]) +def test_multi_font_type42(): + fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) + if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": + pytest.skip("Font may be missing") + + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + plt.rc('ps', fonttype=42) + + fig = plt.figure() + fig.text(0.15, 0.475, "There are 几个汉字 in between!") From d6133701f7fa1825d362bcae28012f3b65386c8d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 7 Aug 2022 16:37:49 -0400 Subject: [PATCH 05/10] TST: fix missing implicit import in marker code --- lib/matplotlib/testing/_markers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/matplotlib/testing/_markers.py b/lib/matplotlib/testing/_markers.py index df3ebb08cf8c..fa7885151e33 100644 --- a/lib/matplotlib/testing/_markers.py +++ b/lib/matplotlib/testing/_markers.py @@ -8,6 +8,7 @@ import pytest import matplotlib.testing +import matplotlib.testing.compare from matplotlib import _get_executable_info, ExecutableNotFoundError From 140257e3ac710450c97587f95532a84a28cc526c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 9 Aug 2022 18:41:24 -0400 Subject: [PATCH 06/10] ENH: add font fallback support to svg --- lib/matplotlib/backends/backend_svg.py | 3 +- .../test_backend_svg/multi_font_aspath.svg | 423 ++++++++++++++++++ .../test_backend_svg/multi_font_astext.svg | 35 ++ lib/matplotlib/tests/test_backend_svg.py | 31 ++ lib/matplotlib/textpath.py | 14 +- 5 files changed, 499 insertions(+), 7 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_aspath.svg create mode 100644 lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_astext.svg diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 4f30243db547..37dd638bd5fa 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -1130,7 +1130,8 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None): font_parts.append(f'{weight}') font_parts.extend([ f'{_short_float_fmt(prop.get_size())}px', - f'{prop.get_family()[0]!r}', # ensure quoting + # ensure quoting + f'{", ".join(repr(f) for f in prop.get_family())}', ]) style['font'] = ' '.join(font_parts) if prop.get_stretch() != 'normal': diff --git a/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_aspath.svg b/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_aspath.svg new file mode 100644 index 000000000000..7184700caf17 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_aspath.svg @@ -0,0 +1,423 @@ + + + + + + + + 2022-08-09T18:12:28.920123 + image/svg+xml + + + Matplotlib v3.6.0.dev2839+gb0bf8fb1de.d20220809, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_astext.svg b/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_astext.svg new file mode 100644 index 000000000000..373103f61b9f --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_backend_svg/multi_font_astext.svg @@ -0,0 +1,35 @@ + + + + + + + + 2022-08-09T18:42:37.025191 + image/svg+xml + + + Matplotlib v3.6.0.dev2840+g372782e258.d20220809, https://matplotlib.org/ + + + + + + + + + + + + + + There are 几个汉字 in between! + + + diff --git a/lib/matplotlib/tests/test_backend_svg.py b/lib/matplotlib/tests/test_backend_svg.py index 1b0ddba219cb..48e6ae68c5f9 100644 --- a/lib/matplotlib/tests/test_backend_svg.py +++ b/lib/matplotlib/tests/test_backend_svg.py @@ -1,16 +1,21 @@ import datetime from io import BytesIO +from pathlib import Path import xml.etree.ElementTree import xml.parsers.expat +import pytest + import numpy as np + import matplotlib as mpl from matplotlib.figure import Figure from matplotlib.text import Text import matplotlib.pyplot as plt from matplotlib.testing.decorators import check_figures_equal, image_comparison from matplotlib.testing._markers import needs_usetex +from matplotlib import font_manager as fm def test_visibility(): @@ -464,3 +469,29 @@ def test_svg_metadata(): values = [node.text for node in rdf.findall(f'./{CCNS}Work/{DCNS}subject/{RDFNS}Bag/{RDFNS}li')] assert values == metadata['Keywords'] + + +@image_comparison(["multi_font_aspath.svg"]) +def test_multi_font_type3(): + fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) + if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": + pytest.skip("Font may be missing") + + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + plt.rc('svg', fonttype='path') + + fig = plt.figure() + fig.text(0.15, 0.475, "There are 几个汉字 in between!") + + +@image_comparison(["multi_font_astext.svg"]) +def test_multi_font_type42(): + fp = fm.FontProperties(family=["WenQuanYi Zen Hei"]) + if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc": + pytest.skip("Font may be missing") + + fig = plt.figure() + plt.rc('svg', fonttype='none') + + plt.rc('font', family=['DejaVu Sans', 'WenQuanYi Zen Hei'], size=27) + fig.text(0.15, 0.475, "There are 几个汉字 in between!") diff --git a/lib/matplotlib/textpath.py b/lib/matplotlib/textpath.py index 3e8afde4bc66..6eb11b5f09d8 100644 --- a/lib/matplotlib/textpath.py +++ b/lib/matplotlib/textpath.py @@ -4,8 +4,10 @@ import numpy as np -from matplotlib import _api, _text_helpers, dviread, font_manager -from matplotlib.font_manager import FontProperties, get_font +from matplotlib import _api, _text_helpers, dviread +from matplotlib.font_manager import ( + FontProperties, get_font, fontManager as _fontManager +) from matplotlib.ft2font import LOAD_NO_HINTING, LOAD_TARGET_LIGHT from matplotlib.mathtext import MathTextParser from matplotlib.path import Path @@ -29,8 +31,8 @@ def _get_font(self, prop): """ Find the `FT2Font` matching font properties *prop*, with its size set. """ - fname = font_manager.findfont(prop) - font = get_font(fname) + filenames = _fontManager._find_fonts_by_props(prop) + font = get_font(filenames) font.set_size(self.FONT_SCALE, self.DPI) return font @@ -148,11 +150,11 @@ def get_glyphs_with_font(self, font, s, glyph_map=None, xpositions = [] glyph_ids = [] for item in _text_helpers.layout(s, font): - char_id = self._get_char_id(font, ord(item.char)) + char_id = self._get_char_id(item.ft_object, ord(item.char)) glyph_ids.append(char_id) xpositions.append(item.x) if char_id not in glyph_map: - glyph_map_new[char_id] = font.get_path() + glyph_map_new[char_id] = item.ft_object.get_path() ypositions = [0] * len(xpositions) sizes = [1.] * len(xpositions) From 0173a2ba258e945a58e9d13b13950f85e3f99ba8 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 7 Aug 2022 16:22:13 -0400 Subject: [PATCH 07/10] TST: extend ft2font tests to pdf, ps, and svg --- lib/matplotlib/tests/test_ft2font.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py index cd173e99283a..6a67974f9596 100644 --- a/lib/matplotlib/tests/test_ft2font.py +++ b/lib/matplotlib/tests/test_ft2font.py @@ -59,7 +59,7 @@ def test_fallback_smoke(): [("WenQuanYi Zen Hei", "wqy-zenhei.ttc"), ("Noto Sans CJK JP", "NotoSansCJK-Regular.ttc")] ) -@check_figures_equal(extensions=["png"]) +@check_figures_equal(extensions=["png", "pdf", "eps", "svg"]) def test_font_fallback_chinese(fig_test, fig_ref, family_name, file_name): fp = fm.FontProperties(family=[family_name]) if Path(fm.findfont(fp)).name != file_name: From 208b597f52f0740242218524a4df907b5e3a7f91 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 9 Aug 2022 18:55:35 -0400 Subject: [PATCH 08/10] FIX: quoting in expected svg --- .../test_text/text_as_text_opacity.svg | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_as_text_opacity.svg b/lib/matplotlib/tests/baseline_images/test_text/text_as_text_opacity.svg index 69d287e3536c..63cdeb8fbd47 100644 --- a/lib/matplotlib/tests/baseline_images/test_text/text_as_text_opacity.svg +++ b/lib/matplotlib/tests/baseline_images/test_text/text_as_text_opacity.svg @@ -10,22 +10,22 @@ - - 50% using `color` + 50% using `color` - 50% using `alpha` + 50% using `alpha` - 50% using `alpha` and 100% `color` + 50% using `alpha` and 100% `color` From 16142705f398d6f33f7ee9e7f7371518e04c18ab Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 9 Aug 2022 18:59:19 -0400 Subject: [PATCH 09/10] FIX: add quoting on font family in expected svg --- .../bold_font_output_with_none_fonttype.svg | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output_with_none_fonttype.svg b/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output_with_none_fonttype.svg index 20526ec9476e..af3d2e7a8f3c 100644 --- a/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output_with_none_fonttype.svg +++ b/lib/matplotlib/tests/baseline_images/test_backend_svg/bold_font_output_with_none_fonttype.svg @@ -10,61 +10,61 @@ - - - - - - - - @@ -73,8 +73,8 @@ L 0 -4 - @@ -82,7 +82,7 @@ L 0 4 - 0 + 0 @@ -97,7 +97,7 @@ L 0 4 - 1 + 1 @@ -112,7 +112,7 @@ L 0 4 - 2 + 2 @@ -127,7 +127,7 @@ L 0 4 - 3 + 3 @@ -142,7 +142,7 @@ L 0 4 - 4 + 4 @@ -157,7 +157,7 @@ L 0 4 - 5 + 5 @@ -172,7 +172,7 @@ L 0 4 - 6 + 6 @@ -187,7 +187,7 @@ L 0 4 - 7 + 7 @@ -202,7 +202,7 @@ L 0 4 - 8 + 8 @@ -217,19 +217,19 @@ L 0 4 - 9 + 9 - nonbold-xlabel + nonbold-xlabel - @@ -238,8 +238,8 @@ L 4 0 - @@ -247,7 +247,7 @@ L -4 0 - 0 + 0 @@ -262,7 +262,7 @@ L -4 0 - 1 + 1 @@ -277,7 +277,7 @@ L -4 0 - 2 + 2 @@ -292,7 +292,7 @@ L -4 0 - 3 + 3 @@ -307,7 +307,7 @@ L -4 0 - 4 + 4 @@ -322,7 +322,7 @@ L -4 0 - 5 + 5 @@ -337,7 +337,7 @@ L -4 0 - 6 + 6 @@ -352,7 +352,7 @@ L -4 0 - 7 + 7 @@ -367,7 +367,7 @@ L -4 0 - 8 + 8 @@ -382,15 +382,15 @@ L -4 0 - 9 + 9 - bold-ylabel + bold-ylabel - bold-title + bold-title From 5d6ca83f6b7cabcf5ccfa4a452249267a8254b74 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 10 Aug 2022 15:30:44 -0400 Subject: [PATCH 10/10] MNT: match capitalization of argument name Co-authored-by: Oscar Gustafsson --- src/ft2font_wrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 7d1b48992963..2f26d04f7323 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -589,7 +589,7 @@ static PyObject *PyFT2Font_get_fontmap(PyFT2Font *self, PyObject *args, PyObject } #endif } else { - PyErr_SetString(PyExc_TypeError, "String must be str"); + PyErr_SetString(PyExc_TypeError, "string must be str"); return NULL; } PyObject *char_to_font;