Skip to content

Commit d7499b9

Browse files
committed
Simplify dviFontInfo layout in backend pdf.
- Use a simpler deterministic mapping of tex font names to pdf embedding names. - Only resolve the required attributes when needed (in _embedTeXFont), which avoids e.g. having to carry around and worry about attributes with different names (e.g. "encoding" vs. "encodingfile").
1 parent 2918b73 commit d7499b9

File tree

1 file changed

+50
-44
lines changed

1 file changed

+50
-44
lines changed

lib/matplotlib/backends/backend_pdf.py

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ def __init__(self, filename, metadata=None):
721721

722722
self._internal_font_seq = (Name(f'F{i}') for i in itertools.count(1))
723723
self._fontNames = {} # maps filenames to internal font names
724-
self._dviFontInfo = {} # maps dvi font names to embedding information
724+
self._dviFontInfo = {} # maps pdf names to dvifonts
725725
# differently encoded Type-1 fonts may share the same descriptor
726726
self._type1Descriptors = {}
727727
self._character_tracker = _backend_pdf_ps.CharacterTracker()
@@ -766,10 +766,31 @@ def __init__(self, filename, metadata=None):
766766
self.writeObject(self.resourceObject, resources)
767767

768768
fontNames = _api.deprecated("3.11")(property(lambda self: self._fontNames))
769-
dviFontInfo = _api.deprecated("3.11")(property(lambda self: self._dviFontInfo))
770769
type1Descriptors = _api.deprecated("3.11")(
771770
property(lambda self: self._type1Descriptors))
772771

772+
@_api.deprecated("3.11")
773+
@property
774+
def dviFontInfo(self):
775+
d = {}
776+
tex_font_map = dviread.PsfontsMap(dviread.find_tex_file('pdftex.map'))
777+
for pdfname, dvifont in self._dviFontInfo.items():
778+
psfont = tex_font_map[dvifont.texname]
779+
if psfont.filename is None:
780+
raise ValueError(
781+
"No usable font file found for {} (TeX: {}); "
782+
"the font may lack a Type-1 version"
783+
.format(psfont.psname, dvifont.texname))
784+
d[dvifont.texname] = types.SimpleNamespace(
785+
dvifont=dvifont,
786+
pdfname=pdfname,
787+
fontfile=psfont.filename,
788+
basefont=psfont.psname,
789+
encodingfile=psfont.encoding,
790+
effects=psfont.effects,
791+
)
792+
return d
793+
773794
def newPage(self, width, height):
774795
self.endStream()
775796

@@ -930,39 +951,19 @@ def fontName(self, fontprop):
930951
def dviFontName(self, dvifont):
931952
"""
932953
Given a dvi font object, return a name suitable for Op.selectfont.
933-
This registers the font information internally (in ``_dviFontInfo``) if
934-
not yet registered.
935-
"""
936-
937-
dvi_info = self._dviFontInfo.get(dvifont.texname)
938-
if dvi_info is not None:
939-
return dvi_info.pdfname
940954
941-
tex_font_map = dviread.PsfontsMap(dviread.find_tex_file('pdftex.map'))
942-
psfont = tex_font_map[dvifont.texname]
943-
if psfont.filename is None:
944-
raise ValueError(
945-
"No usable font file found for {} (TeX: {}); "
946-
"the font may lack a Type-1 version"
947-
.format(psfont.psname, dvifont.texname))
948-
949-
pdfname = next(self._internal_font_seq)
955+
Register the font internally (in ``_dviFontInfo``) if not yet registered.
956+
"""
957+
pdfname = Name(f"F-{dvifont.texname.decode('ascii')}")
950958
_log.debug('Assigning font %s = %s (dvi)', pdfname, dvifont.texname)
951-
self._dviFontInfo[dvifont.texname] = types.SimpleNamespace(
952-
dvifont=dvifont,
953-
pdfname=pdfname,
954-
fontfile=psfont.filename,
955-
basefont=psfont.psname,
956-
encodingfile=psfont.encoding,
957-
effects=psfont.effects)
958-
return pdfname
959+
self._dviFontInfo[pdfname] = dvifont
960+
return Name(pdfname)
959961

960962
def writeFonts(self):
961963
fonts = {}
962-
for dviname, info in sorted(self._dviFontInfo.items()):
963-
Fx = info.pdfname
964-
_log.debug('Embedding Type-1 font %s from dvi.', dviname)
965-
fonts[Fx] = self._embedTeXFont(info)
964+
for pdfname, dvifont in sorted(self._dviFontInfo.items()):
965+
_log.debug('Embedding Type-1 font %s from dvi.', dvifont.texname)
966+
fonts[pdfname] = self._embedTeXFont(dvifont)
966967
for filename in sorted(self._fontNames):
967968
Fx = self._fontNames[filename]
968969
_log.debug('Embedding font %s.', filename)
@@ -990,13 +991,18 @@ def _write_afm_font(self, filename):
990991
self.writeObject(fontdictObject, fontdict)
991992
return fontdictObject
992993

993-
def _embedTeXFont(self, fontinfo):
994-
_log.debug('Embedding TeX font %s - fontinfo=%s',
995-
fontinfo.dvifont.texname, fontinfo.__dict__)
994+
def _embedTeXFont(self, dvifont):
995+
tex_font_map = dviread.PsfontsMap(dviread.find_tex_file('pdftex.map'))
996+
psfont = tex_font_map[dvifont.texname]
997+
if psfont.filename is None:
998+
raise ValueError(
999+
"No usable font file found for {} (TeX: {}); "
1000+
"the font may lack a Type-1 version"
1001+
.format(psfont.psname, dvifont.texname))
9961002

9971003
# Widths
9981004
widthsObject = self.reserveObject('font widths')
999-
tfm = fontinfo.dvifont._tfm
1005+
tfm = dvifont._tfm
10001006
# convert from TeX's 12.20 representation to 1/1000 text space units.
10011007
widths = [(1000 * metrics.tex_width) >> 20
10021008
if (metrics := tfm.get_metrics(char)) else 0
@@ -1014,28 +1020,28 @@ def _embedTeXFont(self, fontinfo):
10141020
}
10151021

10161022
# Encoding (if needed)
1017-
if fontinfo.encodingfile is not None:
1023+
if psfont.encoding is not None:
10181024
fontdict['Encoding'] = {
10191025
'Type': Name('Encoding'),
10201026
'Differences': [
1021-
0, *map(Name, dviread._parse_enc(fontinfo.encodingfile))],
1027+
0, *map(Name, dviread._parse_enc(psfont.encoding))],
10221028
}
10231029

10241030
# We have a font file to embed - read it in and apply any effects
1025-
t1font = _type1font.Type1Font(fontinfo.fontfile)
1026-
if fontinfo.effects:
1027-
t1font = t1font.transform(fontinfo.effects)
1031+
t1font = _type1font.Type1Font(psfont.filename)
1032+
if psfont.effects:
1033+
t1font = t1font.transform(psfont.effects)
10281034
fontdict['BaseFont'] = Name(t1font.prop['FontName'])
10291035

10301036
# Font descriptors may be shared between differently encoded
10311037
# Type-1 fonts, so only create a new descriptor if there is no
10321038
# existing descriptor for this font.
1033-
effects = (fontinfo.effects.get('slant', 0.0),
1034-
fontinfo.effects.get('extend', 1.0))
1035-
fontdesc = self._type1Descriptors.get((fontinfo.fontfile, effects))
1039+
effects = (psfont.effects.get('slant', 0.0),
1040+
psfont.effects.get('extend', 1.0))
1041+
fontdesc = self._type1Descriptors.get((psfont.filename, effects))
10361042
if fontdesc is None:
1037-
fontdesc = self.createType1Descriptor(t1font)
1038-
self._type1Descriptors[(fontinfo.fontfile, effects)] = fontdesc
1043+
fontdesc = self._type1Descriptors[psfont.filename, effects] = \
1044+
self.createType1Descriptor(t1font)
10391045
fontdict['FontDescriptor'] = fontdesc
10401046

10411047
self.writeObject(fontdictObject, fontdict)

0 commit comments

Comments
 (0)