pythonvideopython-imaging-libraryvideo-processingmoviepy

MoviePy SubtitlesClip will not accept any provided font (Python)


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'

Solution

  • 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.