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 65d38eb5a542..4ab23915e9e2 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*."""
@@ -135,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/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/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/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
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 000000000000..a148a7d571b1
Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type3.pdf differ
diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type42.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type42.pdf
new file mode 100644
index 000000000000..e33f8d803b12
Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_backend_pdf/multi_font_type42.pdf differ
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/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
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 @@
+
+
+
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 @@
+
+
+
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`
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!")
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!")
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/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py
index eee47f5b03f8..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:
@@ -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/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)
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..2f26d04f7323 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__},