chalk
chalk copied to clipboard
Unable to render latex in pdf and png backends
I just discovered this very cool library while following along with the raspy blog. Thanks for creating it!
I was unable to successfully generate latex-containing output in an SVG (no errors were shown, but all latex symbols were missing), so I slightly modified the latex.py example to instead output a PDF since I think latex-containing SVGs are generated by first generating a PDF and then calling pdf2svg
if I understand correctly. As far as I can tell, I have installed all the prerequisites, but I can't really test that because the following code generates a TypeError
:
from chalk import *
from colour import Color
grey = Color("#bbbbbb")
papaya = Color("#ff9700")
left_arrow = make_path([(0, 0), (1, 0)]).reflect_x().line_width(0.03).center_xy()
def box(t):
return rectangle(1.5, 1).line_width(0.05).fill_color(papaya) + latex(t).scale(0.7)
def label(text):
return latex(text).scale(0.5).pad(0.4)
def arrow(text, d=True):
return label(text) // left_arrow
# Autograd 1
d = hcat([arrow(r"$f'_x(g(x))$"), box("$f$"), arrow(r"$f'_{g(x)}(g(x))$"), box("$g$"), arrow("1")], 0.2)
d.render_pdf("latex.pdf")
#d.render_png("latex.png") # also doesn't run; TypeError similar to above
The resulting error is as follows:
Traceback (most recent call last):
File "latex.py", line 20, in <module>
d.render_pdf("latex.pdf")
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 246, in render
for x in to_tikz(diagram, pylatex, Style.root(max(height, width))):
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 207, in to_tikz
return self.accept(ToTikZ(pylatex), style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 256, in accept
return visitor.visit_apply_transform(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 86, in visit_apply_transform
for x in diagram.diagram.accept(self, style=style):
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 256, in accept
return visitor.visit_apply_transform(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 86, in visit_apply_transform
for x in diagram.diagram.accept(self, style=style):
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 78, in visit_compose
elems2 = diagram.diagram2.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 256, in accept
return visitor.visit_apply_transform(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 86, in visit_apply_transform
for x in diagram.diagram.accept(self, style=style):
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 245, in accept
return visitor.visit_compose(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 77, in visit_compose
elems1 = diagram.diagram1.accept(self, style=style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/core.py", line 221, in accept
return visitor.visit_primitive(self, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/tikz.py", line 58, in visit_primitive
inner = diagram.shape.accept(self.shape_renderer, style=style_new)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/shapes/latex.py", line 47, in accept
return visitor.visit_latex(self, **kwargs)
TypeError: visit_latex() got an unexpected keyword argument 'style'
If instead I run the d.render_png("latex.png")
line, the error is instead
Traceback (most recent call last):
File "latex.py", line 21, in <module>
d.render_png("latex.png", 100)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/cairo.py", line 230, in render
render_cairo_prims(s, ctx, Style.root(max(width, height)))
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/backend/cairo.py", line 185, in render_cairo_prims
prim.shape.accept(shape_renderer, ctx=ctx, style=prim.style)
File "/opt/anaconda3/lib/python3.8/site-packages/chalk/shapes/latex.py", line 47, in accept
return visitor.visit_latex(self, **kwargs)
TypeError: visit_latex() got an unexpected keyword argument 'ctx'
Hello!
We currently don't have support for LaTeX in the PNG and PDF backends; but it should work using SVG. Here is a Colab example that renders the code above.
(Sorry for those uninformative error messages; there was a bug in the code, which should be fixed now.)
Thanks for the quick response! The Euler identity equation renders fine for me, but the second example seems to be off, both in Colab and my local Jupyter installation (missing arrowheads maybe, wrong box calculations maybe):
Is this the intended appearance?
The problem that led me to looking for PDF/PNG output was that the result of doing d.render_svg('latex.svg')
in the above arrow diagram leads to something that misses the latex when opened in Inkscape (also notice the odd bounding box, shown with the dashed line by selecting everything in Inkscape):
Similarly, saving the Euler identity with d.render_svg('euler.svg')
also misses the latex bits when opened in Inkscape:
As a follow-up in support of the observation that bounding boxes seem off, here is how the Euler identity looks in colab:
Whereas if I use text instead, it's properly centered:
I'm happy to open a separate issue for that if you'd prefer.
Thanks for the report! I think I've fixed the issue: LaTeX should be properly centered now. As for the initial code, I think it's a bit dated, as it doesn't use the latest features (in particular, the arrows functionality: e.g., connect
, connect_outside
). I would probably rewrite it something like this:
from chalk import *
from colour import Color
papaya = Color("#ff9700")
black = Color("black")
SEP = 3
W = 3
H = 2
DY = -0.5
def box(t):
return rectangle(W, H).line_width(0.05).fill_color(papaya) + latex(t)
def anchor():
return circle(0.05).fill_color(black)
nodes = [
anchor().named("inp"),
box("$f$").named("f"),
box("$g$").named("g"),
anchor().named("out"),
]
d = hcat(nodes, sep=3)
d = d.connect_outside("inp", "f")
d = d.connect_outside("f", "g")
d = d.connect_outside("g", "out")
d = (
d
+ latex(r"$f'_x(g(x))$").translate(SEP / 2, DY)
+ latex(r"$f'_{g(x)}(g(x))$").translate(3 * SEP / 2 + W, DY)
+ latex(r"$1$").translate(5 * SEP / 2 + 2 * W, DY)
)
d.render_svg("examples/output/latex.svg")
which renders this image
Of course, this is still far from perfect. Some things to improve include:
- [ ] simplify the placement of labels on edges
- [ ] introduce a phantom function that produces an empty diagram, but has a given envelope and trace (basically, to hide the anchor points)
- [ ] ~allow to draw head-less arrows~ (this is in fact possible by using
ArrowOpts(head_arrow=empty())
, seeexamples/lenet.py
)
As for the Inkscape rendering, I'm not sure what's happening. I usually use the browsers to view the SVGs, and they currently look fine for me. If it's a pressing issue I can investigate that as well.
Great! Thanks very much.
As to Inkscape rendering, I lived my life up to this point without this great tool, so it's not pressing per se (I've been using TikZ for like 12 years, so I'll manage).
Inkscape is the de-facto open source SVG editing tool, though, and I supervise some PhD students who could make great use of chalk to make diagrams that then get incorporated into other figures (with images, e.g.) in Inkscape. Also, this is a backdoor way to generate PDF and PNG from latex-containing SVG files since Inkscape can do that (also from the CLI if memory serves). So I do think it's important enough to investigate even if it's not urgent for me.
Thanks again very much!
Yes, the use-case you are describing makes a lot of sense! I'll keep it in mind and I'll take a look at the Inkscape issue when I get some time.
Also, thank you very much for the interest in our library—it's a much needed motivational boost, since, as you might imagine, it becomes a bit of challenge to actively maintain the side projects when other responsibilities take priority 🙂