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; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy