How to render math symbols as text in SVG/EPS/PDF images? - python

When creating graphs using, for instance, Python. It is possible to save these figures as vector graphics (SVG, EPS, PDF) and the text is rendered separately. This makes it possible to select or search the text when shown in a pdf file. However, I've tried to render a simple graph using math symbols in addition to text (in latex). The math symbol gets encoded as part of the image, rather than as text.
Here is a minimum reproducible example.
import numpy as np
import matplotlib.pyplot as plt
x_list = np.linspace(-10,10,num=128)
y = list(map(lambda x: (x**2 + x + 1), x_list))
plt.plot(y, label="$\\Psi_{example}$")
plt.legend()
plt.xticks(np.linspace(0, 128, num=8),
map(round, np.linspace(-10, 10, num=8), [0] * 8))
plt.savefig("./example.pdf")
Which produces the following image.
When saving this image as vector graphics, all the numbers as well as the 'example' word in the legend become selectable/searchable (i.e. rendered as text). However, the Ψ (Psi) character is not selectable/searchable.
Is there any way to make math symbols render as text in vector graphics?

I have been able to get it to work in the way I think you want by first installing a LaTeX distribution (I used MikTex, from here) and then setting the matpotlib option to use LaTeX to render your symbols and text.
Note that after installing MikTex, I had to open a new instance of my command prompt or code editor to make sure it was aware of the change to my PATH and where the LaTex is installed.
I added the import and mpl.rcParams line to your example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['text.usetex'] = True
x_list = np.linspace(-10, 10, num=128)
y = list(map(lambda x: (x**2 + x + 1), x_list))
plt.plot(y, label="$\\Psi_{example}$")
plt.legend()
plt.xticks(np.linspace(0, 128, num=8),
map(round, np.linspace(-10, 10, num=8), [0] * 8))
plt.savefig("./example.pdf")

It's two different matters. Characters are represented by codes. In this case, you are not able to select some characters because the software you are using to display the rendered results does not have that Unicode defined in its fonts library. So it's treating that character as an object or an empty box(commonly called “tofu”). But the render engine that is turning your python code(or TeX file) into a PDF/SVG does understand that Unicode and that's why you can see that particular character. So much for understanding the source of the issue.
Solution: You may use another IDE/browser if you are using that platform to see the results. Chrome usually supports most Unicodes. Except for those that are defined very recently.
Moreover, Ψ (Psi) is a Greek letter. Check if your Operating System does have Greek letters installed in its fonts library. If it doesn't, go to The Unicode Consortium website and search "Display Problems" it will come up with a page explaining how to install a font depending on your OS or browser.

Related

Latex font in matplotlib - Script-r

In matplotlib, one can easily use latex script to label axes, or write legends or any other text. But is there a way to use new fonts such as 'script-r' in matplotlib? In the following code, I am labelling the axes using latex fonts.
import numpy as np
import matplotlib.pyplot as plt
tmax=10
h=0.01
number_of_realizations=6
for n in range(number_of_realizations):
xpos1=0
xvel1=0
xlist=[]
tlist=[]
t=0
while t<tmax:
xlist.append(xpos1)
tlist.append(t)
xvel1=np.random.normal(loc=0.0, scale=1.0, size=None)
xpos2=xpos1+(h**0.5)*xvel1 # update position at time t
xpos1=xpos2
t=t+h
plt.plot(tlist, xlist)
plt.xlabel(r'$ t$', fontsize=50)
plt.ylabel(r'$r$', fontsize=50)
plt.title('Brownian motion', fontsize=20)
plt.show()
It produces the following figure
But I want 'script-r' in place of normal 'r'.
In latex one has to add the following lines in preamble to render 'script-r'
\DeclareFontFamily{T1}{calligra}{}
\DeclareFontShape{T1}{calligra}{m}{n}{<->s*[2.2]callig15}{}
\DeclareRobustCommand{\sr}{%
\mspace{-2mu}%
\text{\usefont{T1}{calligra}{m}{n}r\/}%
\mspace{2mu}%
}
I don't understand how to do this in matplotlib. Any help is appreciated.
Matplotlib uses it's own hand-rolled (pure Python) implementation of TeX to do all of the math text stuff, so you absolutely cannot assume that what works in standard LaTeX will work with Matplotlib. That being said, here's how you do it:
Install the calligra font so that Matplotlib can see it, then rebuild the font cache.
Lots of other threads deal with how to do this, I'm not going to go into detail, but here's some reference:
Use a font installed in a random spot on your filesystem.
How to install a new font into the Matplotlib managed font cache.
List all fonts currently known to your install of Matplotlib.
Replace one of Matplotlib's TeX font families with your font of choice.
Here's a function I wrote a while ago that reliably does that:
import matplotlib
def setMathtextFont(fontName='Helvetica', texFontFamilies=None):
texFontFamilies = ['it','rm','tt','bf','cal','sf'] if texFontFamilies is None else texFontFamilies
matplotlib.rcParams.update({'mathtext.fontset': 'custom'})
for texFontFamily in texFontFamilies:
matplotlib.rcParams.update({('mathtext.%s' % texFontFamily): fontName})
For you, a good way to use the function would be to replace the font used by \mathcal with calligra:
setMathtextFont('calligra', ['cal'])
Label your plots, for example, r'$\mathcal{foo}$', and the contents of the \math<whatever> macro should show up in the desired font.
Here's how you'd change your label-making code:
plt.ylabel(r'$\mathcal{r}$', fontsize=50)
and that should do it.

How to create 'normal' looking axis labels using latex in matplotlib

I have the following piece of code to create axis labels with German umlauts:
plt.xlabel('Daten')
plt.ylabel(r'$H\ddot{a}ufigkeit$')
which basically works, and prints the a-umlaut correctly, But the font of the x and y labels are now different, as the x label is printed in math mode. Changing the second line to
plt.ylabel(r'$\textrm{H\ddot{a}ufigkeit}$')
should work as far as I know (in order to create a rm like font instead of the math mode font), but gives a python error:
matplotlib.pyparsing.ParseFatalException: Expected end of math '$'
How can I fix this issue in order to have the same font on both axis, but with umlauts possible?
The non-math umlaut is \":
plt.ylabel(r'H\"{a}ufigkeit')
If you need \ddot only put the $ around that:
plt.ylabel(r'H$\ddot{a}$ufigkeit')
As an aside, the \textrm command only works in text mode. The math-mode equivalent is \mathrm:
plt.ylabel(r'$\mathrm{H\ddot{a}ufigkeit}$')
UPDATE
All of the above assume that you have told matplotlib to render with tex. To do this, add the following at the top of your code:
import matplotlib.pyplot as plt
plt.rc('text', usetex=True)

python latex font xcode

I'm trying to use Matplotlib & Python in Xcode to generate scientific graphics. My boss would like them to be in LaTeX with matching fonts. I know you can modify the fonts in python with something like this:
from matplotlib import rc
rc('font',**{'family':'serif','serif':['Computer Modern Roman']})
rc('text', usetex=True)
Unfortunately, opening or saving the figure with plt.show() or plt.savefig() gives a string of errors, eventually leading to OSError: [Errno 2] No such file or directory.
I know "Google is your friend", but I haven't managed to find anything on how to go about solving this. Any help would be greatly appreciated.
I usually find it easiest to directly specify the font file I'm after. Here's an example:
import matplotlib.pyplot as plt
import matplotlib.font_manager
from numpy import *
# cmunso.otf is a randomly selected font in my tex installation
path = '/usr/local/texlive/2012/texmf-dist/fonts/opentype/public/cm-unicode/cmunso.otf'
f0 = matplotlib.font_manager.FontProperties()
f0.set_file(path)
plt.figure()
plt.xlim(0,1.)
plt.ylim(0,1.)
d = arange(0, 1, .1)
plt.plot(d, d, "ob", label='example')
plt.text(.5, .1, 'text.. abcdef', fontproperties=f0, size=30)
plt.xlabel("x label", fontproperties=f0)
plt.legend(prop=f0, loc=2)
I'm not a font expert, but I think the reason this is easier is that font selection often has a cascading set of defaults for when the way you specify the font doesn't exactly match the way the system does. The file, though, is easy to find and specify exactly (though it's obviously less portable).
Note that for xlabel and text the keyword is fontproperties and for legend it's prop.

Python matlplotlib add hyperlink to text

I've created a plot in Python using matplotlib. After annotating each line, I'd like to make the label a hyperlink (or alternatively, make the line itself a hyperlink). The text item has a property called 'url', but I've tried it and I can't figure out what, if anything, it does.
Is it possible to make text or line objects into hyperlinks?
This example shows how to set hyperlinks if you're outputting an SVG. Note that this only makes sense for SVG. If the plot is just an image, it's just an image, and images can't have hyperlinks in them.
If you want to be able to click on the object in the interactive plotting window and have that act like a hyperlink, you could create an event handler to handle the "pick" event, and have that open a browser or whatever. See this example for how to do pick events. Matplotlib plots aren't web pages or even really documents, they're just windows with graphics displayed in them, so they don't support hyperlinks as such; using a pick event you can emulate a hyperlink by opening a web browser when an object is clicked.
Edit: You are right, it doesn't work. It seems that the URL property is only read and used for certain types of objects. Googling, I see some old matplotlib mailing list discussion of it, where it seems the idea was to gradually add URL support to different artist types, but I guess they never got around to it. I would suggest you raise a bug about this on the matplotlib bug tracker.
In the meantime, there is a way to do it, but it is somewhat roundabout. The URL is drawn for PathCollection objects, so you could make a Path out of your text, then make a PathCollection out of that path, and then add that PathCollection to your plot. Here's an example:
pyplot.scatter([1, 2, 3], [4, 5, 6])
t = mpl.text.TextPath((2, 4), 'This is text', size=0.1)
pc = mpl.collections.PathCollection([t])
pc.set_urls(['http://www.google.com'])
ax = pyplot.gca()
ax.add_collection(pc)
pyplot.draw()
f = pyplot.gcf()
f.canvas.print_figure('fig.svg')
Note that you must use set_urls and not set_url. This method produces an SVG with clickable text, but it has some drawbacks. Most notably, it seems you have to set the text size manually in data coordinates, so it may take some fiddling to find a text size that isn't too ridiculously huge or tiny relative to the magnitude of your plotted data.
Adding a hyperlink makes sense when e.g. using an SVG file.
The url property works in newer matplotlib versions:
text = plt.annotate("Link", xy=(2,5), xytext=(2.2,5.5),
url='http://matplotlib.org',
bbox=dict(color='w', alpha=1e-6, url='http://matplotlib.org'))
For example, in a Jupyter notebook, which runs in a browser anyways, one could display an SVG with hyperlinks like this:
import matplotlib.pyplot as plt
from IPython.display import set_matplotlib_formats
set_matplotlib_formats("svg")
fig, ax = plt.subplots()
ax.scatter([1, 2, 3], [4, 5, 6])
text = ax.annotate("Link", xy=(2,5), xytext=(2.2,5.5),
url='http://matplotlib.org',
bbox=dict(color='w', alpha=1e-6, url='http://matplotlib.org'))
In the figure produced this way you may click on the link and be directed to matplotlib.org.
This is possible with pgf backend:
#!/usr/bin/env python3
import matplotlib
matplotlib.use("pgf")
pgf_with_custom_preamble = {
"text.usetex": True,
"pgf.preamble": [
r"\usepackage{hyperref}"
]
}
matplotlib.rcParams.update(pgf_with_custom_preamble)
from matplotlib import pyplot as plt
x = range(5)
y = range(5)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, "r-", label=r"Hyperlink: \url{http://google.com}")
ax.legend()
fig.savefig("mwe.pdf")

How do I print a PDF file to a printer in landscape from Python?

I'm using Mac OSX but I need a platform independent method to print a pdf file.
I created a graph in Matplotlib and want to print it to my printer.
I can set the orientation of the canvas to fit a portrait layout with:
fig.set_size_inches( 8.27,11.69) # set the figure size in inches
but using:
fig.set_size_inches( 11.69, 8.27)
prints a cropped portrait oriented figure
I found this on another post here:
import subprocess
import shlex
printfile='test.pdf'
fig.savefig(printfile)
proc=subprocess.Popen(shlex.split('lpr {f}'.format(f=printfile)))
Can anyone help me with the format of the code to set the print orientation to landscape?
I have seen lpr -o landscape, but do not have enough experience with it to know if it works for all printers.
Rather than changing orientation while printing, you can do it when generating the image (if it fits with your workflow). The matplotlib savefig command allows you to specify saving in landscape orientation, but currently only for postscript. That is not a problem, however, since we can easily convert the postscript file to PDF format. Below is an example.
In Python:
from pylab import *
import numpy as np
x = np.arange(0, 10, 0.1)
y = np.sin(x)
plot(x, y)
xlabel('x')
ylabel('y')
savefig('img.eps', orientation='landscape')
I left out the canvas size for convenience and brevity.
Now we have a file named img.eps. In the shell do the following.
epstopdf img.eps
Here is what the resulting img.pdf file looks like:
One downside to keep in mind with this approach is that postscript does not like transparency, so if you want transparency this is not the approach for you. To see what I mean take the matplotlib patch_collection.py example. Replace the pylab.show() on the last line with pylab.savefig('patch.pdf'), run it, and then look at the resulting PDF file. It will look like the image in the example. If, however, you do pylab.savefig('patch.eps'), you will see that the objects are all opaque.

Categories

Resources