diff --git a/manimlib/animation/transform_matching_parts.py b/manimlib/animation/transform_matching_parts.py index ef62ff7d..452333fd 100644 --- a/manimlib/animation/transform_matching_parts.py +++ b/manimlib/animation/transform_matching_parts.py @@ -167,83 +167,95 @@ class TransformMatchingStrings(AnimationGroup): digest_config(self, kwargs) assert isinstance(source, StringMobject) assert isinstance(target, StringMobject) - anims = [] - source_indices = list(range(len(source.labels))) - target_indices = list(range(len(target.labels))) - def get_filtered_indices_lists(indices_lists, rest_indices): + def get_matched_indices_lists(*part_items_list): + part_items_list_len = len(part_items_list) + indexed_part_items = sorted(it.chain(*[ + [ + (substr, items_index, indices_list) + for substr, indices_list in part_items + ] + for items_index, part_items in enumerate(part_items_list) + ])) + grouped_part_items = [ + (substr, [ + [indices_lists for _, _, indices_lists in grouper_2] + for _, grouper_2 in it.groupby( + grouper_1, key=lambda t: t[1] + ) + ]) + for substr, grouper_1 in it.groupby( + indexed_part_items, key=lambda t: t[0] + ) + ] + return [ + tuple(indices_lists_list) + for _, indices_lists_list in sorted(filter( + lambda t: t[0] and len(t[1]) == part_items_list_len, + grouped_part_items + ), key=lambda t: len(t[0]), reverse=True) + ] + + def get_filtered_indices_lists(indices_lists, used_indices): result = [] + used = [] for indices_list in indices_lists: - if not indices_list: - continue - if not all(index in rest_indices for index in indices_list): + if not all( + index not in used_indices and index not in used + for index in indices_list + ): continue result.append(indices_list) - for index in indices_list: - rest_indices.remove(index) - return result + used.extend(indices_list) + return result, used - def add_anims(anim_class, indices_lists_pairs): - for source_indices_lists, target_indices_lists in indices_lists_pairs: - source_indices_lists = get_filtered_indices_lists( - source_indices_lists, source_indices - ) - target_indices_lists = get_filtered_indices_lists( - target_indices_lists, target_indices - ) - if not source_indices_lists or not target_indices_lists: - source_indices.extend(it.chain(*source_indices_lists)) - target_indices.extend(it.chain(*target_indices_lists)) - continue - anims.append(anim_class( - source.build_parts_from_indices_lists(source_indices_lists), - target.build_parts_from_indices_lists(target_indices_lists), - **kwargs - )) - - def get_substr_to_indices_lists_map(part_items): - result = {} - for substr, indices_list in part_items: - if substr not in result: - result[substr] = [] - result[substr].append(indices_list) - return result - - def add_anims_from(anim_class, func): - source_substr_map = get_substr_to_indices_lists_map(func(source)) - target_substr_map = get_substr_to_indices_lists_map(func(target)) - common_substrings = sorted([ - s for s in source_substr_map if s and s in target_substr_map - ], key=len, reverse=True) - add_anims( - anim_class, - [ - (source_substr_map[substr], target_substr_map[substr]) - for substr in common_substrings - ] - ) - - add_anims( - ReplacementTransform, - [ + anim_class_items = [ + (ReplacementTransform, [ ( source.get_submob_indices_lists_by_selector(k), target.get_submob_indices_lists_by_selector(v) ) for k, v in self.key_map.items() - ] - ) - add_anims_from( - FadeTransformPieces, - StringMobject.get_specified_part_items - ) - add_anims_from( - FadeTransformPieces, - StringMobject.get_group_part_items - ) + ]), + (FadeTransformPieces, get_matched_indices_lists( + source.get_specified_part_items(), + target.get_specified_part_items() + )), + (FadeTransformPieces, get_matched_indices_lists( + source.get_group_part_items(), + target.get_group_part_items() + )) + ] - rest_source = VGroup(*[source[index] for index in source_indices]) - rest_target = VGroup(*[target[index] for index in target_indices]) + anims = [] + source_used_indices = [] + target_used_indices = [] + for anim_class, pairs in anim_class_items: + for source_indices_lists, target_indices_lists in pairs: + source_filtered, source_used = get_filtered_indices_lists( + source_indices_lists, source_used_indices + ) + target_filtered, target_used = get_filtered_indices_lists( + target_indices_lists, target_used_indices + ) + if not source_filtered or not target_filtered: + continue + anims.append(anim_class( + source.build_parts_from_indices_lists(source_filtered), + target.build_parts_from_indices_lists(target_filtered), + **kwargs + )) + source_used_indices.extend(source_used) + target_used_indices.extend(target_used) + + rest_source = VGroup(*[ + submob for index, submob in enumerate(source.submobjects) + if index not in source_used_indices + ]) + rest_target = VGroup(*[ + submob for index, submob in enumerate(target.submobjects) + if index not in target_used_indices + ]) if self.transform_mismatches: anims.append( ReplacementTransform(rest_source, rest_target, **kwargs) diff --git a/manimlib/default_config.yml b/manimlib/default_config.yml index 573ba2b2..59a88945 100644 --- a/manimlib/default_config.yml +++ b/manimlib/default_config.yml @@ -18,6 +18,8 @@ directories: temporary_storage: "" universal_import_line: "from manimlib import *" style: + # "latex" | "xelatex" + tex_compiler: "latex" tex_font: "default" font: "Consolas" text_alignment: "LEFT" @@ -41,4 +43,4 @@ camera_resolutions: high: "1920x1080" 4k: "3840x2160" default_resolution: "high" -fps: 30 \ No newline at end of file +fps: 30 diff --git a/manimlib/mobject/svg/mtex_mobject.py b/manimlib/mobject/svg/mtex_mobject.py index 452344ab..055fc1b6 100644 --- a/manimlib/mobject/svg/mtex_mobject.py +++ b/manimlib/mobject/svg/mtex_mobject.py @@ -40,6 +40,16 @@ class MTex(StringMobject): "additional_preamble": "", } + CMD_PATTERN = r"\\(?:[a-zA-Z]+|.)|[_^{}]" + FLAG_DICT = { + r"{": 1, + r"}": -1 + } + CONTENT_REPL = {} + MATCH_REPL = { + r"[_^{}]": "" + } + def __init__(self, tex_string: str, **kwargs): # Prevent from passing an empty string. if not tex_string.strip(): @@ -75,44 +85,32 @@ class MTex(StringMobject): # Parsing - def get_cmd_spans(self) -> list[Span]: - return self.find_spans(r"\\(?:[a-zA-Z]+|\s|\S)|[_^{}]") - - def get_substr_flag(self, substr: str) -> int: - return {"{": 1, "}": -1}.get(substr, 0) - - def get_repl_substr_for_content(self, substr: str) -> str: - return substr - - def get_repl_substr_for_matching(self, substr: str) -> str: - return substr if substr.startswith("\\") else "" - - def get_specified_items( + def get_internal_specified_items( self, cmd_span_pairs: list[tuple[Span, Span]] ) -> list[tuple[Span, dict[str, str]]]: cmd_content_spans = [ (span_begin, span_end) for (_, span_begin), (span_end, _) in cmd_span_pairs ] - specified_spans = [ - *[ - cmd_content_spans[range_begin] - for _, (range_begin, range_end) in self.compress_neighbours([ - (span_begin + index, span_end - index) - for index, (span_begin, span_end) in enumerate( - cmd_content_spans - ) - ]) - if range_end - range_begin >= 2 - ], - *[ - span - for selector in self.tex_to_color_map - for span in self.find_spans_by_selector(selector) - ], - *self.find_spans_by_selector(self.isolate) + return [ + (cmd_content_spans[range_begin], {}) + for _, (range_begin, range_end) in self.group_neighbours([ + (span_begin + index, span_end - index) + for index, (span_begin, span_end) in enumerate( + cmd_content_spans + ) + ]) + if range_end - range_begin >= 2 + ] + + def get_external_specified_items( + self + ) -> list[tuple[Span, dict[str, str]]]: + return [ + (span, {}) + for selector in self.tex_to_color_map + for span in self.find_spans_by_selector(selector) ] - return [(span, {}) for span in specified_spans] @staticmethod def get_color_cmd_str(rgb_hex: str) -> str: @@ -156,8 +154,8 @@ class MTex(StringMobject): def get_parts_by_tex(self, selector: Selector) -> VGroup: return self.select_parts(selector) - def get_part_by_tex(self, selector: Selector) -> VGroup: - return self.select_part(selector) + def get_part_by_tex(self, selector: Selector, **kwargs) -> VGroup: + return self.select_part(selector, **kwargs) def set_color_by_tex(self, selector: Selector, color: ManimColor): return self.set_parts_color(selector, color) diff --git a/manimlib/mobject/svg/string_mobject.py b/manimlib/mobject/svg/string_mobject.py index e4376a33..ca324216 100644 --- a/manimlib/mobject/svg/string_mobject.py +++ b/manimlib/mobject/svg/string_mobject.py @@ -18,7 +18,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from colour import Color - from typing import Iterable, Sequence, TypeVar, Union + from typing import Callable, Iterable, TypeVar, Union ManimColor = Union[str, Color] Span = tuple[int, int] @@ -66,6 +66,11 @@ class StringMobject(SVGMobject, ABC): "isolate": (), } + CMD_PATTERN: str | None = None + FLAG_DICT: dict[str, int] = {} + CONTENT_REPL: dict[str, str | Callable[[re.Match], str]] = {} + MATCH_REPL: dict[str, str | Callable[[re.Match], str]] = {} + def __init__(self, string: str, **kwargs): self.string = string digest_config(self, kwargs) @@ -153,21 +158,18 @@ class StringMobject(SVGMobject, ABC): # Toolkits - def get_substr(self, span: Span) -> str: - return self.string[slice(*span)] - - def find_spans(self, pattern: str | re.Pattern) -> list[Span]: - return [ - match_obj.span() - for match_obj in re.finditer(pattern, self.string) - ] - def find_spans_by_selector(self, selector: Selector) -> list[Span]: def find_spans_by_single_selector(sel): if isinstance(sel, str): - return self.find_spans(re.escape(sel)) + return [ + match_obj.span() + for match_obj in re.finditer(re.escape(sel), self.string) + ] if isinstance(sel, re.Pattern): - return self.find_spans(sel) + return [ + match_obj.span() + for match_obj in sel.finditer(self.string) + ] if isinstance(sel, tuple) and len(sel) == 2 and all( isinstance(index, int) or index is None for index in sel @@ -191,24 +193,59 @@ class StringMobject(SVGMobject, ABC): result.extend(spans) return result - @staticmethod - def get_neighbouring_pairs(vals: Sequence[T]) -> list[tuple[T, T]]: - return list(zip(vals[:-1], vals[1:])) + def get_substr(self, span: Span) -> str: + return self.string[slice(*span)] @staticmethod - def compress_neighbours(vals: Sequence[T]) -> list[tuple[T, Span]]: + def get_substr_matched_obj( + substr: str, match_dict: dict[str, T] + ) -> tuple[re.Match, T] | None: + for pattern, val in match_dict.items(): + match_obj = re.fullmatch(pattern, substr, re.S) + if match_obj is None: + continue + return match_obj, val + return None + + @staticmethod + def get_substr_matched_val( + substr: str, match_dict: dict[str, T], default: T + ) -> T: + obj = StringMobject.get_substr_matched_obj(substr, match_dict) + if obj is None: + return default + _, val = obj + return val + + @staticmethod + def get_substr_matched_str( + substr: str, match_dict: dict[str, str | Callable[[re.Match], str]] + ) -> str: + obj = StringMobject.get_substr_matched_obj(substr, match_dict) + if obj is None: + return substr + match_obj, val = obj + if isinstance(val, str): + return val + return val(match_obj) + + @staticmethod + def get_neighbouring_pairs(vals: Iterable[T]) -> list[tuple[T, T]]: + val_list = list(vals) + return list(zip(val_list[:-1], val_list[1:])) + + @staticmethod + def group_neighbours(vals: Iterable[T]) -> list[tuple[T, Span]]: if not vals: return [] - unique_vals = [vals[0]] - indices = [0] - for index, val in enumerate(vals): - if val == unique_vals[-1]: - continue - unique_vals.append(val) - indices.append(index) - indices.append(len(vals)) - val_ranges = StringMobject.get_neighbouring_pairs(indices) + unique_vals, range_lens = zip(*( + (val, len(list(grouper))) + for val, grouper in it.groupby(vals) + )) + val_ranges = StringMobject.get_neighbouring_pairs( + [0, *it.accumulate(range_lens)] + ) return list(zip(unique_vals, val_ranges)) @staticmethod @@ -228,18 +265,6 @@ class StringMobject(SVGMobject, ABC): (*span_ends, universal_span[1]) )) - def replace_substr(self, span: Span, repl_items: list[Span, str]): - if not repl_items: - return self.get_substr(span) - - repl_spans, repl_strs = zip(*sorted(repl_items, key=lambda t: t[0])) - pieces = [ - self.get_substr(piece_span) - for piece_span in self.get_complement_spans(span, repl_spans) - ] - repl_strs = [*repl_strs, ""] - return "".join(it.chain(*zip(pieces, repl_strs))) - @staticmethod def color_to_hex(color: ManimColor) -> str: return rgb_to_hex(color_to_rgb(color)) @@ -255,12 +280,26 @@ class StringMobject(SVGMobject, ABC): # Parsing def parse(self) -> None: - cmd_spans = self.get_cmd_spans() + pattern = self.CMD_PATTERN + cmd_spans = [] if pattern is None else [ + match_obj.span() + for match_obj in re.finditer(pattern, self.string, re.S) + ] cmd_substrs = [self.get_substr(span) for span in cmd_spans] - flags = [self.get_substr_flag(substr) for substr in cmd_substrs] - specified_items = self.get_specified_items( - self.get_cmd_span_pairs(cmd_spans, flags) - ) + flags = [ + self.get_substr_matched_val(substr, self.FLAG_DICT, 0) + for substr in cmd_substrs + ] + specified_items = [ + *self.get_internal_specified_items( + self.get_cmd_span_pairs(cmd_spans, flags) + ), + *self.get_external_specified_items(), + *[ + (span, {}) + for span in self.find_spans_by_selector(self.isolate) + ] + ] split_items = [ (span, attr_dict) for specified_span, attr_dict in specified_items @@ -273,31 +312,15 @@ class StringMobject(SVGMobject, ABC): self.split_items = split_items self.labelled_spans = [span for span, _ in split_items] self.cmd_repl_items_for_content = [ - (span, self.get_repl_substr_for_content(substr)) + (span, self.get_substr_matched_str(substr, self.CONTENT_REPL)) for span, substr in zip(cmd_spans, cmd_substrs) ] self.cmd_repl_items_for_matching = [ - (span, self.get_repl_substr_for_matching(substr)) + (span, self.get_substr_matched_str(substr, self.MATCH_REPL)) for span, substr in zip(cmd_spans, cmd_substrs) ] self.check_overlapping() - @abstractmethod - def get_cmd_spans(self) -> list[Span]: - return [] - - @abstractmethod - def get_substr_flag(self, substr: str) -> int: - return 0 - - @abstractmethod - def get_repl_substr_for_content(self, substr: str) -> str: - return "" - - @abstractmethod - def get_repl_substr_for_matching(self, substr: str) -> str: - return "" - @staticmethod def get_cmd_span_pairs( cmd_spans: list[Span], flags: list[int] @@ -317,11 +340,17 @@ class StringMobject(SVGMobject, ABC): return result @abstractmethod - def get_specified_items( + def get_internal_specified_items( self, cmd_span_pairs: list[tuple[Span, Span]] ) -> list[tuple[Span, dict[str, str]]]: return [] + @abstractmethod + def get_external_specified_items( + self + ) -> list[tuple[Span, dict[str, str]]]: + return [] + def split_span_by_levels( self, arbitrary_span: Span, cmd_spans: list[Span], flags: list[int] ) -> list[Span]: @@ -387,6 +416,18 @@ class StringMobject(SVGMobject, ABC): ) -> tuple[str, str]: return "", "" + def replace_substr(self, span: Span, repl_items: list[Span, str]): + if not repl_items: + return self.get_substr(span) + + repl_spans, repl_strs = zip(*sorted(repl_items, key=lambda t: t[0])) + pieces = [ + self.get_substr(piece_span) + for piece_span in self.get_complement_spans(span, repl_spans) + ] + repl_strs = [*repl_strs, ""] + return "".join(it.chain(*zip(pieces, repl_strs))) + def get_content(self, is_labelled: bool) -> str: inserted_str_pairs = [ (span, self.get_cmd_str_pair( @@ -446,7 +487,7 @@ class StringMobject(SVGMobject, ABC): return [] group_labels, labelled_submob_ranges = zip( - *self.compress_neighbours(self.labels) + *self.group_neighbours(self.labels) ) ordered_spans = [ self.labelled_spans[label] if label != -1 else self.full_span diff --git a/manimlib/mobject/svg/svg_mobject.py b/manimlib/mobject/svg/svg_mobject.py index 01623aa3..adfe05c0 100644 --- a/manimlib/mobject/svg/svg_mobject.py +++ b/manimlib/mobject/svg/svg_mobject.py @@ -122,7 +122,8 @@ class SVGMobject(VMobject): if k in style_keys } - new_root = ET.Element("svg", {}) # TODO: width, height + # Ignore other attributes in case that svgelements cannot parse them + new_root = ET.Element("svg", {}) config_style_node = ET.SubElement(new_root, "g", config_style_attrs) root_style_node = ET.SubElement(config_style_node, "g", style_attrs) root_style_node.extend(root) diff --git a/manimlib/mobject/svg/text_mobject.py b/manimlib/mobject/svg/text_mobject.py index 474ac7e3..a79d5375 100644 --- a/manimlib/mobject/svg/text_mobject.py +++ b/manimlib/mobject/svg/text_mobject.py @@ -61,9 +61,8 @@ class _Alignment: self.value = _Alignment.VAL_DICT[s.upper()] -class MarkupText(StringMobject): +class Text(StringMobject): CONFIG = { - "is_markup": True, "font_size": 48, "lsh": None, "justify": False, @@ -85,28 +84,16 @@ class MarkupText(StringMobject): "isolate": (re.compile(r"[a-zA-Z]+"), re.compile(r"\S+")), } - # See https://docs.gtk.org/Pango/pango_markup.html - MARKUP_COLOR_KEYS = { - "foreground": False, - "fgcolor": False, - "color": False, - "background": True, - "bgcolor": True, - "underline_color": True, - "overline_color": True, - "strikethrough_color": True, - } - MARKUP_TAGS = { - "b": {"font_weight": "bold"}, - "big": {"font_size": "larger"}, - "i": {"font_style": "italic"}, - "s": {"strikethrough": "true"}, - "sub": {"baseline_shift": "subscript", "font_scale": "subscript"}, - "sup": {"baseline_shift": "superscript", "font_scale": "superscript"}, - "small": {"font_size": "smaller"}, - "tt": {"font_family": "monospace"}, - "u": {"underline": "single"}, + CMD_PATTERN = r"""[<>&"']""" + FLAG_DICT = {} + CONTENT_REPL = { + r"<": "<", + r">": ">", + r"&": "&", + r"\"": """, + r"'": "'" } + MATCH_REPL = {} def __init__(self, text: str, **kwargs): self.full2short(kwargs) @@ -116,8 +103,6 @@ class MarkupText(StringMobject): self.font = get_customization()["style"]["font"] if not self.alignment: self.alignment = get_customization()["style"]["text_alignment"] - if self.is_markup: - self.validate_markup_string(text) self.text = text super().__init__(text, **kwargs) @@ -141,7 +126,6 @@ class MarkupText(StringMobject): self.base_color, self.isolate, self.text, - self.is_markup, self.font_size, self.lsh, self.justify, @@ -231,73 +215,15 @@ class MarkupText(StringMobject): # Parsing - def get_cmd_spans(self) -> list[Span]: - if not self.is_markup: - return self.find_spans(r"""[<>&"']""") - - # Unsupported passthroughs: - # "", "", "", "" - # See https://gitlab.gnome.org/GNOME/glib/-/blob/main/glib/gmarkup.c - return self.find_spans( - r"""&[\s\S]*?;|[>"']|""" - ) - - def get_substr_flag(self, substr: str) -> int: - if re.fullmatch(r"<\w[\s\S]*[^/]>", substr): - return 1 - if substr.startswith(" str: - if substr.startswith("<") and substr.endswith(">"): - return "" - return { - "<": "<", - ">": ">", - "&": "&", - "\"": """, - "'": "'" - }.get(substr, substr) - - def get_repl_substr_for_matching(self, substr: str) -> str: - if substr.startswith("<") and substr.endswith(">"): - return "" - if substr.startswith("&#") and substr.endswith(";"): - if substr.startswith("&#x"): - char_reference = int(substr[3:-1], 16) - else: - char_reference = int(substr[2:-1], 10) - return chr(char_reference) - return { - "<": "<", - ">": ">", - "&": "&", - """: "\"", - "'": "'" - }.get(substr, substr) - - def get_specified_items( + def get_internal_specified_items( self, cmd_span_pairs: list[tuple[Span, Span]] ) -> list[tuple[Span, dict[str, str]]]: - attr_pattern = r"""(\w+)\s*\=\s*(["'])([\s\S]*?)\2""" - internal_items = [] - for begin_cmd_span, end_cmd_span in cmd_span_pairs: - begin_tag = self.get_substr(begin_cmd_span) - tag_name = re.match(r"<(\w+)", begin_tag).group(1) - if tag_name == "span": - attr_dict = { - attr_match_obj.group(1): attr_match_obj.group(3) - for attr_match_obj in re.finditer(attr_pattern, begin_tag) - } - else: - attr_dict = MarkupText.MARKUP_TAGS.get(tag_name, {}) - internal_items.append( - ((begin_cmd_span[1], end_cmd_span[0]), attr_dict) - ) + return [] + def get_external_specified_items( + self + ) -> list[tuple[Span, dict[str, str]]]: return [ - *internal_items, *[ (span, {key: val}) for t2x_dict, key in ( @@ -313,10 +239,6 @@ class MarkupText(StringMobject): (span, local_config) for selector, local_config in self.local_configs.items() for span in self.find_spans_by_selector(selector) - ], - *[ - (span, {}) - for span in self.find_spans_by_selector(self.isolate) ] ] @@ -327,11 +249,13 @@ class MarkupText(StringMobject): if label_hex is not None: converted_attr_dict = {"foreground": label_hex} for key, val in attr_dict.items(): - substitute_key = MarkupText.MARKUP_COLOR_KEYS.get(key, None) - if substitute_key is None: - converted_attr_dict[key] = val - elif substitute_key: + if key in ( + "background", "bgcolor", + "underline_color", "overline_color", "strikethrough_color" + ): converted_attr_dict[key] = "black" + elif key not in ("foreground", "fgcolor", "color"): + converted_attr_dict[key] = val else: converted_attr_dict = attr_dict.copy() attrs_str = " ".join([ @@ -376,8 +300,8 @@ class MarkupText(StringMobject): def get_parts_by_text(self, selector: Selector) -> VGroup: return self.select_parts(selector) - def get_part_by_text(self, selector: Selector) -> VGroup: - return self.select_part(selector) + def get_part_by_text(self, selector: Selector, **kwargs) -> VGroup: + return self.select_part(selector, **kwargs) def set_color_by_text(self, selector: Selector, color: ManimColor): return self.set_parts_color(selector, color) @@ -391,10 +315,70 @@ class MarkupText(StringMobject): return self.get_string() -class Text(MarkupText): - CONFIG = { - "is_markup": False, +class MarkupText(Text): + # Unsupported passthroughs: + # "", "", "", "" + # See https://gitlab.gnome.org/GNOME/glib/-/blob/main/glib/gmarkup.c + CMD_PATTERN = r"""|&.*?;|[>"']""" + FLAG_DICT = { + r"": -1, + r"<.*/>": 0, + r"<.*>": 1 } + CONTENT_REPL = { + r">": ">", + r"\"": """, + r"'": "'" + } + MATCH_REPL = { + r"<.*>": "", + r"&#x(.*);": lambda m: chr(int(m.group(1), 16)), + r"&#(.*);": lambda m: chr(int(m.group(1), 10)), + r"<": "<", + r">": ">", + r"&": "&", + r""": "\"", + r"'": "'" + } + + # See https://docs.gtk.org/Pango/pango_markup.html + MARKUP_TAGS = { + "b": {"font_weight": "bold"}, + "big": {"font_size": "larger"}, + "i": {"font_style": "italic"}, + "s": {"strikethrough": "true"}, + "sub": {"baseline_shift": "subscript", "font_scale": "subscript"}, + "sup": {"baseline_shift": "superscript", "font_scale": "superscript"}, + "small": {"font_size": "smaller"}, + "tt": {"font_family": "monospace"}, + "u": {"underline": "single"}, + } + + def __init__(self, text: str, **kwargs): + self.validate_markup_string(text) + super().__init__(text, **kwargs) + + def get_internal_specified_items( + self, cmd_span_pairs: list[tuple[Span, Span]] + ) -> list[tuple[Span, dict[str, str]]]: + attr_pattern = r"""(\w+)\s*\=\s*(["'])(.*?)\2""" + result = [] + for begin_cmd_span, end_cmd_span in cmd_span_pairs: + begin_tag = self.get_substr(begin_cmd_span) + tag_name = re.search(r"\w+", begin_tag).group() + if tag_name == "span": + attr_dict = { + attr_match_obj.group(1): attr_match_obj.group(3) + for attr_match_obj in re.finditer( + attr_pattern, begin_tag, re.S + ) + } + else: + attr_dict = self.MARKUP_TAGS.get(tag_name, {}) + result.append( + ((begin_cmd_span[1], end_cmd_span[0]), attr_dict) + ) + return result class Code(MarkupText): diff --git a/manimlib/tex_fonts.yml b/manimlib/tex_fonts.yml index b380428b..51a2d60e 100644 --- a/manimlib/tex_fonts.yml +++ b/manimlib/tex_fonts.yml @@ -1,552 +1,440 @@ -default: - compiler: latex - preamble: |- - \usepackage[english]{babel} - \usepackage[utf8]{inputenc} - \usepackage[T1]{fontenc} - \usepackage{amsmath} - \usepackage{amssymb} - \usepackage{dsfont} - \usepackage{setspace} - \usepackage{tipa} - \usepackage{relsize} - \usepackage{textcomp} - \usepackage{mathrsfs} - \usepackage{calligra} - \usepackage{wasysym} - \usepackage{ragged2e} - \usepackage{physics} - \usepackage{xcolor} - \usepackage{microtype} - \usepackage{pifont} - \DisableLigatures{encoding = *, family = * } - \linespread{1} +default: |- + \usepackage[english]{babel} + \usepackage[utf8]{inputenc} + \usepackage[T1]{fontenc} + \usepackage{amsmath} + \usepackage{amssymb} + \usepackage{dsfont} + \usepackage{setspace} + \usepackage{tipa} + \usepackage{relsize} + \usepackage{textcomp} + \usepackage{mathrsfs} + \usepackage{calligra} + \usepackage{wasysym} + \usepackage{ragged2e} + \usepackage{physics} + \usepackage{xcolor} + \usepackage{microtype} + \usepackage{pifont} + \DisableLigatures{encoding = *, family = * } + \linespread{1} -ctex: - compiler: xelatex - preamble: |- - \usepackage[UTF8]{ctex} - \usepackage[english]{babel} - \usepackage{amsmath} - \usepackage{amssymb} - \usepackage{dsfont} - \usepackage{setspace} - \usepackage{tipa} - \usepackage{relsize} - \usepackage{textcomp} - \usepackage{mathrsfs} - \usepackage{calligra} - \usepackage{wasysym} - \usepackage{ragged2e} - \usepackage{physics} - \usepackage{xcolor} - \usepackage{microtype} - \linespread{1} +ctex: |- + \usepackage[UTF8]{ctex} + \usepackage[english]{babel} + \usepackage{amsmath} + \usepackage{amssymb} + \usepackage{dsfont} + \usepackage{setspace} + \usepackage{tipa} + \usepackage{relsize} + \usepackage{textcomp} + \usepackage{mathrsfs} + \usepackage{calligra} + \usepackage{wasysym} + \usepackage{ragged2e} + \usepackage{physics} + \usepackage{xcolor} + \usepackage{microtype} + \linespread{1} -blank: - compiler: latex - preamble: |- - \usepackage{xcolor} +basic: |- + \usepackage[english]{babel} + \usepackage{amsmath} + \usepackage{amssymb} + \usepackage{xcolor} -basic: - compiler: latex - preamble: |- - \usepackage[english]{babel} - \usepackage{amsmath} - \usepackage{amssymb} - \usepackage{xcolor} +# A collection of TeX templates for the fonts described at +# http://jf.burnol.free.fr/showcase.html # American Typewriter -american_typewriter: - compiler: xelatex - preamble: |- - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{American Typewriter} - \usepackage[defaultmathsizes]{mathastext} +american_typewriter: |- + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{American Typewriter} + \usepackage[defaultmathsizes]{mathastext} # Antykwa Poltawskiego (TX Fonts for Greek and math symbols) -antykwa: - compiler: latex - preamble: |- - \usepackage[OT4,OT1]{fontenc} - \usepackage{txfonts} - \usepackage[upright]{txgreeks} - \usepackage{antpolt} - \usepackage[defaultmathsizes,nolessnomore]{mathastext} +antykwa: |- + \usepackage[OT4,OT1]{fontenc} + \usepackage{txfonts} + \usepackage[upright]{txgreeks} + \usepackage{antpolt} + \usepackage[defaultmathsizes,nolessnomore]{mathastext} # Apple Chancery -apple_chancery: - compiler: xelatex - preamble: |- - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{Apple Chancery} - \usepackage[defaultmathsizes]{mathastext} +apple_chancery: |- + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{Apple Chancery} + \usepackage[defaultmathsizes]{mathastext} # Auriocus Kalligraphicus (Symbol Greek) -auriocus_kalligraphicus: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{aurical} - \renewcommand{\rmdefault}{AuriocusKalligraphicus} - \usepackage[symbolgreek]{mathastext} +auriocus_kalligraphicus: |- + \usepackage[T1]{fontenc} + \usepackage{aurical} + \renewcommand{\rmdefault}{AuriocusKalligraphicus} + \usepackage[symbolgreek]{mathastext} # Baskervald ADF with Fourier -baskervald_adf_fourier: - compiler: latex - preamble: |- - \usepackage[upright]{fourier} - \usepackage{baskervald} - \usepackage[defaultmathsizes,noasterisk]{mathastext} +baskervald_adf_fourier: |- + \usepackage[upright]{fourier} + \usepackage{baskervald} + \usepackage[defaultmathsizes,noasterisk]{mathastext} # Baskerville (Italic) -baskerville_it: - compiler: xelatex - preamble: |- - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{Baskerville} - \usepackage[defaultmathsizes,italic]{mathastext} +baskerville_it: |- + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{Baskerville} + \usepackage[defaultmathsizes,italic]{mathastext} # Biolinum -biolinum: - compiler: latex - preamble: |- - \usepackage{txfonts} - \usepackage[upright]{txgreeks} - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{Minion Pro} - \setsansfont[Mapping=tex-text,Scale=MatchUppercase]{Myriad Pro} - \renewcommand\familydefault\sfdefault - \usepackage[defaultmathsizes]{mathastext} - \renewcommand\familydefault\rmdefault +biolinum: |- + \usepackage{txfonts} + \usepackage[upright]{txgreeks} + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{Minion Pro} + \setsansfont[Mapping=tex-text,Scale=MatchUppercase]{Myriad Pro} + \renewcommand\familydefault\sfdefault + \usepackage[defaultmathsizes]{mathastext} + \renewcommand\familydefault\rmdefault # BrushScriptX-Italic (PX math and Greek) -brushscriptx: - compiler: xelatex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{pxfonts} - \renewcommand{\rmdefault}{pbsi} - \renewcommand{\mddefault}{xl} - \renewcommand{\bfdefault}{xl} - \usepackage[defaultmathsizes,noasterisk]{mathastext} - \boldmath +brushscriptx: |- + \usepackage[T1]{fontenc} + \usepackage{pxfonts} + \renewcommand{\rmdefault}{pbsi} + \renewcommand{\mddefault}{xl} + \renewcommand{\bfdefault}{xl} + \usepackage[defaultmathsizes,noasterisk]{mathastext} + \boldmath # Chalkboard SE -chalkboard_se: - compiler: xelatex - preamble: |- - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{Chalkboard SE} - \usepackage[defaultmathsizes]{mathastext} +chalkboard_se: |- + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{Chalkboard SE} + \usepackage[defaultmathsizes]{mathastext} # Chalkduster -chalkduster: - compiler: xelatex - preamble: |- - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{Chalkduster} - \usepackage[defaultmathsizes]{mathastext} +chalkduster: |- + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{Chalkduster} + \usepackage[defaultmathsizes]{mathastext} # Comfortaa -comfortaa: - compiler: latex - preamble: |- - \usepackage[default]{comfortaa} - \usepackage[LGRgreek,defaultmathsizes,noasterisk]{mathastext} - \let\varphi\phi - \linespread{1.06} +comfortaa: |- + \usepackage[default]{comfortaa} + \usepackage[LGRgreek,defaultmathsizes,noasterisk]{mathastext} + \let\varphi\phi + \linespread{1.06} # Comic Sans MS -comic_sans: - compiler: xelatex - preamble: |- - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{Comic Sans MS} - \usepackage[defaultmathsizes]{mathastext} +comic_sans: |- + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{Comic Sans MS} + \usepackage[defaultmathsizes]{mathastext} # Droid Sans -droid_sans: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[default]{droidsans} - \usepackage[LGRgreek]{mathastext} - \let\varepsilon\epsilon +droid_sans: |- + \usepackage[T1]{fontenc} + \usepackage[default]{droidsans} + \usepackage[LGRgreek]{mathastext} + \let\varepsilon\epsilon # Droid Sans (Italic) -droid_sans_it: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[default]{droidsans} - \usepackage[LGRgreek,defaultmathsizes,italic]{mathastext} - \let\varphi\phi +droid_sans_it: |- + \usepackage[T1]{fontenc} + \usepackage[default]{droidsans} + \usepackage[LGRgreek,defaultmathsizes,italic]{mathastext} + \let\varphi\phi # Droid Serif -droid_serif: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[default]{droidserif} - \usepackage[LGRgreek]{mathastext} - \let\varepsilon\epsilon +droid_serif: |- + \usepackage[T1]{fontenc} + \usepackage[default]{droidserif} + \usepackage[LGRgreek]{mathastext} + \let\varepsilon\epsilon # Droid Serif (PX math symbols) (Italic) -droid_serif_px_it: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{pxfonts} - \usepackage[default]{droidserif} - \usepackage[LGRgreek,defaultmathsizes,italic,basic]{mathastext} - \let\varphi\phi +droid_serif_px_it: |- + \usepackage[T1]{fontenc} + \usepackage{pxfonts} + \usepackage[default]{droidserif} + \usepackage[LGRgreek,defaultmathsizes,italic,basic]{mathastext} + \let\varphi\phi # ECF Augie (Euler Greek) -ecf_augie: - compiler: latex - preamble: |- - \renewcommand\familydefault{fau} - \usepackage[defaultmathsizes,eulergreek]{mathastext} +ecf_augie: |- + \renewcommand\familydefault{fau} + \usepackage[defaultmathsizes,eulergreek]{mathastext} # ECF JD (with TX fonts) -ecf_jd: - compiler: latex - preamble: |- - \usepackage{txfonts} - \usepackage[upright]{txgreeks} - \renewcommand\familydefault{fjd} - \usepackage{mathastext} - \mathversion{bold} +ecf_jd: |- + \usepackage{txfonts} + \usepackage[upright]{txgreeks} + \renewcommand\familydefault{fjd} + \usepackage{mathastext} + \mathversion{bold} # ECF Skeetch (CM Greek) -ecf_skeetch: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \DeclareFontFamily{T1}{fsk}{} - \DeclareFontShape{T1}{fsk}{m}{n}{<->s*[1.315] fskmw8t}{} - \renewcommand\rmdefault{fsk} - \usepackage[noendash,defaultmathsizes,nohbar,defaultimath]{mathastext} +ecf_skeetch: |- + \usepackage[T1]{fontenc} + \DeclareFontFamily{T1}{fsk}{} + \DeclareFontShape{T1}{fsk}{m}{n}{<->s*[1.315] fskmw8t}{} + \renewcommand\rmdefault{fsk} + \usepackage[noendash,defaultmathsizes,nohbar,defaultimath]{mathastext} # ECF Tall Paul (with Symbol font) -ecf_tall_paul: - compiler: latex - preamble: |- - \DeclareFontFamily{T1}{ftp}{} - \DeclareFontShape{T1}{ftp}{m}{n}{<->s*[1.4] ftpmw8t}{} - \renewcommand\familydefault{ftp} - \usepackage[symbol]{mathastext} - \let\infty\inftypsy +ecf_tall_paul: |- + \DeclareFontFamily{T1}{ftp}{} + \DeclareFontShape{T1}{ftp}{m}{n}{<->s*[1.4] ftpmw8t}{} + \renewcommand\familydefault{ftp} + \usepackage[symbol]{mathastext} + \let\infty\inftypsy # ECF Webster (with TX fonts) -ecf_webster: - compiler: xelatex - preamble: |- - \usepackage{txfonts} - \usepackage[upright]{txgreeks} - \renewcommand\familydefault{fwb} - \usepackage{mathastext} - \renewcommand{\int}{\intop\limits} - \linespread{1.5} - \mathversion{bold} +ecf_webster: |- + \usepackage{txfonts} + \usepackage[upright]{txgreeks} + \renewcommand\familydefault{fwb} + \usepackage{mathastext} + \renewcommand{\int}{\intop\limits} + \linespread{1.5} + \mathversion{bold} # Electrum ADF (CM Greek) -electrum_adf: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[LGRgreek,basic,defaultmathsizes]{mathastext} - \usepackage[lf]{electrum} - \Mathastext - \let\varphi\phi +electrum_adf: |- + \usepackage[T1]{fontenc} + \usepackage[LGRgreek,basic,defaultmathsizes]{mathastext} + \usepackage[lf]{electrum} + \Mathastext + \let\varphi\phi # Epigrafica -epigrafica: - compiler: latex - preamble: |- - \usepackage[LGR,OT1]{fontenc} - \usepackage{epigrafica} - \usepackage[basic,LGRgreek,defaultmathsizes]{mathastext} - \let\varphi\phi - \linespread{1.2} +epigrafica: |- + \usepackage[LGR,OT1]{fontenc} + \usepackage{epigrafica} + \usepackage[basic,LGRgreek,defaultmathsizes]{mathastext} + \let\varphi\phi + \linespread{1.2} # Fourier Utopia (Fourier upright Greek) -fourier_utopia: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[upright]{fourier} - \usepackage{mathastext} +fourier_utopia: |- + \usepackage[T1]{fontenc} + \usepackage[upright]{fourier} + \usepackage{mathastext} # French Cursive (Euler Greek) -french_cursive: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[default]{frcursive} - \usepackage[eulergreek,noplusnominus,noequal,nohbar,nolessnomore,noasterisk]{mathastext} +french_cursive: |- + \usepackage[T1]{fontenc} + \usepackage[default]{frcursive} + \usepackage[eulergreek,noplusnominus,noequal,nohbar,nolessnomore,noasterisk]{mathastext} # GFS Bodoni -gfs_bodoni: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \renewcommand{\rmdefault}{bodoni} - \usepackage[LGRgreek]{mathastext} - \let\varphi\phi - \linespread{1.06} +gfs_bodoni: |- + \usepackage[T1]{fontenc} + \renewcommand{\rmdefault}{bodoni} + \usepackage[LGRgreek]{mathastext} + \let\varphi\phi + \linespread{1.06} # GFS Didot (Italic) -gfs_didot: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \renewcommand\rmdefault{udidot} - \usepackage[LGRgreek,defaultmathsizes,italic]{mathastext} - \let\varphi\phi +gfs_didot: |- + \usepackage[T1]{fontenc} + \renewcommand\rmdefault{udidot} + \usepackage[LGRgreek,defaultmathsizes,italic]{mathastext} + \let\varphi\phi # GFS NeoHellenic -gfs_neoHellenic: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \renewcommand{\rmdefault}{neohellenic} - \usepackage[LGRgreek]{mathastext} - \let\varphi\phi - \linespread{1.06} +gfs_neoHellenic: |- + \usepackage[T1]{fontenc} + \renewcommand{\rmdefault}{neohellenic} + \usepackage[LGRgreek]{mathastext} + \let\varphi\phi + \linespread{1.06} # GNU FreeSerif (and TX fonts symbols) -gnu_freesans_tx: - compiler: xelatex - preamble: |- - \usepackage[no-math]{fontspec} - \usepackage{txfonts} - \setmainfont[ExternalLocation,Mapping=tex-text,BoldFont=FreeSerifBold,ItalicFont=FreeSerifItalic,BoldItalicFont=FreeSerifBoldItalic]{FreeSerif} - \usepackage[defaultmathsizes]{mathastext} +gnu_freesans_tx: |- + \usepackage[no-math]{fontspec} + \usepackage{txfonts} + \setmainfont[ExternalLocation,Mapping=tex-text,BoldFont=FreeSerifBold,ItalicFont=FreeSerifItalic,BoldItalicFont=FreeSerifBoldItalic]{FreeSerif} + \usepackage[defaultmathsizes]{mathastext} # GNU FreeSerif and FreeSans -gnu_freeserif_freesans: - compiler: xelatex - preamble: |- - \usepackage[no-math]{fontspec} - \setmainfont[ExternalLocation,Mapping=tex-text,BoldFont=FreeSerifBold,ItalicFont=FreeSerifItalic,BoldItalicFont=FreeSerifBoldItalic]{FreeSerif} - \setsansfont[ExternalLocation,Mapping=tex-text,BoldFont=FreeSansBold,ItalicFont=FreeSansOblique,BoldItalicFont=FreeSansBoldOblique,Scale=MatchLowercase]{FreeSans} - \renewcommand{\familydefault}{lmss} - \usepackage[LGRgreek,defaultmathsizes,noasterisk]{mathastext} - \renewcommand{\familydefault}{\sfdefault} - \Mathastext - \let\varphi\phi - \renewcommand{\familydefault}{\rmdefault} +gnu_freeserif_freesans: |- + \usepackage[no-math]{fontspec} + \setmainfont[ExternalLocation,Mapping=tex-text,BoldFont=FreeSerifBold,ItalicFont=FreeSerifItalic,BoldItalicFont=FreeSerifBoldItalic]{FreeSerif} + \setsansfont[ExternalLocation,Mapping=tex-text,BoldFont=FreeSansBold,ItalicFont=FreeSansOblique,BoldItalicFont=FreeSansBoldOblique,Scale=MatchLowercase]{FreeSans} + \renewcommand{\familydefault}{lmss} + \usepackage[LGRgreek,defaultmathsizes,noasterisk]{mathastext} + \renewcommand{\familydefault}{\sfdefault} + \Mathastext + \let\varphi\phi + \renewcommand{\familydefault}{\rmdefault} # Helvetica with Fourier (Italic) -helvetica_fourier_it: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[scaled]{helvet} - \usepackage{fourier} - \renewcommand{\rmdefault}{phv} - \usepackage[italic,defaultmathsizes,noasterisk]{mathastext} +helvetica_fourier_it: |- + \usepackage[T1]{fontenc} + \usepackage[scaled]{helvet} + \usepackage{fourier} + \renewcommand{\rmdefault}{phv} + \usepackage[italic,defaultmathsizes,noasterisk]{mathastext} # Latin Modern Typewriter Proportional -latin_modern_tw: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[variablett]{lmodern} - \renewcommand{\rmdefault}{\ttdefault} - \usepackage[LGRgreek]{mathastext} - \MTgreekfont{lmtt} - \Mathastext - \let\varepsilon\epsilon +latin_modern_tw: |- + \usepackage[T1]{fontenc} + \usepackage[variablett]{lmodern} + \renewcommand{\rmdefault}{\ttdefault} + \usepackage[LGRgreek]{mathastext} + \MTgreekfont{lmtt} + \Mathastext + \let\varepsilon\epsilon # Latin Modern Typewriter Proportional (CM Greek) (Italic) -latin_modern_tw_it: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[variablett,nomath]{lmodern} - \renewcommand{\familydefault}{\ttdefault} - \usepackage[frenchmath]{mathastext} - \linespread{1.08} +latin_modern_tw_it: |- + \usepackage[T1]{fontenc} + \usepackage[variablett,nomath]{lmodern} + \renewcommand{\familydefault}{\ttdefault} + \usepackage[frenchmath]{mathastext} + \linespread{1.08} # Libertine -libertine: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{libertine} - \usepackage[greek=n]{libgreek} - \usepackage[noasterisk,defaultmathsizes]{mathastext} +libertine: |- + \usepackage[T1]{fontenc} + \usepackage{libertine} + \usepackage[greek=n]{libgreek} + \usepackage[noasterisk,defaultmathsizes]{mathastext} # Libris ADF with Fourier -libris_adf_fourier: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[upright]{fourier} - \usepackage{libris} - \renewcommand{\familydefault}{\sfdefault} - \usepackage[noasterisk]{mathastext} +libris_adf_fourier: |- + \usepackage[T1]{fontenc} + \usepackage[upright]{fourier} + \usepackage{libris} + \renewcommand{\familydefault}{\sfdefault} + \usepackage[noasterisk]{mathastext} # Minion Pro and Myriad Pro (and TX fonts symbols) -minion_pro_myriad_pro: - compiler: xelatex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage[default]{droidserif} - \usepackage[LGRgreek]{mathastext} - \let\varepsilon\epsilon +minion_pro_myriad_pro: |- + \usepackage[T1]{fontenc} + \usepackage[default]{droidserif} + \usepackage[LGRgreek]{mathastext} + \let\varepsilon\epsilon # Minion Pro (and TX fonts symbols) -minion_pro_tx: - compiler: xelatex - preamble: |- - \usepackage{txfonts} - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{Minion Pro} - \usepackage[defaultmathsizes]{mathastext} +minion_pro_tx: |- + \usepackage{txfonts} + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{Minion Pro} + \usepackage[defaultmathsizes]{mathastext} # New Century Schoolbook (Symbol Greek) -new_century_schoolbook: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{newcent} - \usepackage[symbolgreek]{mathastext} - \linespread{1.1} +new_century_schoolbook: |- + \usepackage[T1]{fontenc} + \usepackage{newcent} + \usepackage[symbolgreek]{mathastext} + \linespread{1.1} # New Century Schoolbook (Symbol Greek, PX math symbols) -new_century_schoolbook_px: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{pxfonts} - \usepackage{newcent} - \usepackage[symbolgreek,defaultmathsizes]{mathastext} - \linespread{1.06} +new_century_schoolbook_px: |- + \usepackage[T1]{fontenc} + \usepackage{pxfonts} + \usepackage{newcent} + \usepackage[symbolgreek,defaultmathsizes]{mathastext} + \linespread{1.06} # Noteworthy Light -noteworthy_light: - compiler: latex - preamble: |- - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{Noteworthy Light} - \usepackage[defaultmathsizes]{mathastext} +noteworthy_light: |- + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{Noteworthy Light} + \usepackage[defaultmathsizes]{mathastext} # Palatino (Symbol Greek) -palatino: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{palatino} - \usepackage[symbolmax,defaultmathsizes]{mathastext} +palatino: |- + \usepackage[T1]{fontenc} + \usepackage{palatino} + \usepackage[symbolmax,defaultmathsizes]{mathastext} # Papyrus -papyrus: - compiler: xelatex - preamble: |- - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{Papyrus} - \usepackage[defaultmathsizes]{mathastext} +papyrus: |- + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{Papyrus} + \usepackage[defaultmathsizes]{mathastext} # Romande ADF with Fourier (Italic) -romande_adf_fourier_it: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{fourier} - \usepackage{romande} - \usepackage[italic,defaultmathsizes,noasterisk]{mathastext} - \renewcommand{\itshape}{\swashstyle} +romande_adf_fourier_it: |- + \usepackage[T1]{fontenc} + \usepackage{fourier} + \usepackage{romande} + \usepackage[italic,defaultmathsizes,noasterisk]{mathastext} + \renewcommand{\itshape}{\swashstyle} # SliTeX (Euler Greek) -slitex: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{tpslifonts} - \usepackage[eulergreek,defaultmathsizes]{mathastext} - \MTEulerScale{1.06} - \linespread{1.2} +slitex: |- + \usepackage[T1]{fontenc} + \usepackage{tpslifonts} + \usepackage[eulergreek,defaultmathsizes]{mathastext} + \MTEulerScale{1.06} + \linespread{1.2} # Times with Fourier (Italic) -times_fourier_it: - compiler: latex - preamble: |- - \usepackage{fourier} - \renewcommand{\rmdefault}{ptm} - \usepackage[italic,defaultmathsizes,noasterisk]{mathastext} +times_fourier_it: |- + \usepackage{fourier} + \renewcommand{\rmdefault}{ptm} + \usepackage[italic,defaultmathsizes,noasterisk]{mathastext} # URW Avant Garde (Symbol Greek) -urw_avant_garde: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{avant} - \renewcommand{\familydefault}{\sfdefault} - \usepackage[symbolgreek,defaultmathsizes]{mathastext} +urw_avant_garde: |- + \usepackage[T1]{fontenc} + \usepackage{avant} + \renewcommand{\familydefault}{\sfdefault} + \usepackage[symbolgreek,defaultmathsizes]{mathastext} # URW Zapf Chancery (CM Greek) -urw_zapf_chancery: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \DeclareFontFamily{T1}{pzc}{} - \DeclareFontShape{T1}{pzc}{mb}{it}{<->s*[1.2] pzcmi8t}{} - \DeclareFontShape{T1}{pzc}{m}{it}{<->ssub * pzc/mb/it}{} - \DeclareFontShape{T1}{pzc}{mb}{sl}{<->ssub * pzc/mb/it}{} - \DeclareFontShape{T1}{pzc}{m}{sl}{<->ssub * pzc/mb/sl}{} - \DeclareFontShape{T1}{pzc}{m}{n}{<->ssub * pzc/mb/it}{} - \usepackage{chancery} - \usepackage{mathastext} - \linespread{1.05} - \boldmath +urw_zapf_chancery: |- + \usepackage[T1]{fontenc} + \DeclareFontFamily{T1}{pzc}{} + \DeclareFontShape{T1}{pzc}{mb}{it}{<->s*[1.2] pzcmi8t}{} + \DeclareFontShape{T1}{pzc}{m}{it}{<->ssub * pzc/mb/it}{} + \DeclareFontShape{T1}{pzc}{mb}{sl}{<->ssub * pzc/mb/it}{} + \DeclareFontShape{T1}{pzc}{m}{sl}{<->ssub * pzc/mb/sl}{} + \DeclareFontShape{T1}{pzc}{m}{n}{<->ssub * pzc/mb/it}{} + \usepackage{chancery} + \usepackage{mathastext} + \linespread{1.05} + \boldmath # Venturis ADF with Fourier (Italic) -venturis_adf_fourier_it: - compiler: latex - preamble: |- - \usepackage{fourier} - \usepackage[lf]{venturis} - \usepackage[italic,defaultmathsizes,noasterisk]{mathastext} +venturis_adf_fourier_it: |- + \usepackage{fourier} + \usepackage[lf]{venturis} + \usepackage[italic,defaultmathsizes,noasterisk]{mathastext} # Verdana (Italic) -verdana_it: - compiler: xelatex - preamble: |- - \usepackage[no-math]{fontspec} - \setmainfont[Mapping=tex-text]{Verdana} - \usepackage[defaultmathsizes,italic]{mathastext} +verdana_it: |- + \usepackage[no-math]{fontspec} + \setmainfont[Mapping=tex-text]{Verdana} + \usepackage[defaultmathsizes,italic]{mathastext} # Vollkorn (TX fonts for Greek and math symbols) -vollkorn: - compiler: latex - preamble: |- - \usepackage[T1]{fontenc} - \usepackage{txfonts} - \usepackage[upright]{txgreeks} - \usepackage{vollkorn} - \usepackage[defaultmathsizes]{mathastext} +vollkorn: |- + \usepackage[T1]{fontenc} + \usepackage{txfonts} + \usepackage[upright]{txgreeks} + \usepackage{vollkorn} + \usepackage[defaultmathsizes]{mathastext} # Vollkorn with Fourier (Italic) -vollkorn_fourier_it: - compiler: latex - preamble: |- - \usepackage{fourier} - \usepackage{vollkorn} - \usepackage[italic,nohbar]{mathastext} +vollkorn_fourier_it: |- + \usepackage{fourier} + \usepackage{vollkorn} + \usepackage[italic,nohbar]{mathastext} # Zapf Chancery -zapf_chancery: - compiler: latex - preamble: |- - \DeclareFontFamily{T1}{pzc}{} - \DeclareFontShape{T1}{pzc}{mb}{it}{<->s*[1.2] pzcmi8t}{} - \DeclareFontShape{T1}{pzc}{m}{it}{<->ssub * pzc/mb/it}{} - \usepackage{chancery} - \renewcommand\shapedefault\itdefault - \renewcommand\bfdefault\mddefault - \usepackage[defaultmathsizes]{mathastext} - \linespread{1.05} +zapf_chancery: |- + \DeclareFontFamily{T1}{pzc}{} + \DeclareFontShape{T1}{pzc}{mb}{it}{<->s*[1.2] pzcmi8t}{} + \DeclareFontShape{T1}{pzc}{m}{it}{<->ssub * pzc/mb/it}{} + \usepackage{chancery} + \renewcommand\shapedefault\itdefault + \renewcommand\bfdefault\mddefault + \usepackage[defaultmathsizes]{mathastext} + \linespread{1.05} diff --git a/manimlib/utils/init_config.py b/manimlib/utils/init_config.py index f71d4039..41d9faf5 100644 --- a/manimlib/utils/init_config.py +++ b/manimlib/utils/init_config.py @@ -44,8 +44,9 @@ def init_customization() -> None: }, "universal_import_line": "from manimlib import *", "style": { + "tex_compiler": "", "tex_font": "", - "font": "", + "font": "Consolas", "background_color": "", }, "window_position": "UR", @@ -57,7 +58,7 @@ def init_customization() -> None: "medium": "1280x720", "high": "1920x1080", "4k": "3840x2160", - "default_resolution": "high", + "default_resolution": "", }, "fps": 30, } @@ -106,14 +107,16 @@ def init_customization() -> None: console.print("[bold]Styles:[/bold]") style_config = configuration["style"] - style_config["tex_font"] = Prompt.ask( - " Which [bold]font[/bold] for LaTeX do you want", - default="default" - ) - style_config["font"] = Prompt.ask( - " Which [bold]font[/bold] for non-LaTeX text do you want", - default="Consolas" + compiler = Prompt.ask( + " Select an executable program to use to compile a LaTeX source file", + choices=["latex", "xelatex"], + default="latex" ) + style_config["tex_compiler"] = compiler + if compiler == "latex": + style_config["tex_font"] = "default" + else: + style_config["tex_font"] = "ctex" style_config["background_color"] = Prompt.ask( " Which [bold]background color[/bold] do you want [italic](hex code)", default="#333333" @@ -127,7 +130,7 @@ def init_customization() -> None: ) table.add_row("480p15", "720p30", "1080p60", "2160p60") console.print(table) - configuration["camera_qualities"]["default_quality"] = Prompt.ask( + configuration["camera_resolutions"]["default_resolution"] = Prompt.ask( " Which one to choose as the default rendering quality", choices=["low", "medium", "high", "ultra_high"], default="high" diff --git a/manimlib/utils/tex_file_writing.py b/manimlib/utils/tex_file_writing.py index 2da51caa..00a95a52 100644 --- a/manimlib/utils/tex_file_writing.py +++ b/manimlib/utils/tex_file_writing.py @@ -15,14 +15,7 @@ from manimlib.utils.simple_functions import hash_string SAVED_TEX_CONFIG = {} -def get_tex_font_config(tex_font: str) -> str: - """ - Returns a dict which should look something like this: - { - "compiler": "latex", - "preamble": "..." - } - """ +def get_tex_font_preamble(tex_font: str) -> str: name = re.sub(r"[^a-zA-Z]", "_", tex_font).lower() with open(os.path.join( get_manim_dir(), "manimlib", "tex_fonts.yml" @@ -35,30 +28,40 @@ def get_tex_font_config(tex_font: str) -> str: ) name = "default" result = templates_dict[name] - if name not in ("default", "ctex", "blank", "basic"): - result["preamble"] = "\n".join(( - templates_dict["basic"]["preamble"], result["preamble"] - )) + if name not in ("default", "ctex", "basic"): + result = templates_dict["basic"] + "\n" + result return result def get_tex_config() -> dict[str, str]: + """ + Returns a dict which should look something like this: + { + "compiler": "latex", + "font": "default", + "preamble": "..." + } + """ # Only load once, then save thereafter if not SAVED_TEX_CONFIG: - tex_font = get_custom_config()["style"]["tex_font"] - SAVED_TEX_CONFIG.update(get_tex_font_config(tex_font)) + style_config = get_custom_config()["style"] + SAVED_TEX_CONFIG.update({ + "compiler": style_config["tex_compiler"], + "font": style_config["tex_font"], + "preamble": get_tex_font_preamble(style_config["tex_font"]) + }) return SAVED_TEX_CONFIG def tex_content_to_svg_file( content: str, tex_font: str, additional_preamble: str ) -> str: - if not tex_font: - tex_config = get_tex_config() + tex_config = get_tex_config() + if not tex_font or tex_font == tex_config["font"]: + preamble = tex_config["preamble"] else: - tex_config = get_tex_font_config(tex_font) + preamble = get_tex_font_preamble(tex_font) - preamble = tex_config["preamble"] if additional_preamble: preamble += "\n" + additional_preamble full_tex = "\n\n".join((