Skip to content

Commit e604985

Browse files
More tex options (#457)
* Tex Template can be set per MathTex object. All Tex Fonts example scene added * Short example scene only * Added entry to changelog accepted formatting suggestion by behackl * ok, no example scene at all then. * Update manim/mobject/svg/tex_mobject.py: remove template from CONFIG and keep it as a kwarg thanks naveen Co-authored-by: Naveen M K <[email protected]> * Update manim/utils/tex_file_writing.py use fstrings Co-authored-by: Naveen M K <[email protected]> * Update manim/utils/tex_file_writing.py use fstrings Co-authored-by: Naveen M K <[email protected]> * Update manim/utils/tex_file_writing.py use fstrings Co-authored-by: Naveen M K <[email protected]> * revert changing CONFIG - deal with it in next PR Co-authored-by: Aron Fischer <co-bordism on gitlab> Co-authored-by: Naveen M K <[email protected]>
1 parent 5233c59 commit e604985

File tree

5 files changed

+84
-48
lines changed

5 files changed

+84
-48
lines changed

docs/source/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Mobjects, Scenes, and Animations
6060
#. The :code:`Container` class has been made into an AbstractBaseClass, i.e. in cannot be instantiated. Instead, use one of its children classes
6161
#. The ``TextMobject`` and ``TexMobject`` objects have been deprecated, due to their confusing names, in favour of ``Tex`` and ``MathTex``. You can still, however, continue to use ``TextMobject`` and ``TexMobject``, albeit with Deprecation Warnings constantly reminding you to switch.
6262
#. Add a :code:`Variable` class for displaying text that continuously updates to reflect the value of a python variable.
63+
#. The ``Tex`` and ``MathTex`` objects allow you to specify a custom TexTemplate using the ``template`` keyword argument.
6364
#. :code:`VGroup` now supports printing the class names of contained mobjects and :code:`VDict` supports printing the internal dict of mobjects
6465

6566

manim/mobject/svg/svg_mobject.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def get_all_childNodes_have_id(self, element):
287287
all_childNodes_have_id = []
288288
if not isinstance(element, minidom.Element):
289289
return
290-
if element.hasAttribute("id"):
290+
if element.hasAttribute("id") and element.tagName not in ("g", "defs"):
291291
return [element]
292292
for e in element.childNodes:
293293
all_childNodes_have_id.append(self.get_all_childNodes_have_id(e))

manim/mobject/svg/tex_mobject.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,20 @@ class SingleStringMathTex(SVGMobject):
4848
"organize_left_to_right": False,
4949
"alignment": "",
5050
"type": "tex",
51+
"template": None,
5152
}
5253

5354
def __init__(self, tex_string, **kwargs):
5455
digest_config(self, kwargs)
56+
if self.template is None:
57+
self.template = kwargs.get("tex_template", config["tex_template"])
5558
assert isinstance(tex_string, str)
5659
self.tex_string = tex_string
57-
file_name = tex_to_svg_file(self.get_modified_expression(tex_string), self.type)
60+
file_name = tex_to_svg_file(
61+
self.get_modified_expression(tex_string),
62+
self.type,
63+
tex_template=self.template,
64+
)
5865
SVGMobject.__init__(self, file_name=file_name, **kwargs)
5966
if self.height is None:
6067
self.scale(TEX_MOB_SCALE_FACTOR)

manim/utils/tex.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ class TexTemplateFromFile:
1616
"use_ctex": False,
1717
"filename": "tex_template.tex",
1818
"text_to_replace": "YourTextHere",
19+
"tex_compiler": {
20+
"command": "latex",
21+
"output_format": ".dvi",
22+
},
1923
}
2024
body = ""
2125

manim/utils/tex_file_writing.py

Lines changed: 70 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ def tex_hash(expression):
2121
return hasher.hexdigest()[:16]
2222

2323

24-
def tex_to_svg_file(expression, source_type):
25-
tex_template = config["tex_template"]
24+
def tex_to_svg_file(expression, source_type, tex_template=None):
25+
if tex_template is None:
26+
tex_template = config["tex_template"]
2627
tex_file = generate_tex_file(expression, tex_template, source_type)
27-
dvi_file = tex_to_dvi(tex_file, tex_template.use_ctex)
28-
return dvi_to_svg(dvi_file, use_ctex=tex_template.use_ctex)
28+
dvi_file = tex_to_dvi(tex_file, tex_template.use_ctex, tex_template.tex_compiler)
29+
return dvi_to_svg(
30+
dvi_file, use_ctex=tex_template.use_ctex, tex_compiler=tex_template.tex_compiler
31+
)
2932

3033

3134
def generate_tex_file(expression, tex_template, source_type):
@@ -42,67 +45,88 @@ def generate_tex_file(expression, tex_template, source_type):
4245
return result
4346

4447

45-
def tex_to_dvi(tex_file, use_ctex=False):
46-
result = tex_file.replace(".tex", ".dvi" if not use_ctex else ".xdv")
48+
def tex_compilation_command(compiler, tex_file, tex_dir):
49+
if compiler["command"] in {"latex", "pdflatex", "luatex", "lualatex"}:
50+
commands = [
51+
compiler["command"],
52+
"-interaction=batchmode",
53+
f'-output-format="{compiler["output_format"][1:]}"',
54+
"-halt-on-error",
55+
f'-output-directory="{tex_dir}"',
56+
f'"{tex_file}"',
57+
">",
58+
os.devnull,
59+
]
60+
elif compiler["command"] == "xelatex":
61+
if compiler["output_format"] == ".xdv":
62+
outflag = "-no-pdf"
63+
elif compiler["output_format"] == ".pdf":
64+
outflag = ""
65+
else:
66+
raise ValueError("xelatex output is either pdf or xdv")
67+
commands = [
68+
"xelatex",
69+
outflag,
70+
"-interaction=batchmode",
71+
"-halt-on-error",
72+
'-output-directory="{}"'.format(tex_dir),
73+
'"{}"'.format(tex_file),
74+
">",
75+
os.devnull,
76+
]
77+
else:
78+
raise ValueError(f"Tex compiler {compiler['command']} unknown.")
79+
return " ".join(commands)
80+
81+
82+
def tex_to_dvi(tex_file, use_ctex=False, tex_compiler=None):
83+
if tex_compiler is None:
84+
tex_compiler = (
85+
{"command": "xelatex", "output_format": ".xdv"}
86+
if use_ctex
87+
else {"command": "latex", "output_format": ".dvi"}
88+
)
89+
result = tex_file.replace(".tex", tex_compiler["output_format"])
4790
result = Path(result).as_posix()
4891
tex_file = Path(tex_file).as_posix()
4992
tex_dir = Path(file_writer_config["tex_dir"]).as_posix()
5093
if not os.path.exists(result):
51-
commands = (
52-
[
53-
"latex",
54-
"-interaction=batchmode",
55-
"-halt-on-error",
56-
'-output-directory="{}"'.format(tex_dir),
57-
'"{}"'.format(tex_file),
58-
">",
59-
os.devnull,
60-
]
61-
if not use_ctex
62-
else [
63-
"xelatex",
64-
"-no-pdf",
65-
"-interaction=batchmode",
66-
"-halt-on-error",
67-
'-output-directory="{}"'.format(tex_dir),
68-
'"{}"'.format(tex_file),
69-
">",
70-
os.devnull,
71-
]
72-
)
73-
exit_code = os.system(" ".join(commands))
94+
command = tex_compilation_command(tex_compiler, tex_file, tex_dir)
95+
exit_code = os.system(command)
7496
if exit_code != 0:
7597
log_file = tex_file.replace(".tex", ".log")
76-
raise Exception(
77-
(
78-
"LaTeX error converting to dvi. "
79-
if not use_ctex
80-
else "XeLaTeX error converting to xdv. "
81-
)
82-
+ f"See log output above or the log file: {log_file}"
98+
raise ValueError(
99+
f"{tex_compiler['command']} error converting to"
100+
f" {tex_compiler['output_format'][1:]}. See log output above or"
101+
f" the log file: {log_file}"
83102
)
84103
return result
85104

86105

87-
def dvi_to_svg(dvi_file, use_ctex=False, regen_if_exists=False):
106+
def dvi_to_svg(
107+
dvi_file, use_ctex=False, tex_compiler=None, regen_if_exists=False, page=1
108+
):
88109
"""
89-
Converts a dvi, which potentially has multiple slides, into a
90-
directory full of enumerated pngs corresponding with these slides.
91-
Returns a list of PIL Image objects for these images sorted as they
92-
where in the dvi
110+
Converts a dvi, xdv, or pdf file into an svg using dvisvgm.
93111
"""
94-
result = dvi_file.replace(".dvi" if not use_ctex else ".xdv", ".svg")
112+
if tex_compiler is None:
113+
tex_compiler = (
114+
{"command": "xelatex", "output_format": ".xdv"}
115+
if use_ctex
116+
else {"command": "latex", "output_format": ".dvi"}
117+
)
118+
result = dvi_file.replace(tex_compiler["output_format"], ".svg")
95119
result = Path(result).as_posix()
96120
dvi_file = Path(dvi_file).as_posix()
97121
if not os.path.exists(result):
98122
commands = [
99123
"dvisvgm",
124+
"--pdf" if tex_compiler["output_format"] == ".pdf" else "",
125+
"-p " + str(page),
100126
'"{}"'.format(dvi_file),
101127
"-n",
102-
"-v",
103-
"0",
104-
"-o",
105-
'"{}"'.format(result),
128+
"-v 0",
129+
"-o " + f'"{result}"',
106130
">",
107131
os.devnull,
108132
]

0 commit comments

Comments
 (0)