Skip to content
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

savefig to vector graphics with LaTeX fails with certain characters #10272

Closed
miromarszal opened this issue Jan 20, 2018 · 16 comments
Closed

savefig to vector graphics with LaTeX fails with certain characters #10272

miromarszal opened this issue Jan 20, 2018 · 16 comments

Comments

@miromarszal
Copy link

miromarszal commented Jan 20, 2018

Saving vector graphic figures with certain characters in LaTeX will fail. Here I tried to use the \textmu character. Could it be related to #8068?

Code for reproduction

Here I'm saving to PostScript. It gives an error that I think is the most informative and points to some problems with ghostscript. Outputs for SVG and PDF below.

import matplotlib
matplotlib.rcParams['text.usetex'] = True
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_ylabel(r'\textmu')
plt.savefig('fig.ps')

Output - PS

Traceback (most recent call last):
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backends/backend_ps.py", line 1520, in gs_distill
    report = subprocess.check_output(command, stderr=subprocess.STDOUT)
  File "/usr/lib/python3.6/subprocess.py", line 336, in check_output
    **kwargs).stdout
  File "/usr/lib/python3.6/subprocess.py", line 418, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['gs', '-dBATCH', '-dNOPAUSE', '-r6000', '-sDEVICE=ps2write', '-sPAPERSIZE=letter', '-sOutputFile=/tmp/tmpt8w23igm.ps', '/tmp/tmpt8w23igm']' returned non-zero exit status 1.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "Untitled.py", line 8, in <module>
    plt.savefig('fig.ps')
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/pyplot.py", line 701, in savefig
    res = fig.savefig(*args, **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/figure.py", line 1834, in savefig
    self.canvas.print_figure(fname, **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2267, in print_figure
    **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backends/backend_ps.py", line 910, in print_ps
    return self._print_ps(outfile, 'ps', *args, **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backends/backend_ps.py", line 937, in _print_ps
    **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backends/backend_ps.py", line 1359, in _print_figure_tex
    rotated=psfrag_rotated)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backends/backend_ps.py", line 1525, in gs_distill
    '\n\n' % exc.output.decode("utf-8")))
RuntimeError: ghostscript was not able to process your image.
Here is the full report generated by ghostscript:
GPL Ghostscript 9.22 (2017-10-04)
Copyright (C) 2017 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
Error: /undefined in --get--
Operand stack:
   --nostringval--   --dict:9/18(ro)(L)--   112   --dict:13/13(L)--   --dict:13/13(L)--   base
Execution stack:
   %interp_exit   .runexec2   --nostringval--   --nostringval--   --nostringval--   2   %stopped_push   --nostringval--   --nostringval--   --nostringval--   false   1   %stopped_push   2015   1   3   %oparray_pop   2014   1   3   %oparray_pop   1998   1   3   %oparray_pop   1884   1   3   %oparray_pop   --nostringval--   %errorexec_pop   .runexec2   --nostringval--   --nostringval--   --nostringval--   2   %stopped_push   --nostringval--   --nostringval--   --nostringval--   %finish_stringwidth   --nostringval--   --nostringval--   14   8   0   --nostringval--   (pdf_text_enum_t)   %op_show_continue   --nostringval--
Dictionary stack:
   --dict:986/1684(ro)(G)--   --dict:1/20(G)--   --dict:82/200(L)--   --dict:5/6(ro)(L)--   --dict:180/300(L)--   --dict:44/200(L)--   --dict:8/17(L)--   --dict:51/90(L)--
Current allocation mode is local
Last OS error: No such file or directory
Current file position is 58919
GPL Ghostscript 9.22: Unrecoverable error, exit code 1

Output - SVG, PDF

Traceback (most recent call last):
  File "Untitled.py", line 8, in <module>
    plt.savefig('fig.svg')
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/pyplot.py", line 701, in savefig
    res = fig.savefig(*args, **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/figure.py", line 1834, in savefig
    self.canvas.print_figure(fname, **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2267, in print_figure
    **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backends/backend_svg.py", line 1193, in print_svg
    return self._print_svg(filename, svgwriter, **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backends/backend_svg.py", line 1248, in _print_svg
    self.figure.draw(renderer)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/figure.py", line 1299, in draw
    renderer, self, artists, self.suppressComposite)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/axes/_base.py", line 2437, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/axis.py", line 1147, in draw
    self.label.draw(renderer)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/text.py", line 762, in draw
    mtext=mtext)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backends/backend_svg.py", line 1150, in draw_tex
    self._draw_text_as_path(gc, x, y, s, prop, angle, ismath="TeX")
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/backends/backend_svg.py", line 950, in _draw_text_as_path
    return_new_glyphs_only=True)
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/textpath.py", line 335, in get_glyphs_tex
    font_bunch = self.tex_font_map[dvifont.texname]
  File "/home/mirek/.local/lib/python3.6/site-packages/matplotlib/dviread.py", line 850, in __getitem__
    result = self._font[texname]
KeyError: b'tcss3583'

For PDF it's nearly identical; the last line reads:

KeyError: b'tcss1000'

In case of PS and SVG, no file is produced. In case of PDF, a corrupted file results. Saving to raster formats and inline previews in jupyter notebook work fine.

Matplotlib version

  • Operating system: Debian 10
  • Matplotlib version: 2.1.1
  • Python version: 3.6.4

Matplotlib from pip. Both Matplotlib and LaTeX are installed in userspace

@jklymak
Copy link
Member

jklymak commented Jan 20, 2018

Oddly works fine for me on macosx both master and 2.1.1... @ImportanceOfBeingErnest did you duplicate this error?

@ImportanceOfBeingErnest
Copy link
Member

ImportanceOfBeingErnest commented Jan 20, 2018

Yes I can replicate this using matplotlib 2.1 on python 2.7, windows 8.1. Using latex from miktex 2.9.
Saving to any vector format does not work, throwing the same error as reported in the post above. Saving to png (plt.savefig('fig.png')) works as expected, also showing (plt.show()) works fine.

This seems to be somehow related to the \textmu or some special symbols. Other symbols, like e.g. '\rightarrow' work as expected.

Edit: Here is a list of symbols I tried.
Works:

  • \rightarrow
  • \leftarrow
  • \AE
  • \{
  • \alpha
  • \triangle

Does not work (fails with same error as above):

  • \textmu
  • \copyright
  • \textcopyright
  • \P (paragraph symbol)
  • \textparagraph
  • \pounds
  • \textdagger
  • \$
  • \textdollar

@miromarszal
Copy link
Author

Also the \textwon symbol mentioned in #8068 is causing the error, which fits to the many \text* symbols in your list.

@ImportanceOfBeingErnest
Copy link
Member

The issue seems related/the same, but the outcome for me is different, since also for svg those symbols cause an error, not simply a warning as reported in #8068.

@miromarszal
Copy link
Author

Is there a way to isolate the file that is being fed to ghostscript causing it to fail? I can see that normally it's immediately destroyed after the exception occurs.

@jklymak
Copy link
Member

jklymak commented Jan 20, 2018

Try adding logging to the end of the imports. I also got rid of the ticks so I only had three dvi files...

import logging
logging.basicConfig(level=logging.DEBUG)

...
ax.set_xticks([])
ax.set_yticks([])

Your dvi files should be cached...

@miromarszal
Copy link
Author

Those DVI files do persist. One contains the symbol and the other says "lp", both very tiny.

I'm interested in this line of the error message:

subprocess.CalledProcessError: Command '['gs', '-dBATCH', '-dNOPAUSE', '-r6000', '-sDEVICE=ps2write', '-sPAPERSIZE=letter', '-sOutputFile=/tmp/tmpt8w23igm.ps', '/tmp/tmpt8w23igm']' returned non-zero exit status 1.

The output file is created and it's corrupted, while the input file is not there, presumably being deleted right after the exception is caught. It would be nice to somehow grab it and see what bothers ghostscript.

@anntzer
Copy link
Contributor

anntzer commented Jan 20, 2018

Looks like you need to install the miktex/texlive "cm-super" package. Solves the issue for ps/pdf but not svg.

todo: 1) investigate whether this is really necessary, 2) fix the svg situation, 3) update the docs accordingly.

@miromarszal
Copy link
Author

I have just installed the package but the problem persists.

@jklymak
Copy link
Member

jklymak commented Jan 21, 2018

@tacaswell was saying something about gs on the gitter channel. Maybe related?

@miromarszal
Copy link
Author

miromarszal commented Jan 21, 2018

I managed to capture the temporary PS file that crashes gs. You can find the file here: tmpfile_bad. I also did the same for a good input (no problematic characters): tmpfile_good.

What someone could now do is:

  • try opening those files on their system
  • try processing them with gs
  • diff the two and try to figure out what is the issue

I have no idea about ghostscript so that's about all I could do.

EDIT: Here's the diff of the two. Apart from the obvious difference in positions there's something going on with fonts.

@anntzer
Copy link
Contributor

anntzer commented Mar 26, 2020

I think the pdf/ps part has been fixed by requiring cm-super, and the svg part is mostly fixed, though some issues remain but are tracked by #8068 / #14159. Closing, but feel free to reopen with new info. (Note also that the ps backend is currently independently broken per #16898.)

@anntzer anntzer closed this as completed Mar 26, 2020
@oorc06
Copy link

oorc06 commented Jun 19, 2021

Hello all,

I seem to run into a problem when I use the (latex) package concmath, and try saving the resultant plot as a pdf (png and jpeg work fine). As suggested above, I've tried installing the "cm-super" package, but this does not solve the issue. I've also played around with the backends, but the problem still persists. A short code that generates this issue:

fig = plt.figure(figsize=(4, 4))
ax = fig.add_axes([0, 0, 1, 1])

plt.rcParams['font.family'] = 'monospace'
plt.rcParams['text.usetex'] = True
rc('text.latex', preamble=r'\usepackage[OT1]{fontenc}\usepackage{concmath}')

plt.plot([10**1,10**9], [1,10], label='Test M$_\star$')
plt.scatter([10**1,10**9], [1,10], label='Test1 M$_\star$ = 10M$_\odot$')
plt.xscale('log')
plt.yscale('log')

plt.xlabel('Test')
plt.ylabel('Test')

plt.xlim(10, 10**9)

plt.legend()

plt.savefig('test.pdf', bbox_inches='tight')

with the following traceback:

KeyError                                  Traceback (most recent call last)
<ipython-input-3-de7a1b0d622a> in <module>
     14 plt.legend()
     15 
---> 16 plt.savefig('test_sm.pdf', bbox_inches='tight')

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/pyplot.py in savefig(*args, **kwargs)
    964 def savefig(*args, **kwargs):
    965     fig = gcf()
--> 966     res = fig.savefig(*args, **kwargs)
    967     fig.canvas.draw_idle()   # need this if 'transparent=True' to reset colors
    968     return res

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/figure.py in savefig(self, fname, transparent, **kwargs)
   3003                 patch.set_edgecolor('none')
   3004 
-> 3005         self.canvas.print_figure(fname, **kwargs)
   3006 
   3007         if transparent:

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
   2253                 # force the figure dpi to 72), so we need to set it again here.
   2254                 with cbook._setattr_cm(self.figure, dpi=dpi):
-> 2255                     result = print_method(
   2256                         filename,
   2257                         facecolor=facecolor,

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backend_bases.py in wrapper(*args, **kwargs)
   1667             kwargs.pop(arg)
   1668 
-> 1669         return func(*args, **kwargs)
   1670 
   1671     return wrapper

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/_api/deprecation.py in wrapper(*inner_args, **inner_kwargs)
    429                          else deprecation_addendum,
    430                 **kwargs)
--> 431         return func(*inner_args, **inner_kwargs)
    432 
    433     return wrapper

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backends/backend_pdf.py in print_pdf(self, filename, dpi, bbox_inches_restore, metadata)
   2723                 RendererPdf(file, dpi, height, width),
   2724                 bbox_inches_restore=bbox_inches_restore)
-> 2725             self.figure.draw(renderer)
   2726             renderer.finalize()
   2727             if not isinstance(filename, PdfPages):

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     72     @wraps(draw)
     73     def draw_wrapper(artist, renderer, *args, **kwargs):
---> 74         result = draw(artist, renderer, *args, **kwargs)
     75         if renderer._rasterizing:
     76             renderer.stop_rasterizing()

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     49                 renderer.start_filter()
     50 
---> 51             return draw(artist, renderer, *args, **kwargs)
     52         finally:
     53             if artist.get_agg_filter() is not None:

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/figure.py in draw(self, renderer)
   2778 
   2779             self.patch.draw(renderer)
-> 2780             mimage._draw_list_compositing_images(
   2781                 renderer, self, artists, self.suppressComposite)
   2782 

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    130     if not_composite or not has_images:
    131         for a in artists:
--> 132             a.draw(renderer)
    133     else:
    134         # Composite any adjacent images together

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     49                 renderer.start_filter()
     50 
---> 51             return draw(artist, renderer, *args, **kwargs)
     52         finally:
     53             if artist.get_agg_filter() is not None:

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/_api/deprecation.py in wrapper(*inner_args, **inner_kwargs)
    429                          else deprecation_addendum,
    430                 **kwargs)
--> 431         return func(*inner_args, **inner_kwargs)
    432 
    433     return wrapper

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axes/_base.py in draw(self, renderer, inframe)
   2919             renderer.stop_rasterizing()
   2920 
-> 2921         mimage._draw_list_compositing_images(renderer, self, artists)
   2922 
   2923         renderer.close_group('axes')

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    130     if not_composite or not has_images:
    131         for a in artists:
--> 132             a.draw(renderer)
    133     else:
    134         # Composite any adjacent images together

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     49                 renderer.start_filter()
     50 
---> 51             return draw(artist, renderer, *args, **kwargs)
     52         finally:
     53             if artist.get_agg_filter() is not None:

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axis.py in draw(self, renderer, *args, **kwargs)
   1139 
   1140         for tick in ticks_to_draw:
-> 1141             tick.draw(renderer)
   1142 
   1143         # scale up the axis label box to also find the neighbors, not

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     49                 renderer.start_filter()
     50 
---> 51             return draw(artist, renderer, *args, **kwargs)
     52         finally:
     53             if artist.get_agg_filter() is not None:

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axis.py in draw(self, renderer)
    300         for artist in [self.gridline, self.tick1line, self.tick2line,
    301                        self.label1, self.label2]:
--> 302             artist.draw(renderer)
    303         renderer.close_group(self.__name__)
    304         self.stale = False

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     49                 renderer.start_filter()
     50 
---> 51             return draw(artist, renderer, *args, **kwargs)
     52         finally:
     53             if artist.get_agg_filter() is not None:

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/text.py in draw(self, renderer)
    721 
    722                 if textobj.get_usetex():
--> 723                     textrenderer.draw_tex(gc, x, y, clean_line,
    724                                           textobj._fontproperties, angle,
    725                                           mtext=mtext)

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/_api/deprecation.py in wrapper(*inner_args, **inner_kwargs)
    429                          else deprecation_addendum,
    430                 **kwargs)
--> 431         return func(*inner_args, **inner_kwargs)
    432 
    433     return wrapper

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backends/backend_pdf.py in draw_tex(self, gc, x, y, s, prop, angle, ismath, mtext)
   2190         for x1, y1, dvifont, glyph, width in page.text:
   2191             if dvifont != oldfont:
-> 2192                 pdfname = self.file.dviFontName(dvifont)
   2193                 seq += [['font', pdfname, dvifont.size]]
   2194                 oldfont = dvifont

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backends/backend_pdf.py in dviFontName(self, dvifont)
    863 
    864         tex_font_map = dviread.PsfontsMap(dviread.find_tex_file('pdftex.map'))
--> 865         psfont = tex_font_map[dvifont.texname]
    866         if psfont.filename is None:
    867             raise ValueError(

~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/dviread.py in __getitem__(self, texname)
    856         assert isinstance(texname, bytes)
    857         try:
--> 858             result = self._font[texname]
    859         except KeyError:
    860             fmt = ('A PostScript file for the font whose TeX name is "{0}" '

KeyError: b'ccr10'

Please let me know if there's a way to circumvent this issue. Thank you all in advance!

@anntzer
Copy link
Contributor

anntzer commented Jun 19, 2021

Can you clarify how you installed concmath? I can't even make it work on a plain latex source, e.g. running latex on

\documentclass{article}
\usepackage[OT1]{fontenc}
\usepackage{concmath}
\begin{document}
  Hello, world.
\end{document}

gives (with or without concmath-fonts installed as well)

kpathsea: Running mktextfm ccr10
/usr/local/texlive/2021/texmf-dist/web2c/mktexnam: Could not map source abbreviation  for ccr10.
/usr/local/texlive/2021/texmf-dist/web2c/mktexnam: Need to update ?
mktextfm: Running mf-nowin -progname=mf \mode:=ljfour; mag:=1; ; nonstopmode; input ccr10
This is METAFONT, Version 2.71828182 (TeX Live 2021) (preloaded base=mf)

kpathsea: Running mktexmf ccr10

! I can't find file `ccr10'.
<*> ...=ljfour; mag:=1; ; nonstopmode; input ccr10
                                                  
Please type another input file name
! Emergency stop.
<*> ...=ljfour; mag:=1; ; nonstopmode; input ccr10
                                                  
Transcript written on mfput.log.
grep: ccr10.log: No such file or directory
mktextfm: `mf-nowin -progname=mf \mode:=ljfour; mag:=1; ; nonstopmode; input ccr10' failed to make ccr10.tfm.
kpathsea: Appending font creation commands to missfont.log.

! Font OT1/ccr/m/n/10=ccr10 at 10.0pt not loadable: Metric (TFM) file not found
.
<to be read again> 
                   relax 
l.12 \begin{document}

@oorc06
Copy link

oorc06 commented Jun 19, 2021

Hi @anntzer, as far as I’m aware, it came pre installed with TexShop. The font seems to work fine for me, on both plain latex and matplotlib on python. Only saving to a pdf seems problematic.

@anntzer
Copy link
Contributor

anntzer commented Jun 19, 2021

Please open a separate issue to track this, then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants