Python Matplotlib xkcd: Fonts Humor Sans missing - python

I have tried to install xkcd fonts 'Humor Sans', but unfortunately, I am not able to use the fonts. Here, is what I did so far:
iPad Pro
installed carnets and juno (for Jupyter notebooks)
Matplotlib version 3.1.1
installed iFonts to install Humor Sans
run xkcd examples, e.g.
%pylab inline
plt.xkcd()
plt.plot(sin(linspace(0,10)))
plt.title('Whoo Hoo!!!')
The plot, with the exception of the fonts is working as expected. The error message gives me, what I expected:
findfont: Font family ['xkcd', 'xkcd Script', 'Humor Sans', 'Comic Sans MS'] not found. Falling back to DejaVu Sans.
The font manager does not find any Humor* Sans although in the Settings -> General -> Fonts, I do find the fonts.
import matplotlib.font_manager
x = [f.name for f in matplotlib.font_manager.fontManager.ttflist if f.name.startswith('Humor')]
print(set(x))
the outcome is an empty result:
set()
Since, the folder structure is not clear using the iPad, i have copied the HumorSans.ttf file in a folder "Fonts", e.g.
import matplotlib.font_manager as fm
import matplotlib.pyplot as plot
import numpy as np
font_path = 'Fonts/Humor-Sans.ttf'
my_font = fm.FontProperties(fname=font_path)
plt.xkcd()
fig, ax = plt.subplot()
x = np.linspace(0,3)
y = np.sin(x)
ax.bar(x, y, color='green')
ax.set_xlabel(u'Some x label', fontproperties=my_font)
ax.set_ylabel(u'Some y label', fontproperties=my_font)
ax.set_title(u'Some fancy title', fontproperties=my_font)
for label in ax.get_xticklabels():
label.set_fontproperties(my_font)
This works as expected. The fonts is the Humor Sans fonts, where I have explicitly changed the fonts. For all other texts, e.g. yticklabels, the fonts is the standard fonts such that the problem is still there.
How can I correctly install, either directly or load in each notebook the fonts. The problem is that i don't have access to the folders, I need to work in, e.g. matplotlib.get_cachedir()
/private/var/mobile/Containers/Data/Application/AAD3....5693/tmp/matplotlib-nu6taz9_
Any help?

Solved!
Luke Davis Helped with his post: How can I configure matplotlib to be able to read fonts from a local path?
#!/usr/bin/env python3
# Imports
import os
import re
import shutil
from glob import glob
from matplotlib import matplotlib_fname
from matplotlib import get_cachedir
# Copy files over
_dir_data = re.sub('/matplotlibrc$', '', matplotlib_fname())
dir_source = '<your-font-directory-here>'
dir_dest = f'{_dir_data}/fonts/ttf'
# print(f'Transfering .ttf and .otf files from {dir_source} to {dir_dest}.')
for file in glob(f'{dir_source}/*.[ot]tf'):
if not os.path.exists(f'{dir_dest}/{os.path.basename(file)}'):
print(f'Adding font "{os.path.basename(file)}".')
shutil.copy(file, dir_dest)
# Delete cache
dir_cache = get_cachedir()
for file in glob(f'{dir_cache}/*.cache') + glob(f'{dir_cache}/font*'):
if not os.path.isdir(file): # don't dump the tex.cache folder... because dunno why
os.remove(file)
print(f'Deleted font cache {file}.')

Related

How to use rc param `usetex=True` with other fonts in matplotlib

I want to plot with latex and other fonts but only latex font seems to be available. How can I enable other fonts with usetex?
import numpy as np
import matplotlib.pyplot as plt
plt.rc('text', usetex=True)
plt.rc('font', family='Arial')
plt.imshow(np.random.randn(100, 100))
plt.title('This is a test')
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.show()
Plotted image
Using usetex=True
You have to use your own LaTeX header for matplotlib.
You can then use the fontpackages to select fonts.
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['text.usetex'] = True
plt.rcParams['text.latex.unicode'] = True
plt.rcParams['text.latex.preamble'] = r'''
\usepackage{mathtools}
\usepackage{helvet}
\renewcommand{\familydefault}{\sfdefault}
% more packages here
'''
plt.imshow(np.random.randn(100, 100))
plt.title('This is a test')
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.savefig('test.pdf')
Result:
using the pgf backend
You get the most flexibility by using the pgf backend. This needs a recent LaTeX installation in the system.
For me, the most practical approach is to have a matplotlibrc and a header-matplotlib.tex and just include the texfile in the matplotlibrc. However, because matplotlib runs tex in a tmp directory, you need to add the current directory to the TEXINPUTS.
Example:
matplotlibrc
backend: pgf # use the pgf backend
pgf.rcfonts : False # setup the fonts yourself in the header
text.usetex : True
text.latex.unicode : True
pgf.texsystem : lualatex
pgf.preamble : \input{header-matplotlib.tex}
header-matplotlib.tex
\usepackage{fontspec}
\setsansfont{Arial} # for the example I used Fira Sans
\usepackage{amssymb}
\usepackage{mathtools}
\usepackage{unicode-math}
\setmathfont{Latin Modern Math}
% more packages here
pgf_plot.py (This got much smaller, as we are setting the options in `matplotlibrc)
import matplotlib.pyplot as plt
import numpy as np
plt.imshow(np.random.randn(100, 100))
plt.title('This is a test')
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.savefig('test.pdf')
Run using
$ TEXINPUTS=$(pwd): python pgf_plots.py
Result:
This approach can be extended, to match the fonts and font size of the plot to the ones used in your document, see the example here:
https://github.com/Python4AstronomersAndParticlePhysicists/PythonWorkshop-ICE/tree/master/examples/use_system_latex

_tkinter.TclError: no display name and no $DISPLAY environment variable for new OS X Mavericks

This is my first post so please be forgiving.
I am trying to create an animation using matplotlib, and I could do this perfectly well until a few days ago, until I upgraded to OS X Mavericks, upon which all hell broke loose. Note, the below code worked in OS X Lion.
Suddenly I was getting an error message whilst trying to run this code in Mavericks:
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import pandas as pd
import mpl_toolkits.mplot3d.axes3d as p3
#----->READ AND FORMAT DATA-----------------------------------------------#
.
.
.
.
#-------------------------------------------------------------------------#
# Set up figure & 3D axis for animation
fig = plt.figure(figsize=(10,10))
ax = fig.add_axes([0, 0, 1, 1], projection='3d') # Correct way to make 3D plots
# set up lines and points
lines = sum([ax.plot([], [], [], '-')], [])
pts = sum([ax.plot([], [], [], 'o')], [])
# Setting the axes properties
ax.set_xlim3d([-2000, 2000])
ax.set_xlabel('X')
ax.set_ylim3d([1350, 1400])
ax.set_ylabel('Y')
ax.set_zlim3d([-340, 600])
ax.set_zlabel('Z')
# set point-of-view: specified by (altitude degrees, azimuth degrees)
ax.view_init(40, 0)
ax.set_title('animation')
ax.grid() # add grid
# Initialisation function: plot the background of each frame
def init():
for line, pt in zip(lines, pts):
# Lines
line.set_data([], [])
line.set_3d_properties([])
# Points
pt.set_data([], [])
pt.set_3d_properties([])
return lines + pts
# Animation function. This will be called sequentially with the frame number
def animate(i):
# we'll step two time-steps per frame. This leads to nice results.
i = (2.5 * i)
for line, pt, dat in zip(lines, pts, data):
x, y, z = dat[:i].T
# Lines
line.set_data(x, y)
line.set_3d_properties(z)
# Points
pt.set_data(x[-1:], y[-1:])
pt.set_3d_properties(z[-1:])
ax.view_init(45, i)
fig.canvas.draw()
return lines + pts
#----->ANIMATION-----------------------------------------------------------#
# Creating the Animation object
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=120, interval=30, blit=True)
# Save as mp4. This requires mplayer or ffmpeg to be installed
# anim.save('one_marker_3D.mp4', fps=15, extra_args=['-vcodec', 'libx264'])
plt.show()
This produces the following error message:
Traceback (most recent call last):
File "shoulder_animation_walking_straight_3D.py", line 26, in <module>
fig = plt.figure(figsize=(10,10))
File "/Users/me/anaconda/lib/python2.7/site-packages/matplotlib/pyplot.py", line 423, in figure
**kwargs)
File "/Users/me/anaconda/lib/python2.7/site-packages/matplotlib/backends/backend_tkagg.py", line 79, in new_figure_manager
return new_figure_manager_given_figure(num, figure)
File "/Users/me/anaconda/lib/python2.7/site-packages/matplotlib/backends/backend_tkagg.py", line 87, in new_figure_manager_given_figure
window = Tk.Tk()
File "/Users/me/anaconda/lib/python2.7/lib-tk/Tkinter.py", line 1745, in __init__
self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
_tkinter.TclError: no display name and no $DISPLAY environment variable
Now as you can hopefully see, I have attempted to use the TkAgg backend instead of the 'Agg' backend, as recommended by the documentation (https://wiki.python.org/moin/TkInter). This seems to cause problems with the new version of OS X because X11 is not supported anymore, and XQuartz has to be installed instead. This I have done too. But it still give me that same error.
I also tried installing py33-tkinter using macports, but that didn't work either, but perhaps I did not do it properly. I simply did
sudo port py33-tkinter
I am still fairly new to python and unix in general.
UPDATE (22/12/13):
I did the following
Python 2.7.5 |Anaconda 1.8.0 (x86_64)| (default, Oct 24 2013, 07:02:20)
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import Tkinter
Then ran the Tkinter test
>>> Tkinter._test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/me/anaconda/lib/python2.7/lib-tk/Tkinter.py", line 3770, in _test
root = Tk()
File "/Users/me/anaconda/lib/python2.7/lib-tk/Tkinter.py", line 1745, in __init__
self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
_tkinter.TclError: no display name and no $DISPLAY environment variable
>>>
Might be helpful to someone who knows this better than me.
SOLUTION
This was solved by doing the following, which was caused by a problem with XQuartz;
launchctl load -w /Library/LaunchAgents/org.macosforge.xquartz.startx.plist
I thought that a reinstall of XQuartz would re-enable launchd, but apparently that did not happen. Now it all works.
This was solved by doing the following
matplotlib.use('Agg')
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')
above this two lines must be in the top codes

Convert SVG to PNG in Python

How do I convert an svg to png, in Python? I am storing the svg in an instance of StringIO. Should I use the pyCairo library? How do I write that code?
Here is what I did using cairosvg:
from cairosvg import svg2png
svg_code = """
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12" y2="16"/>
</svg>
"""
svg2png(bytestring=svg_code,write_to='output.png')
And it works like a charm!
See more: cairosvg document
The answer is "pyrsvg" - a Python binding for librsvg.
There is an Ubuntu python-rsvg package providing it. Searching Google for its name is poor because its source code seems to be contained inside the "gnome-python-desktop" Gnome project GIT repository.
I made a minimalist "hello world" that renders SVG to a cairo
surface and writes it to disk:
import cairo
import rsvg
img = cairo.ImageSurface(cairo.FORMAT_ARGB32, 640,480)
ctx = cairo.Context(img)
## handle = rsvg.Handle(<svg filename>)
# or, for in memory SVG data:
handle= rsvg.Handle(None, str(<svg data>))
handle.render_cairo(ctx)
img.write_to_png("svg.png")
Update: as of 2014 the needed package for Fedora Linux distribution is: gnome-python2-rsvg. The above snippet listing still works as-is.
Install Inkscape and call it as command line:
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -e ${dest_png}
You can also snap specific rectangular area only using parameter -j, e.g. co-ordinate "0:125:451:217"
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -a ${coordinates} -e ${dest_png}
If you want to show only one object in the SVG file, you can specify the parameter -i with the object id that you have setup in the SVG. It hides everything else.
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -i ${object} -j -a ${coordinates} -e ${dest_png}
I'm using Wand-py (an implementation of the Wand wrapper around ImageMagick) to import some pretty advanced SVGs and so far have seen great results! This is all the code it takes:
with wand.image.Image( blob=svg_file.read(), format="svg" ) as image:
png_image = image.make_blob("png")
I just discovered this today, and felt like it was worth sharing for anyone else who might straggle across this answer as it's been a while since most of these questions were answered.
NOTE: Technically in testing I discovered you don't even actually have to pass in the format parameter for ImageMagick, so with wand.image.Image( blob=svg_file.read() ) as image: was all that was really needed.
EDIT: From an attempted edit by qris, here's some helpful code that lets you use ImageMagick with an SVG that has a transparent background:
from wand.api import library
import wand.color
import wand.image
with wand.image.Image() as image:
with wand.color.Color('transparent') as background_color:
library.MagickSetBackgroundColor(image.wand,
background_color.resource)
image.read(blob=svg_file.read(), format="svg")
png_image = image.make_blob("png32")
with open(output_filename, "wb") as out:
out.write(png_image)
I did not find any of the answers satisfactory. All the mentioned libraries have some problem or the other like Cairo dropping support for python 3.6 (they dropped Python 2 support some 3 years ago!). Also, installing the mentioned libraries on the Mac was a pain.
Finally, I found the best solution was svglib + reportlab. Both installed without a hitch using pip and first call to convert from svg to png worked beautifully! Very happy with the solution.
Just 2 commands do the trick:
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
drawing = svg2rlg("my.svg")
renderPM.drawToFile(drawing, "my.png", fmt="PNG")
Are there any limitations with these I should be aware of?
Try this: http://cairosvg.org/
The site says:
CairoSVG is written in pure python and only depends on Pycairo. It is
known to work on Python 2.6 and 2.7.
Update November 25, 2016:
2.0.0 is a new major version, its changelog includes:
Drop Python 2 support
Another solution I've just found here How to render a scaled SVG to a QImage?
from PySide.QtSvg import *
from PySide.QtGui import *
def convertSvgToPng(svgFilepath,pngFilepath,width):
r=QSvgRenderer(svgFilepath)
height=r.defaultSize().height()*width/r.defaultSize().width()
i=QImage(width,height,QImage.Format_ARGB32)
p=QPainter(i)
r.render(p)
i.save(pngFilepath)
p.end()
PySide is easily installed from a binary package in Windows (and I use it for other things so is easy for me).
However, I noticed a few problems when converting country flags from Wikimedia, so perhaps not the most robust svg parser/renderer.
A little extension on the answer of jsbueno:
#!/usr/bin/env python
import cairo
import rsvg
from xml.dom import minidom
def convert_svg_to_png(svg_file, output_file):
# Get the svg files content
with open(svg_file) as f:
svg_data = f.read()
# Get the width / height inside of the SVG
doc = minidom.parse(svg_file)
width = int([path.getAttribute('width') for path
in doc.getElementsByTagName('svg')][0])
height = int([path.getAttribute('height') for path
in doc.getElementsByTagName('svg')][0])
doc.unlink()
# create the png
img = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
ctx = cairo.Context(img)
handler = rsvg.Handle(None, str(svg_data))
handler.render_cairo(ctx)
img.write_to_png(output_file)
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="svg_file",
help="SVG input file", metavar="FILE")
parser.add_argument("-o", "--output", dest="output", default="svg.png",
help="PNG output file", metavar="FILE")
args = parser.parse_args()
convert_svg_to_png(args.svg_file, args.output)
Here is a another solution without using rsvg(which is currently not available for windows).Only install cairosvg using pip install CairoSVG
svg2png.py
from cairosvg import svg2png
svg_code = open("input.svg", 'rt').read()
svg2png(bytestring=svg_code,write_to='output.png')
SVG scaling and PNG rendering
Using pycairo and librsvg I was able to achieve SVG scaling and rendering to a bitmap. Assuming your SVG is not exactly 256x256 pixels, the desired output, you can read in the SVG to a Cairo context using rsvg and then scale it and write to a PNG.
main.py
import cairo
import rsvg
width = 256
height = 256
svg = rsvg.Handle('cool.svg')
unscaled_width = svg.props.width
unscaled_height = svg.props.height
svg_surface = cairo.SVGSurface(None, width, height)
svg_context = cairo.Context(svg_surface)
svg_context.save()
svg_context.scale(width/unscaled_width, height/unscaled_height)
svg.render_cairo(svg_context)
svg_context.restore()
svg_surface.write_to_png('cool.png')
RSVG C binding
From the Cario website with some minor modification. Also a good example of how to call a C-library from Python
from ctypes import CDLL, POINTER, Structure, byref, util
from ctypes import c_bool, c_byte, c_void_p, c_int, c_double, c_uint32, c_char_p
class _PycairoContext(Structure):
_fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
("ctx", c_void_p),
("base", c_void_p)]
class _RsvgProps(Structure):
_fields_ = [("width", c_int), ("height", c_int),
("em", c_double), ("ex", c_double)]
class _GError(Structure):
_fields_ = [("domain", c_uint32), ("code", c_int), ("message", c_char_p)]
def _load_rsvg(rsvg_lib_path=None, gobject_lib_path=None):
if rsvg_lib_path is None:
rsvg_lib_path = util.find_library('rsvg-2')
if gobject_lib_path is None:
gobject_lib_path = util.find_library('gobject-2.0')
l = CDLL(rsvg_lib_path)
g = CDLL(gobject_lib_path)
g.g_type_init()
l.rsvg_handle_new_from_file.argtypes = [c_char_p, POINTER(POINTER(_GError))]
l.rsvg_handle_new_from_file.restype = c_void_p
l.rsvg_handle_render_cairo.argtypes = [c_void_p, c_void_p]
l.rsvg_handle_render_cairo.restype = c_bool
l.rsvg_handle_get_dimensions.argtypes = [c_void_p, POINTER(_RsvgProps)]
return l
_librsvg = _load_rsvg()
class Handle(object):
def __init__(self, path):
lib = _librsvg
err = POINTER(_GError)()
self.handle = lib.rsvg_handle_new_from_file(path.encode(), byref(err))
if self.handle is None:
gerr = err.contents
raise Exception(gerr.message)
self.props = _RsvgProps()
lib.rsvg_handle_get_dimensions(self.handle, byref(self.props))
def get_dimension_data(self):
svgDim = self.RsvgDimensionData()
_librsvg.rsvg_handle_get_dimensions(self.handle, byref(svgDim))
return (svgDim.width, svgDim.height)
def render_cairo(self, ctx):
"""Returns True is drawing succeeded."""
z = _PycairoContext.from_address(id(ctx))
return _librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)
Here is an approach where Inkscape is called by Python.
Note that it suppresses certain crufty output that Inkscape writes to the console (specifically, stderr and stdout) during normal error-free operation. The output is captured in two string variables, out and err.
import subprocess # May want to use subprocess32 instead
cmd_list = [ '/full/path/to/inkscape', '-z',
'--export-png', '/path/to/output.png',
'--export-width', 100,
'--export-height', 100,
'/path/to/input.svg' ]
# Invoke the command. Divert output that normally goes to stdout or stderr.
p = subprocess.Popen( cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
# Below, < out > and < err > are strings or < None >, derived from stdout and stderr.
out, err = p.communicate() # Waits for process to terminate
# Maybe do something with stdout output that is in < out >
# Maybe do something with stderr output that is in < err >
if p.returncode:
raise Exception( 'Inkscape error: ' + (err or '?') )
For example, when running a particular job on my Mac OS system, out ended up being:
Background RRGGBBAA: ffffff00
Area 0:0:339:339 exported to 100 x 100 pixels (72.4584 dpi)
Bitmap saved as: /path/to/output.png
(The input svg file had a size of 339 by 339 pixels.)
Try this python script:
Don't forget to install cairosvg: pip3 install cairosvg
#!/usr/bin/env python3
import os
import cairosvg
for file in os.listdir('.'):
if os.path.isfile(file) and file.endswith(".svg"):
name = file.split('.svg')[0]
cairosvg.svg2png(url=name+'.svg',write_to=name+'.png')
Try using Gtk.Image and Gdk.Pixbuf
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gdk, Gtk
from PIL import Image
image = Gtk.Image()
image.set_from_file("path/to/image.svg")
pb = image.get_pixbuf()
pb.savev("path/to/convented/image.jpeg","jpeg",[],[])
im = Image.open("path/to/convented/image.jpeg")
pix = im.load()
print(pix[1,1])
Actually, I did not want to be dependent of anything else but Python (Cairo, Ink.., etc.)
My requirements were to be as simple as possible, at most, a simple pip install "savior" would suffice, that's why any of those above didn't suit for me.
I came through this (going further than Stackoverflow on the research).
https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/
Looks good, so far. So I share it in case anyone in the same situation.
All the answer's here are great, but I figure I'll mention that I have made a simple library that loads SVG's files as pillow Image instances which can then be exported. It uses inkscape like in blj's answer, but renders to stdout so that no temporary files are made. There's some basic usage stuff in the README.
https://github.com/jlwoolf/pillow-svg
EDIT:
As suggested, here's a brief explanation, since the link could become invalid:
The library uses inkscape's command line interface to convert the image to a png of a specific size or dpi using the python subprocess library. By setting --export-filename to -, inkscape redirects the output to the stdout. The first two lines are discarded, and the remaining output is passed to PIL.Image.open, converting it to pillow image instance.
import subprocess
from PIL import Image
options = ["inkscape", "--export-filename=-", "--export-type=png", "file.svg"]
pipe = subprocess.Popen(options, stdout=subprocess.PIPE)
pipe.stdout.readline()
pipe.stdout.readline()
img = Image.open(pipe.stdout)
From there you can do whatever pillow image operations you need (like export as a jpg, resize, crop, etc).
EDIT 2:
Just added support for skia-python (haven't fully tested it, but seems to work so far). This way you can convert an svg to png with only a single pip install (no need to use inkscape).
Here is an explanation of how the library uses skia-python:
First, the svg file is loaded into a skia.SVGDOM. From there you can grab the SVGDOM's dimensions, using containerSize. Then a skia.Surface of the desired image output size is made. The canvas is scaled to fit the svg to the surface, and then the svg is rendered. From there, an image snapshot can be made, which can then be fed to PIL.Image.open.
import skia
from PIL import Image
skia_stream = skia.Stream.MakeFromFile("file.svg")
skia_svg = skia.SVGDOM.MakeFromStream(skia_stream)
svg_width, svg_height = skia_svg.containerSize()
surface_width, surface_height = 512, 512
surface = skia.Surface(surface_width, surface_height)
with surface as canvas:
canvas.scale(surface_width / svg_width, surface_height / svg_height)
skia_svg.render(canvas)
with io.BytesIO(surface.makeImageSnapshot().encodeToData()) as f:
img = Image.open(f)
img.load()
Edit 3:
I have fleshed out the library much much more. There is a command line utility now for easy svg conversion, along with more documentation explaining usage. Hope it helps!
Posting my code from this StackOverflow answer. It's a workaround to svglib+reportlib not supporting a transparent background and no scaling (see #sarang's answer and #ualter-jr's answer as well as these Github issues on scaling not working and this one on transparency)
This uses pyMuPDF to render an intermediate pdf from reportlab to PNG.
The big advantage is that it doesn't need any external libraries as pymupdf comes with precompiled wheels for Windows, Linux and MacOS.
The whole thing is as easy as
pip install pymupdf svglib
and then executing the following lines
import fitz
from svglib import svglib
from reportlab.graphics import renderPDF
# Convert svg to pdf in memory with svglib+reportlab
# directly rendering to png does not support transparency nor scaling
drawing = svglib.svg2rlg(path="input.svg")
pdf = renderPDF.drawToString(drawing)
# Open pdf with fitz (pyMuPdf) to convert to PNG
doc = fitz.Document(stream=pdf)
pix = doc.load_page(0).get_pixmap(alpha=True, dpi=300)
pix.save("output.png")

List of all available matplotlib backends

The current backend name is accessible via
>>> import matplotlib.pyplot as plt
>>> plt.get_backend()
'GTKAgg'
Is there a way to get a list of all backends that can be used on a particular machine?
You can access the lists
matplotlib.rcsetup.interactive_bk
matplotlib.rcsetup.non_interactive_bk
matplotlib.rcsetup.all_backends
the third being the concatenation of the former two. If I read the source code correctly, those lists are hard-coded though, and don't tell you what backends are actually usable. There is also
matplotlib.rcsetup.validate_backend(name)
but this also only checks against the hard-coded list.
Here is a modification of the script posted previously. It finds all supported backends, validates them and measures their fps. On OSX it crashes python when it comes to tkAgg, so use at your own risk ;)
from __future__ import print_function, division, absolute_import
from pylab import *
import time
import matplotlib.backends
import matplotlib.pyplot as p
import os.path
def is_backend_module(fname):
"""Identifies if a filename is a matplotlib backend module"""
return fname.startswith('backend_') and fname.endswith('.py')
def backend_fname_formatter(fname):
"""Removes the extension of the given filename, then takes away the leading 'backend_'."""
return os.path.splitext(fname)[0][8:]
# get the directory where the backends live
backends_dir = os.path.dirname(matplotlib.backends.__file__)
# filter all files in that directory to identify all files which provide a backend
backend_fnames = filter(is_backend_module, os.listdir(backends_dir))
backends = [backend_fname_formatter(fname) for fname in backend_fnames]
print("supported backends: \t" + str(backends))
# validate backends
backends_valid = []
for b in backends:
try:
p.switch_backend(b)
backends_valid += [b]
except:
continue
print("valid backends: \t" + str(backends_valid))
# try backends performance
for b in backends_valid:
ion()
try:
p.switch_backend(b)
clf()
tstart = time.time() # for profiling
x = arange(0,2*pi,0.01) # x-array
line, = plot(x,sin(x))
for i in arange(1,200):
line.set_ydata(sin(x+i/10.0)) # update the data
draw() # redraw the canvas
print(b + ' FPS: \t' , 200/(time.time()-tstart))
ioff()
except:
print(b + " error :(")
To just see supported interactive backends see:
#!/usr/bin/env python
from __future__ import print_function
import matplotlib.pyplot as plt
import matplotlib
backends = matplotlib.rcsetup.interactive_bk
# validate backends
backends_valid = []
for b in backends:
try:
plt.switch_backend(b)
backends_valid += [b]
except:
continue
print(backends_valid)
You can pretend to put a wrong backend argument, then it will return you a ValueError with the list of valid matplotlib backends, like this:
Input:
import matplotlib
matplotlib.use('WRONG_ARG')
Output:
ValueError: Unrecognized backend string 'test': valid strings are ['GTK3Agg', 'GTK3Cairo', 'MacOSX', 'nbAgg', 'Qt4Agg', 'Qt4Cairo', 'Qt5Agg', 'Qt
5Cairo', 'TkAgg', 'TkCairo', 'WebAgg', 'WX', 'WXAgg', 'WXCairo', 'agg', 'cairo', 'pdf', 'pgf', 'ps', 'svg', 'template']
There is the hard-coded list mentioned by Sven, but to find every backend which Matplotlib can use (based on the current implementation for setting up a backend) the matplotlib/backends folder can be inspected.
The following code does this:
import matplotlib.backends
import os.path
def is_backend_module(fname):
"""Identifies if a filename is a matplotlib backend module"""
return fname.startswith('backend_') and fname.endswith('.py')
def backend_fname_formatter(fname):
"""Removes the extension of the given filename, then takes away the leading 'backend_'."""
return os.path.splitext(fname)[0][8:]
# get the directory where the backends live
backends_dir = os.path.dirname(matplotlib.backends.__file__)
# filter all files in that directory to identify all files which provide a backend
backend_fnames = filter(is_backend_module, os.listdir(backends_dir))
backends = [backend_fname_formatter(fname) for fname in backend_fnames]
print backends
You can also see some documentation for a few backends here:
http://matplotlib.org/api/index_backend_api.html
the pages lists just a few backends, some of them don't have a proper documentation:
matplotlib.backend_bases
matplotlib.backends.backend_gtkagg
matplotlib.backends.backend_qt4agg
matplotlib.backends.backend_wxagg
matplotlib.backends.backend_pdf
matplotlib.dviread
matplotlib.type1font
What about this?
%matplotlib --list
Available matplotlib backends: ['tk', 'gtk', 'gtk3', 'wx', 'qt4', 'qt5', 'qt', 'osx', 'nbagg', 'notebook', 'agg', 'svg', 'pdf', 'ps', 'inline', 'ipympl', 'widget']
You could look at the following folder for a list of possible backends...
/Library/Python/2.6/site-packages/matplotlib/backends
/usr/lib64/Python2.6/site-packages/matplotlib/backends

How can I find the full path to a font from its display name on a Mac?

I am using the Photoshop's javascript API to find the fonts in a given PSD.
Given a font name returned by the API, I want to find the actual physical font file that font name corresponds to on the disc.
This is all happening in a python program running on OSX so I guess I'm looking for one of:
Some Photoshop javascript
A Python function
An OSX API that I can call from python
Unfortunately the only API that isn't deprecated is located in the ApplicationServices framework, which doesn't have a bridge support file, and thus isn't available in the bridge. If you're wanting to use ctypes, you can use ATSFontGetFileReference after looking up the ATSFontRef.
Cocoa doesn't have any native support, at least as of 10.5, for getting the location of a font.
open up a terminal (Applications->Utilities->Terminal) and type this in:
locate InsertFontHere
This will spit out every file that has the name you want.
Warning: there may be alot to wade through.
I haven't been able to find anything that does this directly. I think you'll have to iterate through the various font folders on the system: /System/Library/Fonts, /Library/Fonts, and there can probably be a user-level directory as well ~/Library/Fonts.
There must be a method in Cocoa to get a list of fonts, then you would have to use the PyObjC bindings to call it..
Depending on what you need them for, you could probably just use something like the following..
import os
def get_font_list():
fonts = []
for font_path in ["/Library/Fonts", os.path.expanduser("~/Library/Fonts")]:
if os.path.isdir(font_path):
fonts.extend(
[os.path.join(font_path, cur_font)
for cur_font in os.listdir(font_path)
]
)
return fonts
I had encountered similar requirements and I ended up by this method:
def get_font_path(font):
ttf_filename = os.path.basename(font)
dirs = []
if sys.platform == "win32":
# check the windows font repository
# NOTE: must use uppercase WINDIR, to work around bugs in
# 1.5.2's os.environ.get()
windir = os.environ.get("WINDIR")
if windir:
dirs.append(os.path.join(windir, "fonts"))
elif sys.platform in ("linux", "linux2"):
lindirs = os.environ.get("XDG_DATA_DIRS", "")
if not lindirs:
# According to the freedesktop spec, XDG_DATA_DIRS should
# default to /usr/share
lindirs = "/usr/share"
dirs += [
os.path.join(lindir, "fonts") for lindir in lindirs.split(":")
]
elif sys.platform == "darwin":
dirs += [
"/Library/Fonts",
"/System/Library/Fonts",
os.path.expanduser("~/Library/Fonts"),
]
ext = os.path.splitext(ttf_filename)[1]
first_font_with_a_different_extension = None
for directory in dirs:
for walkroot, walkdir, walkfilenames in os.walk(directory):
for walkfilename in walkfilenames:
if ext and walkfilename == ttf_filename:
return os.path.join(walkroot, walkfilename)
elif (
not ext
and os.path.splitext(walkfilename)[0] == ttf_filename
):
fontpath = os.path.join(walkroot, walkfilename)
if os.path.splitext(fontpath)[1] == ".ttf":
return fontpath
if (
not ext
and first_font_with_a_different_extension is None
):
first_font_with_a_different_extension = fontpath
if first_font_with_a_different_extension:
return first_font_with_a_different_extension
Note that the original code is from PIL
With matplotlib (pip3 install -U matplotlib):
from matplotlib import font_manager
fontmap = {font.name: font for font in font_manager.fontManager.ttflist}
fontmap.update({font.name: font for font in font_manager.fontManager.afmlist})
print(f'Total fonts: {len(fontmap.keys())}')
for family in sorted(fontmap.keys()):
font = fontmap[family]
print(f'{family:<30}: {font.fname}')
Sample output
Total fonts: 312
.Aqua Kana : /System/Library/Fonts/AquaKana.ttc
Academy Engraved LET : /System/Library/Fonts/Supplemental/Academy Engraved LET Fonts.ttf
Al Bayan : /System/Library/Fonts/Supplemental/AlBayan.ttc
American Typewriter : /System/Library/Fonts/Supplemental/AmericanTypewriter.ttc
...
Zapf Dingbats : /System/Library/Fonts/ZapfDingbats.ttf
ZapfDingbats : /usr/local/lib/python3.9/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/ZapfDingbats.afm
Zapfino : /System/Library/Fonts/Supplemental/Zapfino.ttf
NOTE: The font families from matplotlib do not seem to include all system fonts that are available for example to the PyQt5 library:
from PyQt5.QtGui import QFontDatabase
from PyQt5.QtWidgets import QApplication
app = QApplication([])
print('\n'.join(QFontDatabase().families()))

Categories