Skip to content

Give a better error message on missing PostScript fonts #6428

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions lib/matplotlib/backends/backend_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -552,9 +552,10 @@ def newTextnote(self, text, positionRect=[-100, -100, 0, 0]):
self.writeObject(annotObject, theNote)
self.pageAnnotations.append(annotObject)

def close(self):
def finalize(self):
"Write out the various deferred objects and the pdf end matter."

self.endStream()
# Write out the various deferred objects
self.writeFonts()
self.writeObject(self.alphaStateObject,
dict([(val[0], val[1])
Expand Down Expand Up @@ -582,12 +583,16 @@ def close(self):
# Finalize the file
self.writeXref()
self.writeTrailer()

def close(self):
"Flush all buffers and free all resources."

self.endStream()
if self.passed_in_file_object:
self.fh.flush()
elif self.original_file_like is not None:
self.original_file_like.write(self.fh.getvalue())
self.fh.close()
else:
if self.original_file_like is not None:
self.original_file_like.write(self.fh.getvalue())
self.fh.close()

def write(self, data):
Expand Down Expand Up @@ -1871,6 +1876,12 @@ def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None):
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,
Expand Down Expand Up @@ -2438,6 +2449,7 @@ def close(self):
Finalize this object, making the underlying file a complete
PDF file.
"""
self._file.finalize()
self._file.close()
if (self.get_pagecount() == 0 and not self.keep_empty and
not self._file.passed_in_file_object):
Expand Down Expand Up @@ -2534,6 +2546,7 @@ def print_pdf(self, filename, **kwargs):
bbox_inches_restore=_bbox_inches_restore)
self.figure.draw(renderer)
renderer.finalize()
file.finalize()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this get added here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file.close() call in the finally section no longer does the finalization. The finalization is the part that cannot be reasonably done if something has raised an exception (in this case, the missing font) so it belongs in the try section.

finally:
if isinstance(filename, PdfPages): # finish off this page
file.endStream()
Expand Down
30 changes: 29 additions & 1 deletion lib/matplotlib/tests/test_backend_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@

import io
import os
import tempfile

try:
from unittest.mock import patch
except ImportError:
from mock import patch
from nose.tools import raises

import numpy as np
from matplotlib import cm, rcParams
from matplotlib import checkdep_tex, cm, rcParams
from matplotlib.backends.backend_pdf import PdfPages
from matplotlib import pyplot as plt
from matplotlib.testing.decorators import (image_comparison, knownfailureif,
cleanup)
from matplotlib import dviread

if 'TRAVIS' not in os.environ:
@image_comparison(baseline_images=['pdf_use14corefonts'],
Expand All @@ -29,6 +37,10 @@ def test_use14corefonts():
and containing some French characters and the euro symbol:
"Merci pépé pour les 10 €"'''

needs_tex = knownfailureif(
not checkdep_tex(),
"This test needs a TeX installation")


@cleanup
def test_type42():
Expand Down Expand Up @@ -132,3 +144,19 @@ def test_grayscale_alpha():
ax.imshow(dd, interpolation='none', cmap='gray_r')
ax.set_xticks([])
ax.set_yticks([])


@cleanup
@needs_tex
@raises(ValueError)
@patch('matplotlib.dviread.PsfontsMap.__getitem__')
def test_missing_psfont(mock):
"""An error is raised if a TeX font lacks a Type-1 equivalent"""
psfont = dviread.PsFont(texname='texfont', psname='Some Font',
effects=None, encoding=None, filename=None)
mock.configure_mock(return_value=psfont)
rcParams['text.usetex'] = True
fig, ax = plt.subplots()
ax.text(0.5, 0.5, 'hello')
with tempfile.TemporaryFile() as tmpfile:
fig.savefig(tmpfile, format='pdf')
27 changes: 27 additions & 0 deletions lib/matplotlib/tests/test_backend_svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,22 @@

import numpy as np
from io import BytesIO
import os
import tempfile
import xml.parsers.expat

try:
from unittest.mock import patch
except ImportError:
from mock import patch
from nose.tools import raises

import matplotlib.pyplot as plt
from matplotlib.testing.decorators import cleanup
from matplotlib.testing.decorators import image_comparison, knownfailureif
import matplotlib
from matplotlib import dviread


needs_tex = knownfailureif(
not matplotlib.checkdep_tex(),
Expand Down Expand Up @@ -183,6 +193,23 @@ def test_determinism_tex():
_test_determinism('determinism_tex.svg', usetex=True)


@cleanup
@needs_tex
@raises(ValueError)
@patch('matplotlib.dviread.PsfontsMap.__getitem__')
def test_missing_psfont(mock):
"""An error is raised if a TeX font lacks a Type-1 equivalent"""
from matplotlib import rc
psfont = dviread.PsFont(texname='texfont', psname='Some Font',
effects=None, encoding=None, filename=None)
mock.configure_mock(return_value=psfont)
rc('text', usetex=True)
fig, ax = plt.subplots()
ax.text(0.5, 0.5, 'hello')
with tempfile.NamedTemporaryFile(suffix='.svg') as tmpfile:
fig.savefig(tmpfile.name)


if __name__ == '__main__':
import nose
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)
6 changes: 6 additions & 0 deletions lib/matplotlib/textpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,12 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,
font_bunch = self.tex_font_map[dvifont.texname]

if font_and_encoding is None:
if font_bunch.filename is None:
raise ValueError(
("No usable font file found for %s (%s). "
"The font may lack a Type-1 version.")
% (font_bunch.psname, dvifont.texname))

font = get_font(font_bunch.filename)

for charmap_name, charmap_code in [("ADOBE_CUSTOM",
Expand Down