diff --git a/docs/source/guides/using_text.rst b/docs/source/guides/using_text.rst
index a71b29ec81..739510621c 100644
--- a/docs/source/guides/using_text.rst
+++ b/docs/source/guides/using_text.rst
@@ -424,7 +424,7 @@ may be expected. To color only ``x`` yellow, we have to do the following:
class CorrectLaTeXSubstringColoring(Scene):
def construct(self):
equation = MathTex(
- r"e^x = x^0 + x^1 + \frac{1}{2} x^2 + \frac{1}{6} x^3 + \cdots + \frac{1}{n!} x^n + \cdots",
+ r"e^{x} = x^0 + x^1 + \frac{1}{2} x^2 + \frac{1}{6} x^3 + \cdots + \frac{1}{n!} x^n + \cdots",
substrings_to_isolate="x"
)
equation.set_color_by_tex("x", YELLOW)
@@ -434,6 +434,8 @@ By setting ``substrings_to_isolate`` to ``x``, we split up the
:class:`~.MathTex` into substrings automatically and isolate the ``x`` components
into individual substrings. Only then can :meth:`~.set_color_by_tex` be used
to achieve the desired result.
+If one of the ``substrings_to_isolate`` is in a sub or superscript, it needs
+to be enclosed by curly brackets.
Note that Manim also supports a custom syntax that allows splitting
a TeX string into substrings easily: simply enclose parts of your formula
diff --git a/manim/mobject/svg/svg_mobject.py b/manim/mobject/svg/svg_mobject.py
index bd494c0211..c296130a27 100644
--- a/manim/mobject/svg/svg_mobject.py
+++ b/manim/mobject/svg/svg_mobject.py
@@ -21,12 +21,12 @@
from ..geometry.line import Line
from ..geometry.polygram import Polygon, Rectangle, RoundedRectangle
from ..opengl.opengl_compatibility import ConvertToOpenGL
-from ..types.vectorized_mobject import VMobject
+from ..types.vectorized_mobject import VGroup, VMobject
__all__ = ["SVGMobject", "VMobjectFromSVGPath"]
-SVG_HASH_TO_MOB_MAP: dict[int, VMobject] = {}
+SVG_HASH_TO_MOB_MAP: dict[int, SVGMobject] = {}
def _convert_point_to_3d(x: float, y: float) -> np.ndarray:
@@ -127,6 +127,7 @@ def __init__(
self.stroke_color = stroke_color
self.stroke_opacity = stroke_opacity # type: ignore[assignment]
self.stroke_width = stroke_width # type: ignore[assignment]
+ self.id_to_vgroup_dict: dict[str, VGroup] = {}
if self.stroke_width is None:
self.stroke_width = 0
@@ -170,6 +171,7 @@ def init_svg_mobject(self, use_svg_cache: bool) -> None:
if hash_val in SVG_HASH_TO_MOB_MAP:
mob = SVG_HASH_TO_MOB_MAP[hash_val].copy()
self.add(*mob)
+ self.id_to_vgroup_dict = mob.id_to_vgroup_dict
return
self.generate_mobject()
@@ -203,8 +205,9 @@ def generate_mobject(self) -> None:
svg = se.SVG.parse(modified_file_path)
modified_file_path.unlink()
- mobjects = self.get_mobjects_from(svg)
+ mobjects, mobject_dict = self.get_mobjects_from(svg)
self.add(*mobjects)
+ self.id_to_vgroup_dict = mobject_dict
self.flip(RIGHT) # Flip y
def get_file_path(self) -> Path:
@@ -258,7 +261,9 @@ def generate_config_style_dict(self) -> dict[str, str]:
result[svg_key] = str(svg_default_dict[style_key])
return result
- def get_mobjects_from(self, svg: se.SVG) -> list[VMobject]:
+ def get_mobjects_from(
+ self, svg: se.SVG
+ ) -> tuple[list[VMobject], dict[str, VGroup]]:
"""Convert the elements of the SVG to a list of mobjects.
Parameters
@@ -267,36 +272,77 @@ def get_mobjects_from(self, svg: se.SVG) -> list[VMobject]:
The parsed SVG file.
"""
result: list[VMobject] = []
- for shape in svg.elements():
- # can we combine the two continue cases into one?
- if isinstance(shape, se.Group): # noqa: SIM114
- continue
- elif isinstance(shape, se.Path):
- mob: VMobject = self.path_to_mobject(shape)
- elif isinstance(shape, se.SimpleLine):
- mob = self.line_to_mobject(shape)
- elif isinstance(shape, se.Rect):
- mob = self.rect_to_mobject(shape)
- elif isinstance(shape, (se.Circle, se.Ellipse)):
- mob = self.ellipse_to_mobject(shape)
- elif isinstance(shape, se.Polygon):
- mob = self.polygon_to_mobject(shape)
- elif isinstance(shape, se.Polyline):
- mob = self.polyline_to_mobject(shape)
- elif isinstance(shape, se.Text):
- mob = self.text_to_mobject(shape)
- elif isinstance(shape, se.Use) or type(shape) is se.SVGElement:
- continue
- else:
- logger.warning(f"Unsupported element type: {type(shape)}")
- continue
- if mob is None or not mob.has_points():
- continue
- self.apply_style_to_mobject(mob, shape)
- if isinstance(shape, se.Transformable) and shape.apply:
- self.handle_transform(mob, shape.transform)
- result.append(mob)
- return result
+ stack: list[tuple[se.SVGElement, int]] = []
+ stack.append((svg, 1))
+ group_id_number = 0
+ vgroup_stack: list[str] = ["root"]
+ vgroup_names: list[str] = ["root"]
+ vgroups: dict[str, VGroup] = {"root": VGroup()}
+ while len(stack) > 0:
+ element, depth = stack.pop()
+ # Reduce stack heights
+ vgroup_stack = vgroup_stack[0:(depth)]
+ try:
+ group_name = str(element.values["id"])
+ except Exception:
+ group_name = f"numbered_group_{group_id_number}"
+ group_id_number += 1
+ vg = VGroup()
+ vgroup_names.append(group_name)
+ vgroup_stack.append(group_name)
+ vgroups[group_name] = vg
+
+ if isinstance(element, (se.Group, se.Use)):
+ stack.extend((subelement, depth + 1) for subelement in element[::-1])
+ # Add element to the parent vgroup
+ try:
+ if isinstance(
+ element,
+ (
+ se.Path,
+ se.SimpleLine,
+ se.Rect,
+ se.Circle,
+ se.Ellipse,
+ se.Polygon,
+ se.Polyline,
+ se.Text,
+ ),
+ ):
+ mob = self.get_mob_from_shape_element(element)
+ if mob is not None:
+ result.append(mob)
+ for parent_name in vgroup_stack[:-1]:
+ vgroups[parent_name].add(mob)
+ except Exception as e:
+ logger.error(f"Exception occurred in 'get_mobjects_from'. Details: {e}")
+
+ return result, vgroups
+
+ def get_mob_from_shape_element(self, shape: se.SVGElement) -> VMobject | None:
+ if isinstance(shape, se.Path):
+ mob: VMobject | None = self.path_to_mobject(shape)
+ elif isinstance(shape, se.SimpleLine):
+ mob = self.line_to_mobject(shape)
+ elif isinstance(shape, se.Rect):
+ mob = self.rect_to_mobject(shape)
+ elif isinstance(shape, (se.Circle, se.Ellipse)):
+ mob = self.ellipse_to_mobject(shape)
+ elif isinstance(shape, se.Polygon):
+ mob = self.polygon_to_mobject(shape)
+ elif isinstance(shape, se.Polyline):
+ mob = self.polyline_to_mobject(shape)
+ elif isinstance(shape, se.Text):
+ mob = self.text_to_mobject(shape)
+ else:
+ logger.warning(f"Unsupported element type: {type(shape)}")
+ mob = None
+ if mob is None or not mob.has_points():
+ return mob
+ self.apply_style_to_mobject(mob, shape)
+ if isinstance(shape, se.Transformable) and shape.apply:
+ self.handle_transform(mob, shape.transform)
+ return mob
@staticmethod
def handle_transform(mob: VMobject, matrix: se.Matrix) -> VMobject:
diff --git a/manim/mobject/table.py b/manim/mobject/table.py
index 6aa806277f..14cb7db98f 100644
--- a/manim/mobject/table.py
+++ b/manim/mobject/table.py
@@ -1078,11 +1078,11 @@ def construct(self):
[[0,30,45,60,90],
[90,60,45,30,0]],
col_labels=[
- MathTex(r"\frac{\sqrt{0}}{2}"),
- MathTex(r"\frac{\sqrt{1}}{2}"),
- MathTex(r"\frac{\sqrt{2}}{2}"),
- MathTex(r"\frac{\sqrt{3}}{2}"),
- MathTex(r"\frac{\sqrt{4}}{2}")],
+ MathTex(r"\frac{ \sqrt{0} }{2}"),
+ MathTex(r"\frac{ \sqrt{1} }{2}"),
+ MathTex(r"\frac{ \sqrt{2} }{2}"),
+ MathTex(r"\frac{ \sqrt{3} }{2}"),
+ MathTex(r"\frac{ \sqrt{4} }{2}")],
row_labels=[MathTex(r"\sin"), MathTex(r"\cos")],
h_buff=1,
element_to_mobject_config={"unit": r"^{\circ}"})
diff --git a/manim/mobject/text/tex_mobject.py b/manim/mobject/text/tex_mobject.py
index 5d5cca069b..18546bce89 100644
--- a/manim/mobject/text/tex_mobject.py
+++ b/manim/mobject/text/tex_mobject.py
@@ -12,7 +12,7 @@
from __future__ import annotations
-from manim.utils.color import BLACK, ManimColor, ParsableManimColor
+from manim.utils.color import BLACK, ParsableManimColor
__all__ = [
"SingleStringMathTex",
@@ -23,10 +23,9 @@
]
-import itertools as it
import operator as op
import re
-from collections.abc import Iterable, Sequence
+from collections.abc import Iterable
from functools import reduce
from textwrap import dedent
from typing import Any, Self
@@ -39,6 +38,10 @@
from manim.utils.tex import TexTemplate
from manim.utils.tex_file_writing import tex_to_svg_file
+from ..opengl.opengl_compatibility import ConvertToOpenGL
+
+MATHTEX_SUBSTRING = "substring"
+
class SingleStringMathTex(SVGMobject):
"""Elementary building block for rendering text with LaTeX.
@@ -264,22 +267,30 @@ def __init__(
self.tex_template = kwargs.pop("tex_template", config["tex_template"])
self.arg_separator = arg_separator
self.substrings_to_isolate = (
- [] if substrings_to_isolate is None else substrings_to_isolate
+ [] if substrings_to_isolate is None else list(substrings_to_isolate)
)
if tex_to_color_map is None:
self.tex_to_color_map: dict[str, ParsableManimColor] = {}
else:
self.tex_to_color_map = tex_to_color_map
+ self.substrings_to_isolate.extend(self.tex_to_color_map.keys())
self.tex_environment = tex_environment
self.brace_notation_split_occurred = False
- self.tex_strings = self._break_up_tex_strings(tex_strings)
+ self.tex_strings = self._prepare_tex_strings(tex_strings)
+ self.matched_strings_and_ids: list[tuple[str, str]] = []
+
try:
+ joined_string = self._join_tex_strings_with_unique_deliminters(
+ self.tex_strings, self.substrings_to_isolate
+ )
super().__init__(
- self.arg_separator.join(self.tex_strings),
+ joined_string,
tex_environment=self.tex_environment,
tex_template=self.tex_template,
**kwargs,
)
+ # Save the original tex_string
+ self.tex_string = self.arg_separator.join(self.tex_strings)
self._break_up_by_substrings()
except ValueError as compilation_error:
if self.brace_notation_split_occurred:
@@ -301,36 +312,109 @@ def __init__(
if self.organize_left_to_right:
self._organize_submobjects_left_to_right()
- def _break_up_tex_strings(self, tex_strings: Sequence[str]) -> list[str]:
- # Separate out anything surrounded in double braces
- pre_split_length = len(tex_strings)
- tex_strings_brace_splitted = [
- re.split("{{(.*?)}}", str(t)) for t in tex_strings
+ def _prepare_tex_strings(self, tex_strings: Iterable[str]) -> list[str]:
+ # Deal with the case where tex_strings contains integers instead
+ # of strings.
+ tex_strings_validated = [
+ string if isinstance(string, str) else str(string) for string in tex_strings
]
- tex_strings_combined = sum(tex_strings_brace_splitted, [])
- if len(tex_strings_combined) > pre_split_length:
+ # Locate double curly bracers
+ tex_strings_validated_two = []
+ for tex_string in tex_strings_validated:
+ split = re.split(r"{{|}}", tex_string)
+ tex_strings_validated_two.extend(split)
+ if len(tex_strings_validated_two) > len(tex_strings_validated):
self.brace_notation_split_occurred = True
-
- # Separate out any strings specified in the isolate
- # or tex_to_color_map lists.
- patterns = []
- patterns.extend(
- [
- f"({re.escape(ss)})"
- for ss in it.chain(
- self.substrings_to_isolate,
- self.tex_to_color_map.keys(),
+ return [string for string in tex_strings_validated_two if len(string) > 0]
+
+ def _join_tex_strings_with_unique_deliminters(
+ self, tex_strings: list[str], substrings_to_isolate: Iterable[str]
+ ) -> str:
+ joined_string = ""
+ ssIdx = 0
+ for idx, tex_string in enumerate(tex_strings):
+ string_part = rf"\special{{dvisvgm:raw }}"
+ self.matched_strings_and_ids.append((tex_string, f"unique{idx:03d}"))
+
+ # Try to match with all substrings_to_isolate and apply the first match
+ # then match again (on the rest of the string) and continue until no
+ # characters are left in the string
+ unprocessed_string = str(tex_string)
+ processed_string = ""
+ while len(unprocessed_string) > 0:
+ first_match = self._locate_first_match(
+ substrings_to_isolate, unprocessed_string
)
- ],
+
+ if first_match:
+ processed, unprocessed_string = self._handle_match(
+ ssIdx, first_match
+ )
+ processed_string = processed_string + processed
+ ssIdx += 1
+ else:
+ processed_string = processed_string + unprocessed_string
+ unprocessed_string = ""
+
+ string_part += processed_string
+ if idx < len(tex_strings) - 1:
+ string_part += self.arg_separator
+ string_part += r"\special{dvisvgm:raw }"
+ joined_string = joined_string + string_part
+ return joined_string
+
+ def _locate_first_match(
+ self, substrings_to_isolate: Iterable[str], unprocessed_string: str
+ ) -> re.Match | None:
+ first_match_start = len(unprocessed_string)
+ first_match_length = 0
+ first_match = None
+ for substring in substrings_to_isolate:
+ match = re.match(f"(.*?)({re.escape(substring)})(.*)", unprocessed_string)
+ if match and len(match.group(1)) < first_match_start:
+ first_match = match
+ first_match_start = len(match.group(1))
+ first_match_length = len(match.group(2))
+ elif match and len(match.group(1)) == first_match_start:
+ # Break ties by looking at length of matches.
+ if first_match_length < len(match.group(2)):
+ first_match = match
+ first_match_start = len(match.group(1))
+ first_match_length = len(match.group(2))
+ return first_match
+
+ def _handle_match(self, ssIdx: int, first_match: re.Match) -> tuple[str, str]:
+ pre_match = first_match.group(1)
+ matched_string = first_match.group(2)
+ post_match = first_match.group(3)
+ pre_string = (
+ rf"\special{{dvisvgm:raw }}"
)
- pattern = "|".join(patterns)
- if pattern:
- pieces = []
- for s in tex_strings_combined:
- pieces.extend(re.split(pattern, s))
- else:
- pieces = tex_strings_combined
- return [p for p in pieces if p]
+ post_string = r"\special{dvisvgm:raw }"
+ self.matched_strings_and_ids.append(
+ (matched_string, f"unique{ssIdx:03d}{MATHTEX_SUBSTRING}")
+ )
+ processed_string = pre_match + pre_string + matched_string + post_string
+ unprocessed_string = post_match
+ return processed_string, unprocessed_string
+
+ @property
+ def _substring_matches(self) -> list[tuple[str, str]]:
+ """Return only the 'ss' (substring_to_isolate) matches."""
+ return [
+ (tex, id_)
+ for tex, id_ in self.matched_strings_and_ids
+ if id_.endswith(MATHTEX_SUBSTRING)
+ ]
+
+ @property
+ def _main_matches(self) -> list[tuple[str, str]]:
+ """Return only the main tex_string matches."""
+ return [
+ (tex, id_)
+ for tex, id_ in self.matched_strings_and_ids
+ if not id_.endswith(MATHTEX_SUBSTRING)
+ ]
def _break_up_by_substrings(self) -> Self:
"""
@@ -339,51 +423,32 @@ def _break_up_by_substrings(self) -> Self:
of tex_strings)
"""
new_submobjects: list[VMobject] = []
- curr_index = 0
- for tex_string in self.tex_strings:
- sub_tex_mob = SingleStringMathTex(
- tex_string,
- tex_environment=self.tex_environment,
- tex_template=self.tex_template,
- )
- num_submobs = len(sub_tex_mob.submobjects)
- new_index = (
- curr_index + num_submobs + len("".join(self.arg_separator.split()))
+ try:
+ for tex_string, tex_string_id in self._main_matches:
+ mtp = MathTexPart()
+ mtp.tex_string = tex_string
+ mtp.add(*self.id_to_vgroup_dict[tex_string_id].submobjects)
+ new_submobjects.append(mtp)
+ except KeyError:
+ logger.error(
+ f"MathTex: Could not find SVG group for tex part '{tex_string}' (id: {tex_string_id}). Using fallback to root group."
)
- if num_submobs == 0:
- last_submob_index = min(curr_index, len(self.submobjects) - 1)
- sub_tex_mob.move_to(self.submobjects[last_submob_index], RIGHT)
- else:
- sub_tex_mob.submobjects = self.submobjects[curr_index:new_index]
- new_submobjects.append(sub_tex_mob)
- curr_index = new_index
+ new_submobjects.append(self.id_to_vgroup_dict["root"])
self.submobjects = new_submobjects
return self
- def get_parts_by_tex(
- self, tex: str, substring: bool = True, case_sensitive: bool = True
- ) -> VGroup:
- def test(tex1: str, tex2: str) -> bool:
- if not case_sensitive:
- tex1 = tex1.lower()
- tex2 = tex2.lower()
- if substring:
- return tex1 in tex2
- else:
- return tex1 == tex2
-
- return VGroup(*(m for m in self.submobjects if test(tex, m.get_tex_string())))
-
- def get_part_by_tex(self, tex: str, **kwargs: Any) -> MathTex | None:
- all_parts = self.get_parts_by_tex(tex, **kwargs)
- return all_parts[0] if all_parts else None
+ def get_part_by_tex(self, tex: str, **kwargs: Any) -> VGroup | None:
+ for tex_str, match_id in self.matched_strings_and_ids:
+ if tex_str == tex:
+ return self.id_to_vgroup_dict[match_id]
+ return None
def set_color_by_tex(
self, tex: str, color: ParsableManimColor, **kwargs: Any
) -> Self:
- parts_to_color = self.get_parts_by_tex(tex, **kwargs)
- for part in parts_to_color:
- part.set_color(color)
+ for tex_str, match_id in self.matched_strings_and_ids:
+ if tex_str == tex:
+ self.id_to_vgroup_dict[match_id].set_color(color)
return self
def set_opacity_by_tex(
@@ -409,22 +474,18 @@ def set_opacity_by_tex(
"""
if remaining_opacity is not None:
self.set_opacity(opacity=remaining_opacity)
- for part in self.get_parts_by_tex(tex):
- part.set_opacity(opacity)
+ for tex_str, match_id in self.matched_strings_and_ids:
+ if tex_str == tex:
+ self.id_to_vgroup_dict[match_id].set_opacity(opacity)
return self
def set_color_by_tex_to_color_map(
self, texs_to_color_map: dict[str, ParsableManimColor], **kwargs: Any
) -> Self:
for texs, color in list(texs_to_color_map.items()):
- try:
- # If the given key behaves like tex_strings
- texs + ""
- self.set_color_by_tex(texs, ManimColor(color), **kwargs)
- except TypeError:
- # If the given key is a tuple
- for tex in texs:
- self.set_color_by_tex(tex, ManimColor(color), **kwargs)
+ for match in self.matched_strings_and_ids:
+ if match[0] == texs:
+ self.id_to_vgroup_dict[match[1]].set_color(color)
return self
def index_of_part(self, part: MathTex) -> int:
@@ -433,16 +494,17 @@ def index_of_part(self, part: MathTex) -> int:
raise ValueError("Trying to get index of part not in MathTex")
return split_self.index(part)
- def index_of_part_by_tex(self, tex: str, **kwargs: Any) -> int:
- part = self.get_part_by_tex(tex, **kwargs)
- if part is None:
- return -1
- return self.index_of_part(part)
-
def sort_alphabetically(self) -> None:
self.submobjects.sort(key=lambda m: m.get_tex_string())
+class MathTexPart(VMobject, metaclass=ConvertToOpenGL):
+ tex_string: str
+
+ def __repr__(self) -> str:
+ return f"{type(self).__name__}({repr(self.tex_string)})"
+
+
class Tex(MathTex):
r"""A string compiled with LaTeX in normal mode.
diff --git a/tests/control_data/logs_data/bad_tex_scene_BadTex.txt b/tests/control_data/logs_data/bad_tex_scene_BadTex.txt
index 02c8813969..06a36fb5a1 100644
--- a/tests/control_data/logs_data/bad_tex_scene_BadTex.txt
+++ b/tests/control_data/logs_data/bad_tex_scene_BadTex.txt
@@ -1,8 +1,8 @@
{"levelname": "INFO", "module": "logger_utils", "message": "Log file will be saved in <>"}
{"levelname": "INFO", "module": "tex_file_writing", "message": "Writing <> to <>"}
{"levelname": "ERROR", "module": "tex_file_writing", "message": "LaTeX compilation error: LaTeX Error: File `notapackage.sty' not found.\n"}
-{"levelname": "ERROR", "module": "tex_file_writing", "message": "Context of error: \n\\documentclass[preview]{standalone}\n-> \\usepackage{notapackage}\n\\begin{document}\n\\begin{center}\n\\frac{1}{0}\n"}
+{"levelname": "ERROR", "module": "tex_file_writing", "message": "Context of error: \n\\documentclass[preview]{standalone}\n-> \\usepackage{notapackage}\n\\begin{document}\n\\begin{center}\n\\special{dvisvgm:raw }\\frac{1}{0}\\special{dvisvgm:raw }\n"}
{"levelname": "INFO", "module": "tex_file_writing", "message": "You do not have package notapackage.sty installed."}
{"levelname": "INFO", "module": "tex_file_writing", "message": "Install notapackage.sty it using your LaTeX package manager, or check for typos."}
{"levelname": "ERROR", "module": "tex_file_writing", "message": "LaTeX compilation error: Emergency stop.\n"}
-{"levelname": "ERROR", "module": "tex_file_writing", "message": "Context of error: \n\\documentclass[preview]{standalone}\n-> \\usepackage{notapackage}\n\\begin{document}\n\\begin{center}\n\\frac{1}{0}\n"}
+{"levelname": "ERROR", "module": "tex_file_writing", "message": "Context of error: \n\\documentclass[preview]{standalone}\n-> \\usepackage{notapackage}\n\\begin{document}\n\\begin{center}\n\\special{dvisvgm:raw }\\frac{1}{0}\\special{dvisvgm:raw }\n"}
diff --git a/tests/module/mobject/text/test_texmobject.py b/tests/module/mobject/text/test_texmobject.py
index 0206b07710..ed79633c08 100644
--- a/tests/module/mobject/text/test_texmobject.py
+++ b/tests/module/mobject/text/test_texmobject.py
@@ -10,7 +10,7 @@
def test_MathTex(config):
MathTex("a^2 + b^2 = c^2")
- assert Path(config.media_dir, "Tex", "e4be163a00cf424f.svg").exists()
+ assert Path(config.media_dir, "Tex", "05bb0a41ed575f00.svg").exists()
def test_SingleStringMathTex(config):
@@ -29,7 +29,7 @@ def test_double_braces_testing(text_input, length_sub):
def test_tex(config):
Tex("The horse does not eat cucumber salad.")
- assert Path(config.media_dir, "Tex", "c3945e23e546c95a.svg").exists()
+ assert Path(config.media_dir, "Tex", "5384b41741a246bd.svg").exists()
def test_tex_temp_directory(tmpdir, monkeypatch):
@@ -42,12 +42,12 @@ def test_tex_temp_directory(tmpdir, monkeypatch):
with tempconfig({"media_dir": "media"}):
Tex("The horse does not eat cucumber salad.")
assert Path("media", "Tex").exists()
- assert Path("media", "Tex", "c3945e23e546c95a.svg").exists()
+ assert Path("media", "Tex", "5384b41741a246bd.svg").exists()
def test_percent_char_rendering(config):
Tex(r"\%")
- assert Path(config.media_dir, "Tex", "4a583af4d19a3adf.tex").exists()
+ assert Path(config.media_dir, "Tex", "32509dd0ea993961.tex").exists()
def test_tex_whitespace_arg():
@@ -218,11 +218,11 @@ def test_tex_garbage_collection(tmpdir, monkeypatch, config):
Path(tmpdir, "media").mkdir(exist_ok=True)
config.media_dir = "media"
- tex_without_log = Tex("Hello World!") # d771330b76d29ffb.tex
- assert Path("media", "Tex", "d771330b76d29ffb.tex").exists()
- assert not Path("media", "Tex", "d771330b76d29ffb.log").exists()
+ tex_without_log = Tex("Hello World!") # 058a4e242c57db6d.tex
+ assert Path("media", "Tex", "058a4e242c57db6d.tex").exists()
+ assert not Path("media", "Tex", "058a4e242c57db6d.log").exists()
config.no_latex_cleanup = True
- tex_with_log = Tex("Hello World, again!") # da27670a37b08799.tex
- assert Path("media", "Tex", "da27670a37b08799.log").exists()
+ tex_with_log = Tex("Hello World, again!") # 45b4e7819cc20cb1.tex
+ assert Path("media", "Tex", "45b4e7819cc20cb1.log").exists()
diff --git a/tests/opengl/test_texmobject_opengl.py b/tests/opengl/test_texmobject_opengl.py
index e9826f9d8f..618bdb4a4f 100644
--- a/tests/opengl/test_texmobject_opengl.py
+++ b/tests/opengl/test_texmobject_opengl.py
@@ -9,7 +9,7 @@
def test_MathTex(config, using_opengl_renderer):
MathTex("a^2 + b^2 = c^2")
- assert Path(config.media_dir, "Tex", "e4be163a00cf424f.svg").exists()
+ assert Path(config.media_dir, "Tex", "05bb0a41ed575f00.svg").exists()
def test_SingleStringMathTex(config, using_opengl_renderer):
@@ -28,7 +28,7 @@ def test_double_braces_testing(using_opengl_renderer, text_input, length_sub):
def test_tex(config, using_opengl_renderer):
Tex("The horse does not eat cucumber salad.")
- assert Path(config.media_dir, "Tex", "c3945e23e546c95a.svg").exists()
+ assert Path(config.media_dir, "Tex", "5384b41741a246bd.svg").exists()
def test_tex_whitespace_arg(using_opengl_renderer):