From 52776494cb958540c30d0f45d27b9db13cb7bb4b Mon Sep 17 00:00:00 2001 From: Matthias Bisping Date: Mon, 23 Jan 2023 14:15:41 +0100 Subject: [PATCH] Tweak font selection --- test/fixtures/page_generation/page.py | 87 ++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/test/fixtures/page_generation/page.py b/test/fixtures/page_generation/page.py index 2e1493b..b79bbd2 100644 --- a/test/fixtures/page_generation/page.py +++ b/test/fixtures/page_generation/page.py @@ -105,6 +105,11 @@ from funcy import ( repeatedly, mapcat, lmapcat, + without, + merge, + omit, + project, + complement, ) from cv_analysis.locations import TEST_PAGE_TEXTURES_DIR @@ -733,26 +738,39 @@ def dump_plt_to_image(rectangle): class RandomFontPicker: def __init__(self, font_dir=None, size=None): - self.fonts = get_fonts(font_dir) - self.fonts_lower = [font.lower() for font in self.fonts] + fonts = get_fonts(font_dir) + fonts_lower = [font.lower() for font in fonts] + domestic_fonts_mask = lmap(complement(self.looks_foreign), fonts_lower) + self.fonts = list(itertools.compress(fonts, domestic_fonts_mask)) + self.fonts_lower = list(itertools.compress(fonts_lower, domestic_fonts_mask)) self.test_image = Image.new("RGB", (200, 200), (255, 255, 255)) self.draw = ImageDraw.Draw(self.test_image) self.size = size or 11 + def looks_foreign(self, font): + # This filters out foreign fonts (e.g. 'Noto Serif Malayalam') + return len(font.split("-")[0]) > 10 + def pick_random_font_available_on_system(self, includes=None, excludes=None) -> ImageFont: # FIXME: Slow! - self.shuffle_fonts() + includes = [i.lower() for i in includes] if includes else [] + excludes = [i.lower() for i in excludes] if excludes else [] - includes_pattern = (lambda f: includes.lower() in f) if includes else (lambda f: True) - excludes_pattern = (lambda f: excludes.lower() not in f) if excludes else (lambda f: True) + logger.debug(f"Picking font by includes={includes} and excludes={excludes}.") + + def includes_pattern(font): + return not includes or any(include in font for include in includes) + + def excludes_pattern(font): + return not excludes or not any(exclude in font for exclude in excludes) + + self.shuffle_fonts() mask = lmap(lambda f: includes_pattern(f) and excludes_pattern(f), self.fonts_lower) fonts = itertools.compress(self.fonts, mask) fonts = keep(map(self.load_font, fonts)) fonts = filter(self.font_is_renderable, fonts) # FIXME: this does not work - fonts = (font for font in fonts if font.getname()[1].lower() not in ["italic"]) - fonts = (font for font in fonts if font.getname()[1].lower() in ["bold"]) font = first(fonts) logger.info(f"Using font: {font.getname()}") @@ -764,7 +782,7 @@ class RandomFontPicker: self.fonts, self.fonts_lower = lzip(*l) def pick_random_mono_space_font_available_on_system(self) -> ImageFont: - return self.pick_random_font_available_on_system(includes="mono", excludes="oblique") + return self.pick_random_font_available_on_system(includes=["mono"], excludes=["oblique"]) @lru_cache(maxsize=None) def load_font(self, font: str): @@ -794,14 +812,30 @@ def get_font_picker(**kwargs): @lru_cache(maxsize=None) def pick_random_mono_space_font_available_on_system(**kwargs): - font_picker = get_font_picker(**kwargs) + font_picker = get_font_picker(**omit(kwargs, ["includes", "excludes"])) return font_picker.pick_random_mono_space_font_available_on_system() @lru_cache(maxsize=None) def pick_random_font_available_on_system(**kwargs): - font_picker = get_font_picker(**kwargs) - return font_picker.pick_random_font_available_on_system(includes="mono") + # kwargs["excludes"] = ( + # *kwargs.get( + # "excludes", + # ), + # "hebrew", + # "armenian", # TODO: De-hardcode / find a better way to filter out fonts that can not be rendered + # "arabic", + # "cyrillic", + # "georgian", + # "greek", + # "telugu", + # "tamil", + # "thai", + # "vietnamese", + # "ethiopic", + # ) + font_picker = get_font_picker(**omit(kwargs, ["includes", "excludes"])) + return font_picker.pick_random_font_available_on_system(**project(kwargs, ["includes", "excludes"])) class RandomPlot(RandomContentRectangle): @@ -910,7 +944,14 @@ def maybe(): def generate_random_text_block(rectangle: Rectangle, n_sentences=3000) -> ContentRectangle: - block = TextBlock(*rectangle.coords) + block = TextBlock( + *rectangle.coords, + font=pick_random_font_available_on_system( + size=30, # TODO: De-hardcode font size... Seems to have no effect on top of that + includes=("serif", "sans-serif"), + excludes=("bold", "mono", "italic", "oblique", "cursive"), + ), + ) block.content = rectangle.content if isinstance(rectangle, ContentRectangle) else None # TODO: Refactor block.generate_random_text(rectangle, n_sentences) return block @@ -925,14 +966,30 @@ def generate_random_table_caption(rectangle: Rectangle) -> ContentRectangle: def generate_random_caption(rectangle: Rectangle, caption_start, n_sentences=1000) -> ContentRectangle: - block = TextBlock(*rectangle.coords, text_generator=CaptionGenerator(caption_start=caption_start), font_size=5) + block = TextBlock( + *rectangle.coords, + text_generator=CaptionGenerator(caption_start=caption_start), + font=pick_random_font_available_on_system( + size=8, + includes=("italic"), + excludes=("bold", "mono"), + ), + font_size=5, # TODO: De-hardcode font size... Seems to have no effect on top of that + ) block.content = rectangle.content if isinstance(rectangle, ContentRectangle) else None # TODO: Refactor block.generate_random_text(rectangle, n_sentences) return block def generate_text_block(rectangle: Rectangle, text) -> ContentRectangle: - block = TextBlock(*rectangle.coords) + block = TextBlock( + *rectangle.coords, + font=pick_random_font_available_on_system( + size=11, # TODO: De-hardcode font size... Seems to have no effect on top of that + includes=("serif", "sans-serif"), + excludes=("bold", "mono", "italic", "oblique", "cursive"), + ), + ) block.content = rectangle.content if isinstance(rectangle, ContentRectangle) else None # TODO: Refactor block.put_text(text, rectangle) return block @@ -1036,7 +1093,7 @@ class CaptionGenerator(TextBlockGenerator): class TextBlock(ContentRectangle): def __init__(self, x1, y1, x2, y2, text_generator=None, font=None, font_size=None): super().__init__(x1, y1, x2, y2) - self.font = pick_random_font_available_on_system(size=font_size) # ImageFont.load_default() + self.font = font or ImageFont.load_default() # pick_random_font_available_on_system(size=font_size) self.text_generator = text_generator or ParagraphGenerator() def __call__(self, *args, **kwargs):