Issue 1:
Bug report

Bug summary

Got 'Could not convert glyph to bitmap' error when run matplotlib fig.savefig() in parallel

Recently we got this 'Could not convert glyph to bitmap' error from time to time after we implemented a multi-process solution of the pipeline. That is, there could be multiple processes plotting and saving the figures simultaneously. We had removed all individual fig.savefig() invoking at the top level and forced to use util function in every place. Now the error tracks back to that last fig.savefig() within util function which was pretty bad. It seemed that the more complicated the figure, the more processes running at the same time, the higher possibility we got this issue.

Code for reproduction

# Paste your code here
# We used the following line to save a figure in utli.save_figures()
fig.savefig(full_path, pad_inches=pad_inches, dpi=dpi)

Actual outcome

# If applicable, paste the console output here
File "analysis/util.py", line 1041, in save_figures
fig.savefig(full_path, pad_inches=pad_inches, dpi=dpi)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/figure.py", line 2062, in savefig
self.canvas.print_figure(fname, **kwargs)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/backend_bases.py", line 2263, in print_figure
**kwargs)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/backends/backend_agg.py", line 517, in print_png
FigureCanvasAgg.draw(self)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/backends/backend_agg.py", line 437, in draw
self.figure.draw(self.renderer)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/figure.py", line 1493, in draw
renderer, self, artists, self.suppressComposite)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/axes/_base.py", line 2635, in draw
mimage._draw_list_compositing_images(renderer, self, artists)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/text.py", line 755, in draw
ismath=ismath, mtext=mtext)
File "/root/eureka1.0/local/lib/python2.7/site-packages/matplotlib/backends/backend_agg.py", line 212, in draw_text
font.draw_glyphs_to_bitmap(antialiased=rcParams['text.antialiased'])
RuntimeError: In draw_glyphs_to_bitmap: Could not convert glyph to bitmap

Expected outcome

All figures are successfully saved.

Matplotlib version

Operating system: Ubuntu 16.04.4

Matplotlib version: 2.2.4

Matplotlib backend (print(matplotlib.get_backend())): agg

Python version: 2.7.12

Issue 2:
Bug report

Bug summary

When storing multiple PDFs using parallel processes (not threads!) a RuntimeError occurs. This happens regardless of the library used for parallelization, i.e. concurrent, multiprocessing or joblib. The error is either RuntimeError: In set_text: could not load glyph (generated by the code below) or RuntimeError: In load_char: Could not load charcode (which I can't seem to reproduce yet, but maybe you have a better idea of the link behind the two).

Code for reproduction

from multiprocessing import Pool
from joblib import Parallel, delayed
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed

def model_handler(n):
import matplotlib.pyplot as plt
fig_out = plt.figure(figsize=(1, 1))
axes = fig_out.add_subplot(1,1,1)
axes.scatter(range(1000), range(1000))
fig_out.savefig('dummyfig{}.pdf'.format(n), format='pdf')
plt.close()
return n

n_jobs = 2
n_workers = 2

print('List comprehension (serial)... ', end='')
try:
[model_handler(j) for j in range(n_jobs)]
print('SUCCESS')
except:
print('FAILURE')

print('Parallel processes via multiprocessing package... ', end='')
try:
Pool(processes=n_workers).map(model_handler, range(n_jobs))
print('SUCCESS')
except:
print('FAILURE')

print('Parallel processes via joblib package (loky backend)... ', end='')
try:
Parallel(n_jobs=n_workers, backend='loky')(delayed(model_handler)(i) for i in range(n_jobs))
print('SUCCESS')
except:
print('FAILURE')

print('Parallel threads via joblib package... ', end='')
try:
Parallel(n_jobs=n_workers, backend='threading')(delayed(model_handler)(i) for i in range(n_jobs))
print('SUCCESS')
except:
print('FAILURE')

print('Parallel processes via joblib package... ', end='')
try:
Parallel(n_jobs=n_workers, backend='multiprocessing')(delayed(model_handler)(i) for i in range(n_jobs))
print('SUCCESS')
except:
print('FAILURE')

print('Parallel threads via concurrent.futures package... ', end='')
with ThreadPoolExecutor(max_workers=n_workers) as pool:
futures = [pool.submit(model_handler, i) for i in range(n_jobs)]
exceptions = [x.exception() for x in as_completed(futures)]
if all(exception is None for exception in exceptions):
print('SUCCESS')
else:
print('FAILURE')

print('Parallel processes via concurrent.futures package... ', end='')
with ProcessPoolExecutor(max_workers=n_workers) as pool:
futures = [pool.submit(model_handler, i) for i in range(n_jobs)]
exceptions = [x.exception() for x in as_completed(futures)]
if all(exception is None for exception in exceptions):
print('SUCCESS')
else:
print('FAILURE')

Actual outcome

List comprehension (serial)... SUCCESS
Parallel processes via multiprocessing package... FAILURE
Parallel processes via joblib package (loky backend)... SUCCESS
Parallel threads via joblib package... SUCCESS
Parallel processes via joblib package... FAILURE
Parallel threads via concurrent.futures package... SUCCESS
Parallel processes via concurrent.futures package... FAILURE

Traceback

"""
Traceback (most recent call last):
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/multiprocessing/pool.py", line 121, in worker
result = (True, func(*args, **kwds))
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/multiprocessing/pool.py", line 44, in mapstar
return list(map(*args))
File "<ipython-input-16-c44ed7855af1>", line 13, in model_handler
fig_out.savefig('dummyfig{}.pdf'.format(n), format='pdf')
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/figure.py", line 2035, in savefig
self.canvas.print_figure(fname, **kwargs)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/backend_bases.py", line 2263, in print_figure
**kwargs)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/backends/backend_pdf.py", line 2586, in print_pdf
self.figure.draw(renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/figure.py", line 1475, in draw
renderer, self, artists, self.suppressComposite)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/axes/_base.py", line 2607, in draw
mimage._draw_list_compositing_images(renderer, self, artists)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/axis.py", line 1192, in draw
renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/axis.py", line 1130, in _get_tick_bboxes
extent = tick.label1.get_window_extent(renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/text.py", line 922, in get_window_extent
bbox, info, descent = self._get_layout(self._renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/text.py", line 300, in _get_layout
ismath=False)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/backends/backend_pdf.py", line 2164, in get_text_width_height_descent
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
RuntimeError: In set_text: could not load glyph
"""
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3267, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-18-426f53ccd8fe>", line 3, in <module>
Pool(processes=n_workers).map(model_handler, range(n_jobs))
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/multiprocessing/pool.py", line 268, in map
return self._map_async(func, iterable, mapstar, chunksize).get()
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/multiprocessing/pool.py", line 657, in get
raise self._value
RuntimeError: In set_text: could not load glyph

Expected outcome

The code should simply succeed. Notice that the loky backend of joblib succeeds even though it uses parallel processes. I am not sure why this is.

Matplotlib version

Operating system: MacOS High Sierra (10.13.6)

Matplotlib version: 2.2.2

Matplotlib backend (print(matplotlib.get_backend())): module://backend_interagg

Python version: 3.7.2

Jupyter version (if applicable): N/A

Other libraries: joblib==0.12.2

All packages were installed via pip inside a conda environment.