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" bug with unicode characters (version 2.0.0) #8039

Closed
photoniker opened this issue Feb 7, 2017 · 19 comments

Comments

Projects
None yet
8 participants
@photoniker
Copy link

commented Feb 7, 2017

Bug report

I'm using Python 3.5, Numpy 1.12.0, Miktex 2.9 and Matplotlib 2.0.0.

The plot creation of the example from tex_unicode_demo.py works well.
But if I want to save this figure as a pdf-file using "plt.savefig", I get an
AttributeError: 'str' object has no attribute 'decode'

This Error do not occur with an old matplotlib version (1.5.2)!!!

# -*- coding: utf-8 -*-
"""
This demo is tex_demo.py modified to have unicode. See that file for
more information.
"""

from __future__ import unicode_literals
import numpy as np
import matplotlib
matplotlib.rcParams['text.usetex'] = True
matplotlib.rcParams['text.latex.unicode'] = True
import matplotlib.pyplot as plt

plt.figure(1, figsize=(6, 4))
ax = plt.axes([0.1, 0.1, 0.8, 0.7])
t = np.arange(0.0, 1.0 + 0.01, 0.01)
s = np.cos(2*2*np.pi*t) + 2
plt.plot(t, s)

plt.xlabel(r'\textbf{time (s)}')
plt.ylabel('\\textit{Velocity (\u00B0/sec)}', fontsize=16)
plt.title(r'\TeX\ is Number $\displaystyle\sum_{n=1}^\infty'
          r'\frac{-e^{i\pi}}{2^n}$!', fontsize=16, color='r')
plt.grid(True)
plt.show()

plt.savefig('texDemo.pdf')
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\dviread.py", line 701, in __getitem__
    result = self._font[texname.decode('ascii')]
AttributeError: 'str' object has no attribute 'decode'
@photoniker

This comment has been minimized.

Copy link
Author

commented Feb 7, 2017

This problem can be solved if the latex package lmodern is included. But why?

matplotlib.rcParams['text.latex.preamble'] = [r'\usepackage{lmodern}']
@tacaswell

This comment has been minimized.

Copy link
Member

commented Feb 9, 2017

Can you please include the full traceback, the last line by it's self is not super useful (it lands in a __getitem__ definition).

What is the value of texname when this happens (try using IPython + %debug).

@tacaswell

This comment has been minimized.

Copy link
Member

commented Feb 9, 2017

I can not reproduce this.

@tacaswell tacaswell added this to the 2.0.1 (next bug fix release) milestone Feb 9, 2017

@photoniker

This comment has been minimized.

Copy link
Author

commented Feb 10, 2017

This here is the full traceback. Maybe this is some problem with the backend?
I'm using Matplotlib in the software itom.

Traceback (most recent call last):
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\dviread.py", line 699, in __getitem__
    result = self._font[texname]
KeyError: 'tcsi1728'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\users\itokrauter\desktop\matplottest.py", line 27, in <module>
    plt.savefig('texDemo.pdf')
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\pyplot.py", line 697, in savefig
    res = fig.savefig(*args, **kwargs)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\figure.py", line 1572, in savefig
    self.canvas.print_figure(*args, **kwargs)
  File "D:/itom/build/itom/itom-packages\mpl_itom\backend_itomagg.py", line 190, in print_figure
    FigureCanvasAgg.print_figure(self, *args, **kwargs)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\backend_bases.py", line 2244, in print_figure
    **kwargs)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\backends\backend_pdf.py", line 2525, in print_pdf
    self.figure.draw(renderer)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\figure.py", line 1143, in draw
    renderer, self, dsu, self.suppressComposite)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\image.py", line 139, in _draw_list_compositing_images
    a.draw(renderer)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\axes\_base.py", line 2409, in draw
    mimage._draw_list_compositing_images(renderer, self, dsu)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\image.py", line 139, in _draw_list_compositing_images
    a.draw(renderer)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\axis.py", line 1150, in draw
    self.label.draw(renderer)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\text.py", line 798, in draw
    mtext=mtext)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\backends\backend_pdf.py", line 1863, in draw_tex
    psfont = self.tex_font_mapping(dvifont.texname)
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\backends\backend_pdf.py", line 1566, in tex_font_mapping
    return self.tex_font_map[texfont]
  File "C:\Program Files\Python\Python35\lib\site-packages\matplotlib\dviread.py", line 701, in __getitem__
    result = self._font[texname.decode('ascii')]
AttributeError: 'str' object has no attribute 'decode'
@phobson

This comment has been minimized.

Copy link
Member

commented Feb 10, 2017

@photoniker what happens if you just run the script from the command line outside of your IDE?

@tacaswell

This comment has been minimized.

Copy link
Member

commented Feb 10, 2017

As a side-bar, the link to itom was very interesting to me (at my day-job I am developing a similar system, but with very different architecture).

Do you have anything non-standard in your .matplotlibrc? That might help us to reproduce.

It looks like we still have at least one place where we get the byte vs str distinction wrong.

@QuLogic

This comment has been minimized.

Copy link
Member

commented Feb 10, 2017

This looks related to #6977.

@tacaswell

This comment has been minimized.

Copy link
Member

commented Feb 10, 2017

Ah, I remembered that PR, but thought it was merged and that this was a consequence of it.

@jkseppan

This comment has been minimized.

Copy link
Member

commented Feb 11, 2017

#6977 is waiting for review. It won't exactly fix the actual problem here (which is probably a missing font) but it will make the error message more helpful in tracking down what exactly is missing.

@photoniker

This comment has been minimized.

Copy link
Author

commented Feb 13, 2017

@phobson

@photoniker what happens if you just run the script from the command line outside of your IDE?

The same result.

@tacaswell
What do you mean with non-standard in .matplotlibrc?

@jkseppan

This comment has been minimized.

Copy link
Member

commented Feb 13, 2017

It's very likely a missing PostScript font, but the error message you get is useless because the code is trying to support both strings and bytestrings but it's broken on Python 3. #6977 would fix the error message, but the fix for the underlying problem is to install whatever font is missing.

@photoniker

This comment has been minimized.

Copy link
Author

commented Feb 13, 2017

@jkseppan
in this example here I'm using the unicode command for the degree ° characters. This one should be in every font?!

@jkseppan

This comment has been minimized.

Copy link
Member

commented Feb 13, 2017

@QuLogic QuLogic modified the milestones: 2.0.1 (next bug fix release), 2.0.2 (next bug fix release) May 3, 2017

@ghost

This comment has been minimized.

Copy link

commented Sep 6, 2017

Similar problem on with Matplotlib 2.0.0 on Fedora 26. Altering the code from the file
"https://matplotlib.org/2.0.0/mpl_examples/pylab_examples/tex_unicode_demo.py"
to save the plot to a pdf as the following:

# -*- coding: utf-8 -*-
"""
This demo is tex_demo.py modified to have unicode. See that file for
more information.
"""

from __future__ import unicode_literals
import numpy as np
import matplotlib
matplotlib.rcParams['text.usetex'] = True
matplotlib.rcParams['text.latex.unicode'] = True
import matplotlib.pyplot as plt

plt.figure(1, figsize=(6, 4))
ax = plt.axes([0.1, 0.1, 0.8, 0.7])
t = np.arange(0.0, 1.0 + 0.01, 0.01)
s = np.cos(2*2*np.pi*t) + 2
plt.plot(t, s)

plt.xlabel(r'\textbf{time (s)}')
plt.ylabel('\\textit{Velocity (\u00B0/sec)}', fontsize=16)
plt.title(r'\TeX\ is Number $\displaystyle\sum_{n=1}^\infty'
          r'\frac{-e^{i\pi}}{2^n}$!', fontsize=16, color='r')
plt.grid(True)
plt.savefig("tmp.pdf")

results in the following terminal ouput:

Traceback (most recent call last):
  File "/usr/lib64/python3.6/site-packages/matplotlib/dviread.py", line 699, in __getitem__
    result = self._font[texname]
KeyError: 'cmr10'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib64/python3.6/site-packages/matplotlib/backends/backend_pdf.py", line 2525, in print_pdf
    self.figure.draw(renderer)
  File "/usr/lib64/python3.6/site-packages/matplotlib/artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib64/python3.6/site-packages/matplotlib/figure.py", line 1143, in draw
    renderer, self, dsu, self.suppressComposite)
  File "/usr/lib64/python3.6/site-packages/matplotlib/image.py", line 139, in _draw_list_compositing_images
    a.draw(renderer)
  File "/usr/lib64/python3.6/site-packages/matplotlib/artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib64/python3.6/site-packages/matplotlib/axes/_base.py", line 2409, in draw
    mimage._draw_list_compositing_images(renderer, self, dsu)
  File "/usr/lib64/python3.6/site-packages/matplotlib/image.py", line 139, in _draw_list_compositing_images
    a.draw(renderer)
  File "/usr/lib64/python3.6/site-packages/matplotlib/artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib64/python3.6/site-packages/matplotlib/axis.py", line 1141, in draw
    tick.draw(renderer)
  File "/usr/lib64/python3.6/site-packages/matplotlib/artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib64/python3.6/site-packages/matplotlib/axis.py", line 264, in draw
    self.label1.draw(renderer)
  File "/usr/lib64/python3.6/site-packages/matplotlib/artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/usr/lib64/python3.6/site-packages/matplotlib/text.py", line 798, in draw
    mtext=mtext)
  File "/usr/lib64/python3.6/site-packages/matplotlib/backends/backend_pdf.py", line 1863, in draw_tex
    psfont = self.tex_font_mapping(dvifont.texname)
  File "/usr/lib64/python3.6/site-packages/matplotlib/backends/backend_pdf.py", line 1566, in tex_font_mapping
    return self.tex_font_map[texfont]
  File "/usr/lib64/python3.6/site-packages/matplotlib/dviread.py", line 701, in __getitem__
    result = self._font[texname.decode('ascii')]
AttributeError: 'str' object has no attribute 'decode'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tex_unicode_demo.py", line 25, in <module>
    plt.savefig("tmp.pdf")
  File "/usr/lib64/python3.6/site-packages/matplotlib/pyplot.py", line 697, in savefig
    res = fig.savefig(*args, **kwargs)
  File "/usr/lib64/python3.6/site-packages/matplotlib/figure.py", line 1572, in savefig
    self.canvas.print_figure(*args, **kwargs)
  File "/usr/lib64/python3.6/site-packages/matplotlib/backend_bases.py", line 2244, in print_figure
    **kwargs)
  File "/usr/lib64/python3.6/site-packages/matplotlib/backends/backend_pdf.py", line 2531, in print_pdf
    file.close()
  File "/usr/lib64/python3.6/site-packages/matplotlib/backends/backend_pdf.py", line 547, in close
    self.writeFonts()
  File "/usr/lib64/python3.6/site-packages/matplotlib/backends/backend_pdf.py", line 647, in writeFonts
    realpath, stat_key = get_realpath_and_stat(filename)
  File "/usr/lib64/python3.6/site-packages/matplotlib/cbook.py", line 1030, in __call__
    stat = os.stat(realpath)
FileNotFoundError: [Errno 2] No such file or directory: '/~/Downloads/cmr10'

The font cmr10 however is installed as the packages texlive-collection-fontsrecommended and texlive-collection-fontsextra are installed and the fonts are also available in LibreOffice Writer.

Could it be related to: https://fedoraproject.org/wiki/Changes/FontconfigCacheDirChange ?

@jkseppan

This comment has been minimized.

Copy link
Member

commented Sep 7, 2017

@JakobJakobson Does grep cmr10 $(kpsewhich pdftex.map) print out a line resembling "cmr10 CMR10 <cmr10.pfb"? Does kpsewhich cmr10.pfb print out a path to cmr10.pfb? If either fails, the Type-1 file for the font is not installed or the TeX font maps have not been updated to include the font, as in #8259.

@ghost

This comment has been minimized.

Copy link

commented Sep 7, 2017

I tried your solution.

$ kpsewhich pdftex.map
/usr/share/texlive/texmf-var/fonts/map/pdftex/updmap/pdftex.map
$ grep cmr10 $(kpsewhich pdftex.map)
$

The file pdftex.map exits but somehow the combined command shows no output.

@ghost

This comment has been minimized.

Copy link

commented Sep 7, 2017

I found the solution for my problem. I ran updmap and after that the script ran without any problems.
So I guess it is more a problem to report to TeX Live or the linux distributions?

@tacaswell tacaswell modified the milestones: 2.1.1 (next bug fix release), 2.2 (next feature release) Oct 9, 2017

@cbnfreitas

This comment has been minimized.

Copy link

commented Nov 28, 2017

I'm using Windows 10, Anaconda3-5.0.1-Windows-x86_64 and basic-miktex-2.9.6520-x64.exe, but
fig.savefig('dummy.pdf') still does not work for me in Jupyter, for instance:

import matplotlib
matplotlib.rcParams.update({'font.size': 14})
%matplotlib inline

import matplotlib.pyplot as plt
plt.rc('text', usetex=True)

fig, ax = plt.subplots(1, 1, figsize=(8, 8))
ax.plot([0,1,2,3,4], [4,3,2,1,0])
fig.savefig('dummy.pdf')

I've checked
C:\Users\<uder>\AppData\Local\MiKTeX\2.9\pdftex\config\pdftex.map,
and it already contains

cmr10 CMR10 <cmr10.pfb
cmr12 CMR12 <cmr12.pfb

And kpsewhich cmr10.pfb points to
C:/Program Files/MiKTeX 2.9/fonts/type1/public/amsfonts/cm/cmr10.pfb

This is the error shown in Jupyter:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-17-7021e6e2dcbe> in <module>()
      8 fig, ax = plt.subplots(1, 1, figsize=(8, 8))
      9 ax.plot([0,1,2,3,4], [4,3,2,1,0])
---> 10 fig.savefig('dummy.pdf')

~\Anaconda3\lib\site-packages\matplotlib\figure.py in savefig(self, fname, **kwargs)
   1812             self.set_frameon(frameon)
   1813 
-> 1814         self.canvas.print_figure(fname, **kwargs)
   1815 
   1816         if frameon:

~\Anaconda3\lib\site-packages\matplotlib\backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, **kwargs)
   2257                 orientation=orientation,
   2258                 bbox_inches_restore=_bbox_inches_restore,
-> 2259                 **kwargs)
   2260         finally:
   2261             if bbox_inches and restore_bbox:

~\Anaconda3\lib\site-packages\matplotlib\backends\backend_pdf.py in print_pdf(self, filename, **kwargs)
   2590                 RendererPdf(file, image_dpi, height, width),
   2591                 bbox_inches_restore=_bbox_inches_restore)
-> 2592             self.figure.draw(renderer)
   2593             renderer.finalize()
   2594             file.finalize()

~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     53                 renderer.start_filter()
     54 
---> 55             return draw(artist, renderer, *args, **kwargs)
     56         finally:
     57             if artist.get_agg_filter() is not None:

~\Anaconda3\lib\site-packages\matplotlib\figure.py in draw(self, renderer)
   1293 
   1294             mimage._draw_list_compositing_images(
-> 1295                 renderer, self, artists, self.suppressComposite)
   1296 
   1297             renderer.close_group('figure')

~\Anaconda3\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    136     if not_composite or not has_images:
    137         for a in artists:
--> 138             a.draw(renderer)
    139     else:
    140         # Composite any adjacent images together

~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     53                 renderer.start_filter()
     54 
---> 55             return draw(artist, renderer, *args, **kwargs)
     56         finally:
     57             if artist.get_agg_filter() is not None:

~\Anaconda3\lib\site-packages\matplotlib\axes\_base.py in draw(self, renderer, inframe)
   2397             renderer.stop_rasterizing()
   2398 
-> 2399         mimage._draw_list_compositing_images(renderer, self, artists)
   2400 
   2401         renderer.close_group('axes')

~\Anaconda3\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    136     if not_composite or not has_images:
    137         for a in artists:
--> 138             a.draw(renderer)
    139     else:
    140         # Composite any adjacent images together

~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     53                 renderer.start_filter()
     54 
---> 55             return draw(artist, renderer, *args, **kwargs)
     56         finally:
     57             if artist.get_agg_filter() is not None:

~\Anaconda3\lib\site-packages\matplotlib\axis.py in draw(self, renderer, *args, **kwargs)
   1136 
   1137         for tick in ticks_to_draw:
-> 1138             tick.draw(renderer)
   1139 
   1140         # scale up the axis label box to also find the neighbors, not

~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     53                 renderer.start_filter()
     54 
---> 55             return draw(artist, renderer, *args, **kwargs)
     56         finally:
     57             if artist.get_agg_filter() is not None:

~\Anaconda3\lib\site-packages\matplotlib\axis.py in draw(self, renderer)
    280 
    281         if self.label1On:
--> 282             self.label1.draw(renderer)
    283         if self.label2On:
    284             self.label2.draw(renderer)

~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     53                 renderer.start_filter()
     54 
---> 55             return draw(artist, renderer, *args, **kwargs)
     56         finally:
     57             if artist.get_agg_filter() is not None:

~\Anaconda3\lib\site-packages\matplotlib\text.py in draw(self, renderer)
    797                     textrenderer.draw_tex(gc, x, y, clean_line,
    798                                           textobj._fontproperties, angle,
--> 799                                           mtext=mtext)
    800                 else:
    801                     textrenderer.draw_text(gc, x, y, clean_line,

~\Anaconda3\lib\site-packages\matplotlib\backends\backend_pdf.py in draw_tex(self, gc, x, y, s, prop, angle, ismath, mtext)
   1942         for x1, y1, dvifont, glyph, width in page.text:
   1943             if dvifont != oldfont:
-> 1944                 pdfname = self.file.dviFontName(dvifont)
   1945                 seq += [['font', pdfname, dvifont.size]]
   1946                 oldfont = dvifont

~\Anaconda3\lib\site-packages\matplotlib\backends\backend_pdf.py in dviFontName(self, dvifont)
    686             return dvi_info.pdfname
    687 
--> 688         psfont = self.texFontMap[dvifont.texname]
    689         if psfont.filename is None:
    690             raise ValueError(

~\Anaconda3\lib\site-packages\matplotlib\dviread.py in __getitem__(self, texname)
    864         fn, enc = result.filename, result.encoding
    865         if fn is not None and not fn.startswith(b'/'):
--> 866             fn = find_tex_file(fn)
    867         if enc is not None and not enc.startswith(b'/'):
    868             enc = find_tex_file(result.encoding)

~\Anaconda3\lib\site-packages\matplotlib\dviread.py in find_tex_file(filename, format)
   1043     # https://github.com/matplotlib/matplotlib/issues/633
   1044     pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
-> 1045                             stderr=subprocess.PIPE)
   1046     result = pipe.communicate()[0].rstrip()
   1047     matplotlib.verbose.report('find_tex_file result: %s' % result,

~\Anaconda3\lib\subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors)
    707                                 c2pread, c2pwrite,
    708                                 errread, errwrite,
--> 709                                 restore_signals, start_new_session)
    710         except:
    711             # Cleanup if the child failed starting.

~\Anaconda3\lib\subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, unused_restore_signals, unused_start_new_session)
    969 
    970             if not isinstance(args, str):
--> 971                 args = list2cmdline(args)
    972 
    973             # Process startup details

~\Anaconda3\lib\subprocess.py in list2cmdline(seq)
    459             result.append(' ')
    460 
--> 461         needquote = (" " in arg) or ("\t" in arg) or not arg
    462         if needquote:
    463             result.append('"')

TypeError: a bytes-like object is required, not 'str'

Any clue? Thanks!

@jklymak

This comment has been minimized.

Copy link
Contributor

commented Nov 28, 2017

I think #9292 should have fixed this. Looks the same. I'll close, but re-open if you think I'm mistaken. This will be in release 2.1.1.

@jklymak jklymak closed this Nov 28, 2017

@QuLogic QuLogic modified the milestones: needs sorting, v2.2.0 Feb 12, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.