manim icon indicating copy to clipboard operation
manim copied to clipboard

Bad kerning in `Text()`

Open prism0x opened this issue 3 years ago • 8 comments

Description of bug / unexpected behavior

When using Text() or Paragraph(), the spacing between letters is generally off regardless of the choice of the font. This is probably an issue with Pango.

See the attached images.

Expected behavior

The spacing between the letters should be more balanced and evenly distributed.

How to reproduce the issue

Code for reproducing the problem
class DemoScene(Scene):
    def construct(self):
        paragraph = Paragraph(
            """
        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
        sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud.""", 
            font="Helvetica", font_size=32, line_spacing = 0.5
        )
        self.add(paragraph)

Additional media files

Images/GIFs

image image

Logs

Terminal output
PASTE HERE OR PROVIDE LINK TO https://pastebin.com/ OR SIMILAR

System specifications

System Details

I rendered it using the Manimator in the Manim Community Server. The version is 0.15.2

  • OS (with version, e.g., Windows 10 v2004 or macOS 10.15 (Catalina)):
  • RAM:
  • Python version (python/py/python3 --version):
  • Installed modules (provide output from pip list):
PASTE HERE
LaTeX details
  • LaTeX distribution (e.g. TeX Live 2020):
  • Installed LaTeX packages:
FFMPEG

Output of ffmpeg -version:

PASTE HERE

Additional comments

prism0x avatar Jun 22 '22 16:06 prism0x

I've found this same issue. I think it's a problem about pango and/or cairo and the font size. Let me explain.

Cause of the issue

If you run this code:

class BadKerning(Scene):
    def construct(self):
        import manimpango

          g = VGroup()
          for size in range(0,100,2):
              g += Text("{}:THIN".format(size), font="Open Sans", font_size=size, disable_ligatures=True)
          self.add(g.arrange(DOWN).scale(0.25))

You will something like this:

kerning

Using large font sizes, the kerning is good. But if you download the image (it's a 5000x5000), and check smaller font sizes, then you can see bad kerning. I've read: https://gitlab.gnome.org/GNOME/pango/-/issues/686. And I think it's an issue about generating text for screens, in pixel size.

Proposed solution

Using bigger font sizes clearly improves the kerning. But I don't want to have bigger fonts all the time, obviously. That's why I changed the line 77 in text -> text_mobject.py

TEXT_MOB_SCALE_FACTOR = 0.005 # Or even a smaller value.

That reduces the scale or the text object. You have to choose bigger font sizes to look similar, but it will look nice even in small texts.

Fairlight8 avatar Dec 25 '22 12:12 Fairlight8

@Fairlight8 works fine, thank you for your answer

shokirovw avatar Oct 18 '23 18:10 shokirovw

Just in case there are people who are not interested in going directly to modify the source code, this can serve as an alternative:

# Fix for bad kerning in the text
TEXT_SCALE_FACTOR = 0.3

class ScaledMarkupText(MarkupText):
    def __init__(self, *args, **kwargs):
        scale_font = False
        # If the font size is lower than 32, scale it up
        if "font_size" in kwargs and kwargs["font_size"] < 32:
            scale_font = True
            kwargs["font_size"] /= TEXT_SCALE_FACTOR
        super().__init__(*args, **kwargs)
        if scale_font:
            self.scale(TEXT_SCALE_FACTOR)

It is not perfect, since a small TEXT_SCALE_FACTOR may cause the text to line break at random points.

Rapsssito avatar Jan 04 '25 13:01 Rapsssito

Hello @Rapsssito ! Thanks for the addition, I also reached a similar solution, since I had to work with manim in different computers, and having to modify the code in different python environments was a nightmare. Instead of what you propose, I do something more simple: Use a huge font size (for example: original font size * 10) and then scale the mobject itself (.scale(0.1)).

Fairlight8 avatar Jan 08 '25 15:01 Fairlight8

I don't think this is just an issue with smaller fonts. The following example shows that there is a letter spacing problem, which looks like a float gets transformed to an integer somewhere.

class TextIssue(Scene):
    def construct(self):
        font_size = ValueTracker(56)
        text = always_redraw(lambda: Text(f"Test 123\nNewline", font="Arial", font_size=font_size.get_value(), disable_ligatures=True).move_to(ORIGIN, aligned_edge=UL))
        value = always_redraw(lambda: Text(f"{font_size.get_value():g}").to_corner(UL))
        self.add(text, value)
        self.play(font_size.animate.set_value(98), run_time=10, rate_func=rate_functions.linear)
        self.wait(0.2)

https://github.com/user-attachments/assets/b28d42ee-8184-4c5a-b255-aaa9c63ab512

Generated using Manim Community v0.19.0. The font size animates nicely, but the letter spacing jumps in size even in between integer font sizes. The font size can be seen in the top left corner.

blackedout01 avatar Jun 23 '25 07:06 blackedout01

Still this remains a problem in the underlying Pango library and nothing which could easily be affected from within Manim.

uwezi avatar Jun 23 '25 08:06 uwezi

My best solution is still affecting the scale. Huge font size, and then scale down. But two problems still remain. First, using a huge font size typically forces text to break into new lines all the time. And second, as @blackedout01 said, it is still not perfect, thanks for the animation.

Unfortunately, I started using Latex for generating text. Having a miktex installation is a pain in the ass, but it seems more consistent, so...

Fairlight8 avatar Jun 23 '25 09:06 Fairlight8

I don't think it's relevant here, but why do you think that a MikTeX installation on your system is a "pain in the ass"?

uwezi avatar Jun 23 '25 09:06 uwezi