2

I am currently trying out moviepy to burn subtitles on a video. However I keep getting the same error message no matter what I do. This is the code I am using:

from moviepy import TextClip
from moviepy.video.tools.subtitles import SubtitlesClip
...
generator = lambda txt: TextClip(
    text = self.txt,
    font = self.font_path,
    font_size = 100,
    color= self.text_color,
    stroke_color="black",
    stroke_width=5,
)
subtitles = SubtitlesClip(self.subtitles_path, generator)

where self.font_path is currently the string "/fonts/my_font.otf". Regardless of the font however, I keep getting the error message:

ValueError: Invalid font <function _spyderpdb_code.<locals>.<lambda> at 0x7b89f4d60e00>, pillow failed to use it with error 'function' object has no attribute 'read'
1

2 Answers 2

1

I was facing the same issue and I managed to solve it.


Corrected code

from moviepy import TextClip
from moviepy.video.tools.subtitles import SubtitlesClip
...
generator = lambda txt: TextClip(
    self.font_path,
    text = self.txt,
    font_size = 100,
    color= self.text_color,
    stroke_color="black",
    stroke_width=5,
)
subtitles = SubtitlesClip(self.subtitles_path, make_textclip=generator)

Explanation

The error occurs on this portion of code in the VideoClip.py from MoviePy:

try:
    _ = ImageFont.truetype(font)
except Exception as e:
    raise ValueError(
        "Invalid font {}, pillow failed to use it with error {}".format(font, e)
    )

The thing is that font is interpreted as a your generator function.

As said AKX:

pass your generator as make_textclip

But this did not fully solve the issue because you will face another error:

TypeError: multiple values for argument 'font'

To solve it you need to look at the TextClip class:

class TextClip(ImageClip):
    ...
    @convert_path_to_string("filename")
    def __init__(
        self,
        font,
        text=None,
        filename=None,
        font_size=None,
        size=(None, None),
        margin=(None, None),
        color="black",
        bg_color=None,
        stroke_color=None,
        stroke_width=0,
        method="label",
        text_align="left",
        horizontal_align="center",
        vertical_align="center",
        interline=4,
        transparent=True,
        duration=None,
    ):

The first argument needed to initialise a TextClip object is the font as a positional argument and then the text as a named argument.

Note

You also don't need to pass the full path of the font based on PIL's truetype doctring:

:param font: A filename or file-like object containing a TrueType font.
             If the file is not found in this filename, the loader may also
             search in other directories, such as:

             * The :file:`fonts/` directory on Windows,
             * :file:`/Library/Fonts/`, :file:`/System/Library/Fonts/`
              and :file:`~/Library/Fonts/` on macOS.
             * :file:`~/.local/share/fonts`, :file:`/usr/local/share/fonts`,
              and :file:`/usr/share/fonts` on Linux; or those specified by
              the ``XDG_DATA_HOME`` and ``XDG_DATA_DIRS`` environment variables
              for user-installed and system-wide fonts, respectively.

It will automatically search for the font in the correct folder according to your OS.

New contributor
Aizenberg is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
0

The documented example for that functionality seems to be wrong.

You'd probably need

subtitles = SubtitlesClip(self.subtitles_path, make_textclip=generator)

i.e. pass your generator as make_textclip, not as the positional font argument.

(I created a PR to fix the example. https://github.com/Zulko/moviepy/pull/2271)

Not the answer you're looking for? Browse other questions tagged or ask your own question.