diff --git a/camp2023/config.ini b/camp2023/config.ini index 5dbefd6..e8c7ab6 100644 --- a/camp2023/config.ini +++ b/camp2023/config.ini @@ -31,5 +31,5 @@ fontsize = 45 fontcolor = #FB48C4 x = (w-text_w)/2 y = 1000 -text = 'Chaos Communication Camp 2023' +text = Chaos Communication Camp 2023 diff --git a/cccamp19/config.ini b/cccamp19/config.ini index 0316909..d723d7d 100644 --- a/cccamp19/config.ini +++ b/cccamp19/config.ini @@ -31,5 +31,5 @@ fontsize = 45 fontcolor = #c68100 x = (w-text_w)/2 y = 927 -text = 'chaos communication camp 2019' +text = chaos communication camp 2019 diff --git a/denog11/config.ini b/denog11/config.ini index 0716e7e..89c8096 100644 --- a/denog11/config.ini +++ b/denog11/config.ini @@ -31,5 +31,5 @@ fontsize = 45 fontcolor = #ffffff x = 640 y = 1000 -text = '' +; text = diff --git a/jh19-berlin/config.ini b/jh19-berlin/config.ini index 171ea67..73365a8 100644 --- a/jh19-berlin/config.ini +++ b/jh19-berlin/config.ini @@ -31,5 +31,5 @@ fontsize = 45 fontcolor = #c68100 x = (w-text_w)/2 y = 927 -text = '' +; text = diff --git a/jh20-jue/config.ini b/jh20-jue/config.ini index 7f65b89..d48087f 100644 --- a/jh20-jue/config.ini +++ b/jh20-jue/config.ini @@ -42,7 +42,7 @@ fontsize = 45 fontcolor = #ffffff x = 640 y = 1000 -text = '' +; text = ; build intros via diff --git a/jh21-rn/config.ini b/jh21-rn/config.ini index 8c25d4d..626db55 100644 --- a/jh21-rn/config.ini +++ b/jh21-rn/config.ini @@ -31,5 +31,5 @@ fontsize = 45 fontcolor = #ffffff x = 1920 y = 1080 -text = '' +; text = diff --git a/jugendhackt/config.ini b/jugendhackt/config.ini index da1b7bd..8af8ab6 100644 --- a/jugendhackt/config.ini +++ b/jugendhackt/config.ini @@ -42,11 +42,11 @@ fontsize = 50 x = 400 y = 950 -;; optional extra text +;; optional extra text, comment out "text" field to disable [text] in = 0 out = 0 fontsize = 0 x = 0 y = 0 -text = '' +;text = some additional text diff --git a/make-ffmpeg.py b/make-ffmpeg.py index 3b69978..aa67320 100755 --- a/make-ffmpeg.py +++ b/make-ffmpeg.py @@ -16,6 +16,8 @@ from PIL import ImageFont import schedulelib ssl._create_default_https_context = ssl._create_unverified_context +FRAME_WIDTH = 1920 + class TextConfig: inpoint: float @@ -58,31 +60,41 @@ class TextConfig: self.fontsize = cparser_sect.getint('fontsize') self.bordercolor = cparser_sect.get('bordercolor', None) - def fit_text(self, text: str): - global translation_font - translation_font = ImageFont.truetype( + def fit_text(self, text: str) -> list[str]: + if not text: + return [""] + + font = ImageFont.truetype( self.fontfile_path, size=self.fontsize, encoding="unic") # TODO: Make this work with font family as well! - return fit_text(text, (1920-self.x-100)) + return fit_text(text, (FRAME_WIDTH-self.x-100), font) - def get_ffmpeg_filter(self, inout_type: str, text: str): - filter_str = "drawtext=enable='between({},{},{})'".format( - inout_type, self.inpoint, self.outpoint) + def get_ffmpeg_filter(self, inout_type: str, text: list[str]): + if not text: + return "" - if self.uses_fontfile(): - filter_str += ":fontfile='{}'".format(self.fontfile_path) - else: - filter_str += ":font='{}'".format(self.fontfamily) + filter_str = "" + for idx, line in enumerate(text): + filter_str += "drawtext=enable='between({},{},{})'".format( + inout_type, self.inpoint, self.outpoint) - if self.bordercolor is not None: - filter_str += ":borderw={}:bordercolor={}".format(self.fontsize / 30, self.bordercolor) + if self.uses_fontfile(): + filter_str += ":fontfile='{}'".format(self.fontfile_path) + else: + filter_str += ":font='{}'".format(self.fontfamily) - filter_str += ":fontsize={0}:fontcolor={1}:x={2}:y={3}:text={4}".format( - self.fontsize, self.fontcolor, self.x, self.y, ffmpeg_escape_str(text)) + if self.bordercolor is not None: + filter_str += ":borderw={}:bordercolor={}".format( + self.fontsize / 30, self.bordercolor) - return filter_str + filter_str += ":fontsize={0}:fontcolor={1}:x={2}:y={3}:text={4}".format( + self.fontsize, self.fontcolor, self.x, self.y + (idx*self.fontsize), ffmpeg_escape_str(line)) + + filter_str += "," + + return filter_str[:-1] class Config: @@ -97,7 +109,7 @@ class Config: title: TextConfig speaker: TextConfig text: TextConfig - text_text: str = "" # additional text + extra_text: str = "" # additional text def parse_config(filename) -> Config: @@ -129,7 +141,7 @@ def parse_config(filename) -> Config: conf.text = TextConfig() conf.text.parse(cparser['text'], default_fontfile, default_fontfamily, default_fontcolor) - conf.text_text = cparser['text'].get('text', '') + conf.extra_text = cparser['text'].get('text', '') conf.fileext = infile.suffix @@ -165,20 +177,29 @@ def event_print(event, message): print("{} – {}".format(describe_event(event), message)) -def fit_text(string: str, frame_width): +def fit_text(string: str, max_width: int, font: ImageFont) -> list[str]: + """Break text into array of strings which fit certain a width (in pixels) for the specified font.""" + split_line = [x.strip() for x in string.split()] - lines = "" - line = "" + lines = [] + w = 0 + line = [] for word in split_line: - left, top, right, bottom = translation_font.getbbox(" ".join([line, word])) - width, height = right - left, bottom - top - if width > (frame_width - (2 * 6)): - lines += line.strip() + "\n" - line = "" + new_line = line + [word.rstrip(':')] + w = font.getlength(" ".join(new_line)) + if w > max_width: + lines.append(' '.join(line)) + line = [] - line += word + " " + line.append(word.rstrip(':')) + + if word.endswith(':'): + lines.append(' '.join(line)) + line = [] + + if line: + lines.append(' '.join(line)) - lines += line.strip() return lines @@ -210,9 +231,10 @@ def enqueue_job(conf: Config, event): title = conf.title.fit_text(event_title) speakers = conf.speaker.fit_text(event_personnames) + extra_text = conf.text.fit_text(conf.extra_text) if args.debug: - print('Title: ', title) + print('Title: ', title) print('Speaker: ', speakers) if platform.system() == 'Windows': @@ -222,7 +244,7 @@ def enqueue_job(conf: Config, event): videofilter = conf.title.get_ffmpeg_filter(conf.inout_type, title) + "," videofilter += conf.speaker.get_ffmpeg_filter(conf.inout_type, speakers) + "," - videofilter += conf.text.get_ffmpeg_filter(conf.inout_type, conf.text_text) + videofilter += conf.text.get_ffmpeg_filter(conf.inout_type, extra_text) cmd = [ffmpeg_path, '-y', '-i', conf.template_file, '-vf', videofilter] @@ -298,7 +320,7 @@ if __name__ == "__main__": persons = ['Thomas Roth', 'Dmitry Nedospasov', 'Josh Datko',] events = [{ 'id': 'debug', - 'title': 'wallet.fail', + 'title': 'wallet.fail and the longest talk title to test if the template is big enough', 'subtitle': 'Hacking the most popular cryptocurrency hardware wallets', 'persons': persons, 'personnames': ', '.join(persons), diff --git a/mrmcd2019/config.ini b/mrmcd2019/config.ini index 30da816..fb74efc 100755 --- a/mrmcd2019/config.ini +++ b/mrmcd2019/config.ini @@ -32,4 +32,4 @@ fontsize = 45 fontcolor = #c68100 x = (w-text_w)/2 y = 927 -text = '' +; text = diff --git a/vcfb24/config.ini b/vcfb24/config.ini index fff89ad..b363273 100644 --- a/vcfb24/config.ini +++ b/vcfb24/config.ini @@ -34,4 +34,4 @@ fontsize = 0 fontcolor = #ffffff x = 0 y = 0 -text = '' +; text =