I'm trying to create PDFs of plots that I have done with plotly in jupyter lab, but I'm having a lot of trouble with the fonts. Plotly is using the correct font for all the normal text in the browser, but all labels that are LaTeX code use a different font. This is to be expected, because plotly uses MathJax for rendering and apparently it's configured to use STIX
. I could live with that and change the non-LaTeX font to STIX
if it was consistent with exported graphics. The thing that really bothers me is that LaTeX labels use yet another font, when I export the plot as PDF. For some reason MathJax then defaults to MathJax_Main
. And for the sake of complete confusion if exported as PNG or PDF the normal font defaults Liberation Serif
.
So, here's some example code to create a plot. I'm running this on Debian 11 and python 3.9. I can add the full requirements.txt
if that's of any use.
import plotly.graph_objects as go
from plotly.express.colors import qualitative
layout = go.Layout(
font=dict(
family='Latin Modern Math',
color='black',
size=20,
),
colorway=qualitative.D3,
width=600,
height=320,
margin=dict(l=10,r=10,t=10,b=10),
paper_bgcolor='rgba(255,255,255,1)',
plot_bgcolor='rgba(0,0,0,0)',
xaxis=dict(
showline=True,
linecolor='black',
ticks='inside',
exponentformat='e',
mirror='ticks',
),
yaxis=dict(
showline=True,
linecolor='black',
ticks='inside',
exponentformat='e',
mirror='ticks',
),
legend=dict(
xanchor="left",
yanchor="bottom",
x=0.1,
y=1.01,
),
)
fig = go.Figure(layout=layout)
X = np.linspace(0, 2 * np.pi, 100)
Y1 = np.sin(X)
Y2 = np.cos(X)
fig.add_trace(go.Scatter(x=X, y=Y1, name=r'$\text{The quick brown fox jumps over the lazy dog}$'))
fig.add_trace(go.Scatter(x=X, y=Y2, name='The quick brown fox jumps over the lazy dog'))
fig.show()
Browser Output
LaTeX: STIX
Normal: Latin Modern Math
PDF Export
LaTeX: MathJax_Main
Normal: Liberation Serif
PNG Export
LaTeX: MathJax_Main
Normal: Liberation Serif
I guess the root of the problem is that whatever does the export in the background (Kaleido, maybe) and/or MathJax either don't find the right fonts or use a different config. But I have no clue where that config is or how it should look like.
Edit 1
I looked into the source code of plotly and kaleido. It looks like kaleido ships with its own mathjax, so this might be the problem. If I understand correctly, plotly hands the figure over to Kaleido (https://github.com/plotly/plotly.py/blob/24cda54b22b7c541a30fd6a080e4ccf5a0684106/packages/python/plotly/plotly/io/_kaleido.py#L145), which implements a PlotlyScope
. Kaleido then calls _perform_transform
(https://github.com/plotly/Kaleido/blob/6a46ecae926b4c004bf7232383cf7c74c70748fd/repos/kaleido/py/kaleido/scopes/plotly.py#L153), which creates a JSON dump and pipes that to some subprocess, which returns the final image. You can see that PlotlyScope
has a member mathjax
which in my case points to $HOME/venvs/jupyter/lib/python3.9/site-packages/kaleido/executable/etc/mathjax/MathJax.js
. At no point in this chain I can see that any font specification is altered. So I assume that the subprocess (that Kaleido is running) receives the correct font specification, but then defaults to whatever, because it can't find the fonts. Unfortunately, no errors are printed, when that happens.