Matplotlib estimates width of text wrong - pdf

When using Matplotlib (Pylab) for rendering Text with the same metrics as AriaL (e.g., Arial itself or Liberation Sans) output looks ok - e.g., the width of the legend box is right.
However, when using other fonts it seems that Matplotlib wrongly estimates the expected size of the text. For example, with Bitstream Vera Sans the text seems to be wider than expected by Matplotlib: E.g., the right-hand side of the text sometimes overlaps with the right-hand side of the legend's box.
Similiarly, when using use_latex to let Latex render the output the text seems to be narrower than expected, and there's an additional space between the right-hand side of the text and the legend's box.
Is this a bug in Matplotlib? Or am I expected to use fonts with the metrics of Arial?

I'm running matplotlib version 0.99.1.1 on linux (ubuntu 10.04) and cannot reproduce the problem. Here is the code I'm using.
import pylab
a = [1,2,1]
label = "This long label isn't too long."
for family in ['Arial', 'Liberation Sans', 'Bitstream Vera Sans']:
pylab.figure()
pylab.plot(a)
pylab.legend([label], prop={'family':family})
pylab.show()

Related

How to set a default font weight for x & y labels with matplotlib

Using matplotlib 3.0.1, I had to make a figure where all text had to be in Open Sans Light. As the figure is quite complex, with several panels etc, I wanted to set that up using matplotlib.pyplot.rc(..) to define the default font and font weight for all elements of the figure.
The code below provides a minimal example. Because not everybody might have Open Sans installed, here I use the default font and the 'bold' weight, instead of 'light', to make it more general. So I set the default font weight, and size, then I plot a basic axis with x and y labels and some extra text.
import matplotlib.pyplot as plt
plt.style.use('default')
plt.rc('font', weight='bold', size=15)
fig, ax = plt.subplots(num=1, clear=True)
ax.set(xlabel='x-label', ylabel='y-label')
ax.text(0.2,0.4,'Text')
I expect all bits of text to be in 'bold' font weight. The tick labels and the extra bit of text are, but the x-label and y-label are not. They are in the normal weight. Screen capture here.
The x and y labels seem to get the default font name and size, but not the default weight.
As a workaround, I tried to set a default font weight in the 'axes' options, but that does not seem to be possible. "axes.labelsize" affects the font size of the x,y labels, but the font weight cannot be separately specified.
So it seems the font size is inherited by the x and y labels, if not otherwise specified through axes.labelsize, but the font weight is not inherited by the labels, nor can it be specified as a default for the axes. Note that the font name behaves yet differently. It is inherited by the x, y labels but cannot have a different default in the 'axes' option.
Is there a logic, or is that a bug?
Is there a solution to this? Or do I have to directly set the font weight onto the x-label and y-label objects?

How to export svg in matplotlib with correct mm scale

I am trying to export a figure from matplotlib for laser cutting. The figure is plotted with millimeters as the units.
I'm tying to ensure the correct scale by getting the bounding box in inches and then setting the figure size to that value:
import matplotlib.pyplot as plt
ax = plt.subplot(111)
<snipped for brevity...plotting of lines and paths>
x_bound = map(mm_to_inch, ax.get_xbound())
y_bound = map(mm_to_inch, ax.get_ybound())
plt.gcf().set_size_inches(x_bound[1] - x_bound[0], y_bound[1] - y_bound[0])
plt.axis('off')
plt.savefig('{0}.svg'.format(self.name, format='svg'))
The exported .svg is ~2/3rds of the intended scale and I'm not familiar enough with axes and figures to know why. Additionally, there is a black border around the intended geometry. Here is some example output:
.svg output (converted to .png)
How should I remove the black border and scale the .svg correctly?
You probably want to remove the margins around the axes completely,
plt.gcf().subplots_adjust(0,0,1,1)
I might note however that the result may not be precise enough for the application. Definitely also consider creating the figure with a CAD program.
Based off of ImportanceOfBeingErnest's answer and some responses to other stackoverflow questions, the following solution works:
plt.axis('off')
plt.margins(0, 0)
plt.gca().xaxis.set_major_locator(plt.NullLocator())
plt.gca().yaxis.set_major_locator(plt.NullLocator())
plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
x_bound = map(mm_to_inch, self._ax.get_xbound())
y_bound = map(mm_to_inch, self._ax.get_ybound())
plt.gcf().set_size_inches(x_bound[1] - x_bound[0], y_bound[1] - y_bound[0])

matplotlib - savefig with usetex=True

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.

Matplotlib need help using a downloaded font gill sans what is the font family

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()

How do I update the legend label spacing after legend font size is changed in matplotlib?

I'm writing a script that saves a figure with multiple formatting styles among which is the font size of legend text.
The legend.labelspacing in rcparams or the matplotlibrc file specifies the label spacing in fractions of the font size, so I might expect the actual spacing to change if the font size is changed. However, since the actual spacing is probably calculated when the legend is first created, any subsequent change to the font size of existing legend text objects has no effect on the label spacing. Is there a way to update the legend label spacing after an existing legend label object's font size has been changed? In summary here's is what I would like to do:
plot something with a legend
save the figure (format according to rcparams or matplotlibrc file)
change several formatting properties (line widths, font sizes, etc.)
save the figure again with the updated formatting properties, including re-adjusted legend label spacing
Is there a way to do this without changing the rcparams and then rebuilding the figure?
Just call legend() with labelspacing parameter, here is an example:
import pylab as pl
pl.plot([0,1],[0,1], label="a")
pl.plot([0,2],[0,2], label="b")
pl.legend()
pl.savefig("p1.png")
pl.legend(labelspacing=2)
pl.savefig("p2.png")
To reuse parameters:
import pylab as pl
pl.plot([0,1],[0,1], label="a")
pl.plot([0,2],[0,2], label="b")
params = dict(loc="right", prop=dict(size=9))
pl.legend(**params)
pl.savefig("p1.png")
params["labelspacing"] = 2
pl.legend(**params)
pl.savefig("p2.png")