Handle dvi font names as ASCII bytestrings #6977

Merged
merged 24 commits into from Feb 26, 2017
Commits
+306 −204
Split
@@ -0,0 +1,8 @@
+Changes to PDF backend methods
+``````````````````````````````
+
+The methods `embedTeXFont` and `tex_font_mapping` of
+`matplotlib.backend_pdf.PdfFile` have been removed.
+It is unlikely that external users would have called
+these methods, which are related to the font system
+internal to the PDF backend.
View
@@ -8,4 +8,9 @@ dviread
.. automodule:: matplotlib.dviread
:members:
:undoc-members:
+ :exclude-members: Dvi
+ :show-inheritance:
+
+.. autoclass:: matplotlib.dviread.Dvi
+ :members: __iter__,close
:show-inheritance:
@@ -493,7 +493,8 @@ def __init__(self, filename, metadata=None):
self.fontNames = {} # maps filenames to internal font names
self.nextFont = 1 # next free internal font name
- self.dviFontInfo = {} # information on dvi fonts
+ self.dviFontInfo = {} # maps dvi font names to embedding information
+ self._texFontMap = None # maps TeX font names to PostScript fonts
# differently encoded Type-1 fonts may share the same descriptor
self.type1Descriptors = {}
self.used_characters = {}
@@ -637,10 +638,10 @@ def fontName(self, fontprop):
"""
Select a font based on fontprop and return a name suitable for
Op.selectfont. If fontprop is a string, it will be interpreted
- as the filename (or dvi name) of the font.
+ as the filename of the font.
"""
- if is_string_like(fontprop):
+ if isinstance(fontprop, six.string_types):
filename = fontprop
elif rcParams['pdf.use14corefonts']:
filename = findfont(
@@ -662,21 +663,62 @@ def fontName(self, fontprop):
return Fx
+ @property
+ def texFontMap(self):
+ # lazy-load texFontMap, it takes a while to parse
+ # and usetex is a relatively rare use case
+ if self._texFontMap is None:
+ self._texFontMap = dviread.PsfontsMap(
+ dviread.find_tex_file('pdftex.map'))
+
+ return self._texFontMap
+
+ def dviFontName(self, dvifont):
+ """
+ Given a dvi font object, return a name suitable for Op.selectfont.
+ This registers the font information in self.dviFontInfo if not yet
+ registered.
+ """
+
+ dvi_info = self.dviFontInfo.get(dvifont.texname)
+ if dvi_info is not None:
+ return dvi_info.pdfname
+
+ psfont = self.texFontMap[dvifont.texname]
+ if psfont.filename is None:
+ raise ValueError(
+ ("No usable font file found for {0} (TeX: {1}). "
+ "The font may lack a Type-1 version.")
+ .format(psfont.psname, dvifont.texname))
+
+ pdfname = Name('F%d' % self.nextFont)
+ self.nextFont += 1
+ matplotlib.verbose.report(
+ 'Assigning font {0} = {1} (dvi)'.format(pdfname, dvifont.texname),
+ 'debug')
+ self.dviFontInfo[dvifont.texname] = Bunch(
+ dvifont=dvifont,
+ pdfname=pdfname,
+ fontfile=psfont.filename,
+ basefont=psfont.psname,
+ encodingfile=psfont.encoding,
+ effects=psfont.effects)
+ return pdfname
+
def writeFonts(self):
fonts = {}
+ for dviname, info in sorted(self.dviFontInfo.items()):
+ Fx = info.pdfname
+ matplotlib.verbose.report('Embedding Type-1 font %s from dvi'
+ % dviname, 'debug')
+ fonts[Fx] = self._embedTeXFont(info)
for filename in sorted(self.fontNames):
Fx = self.fontNames[filename]
matplotlib.verbose.report('Embedding font %s' % filename, 'debug')
if filename.endswith('.afm'):
# from pdf.use14corefonts
matplotlib.verbose.report('Writing AFM font', 'debug')
fonts[Fx] = self._write_afm_font(filename)
- elif filename in self.dviFontInfo:
- # a Type 1 font from a dvi file;
- # the filename is really the TeX name
- matplotlib.verbose.report('Writing Type-1 font', 'debug')
- fonts[Fx] = self.embedTeXFont(filename,
- self.dviFontInfo[filename])
else:
# a normal TrueType font
matplotlib.verbose.report('Writing TrueType font', 'debug')
@@ -698,9 +740,9 @@ def _write_afm_font(self, filename):
self.writeObject(fontdictObject, fontdict)
return fontdictObject
- def embedTeXFont(self, texname, fontinfo):
@tacaswell

tacaswell Feb 11, 2017

Owner

Do we care about this API change?

@jkseppan

jkseppan Feb 12, 2017

Member

I don't think that's something that an external user would ever have called. Let's mark the function private with an underscore and document the change.

- msg = ('Embedding TeX font ' + texname + ' - fontinfo=' +
- repr(fontinfo.__dict__))
+ def _embedTeXFont(self, fontinfo):
+ msg = ('Embedding TeX font {0} - fontinfo={1}'
+ .format(fontinfo.dvifont.texname, fontinfo.__dict__))
matplotlib.verbose.report(msg, 'debug')
# Widths
@@ -1570,7 +1612,6 @@ def __init__(self, file, image_dpi, height, width):
self.gc = self.new_gc()
self.mathtext_parser = MathTextParser("Pdf")
self.image_dpi = image_dpi
- self.tex_font_map = None
def finalize(self):
self.file.output(*self.gc.finalize())
@@ -1596,12 +1637,6 @@ def check_gc(self, gc, fillcolor=None):
gc._fillcolor = orig_fill
gc._effective_alphas = orig_alphas
- def tex_font_mapping(self, texfont):
@tacaswell

tacaswell Feb 11, 2017

Owner

do we care about this API change?

@jkseppan

jkseppan Feb 12, 2017

Member

Likewise.

- if self.tex_font_map is None:
- self.tex_font_map = \
- dviread.PsfontsMap(dviread.find_tex_file('pdftex.map'))
- return self.tex_font_map[texfont]
-
def track_characters(self, font, s):
"""Keeps track of which characters are required from
each font."""
@@ -1894,21 +1929,7 @@ def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None):
oldfont, seq = None, []
for x1, y1, dvifont, glyph, width in page.text:
if dvifont != oldfont:
- pdfname = self.file.fontName(dvifont.texname)
- if dvifont.texname not in self.file.dviFontInfo:
- psfont = self.tex_font_mapping(dvifont.texname)
- if psfont.filename is None:
- self.file.broken = True
- raise ValueError(
- ("No usable font file found for %s (%s). "
- "The font may lack a Type-1 version.")
- % (psfont.psname, dvifont.texname))
- self.file.dviFontInfo[dvifont.texname] = Bunch(
- fontfile=psfont.filename,
- basefont=psfont.psname,
- encodingfile=psfont.encoding,
- effects=psfont.effects,
- dvifont=dvifont)
+ pdfname = self.file.dviFontName(dvifont)
seq += [['font', pdfname, dvifont.size]]
oldfont = dvifont
# We need to convert the glyph numbers to bytes, and the easiest
Oops, something went wrong.