Open Ricyteach opened 1 week ago
As far as I remember, we do fall back to matplotlib latex rendering when used in jupyter and qtconsole. But maybe that has changed over the years.
We use IPython's latex_to_png
: https://ipython.readthedocs.io/en/stable/api/generated/IPython.lib.latextools.html#IPython.lib.latextools.latex_to_png
This defaults to matplotlib's renderer. If you adjust our printer settings you should be able to enable png printing for math. But I don't know how this would interact with Excel.
Thank you for replying.
I think the problem is it hits these lines, and I get the "latex program is not installed" error:
But matplotlib
is able to render the latex just fine, so there is a latex program somewhere.
Should I modify my path so that sympy
is able to find the latex program being used by IPython
? If so, any idea how I can find it?
Should sympy
try to find this program automatically...?
If you are using the preview function, on quick glance it doesn't seem to connected up to matplotlib's renderer.
You are right, it's not.
Thanks to you pointing me in the right direction I am now just trying to use the IPython
rendering function directly:
from PIL import Image
import io
from IPython.lib.latextools import latex_to_png
latex_expr = '\\int \\frac{1}{x}\\, dx'
img = Image.open(io.BytesIO(latex_to_png(latex_expr, wrap=True)))
(However the resulting image is really low DPI; I am still trying to figure out how to increase the resolution.)
It would be really really nice if sympy.preview
just was able to do this automatically when used inside of the Anaconda environment used by Python in Excel. I suggest this as a feature. Once I figure out how to get it working properly I might do a pull request... would that be welcome?
However I consider myself a pretty novice programmer. So maybe it's not a good idea.
would that be welcome?
Yes, I think having preview be able to use the matplotlib latex backend would be helpful to many.
We use latex_to_png()
in here: https://github.com/sympy/sympy/blob/master/sympy/interactive/printing.py#L113, maybe that is helpful. The scale may bump the DPI, not sure though.
Just an update, it looks like the code below works, and it is very simple.
However, the math_to_image
function does not allow for a transparent background... matplotlib
has that implemented separately in figure.Figure
. It's a bit convoluted to make a transparent version. The second version is essentially what latex_to_png
is doing in the background.
I am wondering if it might be worthwhile to talk to the matplotlib
folks about this. I'm just a guy and don't know any of them... do you think it be welcome to suggest that they add a transparent
argument directly to matplotlib.mathtext.math_to_image
...?
Anyway, as I said this simple code renders an image but you can't make it transparent:
from matplotlib.mathtext import math_to_image
from PIL import Image
import io
# Define LaTeX expression
latex_expr = r'$\int \frac{1}{x} \, dx$'
# Set up DPI and font size
dpi = 300
font_size = 24
# Render the LaTeX expression to a PNG image with specified DPI ( can also provide font info)
math_to_image(latex_expr, buffer, dpi=dpi, format='png')
# Load the image with PIL and save or display it
img = Image.open(io.BytesIO(buffer.getvalue()))
img.save('lala.png')
But to get a transparent version you have to do this (CORRECTION: no no no, you can just call latex_to_png
directly! duh):
from matplotlib import mathtext, figure
from matplotlib.backends import backend_agg
from PIL import Image
import io
color = 'Black'
# Initialize the mathtext parser
parser = mathtext.MathTextParser('path')
# Define LaTeX expression
latex_expr = r'\int \frac{1}{x} \, dx'
s = u'${0}$'.format(latex_expr)
# Set up DPI and font size
dpi = 300
font_size = 24
buffer = io.BytesIO()
# Parse the LaTeX expression and render it into a transparent background PNG
width, height, depth, _, _ = parser.parse(s, dpi=dpi)
fig = figure.Figure(figsize=(width / 72, height / 72))
fig.text(0, depth / height, s, color=color)
backend_agg.FigureCanvasAgg(fig)
fig.savefig(buffer, dpi=dpi, format="png", transparent=True)
# Create an image from the buffer and show it
img = Image.open(io.BytesIO(buffer.getvalue()))
img.save('lala.png', transparent=True)
I suppose it might be more simple to just request that matplotlib
adds some more control to the latex_to_png
function rather than the math_to_image
function... I guess that would a discussion to have with them though.
We use
latex_to_png()
in here: https://github.com/sympy/sympy/blob/master/sympy/interactive/printing.py#L113, maybe that is helpful. The scale may bump the DPI, not sure though.
YES! You're right: scale does bump the DPI. So that solves that problem. Use scale= 2.5 to get 300 dpi.
But you still can't make it transparent. :(
Ugh sorry for all the replies, there are several different functions here and I am not keeping them all straight.
The latex_to_png
function DOES make the background transparent. So this code to make a transparent image, but it does not allow you to control the font (size, etc):
from PIL import Image
import io
from IPython.lib.latextools import latex_to_png
latex_expr = '\\int \\frac{1}{x}\\, dx'
img = Image.open(io.BytesIO(latex_to_png(latex_expr, wrap=True, scale=2.5, color='Black')))
img.save('lala_transparent.png')
And the other code I posted before, using math_to_image
, works to make it non-transparent, and you are able to control the font, but you also have to manually wrap the latex first to make it a math expression:
from matplotlib.mathtext import math_to_image
from PIL import Image
import io
# Define LaTeX expression
latex_expr = r'$\int \frac{1}{x} \, dx$'
# Set up DPI
dpi = 300
# Render the LaTeX expression to a PNG image with specified DPI ( can also provide font info)
math_to_image(latex_expr, buffer, dpi=dpi, format='png')
# Load the image with PIL and save or display it
img = Image.open(io.BytesIO(buffer.getvalue()))
img.save('lala_non_transparent.png')
I wonder if reaching out to matplotlib
about this would be a good idea... seems like you should be able to use just one function and control both font and transparency with it.
Making the matplotlib renderer usable from preview
does sound like it would be useful.
Also, I wonder if we should remove the dependence on IPython to do the conversion. The code here does not look that complicated https://github.com/ipython/ipython/blob/main/IPython/lib/latextools.py
Making the matplotlib renderer usable from
preview
does sound like it would be useful.Also, I wonder if we should remove the dependence on IPython to do the conversion. The code here does not look that complicated https://github.com/ipython/ipython/blob/main/IPython/lib/latextools.py
I mentioned this above but it is probably confused/lost in all my replies (sorry): you can absolutely remove the IPython dependency and depend only on matplotlib
for this.
Basically: there are currently two ways to render an equation to PNG
(which was the impetus of me posting this issue) using matplotlib
that I have found.
sympy
turns out to be using by relying on IPython by default (output='png'
). This function allows you to change the scale (ie, DPI) and font color, but the font size is set at 12 and it is set to a transparent background:(Of course other formats are also available via the latex_to_png
function sympy
is already using.)
matplotlib.mathtext
API, and call the math_to_image
function. This function has more options: you can set the DPI directly (without using "scale", which is a bit cryptic), you can choose all font options you want, and (similar to latex_to_png
) you can specify several different output formats ('svg', 'pdf', 'ps' or 'png'). However, this one does NOT support a transparent background:https://matplotlib.org/stable/api/mathtext_api.html#matplotlib.mathtext.math_to_image
(The lack of support for a transparent background for no. 2 seems to me to have been the motivation for no. 1 above to be written the way it is, but I'm not certain.)
(The lack of support for a transparent background for no. 2 seems to me to have been the motivation for no. 1 above to be written the way it is, but I'm not certain.)
I don't know the history of this code, but it sounds plausible since you really want that for output in a notebook. However, I expect matplotlib would be open to a fix to the function to add support for that.
And just to clarify my point, even if we do have to have something manual instead directly calling matplotlib.math_to_image, it would still be better to copy that code from IPython directly into SymPy. It's the sort of code that makes more sense to have maintained inside of SymPy than IPython IMO.
I'd like to use Python in Excel to display some LATEX math formulas in cells.
Creating these images is easy when just using the
sympy
library, but one has to have latex installed. Python in Excel runs on Anaconda distribution and it does not include a standalone LATEX rendering library (that I am aware of).I came up with a solution to this here, but it's really kludgy:
https://stackoverflow.com/questions/79067766/render-latex-math-in-excel-using-python-in-excel
My idea:
matplotlib
, which comes with Anaconda distribution, is able to render LATEX. I wonder if there's a way to tellsympy
"fall back" into using the same LATEX rendering library that is being used bymatplotlib
?