Summary
I am exporting matplotlib figures as PGF to use in LaTeX.
matplotlib seems to add a \sffamily addition to every text entry (axes labels, ticks, legend entries, annotations) when saving the figure as a PGF. This will stop it from properly inheriting the font from the global document font.
The text can inheret the font from the global document font if it is from the same family, but it will revert to the default sffamily font if the global font is from a different family.
Where I got to
I believe I have isolated the problem: if I edit the PGF document and simply remove the \sffamily part of any text entry, the problem no longer persists and the global font is inherited. The deletion doesn't prevent LaTeX from compiling it properly, and I get no errors.
Because of the above finding, I believe the problem has nothing to do with rcParams or any LaTeX preamble (both in python or in the actual LaTeX document).
MWE
I just tried it on the simplest possible plot and was able to reproduce the problem:
import matplotlib.pyplot as plt
fig = plt.figure()
plt.xlabel('a label')
fig.savefig('fig.pgf')
And pgf document will contain the following line:
\pgftext[x=3.280000in,y=0.240809in,,top]{\color{textcolor}\sffamily\fontsize{10.000000}{12.000000}\selectfont a label}%
so the \sffamily is added. Rendering this in LaTeX will force a sans-serif font. Removing the \sffamily and rendering it will allow it to inherit the font family.
TLDR
Is there a way to avoid the inclusion of a font family in the PGF output of matplotlib?
I cannot offer a solution but a workaround building on #samcarter's comment: You can redefine \sffamily locally, e.g.:
\documentclass{article}
\usepackage{pgf}
\usepackage{fontspec}
\setmainfont{DejaVu Serif}
\setsansfont{DejaVu Sans}
\setmonofont{DejaVu Sans Mono}
\begin{document}
Lorem ipsum {\sffamily Lorem ipsum}
\begin{center}
\renewcommand\sffamily{}
\input{fig.pgf}
\end{center}
Lorem ipsum {\sffamily Lorem ipsum}
\end{document}
Instead of center you can use any environment or \begingroup and \endgroup.
Building on https://matplotlib.org/users/pgf.html#font-specification you could use:
import matplotlib as mpl
import matplotlib.pyplot as plt
pgf_with_rc_fonts = {
"font.family": "serif",
}
mpl.rcParams.update(pgf_with_rc_fonts)
fig = plt.figure()
plt.xlabel('a label')
fig.savefig('fig.pgf')
This way \rmfamily is used instead of \sffamily.
There is another workaround by replacing the font specification with sed before importing the pgf file in your tex-document.
\documentclass{article}
\usepackage{pgf}
\usepackage{pgfplots}
\pgfplotsset{compat=1.8}
\usepackage{filecontents}
\begin{document}
\begin{figure}
\begin{filecontents*}{tmpfile.sed}
# sed command file
s/\\color{textcolor}\\sffamily\\fontsize{.*}{.*}\\selectfont //\end{filecontents*}
\immediate\write18{sed -i -f tmpfile.sed yourplot.pgf}
\import{yourplot.pgf}
\end{figure}
\end{document}
Related
I am using Jupyter Notebook, with a matplotlibrc style that's consistent with its theme set using jupyterthemes. That plotting style however does not look good if I want to export it to PNG to use it within my other documents.
How do I specify a different matplotlibrc when I do a savefig?
Most matplotlib style settings are applied at the moment the object they apply to is created.
You would hence need to create two different plots, one with the usual style of your notebook and another one with the style from the style file. The latter one would be the one to save.
A decent solution would be to create a plot in a function. You can then call this function within a context, with plt.style.context(<your style>): to give the figure a different style.
import matplotlib.pyplot as plt
def plot():
fig, ax = plt.subplots()
ax.plot([2,3,4], label="label")
ax.legend()
# Plot with general style of the notebook
plot()
# Plot with your chosen style for saved figures
with plt.style.context('ggplot'):
plot()
plt.savefig("dark.png")
#plt.close(plt.gcf()) # if you don't want to show this figure on screen
plt.show()
Relevant here: The matplotlib customizing guide.
Perusing matplotlib/__init__.py reveals a number of functions used for managing rcParams. To update rcParams from a file, use matplotlib.rc_file:
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rc_file('/tmp/matplotlibrc')
plt.plot([0,1], [0,10])
plt.savefig('/tmp/out.png')
with /tmp/matplotlibrc containing
lines.linewidth : 10 # line width in points
lines.linestyle : -- # dashed line
yields
PS. In hindsight, having found rc_file, googling shows it is documented here.
I am generating a figure using the PS backend:
import matplotlib
matplotlib.use('ps')
from matplotlib import pyplot as plt
plt.plot((1, 2))
I would like to save the figure:
plt.savefig('test.eps', format='eps', bbox_inches='tight', pad_inches=1.0)
Incidentally, I will make a PNG out of it later (in case it turns out to be relevant to the solution):
gs -q -dSAFER -dBATCH -dNOPAUSE -dUseTrimBox -dEPSCrop -sDEVICE=pngalpha -sOutputFile=test.png test.eps
The result is (PNG viewed in eog):
I would like to change the size of the EPS or the PNG figure, but adding a dpi kwarg to savefig does not change the output. Calling plt.gcf().set_size_inches(10, 10) does nothing as well. How do I make my EPS or PNG file have a higher resolution?
This answer is relevant in that it hints that setting dpi for the ps backend may not be the way to go.
Update
For clarification, it is necessary for me to use the PS backend. At the moment, it is the only one that correctly renders LaTeX colored strings. PGF may or may not do what I want, but I do not have time to research its quirks in depth. I was able to get a hacky solution by adding a -r... argument to gs, but would prefer to control everything from MatPlotLib.
If you create the figure with plt.figure first, you can give the figsize-kwarg. An example:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(10, 1)) # figsize in inches
fig.show() # if running in ipython
# plt.show() # if running as a script with "python script_name.py"
To save with a specific dpi, it should be enough with mentioned dpi-kwarg to the figures savefig-method, i.e.
fig.savefig(..., dpi=300)
Note that if you work with plt to save the figure or to plot, it will always work with the active figure, which might not be the figure you expect if you happen to have several of them opened.
UPDATE
1)
Based on the comment by Mad Physicist, the dpi-kwarg apperently does not work with the PS backend. I do not know why.
I am having problems saving a figure created by matplotlib as a .eps or .ps when I enable usetex=True. This works when this is not enabled. Here's an example:
plt.plot([1,2,3], [1,2,3], 'b.')
plt.text(2,2,r'\textbf{(a)} \lambda_{1} value', usetex=True, fontsize=16, fontname='Times New Roman')
plt.savefig('check.eps')
I receive this error:
File "C:\Python27\lib\site-packages\matplotlib\backends\backend_ps.py", line 671, in draw_tex
thetext = 'psmarker%d' % self.textcnt
AttributeError: 'RendererPS' object has no attribute 'textcnt'
I also cannot set the font to Time New Roman using the text command when I enable usetex=True.
Even if it is a bit late, I would like to give an answer, if someone else searches for this error.
Saving eps files starts the RendererPS backend, which checks whether plt.rcParams['text.usetex'] is set to True to initialise its textcnt attribute. Since usetex is set only for the text object, the backend does not expect to have to handle LaTeX and throws an error when it tries to. These kind of inconsistencies concerning LaTeX rendering are sadly present in a number of places in matplotlib.
If you cannot achieve the text formatting with the standard matplotib functionality, one solution is to set usetex globally: plt.rcParams['text.usetex'] = True. This render all the text of the figure with LaTeX (e.g. also tick labels or axis labels). I would recommend to do this in any case, to have consistent visuals.
Concerning the font, the fontname argument only affects standard matplotlib formatting. For LaTeX rendering, you have to specify the font like you would do in LaTeX. To get a font like Times New Roman, you need to load the respective package e.g. mathptmx (the times package is deprecated) by plt.rcParams['text.latex.preamble'] = [r'\usepackage{mathptmx}']. Of course, the package has to be installed in your local LaTeX installation. The standard font family in matplotlib is sans-serif, so this has to be changed to serif with plt.rcParams['font.family'] = 'serif'.
The final code is:
import matplotlib.pyplot as plt
# LaTeX setup
plt.rcParams['text.latex.preamble'] = [r'\usepackage{mathptmx}'] # load times roman font
plt.rcParams['font.family'] = 'serif' # use serif font as default
plt.rcParams['text.usetex'] = True # enable LaTeX rendering globally
plt.plot([1,2,3], [1,2,3], 'b.')
plt.text(2,2,r'\textbf{(a)} $\lambda_1$ value', fontsize=16)
plt.savefig('check.eps')
Note, that you have to enclose the math variable in dollar signs $\lambda_1$, otherwise you get an error or warning in LaTeX. (Also, single character subscripts do not need to be enclosed in curly brackets).
As a side note: I encountered the same error, when trying to save a figure as eps after turning usetex off again.
noob here. I can't figure out the correct matplotlib line to use my newly loaded gillius font.
So I installed gillius font of the arkandis digital foundry on my machine
ttf-adf-gillius
here is the place i "think" it put it
usr/share/fonts/truetype/adf/GilliusADF-Regular.otf: Gillius
ADF:style=Regular
SO if I want to use it in my Python 3.4 program that imports matplotlib. I set the font_family= 'Gillius" and the program doesn't find it. What do I set the font family to to use my font please?
here is an example message from my python program
Warning (from warnings module):
File
"/usr/lib/python3/dist-packages/matplotlib/font_manager.py", line 1279
(prop.get_family(), self.defaultFamily[fontext]))
UserWarning: findfont: Font
family ['Gillius'] not found. Falling back to Bitstream Vera
Sans
Warning (from warnings module):
File
"/usr/lib/python3/dist-packages/matplotlib/font_manager.py", line 1289
UserWarning)
UserWarning: findfont: Could not match :family=Bitstream Vera
Sans:style=normal:variant=normal:weight=normal:stretch=normal:size=20.0.
Returning /usr/share/matplotlib/mpl-data/fonts/ttf/cmmi10.ttf
I had already tried using the suggested link
How to use a (random) *.otf or *.ttf font in matplotlib?
but it didn't seem to do anything. My question is assuming the font is installed correctly what is the font family I should be entering to have matplotlib use it?
Ok I "think" this is a way to do it based on all the GREAT help I got on SO. thanks to all. I am new to matplotlib so if I screwed up happy to correct it.
import matplotlib
import matplotlib.font_manager as fm
from matplotlib import pyplot as plt
font = fm.FontProperties(
family = 'Gill Sans',
fname = '/usr/share/fonts/truetype/adf/GilliusADF-Regular.otf')
data = range(5)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(data, data)
plt.ylabel('some y numbers')
plt.xlabel('some x numbers')
ax.set_yticklabels(ax.get_yticks(), fontproperties = font)
ax.set_xticklabels(ax.get_xticks(), fontproperties = font)
ax.set_title('this is a test of Gill sans font')
plt.show()
I need to output my plots in EPS with the CMYK color space. Unfortunately this particular format is requested by the journal I am submitting my work to!
This discussion was the only one I could find that has addressed the issue but it is more than 2 years old. I was hoping there might be some updates fixing the problem by now.
All my programming is in Python3 and so far I have been saving my plots in PDF which had no problem. But now that I want to plot EPS there is a problem. For example the code bellow prints the simple plot in .png and .pdf but the .eps output is totally blank!
import numpy as np
import matplotlib.pyplot as plt
X=[1,2,3]
Y=[4,5,6]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(X,Y)
fig.savefig('test.eps')
fig.savefig('test.pdf')
fig.savefig('test.png')
So I have two questions:
How can I fix the eps output?
How can I set the eps output color space to CMYK?
Thanks in advance.
I too have the same problem. One workaround I have found is to save plots as .svg and then use a program like Inkscape to convert to eps. I used to be able to save in .eps without any issues and then lost the ability after an update.
Update I was able to solve this problem for my specific setup by changing a few lines in my .matplotlibrc, so I will post the relevant lines here in the hope that it may be helpful to you as well. Note this requires that you have xpdf and ghostscript already installed.
For me the important one was
##Saving Figures
ps.usedistiller : xpdf
But I also have
path.simplify : True
savefig.format : eps
Now I am able to save directly to .eps and include them in LaTeX'ed journal articles...