Skip to content

Commit 2cf627b

Browse files
committed
pdf/ps: Support any font in a collection
1 parent ba80e42 commit 2cf627b

File tree

11 files changed

+3071
-30
lines changed

11 files changed

+3071
-30
lines changed

lib/matplotlib/backends/_backend_pdf_ps.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def get_glyphs_subset(fontfile, characters):
2828
2929
Parameters
3030
----------
31-
fontfile : str
31+
fontfile : FontPath
3232
Path to the font file
3333
characters : str
3434
Continuous set of characters to include in subset
@@ -66,8 +66,7 @@ def get_glyphs_subset(fontfile, characters):
6666
'xref', # The cross-reference table (some Apple font tooling information).
6767
]
6868
# if fontfile is a ttc, specify font number
69-
if fontfile.endswith(".ttc"):
70-
options.font_number = 0
69+
options.font_number = fontfile.face_index
7170

7271
font = subset.load_font(fontfile, options)
7372
subsetter = subset.Subsetter(options=options)
@@ -110,11 +109,13 @@ def track(self, font, s):
110109
"""Record that string *s* is being typeset using font *font*."""
111110
char_to_font = font._get_fontmap(s)
112111
for _c, _f in char_to_font.items():
113-
self.used.setdefault(_f.fname, set()).add(ord(_c))
112+
font_path = font_manager.FontPath(_f.fname, _f.face_index)
113+
self.used.setdefault(font_path, set()).add(ord(_c))
114114

115115
def track_glyph(self, font, glyph):
116116
"""Record that codepoint *glyph* is being typeset using font *font*."""
117-
self.used.setdefault(font.fname, set()).add(glyph)
117+
font_path = font_manager.FontPath(font.fname, font.face_index)
118+
self.used.setdefault(font_path, set()).add(glyph)
118119

119120

120121
class RendererPDFPSBase(RendererBase):

lib/matplotlib/backends/backend_pdf.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
RendererBase)
3434
from matplotlib.backends.backend_mixed import MixedModeRenderer
3535
from matplotlib.figure import Figure
36-
from matplotlib.font_manager import get_font, fontManager as _fontManager
36+
from matplotlib.font_manager import FontPath, get_font, fontManager as _fontManager
3737
from matplotlib._afm import AFM
3838
from matplotlib.ft2font import FT2Font, FaceFlags, Kerning, LoadFlags, StyleFlags
3939
from matplotlib.transforms import Affine2D, BboxBase
@@ -910,8 +910,10 @@ def fontName(self, fontprop):
910910
as the filename of the font.
911911
"""
912912

913-
if isinstance(fontprop, str):
913+
if isinstance(fontprop, FontPath):
914914
filenames = [fontprop]
915+
elif isinstance(fontprop, str):
916+
filenames = [FontPath(fontprop, 0)]
915917
elif mpl.rcParams['pdf.use14corefonts']:
916918
filenames = _fontManager._find_fonts_by_props(
917919
fontprop, fontext='afm', directory=RendererPdf._afm_font_dir
@@ -950,9 +952,8 @@ def writeFonts(self):
950952
for pdfname, dvifont in sorted(self._dviFontInfo.items()):
951953
_log.debug('Embedding Type-1 font %s from dvi.', dvifont.texname)
952954
fonts[pdfname] = self._embedTeXFont(dvifont)
953-
for filename in sorted(self._fontNames):
954-
Fx = self._fontNames[filename]
955-
_log.debug('Embedding font %s.', filename)
955+
for filename, Fx in sorted(self._fontNames.items()):
956+
_log.debug('Embedding font %r.', filename)
956957
if filename.endswith('.afm'):
957958
# from pdf.use14corefonts
958959
_log.debug('Writing AFM font.')
@@ -1004,7 +1005,8 @@ def _embedTeXFont(self, dvifont):
10041005

10051006
# Reduce the font to only the glyphs used in the document, get the encoding
10061007
# for that subset, and compute various properties based on the encoding.
1007-
chars = frozenset(self._character_tracker.used[dvifont.fname])
1008+
font_path = FontPath(dvifont.fname, dvifont.face_index)
1009+
chars = frozenset(self._character_tracker.used[font_path])
10081010
t1font = t1font.subset(chars, self._get_subset_prefix(chars))
10091011
fontdict['BaseFont'] = Name(t1font.prop['FontName'])
10101012
# createType1Descriptor writes the font data as a side effect
@@ -1113,6 +1115,7 @@ def _get_xobject_glyph_name(self, filename, glyph_name):
11131115
return "-".join([
11141116
Fx.name.decode(),
11151117
os.path.splitext(os.path.basename(filename))[0],
1118+
str(filename.face_index),
11161119
glyph_name])
11171120

11181121
_identityToUnicodeCMap = b"""/CIDInit /ProcSet findresource begin
@@ -1270,11 +1273,11 @@ def embedTTFType42(font, characters, descriptor):
12701273
toUnicodeMapObject = self.reserveObject('ToUnicode map')
12711274

12721275
subset_str = "".join(chr(c) for c in characters)
1273-
_log.debug("SUBSET %s characters: %s", filename, subset_str)
1276+
_log.debug("SUBSET %r characters: %s", filename, subset_str)
12741277
with _backend_pdf_ps.get_glyphs_subset(filename, subset_str) as subset:
12751278
fontdata = _backend_pdf_ps.font_as_file(subset)
12761279
_log.debug(
1277-
"SUBSET %s %d -> %d", filename,
1280+
"SUBSET %r %d -> %d", filename,
12781281
os.stat(filename).st_size, fontdata.getbuffer().nbytes
12791282
)
12801283

@@ -2218,18 +2221,18 @@ def draw_mathtext(self, gc, x, y, s, prop, angle):
22182221
self.file.output(Op.begin_text)
22192222
for font, fontsize, num, ox, oy in glyphs:
22202223
self.file._character_tracker.track_glyph(font, num)
2221-
fontname = font.fname
2224+
font_path = FontPath(font.fname, font.face_index)
22222225
if not _font_supports_glyph(fonttype, num):
22232226
# Unsupported chars (i.e. multibyte in Type 3 or beyond BMP in
22242227
# Type 42) must be emitted separately (below).
22252228
unsupported_chars.append((font, fontsize, ox, oy, num))
22262229
else:
22272230
self._setup_textpos(ox, oy, 0, oldx, oldy)
22282231
oldx, oldy = ox, oy
2229-
if (fontname, fontsize) != prev_font:
2230-
self.file.output(self.file.fontName(fontname), fontsize,
2232+
if (font_path, fontsize) != prev_font:
2233+
self.file.output(self.file.fontName(font_path), fontsize,
22312234
Op.selectfont)
2232-
prev_font = fontname, fontsize
2235+
prev_font = font_path, fontsize
22332236
self.file.output(self.encode_string(chr(num), fonttype),
22342237
Op.show)
22352238
self.file.output(Op.end_text)
@@ -2413,7 +2416,8 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
24132416
self.file.output(Op.begin_text)
24142417
prev_start_x = 0
24152418
for ft_object, start_x, kerns_or_chars in singlebyte_chunks:
2416-
ft_name = self.file.fontName(ft_object.fname)
2419+
font_path = FontPath(ft_object.fname, ft_object.face_index)
2420+
ft_name = self.file.fontName(font_path)
24172421
self.file.output(ft_name, fontsize, Op.selectfont)
24182422
self._setup_textpos(start_x, 0, 0, prev_start_x, 0, 0)
24192423
self.file.output(
@@ -2435,7 +2439,8 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
24352439
def _draw_xobject_glyph(self, font, fontsize, glyph_idx, x, y):
24362440
"""Draw a multibyte character from a Type 3 font as an XObject."""
24372441
glyph_name = font.get_glyph_name(glyph_idx)
2438-
name = self.file._get_xobject_glyph_name(font.fname, glyph_name)
2442+
name = self.file._get_xobject_glyph_name(FontPath(font.fname, font.face_index),
2443+
glyph_name)
24392444
self.file.output(
24402445
Op.gsave,
24412446
0.001 * fontsize, 0, 0, 0.001 * fontsize, x, y, Op.concat_matrix,

lib/matplotlib/backends/backend_ps.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def _font_to_ps_type3(font_path, chars):
9494
9595
Parameters
9696
----------
97-
font_path : path-like
97+
font_path : FontPath
9898
Path to the font to be subsetted.
9999
chars : str
100100
The characters to include in the subsetted font.
@@ -175,22 +175,18 @@ def _font_to_ps_type42(font_path, chars, fh):
175175
176176
Parameters
177177
----------
178-
font_path : path-like
178+
font_path : FontPath
179179
Path to the font to be subsetted.
180180
chars : str
181181
The characters to include in the subsetted font.
182182
fh : file-like
183183
Where to write the font.
184184
"""
185185
subset_str = ''.join(chr(c) for c in chars)
186-
_log.debug("SUBSET %s characters: %s", font_path, subset_str)
186+
_log.debug("SUBSET %r characters: %s", font_path, subset_str)
187187
try:
188-
kw = {}
189-
# fix this once we support loading more fonts from a collection
190-
# https://github.com/matplotlib/matplotlib/issues/3135#issuecomment-571085541
191-
if font_path.endswith('.ttc'):
192-
kw['fontNumber'] = 0
193-
with (fontTools.ttLib.TTFont(font_path, **kw) as font,
188+
with (fontTools.ttLib.TTFont(font_path.path,
189+
fontNumber=font_path.face_index) as font,
194190
_backend_pdf_ps.get_glyphs_subset(font_path, subset_str) as subset):
195191
fontdata = _backend_pdf_ps.font_as_file(subset).getvalue()
196192
_log.debug(

lib/matplotlib/dviread.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,10 @@ def fname(self):
610610
"""A fake filename"""
611611
return self.texname.decode('latin-1')
612612

613+
@property
614+
def face_index(self): # For compatibility with FT2Font.
615+
return 0
616+
613617
def _get_fontmap(self, string):
614618
"""Get the mapping from characters to the font that includes them.
615619

lib/matplotlib/dviread.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class DviFont:
6868
def widths(self) -> list[int]: ...
6969
@property
7070
def fname(self) -> str: ...
71+
@property
72+
def face_index(self) -> int: ...
7173

7274
class Vf(Dvi):
7375
def __init__(self, filename: str | os.PathLike) -> None: ...
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)