Text and Tex
===============
There are three ways to create texts in Manim:
* With system fonts: ``Text``.
* With PangoMarkup: ``MarkupText``.
* With LaTeX: ``Tex`` and ``MathTex``.
The simplest is with ``Text``, so we'll start with that.
Text
-----------
Fonts
"""""""""""
``Text`` is a subclass of ``SVGMobject``. The text is rendered using **Pango** and uses the fonts you have installed.
To see all the fonts you can use the command:
.. code-block:: bash
python -c "import manimpango; from pprint import pprint; pprint(manimpango.list_fonts())"
It will give you an output like this:
.. code-block::
['Academy Engraved LET',
'Al Bayan',
'Al Nile',
'Al Tarikh',
'American Typewriter',
'Andale Mono',
'Apple Braille',
'Apple Chancery',
'Apple Color Emoji',
'Apple SD Gothic Neo',
'Apple Symbols',
'AppleGothic',
'AppleMyungjo',
....
....
]
If you use a shell like BASH (or similar) you can do something like this:
.. code-block::
python -c "import manimpango; from pprint import pprint; pprint(manimpango.list_fonts())" | grep -i arial
To filter your search.
How to use it
""""""""""""""""""
Since it is an ``SVGMobject``, then you can use all the methods of the VMobjects, i.e. set the ``stroke_width``, ``fill_opacity``, etc. By default the stroke is 0, but you can change that yourself.
.. code-block:: python
def construct(self):
t = Text("Hello world")
self.add(t)
I invite you to change the color, stroke width, fill color, stroke color, etc. Either using methods after the instance, or at the time of the instance.
To indicate the font we want to use, we can indicate it in the "font" parameter:
.. code-block:: python
def construct(self):
t = Text("Hello world", font="Arial")
self.add(t)
As array
""""""""""""
Text can be understood as SVG arrays, and thanks to this you can select each letter:
.. code-block:: python
def construct(self):
t = Text("Hello world", font="Arial")
t[0].set_color(RED)
t[3].set_color(TEAL)
self.add(t)
.. raw:: html
Show result
.. image:: ../_static/images/tx1.png
.. raw:: html
Line spacing
""""""""""""""""
If the text is long enough you can use triple quotes:
.. code-block:: python
def construct(self):
t = Text(
"""
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a
type specimen book.
""",
line_spacing=1.3 # space between lines
)
t.width = config.frame_width - 1
t[0].set_color(RED)
self.add(t)
.. raw:: html
Show result
.. image:: ../_static/images/tx2.png
.. raw:: html
But if you want to do it in a single line, you can do it using ``\n``:
.. code-block:: python
def construct(self):
t = Text(
"Lorem Ipsum is simply\ndummy text of the printing",
line_spacing=1.3 # space between lines
)
t.width = config.frame_width - 1
self.add(t)
.. raw:: html
Show result
.. image:: ../_static/images/tx3.png
.. raw:: html
Slant and Weight
""""""""""""""""""
Slant is the style of the Text, and it can be NORMAL (the default), *ITALIC* or *OBLIQUE*. Usually, for many fonts both *ITALIC* and *OBLIQUE* look similar, but *ITALIC* uses Roman Style, whereas *OBLIQUE* uses Italic Style.
.. code-block:: python
def construct(self):
t = Text(
"Lorem Ipsum is simply\ndummy text of the printing",
line_spacing=1.3, # space between lines
slant=ITALIC
)
t.width = config.frame_width - 1
self.add(t)
.. raw:: html
Show result
.. image:: ../_static/images/tx4.png
.. raw:: html
The different types of weight can be obtained from ManimPango:
.. code-block:: python
def construct(self):
import manimpango as mp
weight_list = dict(
sorted(
{
weight: mp.Weight(weight).value
for weight in mp.Weight
}.items(), key=lambda x: x[1])
)
grp = VGroup(*[
Text(weight.name, weight=weight.name, font="Open Sans")
for weight in weight_list
]).arrange(DOWN)
grp.height = config.frame_height - 1
self.add(grp)
.. raw:: html
Show result
.. image:: ../_static/images/tx5.png
.. raw:: html
Text to *
""""""""""""""""
There are some functions that allow us to manipulate sections of text in a simple way, however, if there are repetitions of text, these functions may not behave correctly.
.. code-block:: python
def construct(self):
grp = VGroup(
# Text to color
Text("Hello",t2c={"[1:-1]": BLUE}),
Text("World",t2c={"rl": RED}),
# Text to font
Text("Manim",t2f={"an": "Open Sans"}),
Text("Manim",t2f={"[2:-1]": "Open Sans"}),
# Text to gradient
Text("Hello",t2g={"[1:-1]": (RED,GREEN)}),
Text("World",t2g={"World": (RED,BLUE)}),
# Text to slant
Text("Manim",t2s={"an": ITALIC}),
Text("Manim",t2s={"[2:-1]": ITALIC}),
# Text to weight
Text("Manim",t2w={"an": THIN}, font="Open Sans"),
Text("Manim",t2w={"[2:]": HEAVY}, font="Open Sans"),
# Ligature
Text("fl ligature",font_size=40),
Text("fl ligature", disable_ligatures=True, font_size=40),
).arrange_in_grid(cols=2).scale(1.4)
self.add(grp)
.. raw:: html
Show result
.. image:: ../_static/images/tx6.png
.. raw:: html
MarkupText
------------------
``MarkupText`` is a class that uses **PangoMarkup**, language similar to **HTML** that allows us to modify the text with **XML** tags, if you understand **HTML or **XML** then it will be easy for you to understand this class.
.. code-block:: python
def construct(self):
text = MarkupText(
f'Normal Italic Bold Underline Blue text'
)
self.add(text)
I recommend reading the `official documentation `_ if you want to learn how to use this tool as it has many configurations.
.. raw:: html
Show result
.. image:: ../_static/images/tx7.png
.. raw:: html
Tex
--------
This is the most complex class, not only because it is the one with the most options, but because LaTeX is a world of its own.
If you do not have knowledge of LaTeX I recommend that you watch a 1-2 hour tutorial on the internet.
Templates
"""""""""""""""
As you should know, to use LaTeX you must include the libraries you want to use, by default, Manim uses this template:
.. code-block:: tex
\usepackage[english]{babel}
\usepackage{amsmath}
\usepackage{amssymb}
But you can add, remove or modify predefined templates, you can see the predefined templates `here `_.
If you want to do it manually you must follow these steps:
.. code-block:: python
# Create a new TexTemplate
my_template = TexTemplate()
# Add new preambles (can be more)
my_template.add_to_preamble(r"\newcommand{\st}[2]{{\tt S}_{\rm #1}^{\rm #2}}")
# Create a new subclass with your template
class MyTex(Tex):
def __init__(self, *args, **kwargs):
super().__init__(*args, tex_template=my_template, **kwargs)
class TestMyTexTemplate(Scene):
def construct(self):
text = MyTex("$\st{sub-index}{super-index}$").scale(3)
self.add(text)
.. raw:: html
Show result
.. image:: ../_static/images/tx8.png
.. raw:: html
What Manim renders is the following:
.. code-block:: tex
\documentclass[preview]{standalone}
\usepackage[english]{babel}
\usepackage{amsmath}
\usepackage{amssymb}
\newcommand{\st}[2]{{\tt S}_{\rm #1}^{\rm #2}}
\begin{document}
\begin{center}
$\st{sub-index}{super-index}$
\end{center}
\end{document}
You can add as many preambles as you want.
Enviroments
"""""""""""""""
Also, if you want, you can create enviroments, I use a lot an enviroment that allows me to control the width of the justified paragraph.
.. code-block:: python
class MyTex(Tex):
def __init__(self, *args, j_width=4, **kwargs):
super().__init__(*args, tex_environment="\\begin{tabular}{p{%s cm}}"%j_width, **kwargs)
class TestEnviroment(Scene):
def construct(self):
TEX = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"
t1 = MyTex(TEX)
t2 = MyTex(TEX, j_width=6)
t3 = MyTex(TEX, j_width=9)
grp = VGroup(t1,t2,t3).arrange(DOWN)
grp.height = config.frame_height - 1
self.add(grp)
.. raw:: html
Show result
.. image:: ../_static/images/tx9.png
.. raw:: html
You can find all the exported files in ``media/Tex``.
Tex as array
"""""""""""""""""
Like ``Text``, ``Tex`` is an array of several SVGs, but this one is a bit more complex.
``Tex`` not only accepts a single string, you can add as many strings as you want:
.. code-block:: python
def construct(self):
t1 = Tex("Hello world!")
# | Note the space
# v
t2 = Tex("Hello ","world","!")
grp = VGroup(t1,t2).arrange(DOWN,aligned_edge=LEFT)
self.add(grp)
This means that if we want to select only one element of ``Tex`` there are two possibilities.
If we want to select the third "l" of both texts then we must understand the following:
.. code-block:: python
# Tex("Hello world!")
# returns a container, and that container
# contains another container with the letters,
# we can see it as:
# [['H','e','l','l','o','w','o','r','l','d','!']] (Blanks don't count in Tex)
# So, if you want to select the 3rd "l" you must do:
def construct(self):
t = Tex("Hello world!")
t[0][-3].set_color(RED)
self.add(t)
# Tex("Hello ","world","!")
# returns a container with 3 containers:
# [['H','e','l','l','o'],['w','o','r','l','d'],['!']]
# So, if you want to select the 3rd "l" you must do:
def construct(self):
t = Tex("Hello ","world","!")
t[1][-2].set_color(RED)
self.add(t)
.. raw:: html
Show result
.. image:: ../_static/images/tx11.png
.. raw:: html
.. note::
This topic will be important when we do formula transformations.
Tex to color map
"""""""""""""""""""""
This is equivalent to ``t2c``, it works well if your text is simple, but if you use enviroments or your ``Tex`` is very complex, it will most likely not work, so you should use it with discretion.
.. code-block:: python
def construct(self):
t = Tex(
"Hello my ","world",
tex_to_color_map={
"Hello": RED,
"wor": ORANGE
}
)
self.add(t)
.. raw:: html
Show result
.. image:: ../_static/images/tx12.png
.. raw:: html
MathTex
""""""""""""
If ``Tex`` is LaTeX in normal mode, then ``MathTex`` is LaTeX in math mode, that is, ``MathTex("formula")`` is equivalent to ``Tex("$$formula$$")``.