Refactoring: Move

Move font picker into new font module
This commit is contained in:
Matthias Bisping 2023-02-01 17:05:47 +01:00
parent 9480d58a8a
commit de9b3bad93
2 changed files with 83 additions and 76 deletions

82
synthesis/text/font.py Normal file
View File

@ -0,0 +1,82 @@
import itertools
from functools import lru_cache
from pathlib import Path
from typing import List
from PIL import Image, ImageDraw, ImageFont
from funcy import lmap, complement, keep, first, lzip
from loguru import logger
from synthesis.random import rnd
class RandomFontPicker:
def __init__(self, font_dir=None, return_default_font=False):
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.return_default_font = return_default_font
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!
if self.return_default_font:
return ImageFont.load_default()
includes = [i.lower() for i in includes] if includes else []
excludes = [i.lower() for i in excludes] if excludes else []
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
font = first(fonts)
logger.info(f"Using font: {font.getname()}")
return font
def shuffle_fonts(self):
l = lzip(self.fonts, self.fonts_lower)
rnd.shuffle(l)
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"])
@lru_cache(maxsize=None)
def load_font(self, font: str):
logger.trace(f"Loading font: {font}")
try:
return ImageFont.truetype(font, size=11)
except OSError:
return None
@lru_cache(maxsize=None)
def font_is_renderable(self, font):
text_size = self.draw.textsize("Test String", font=font)
return text_size[0] > 0 and text_size[1]
def get_fonts(path: Path = None) -> List[str]:
path = path or Path("/usr/share/fonts")
fonts = list(path.rglob("*.ttf"))
fonts = [font.name for font in fonts]
return fonts

View File

@ -8,7 +8,6 @@ from copy import deepcopy
from enum import Enum
from functools import lru_cache, partial
from math import sqrt
from pathlib import Path
from typing import Tuple, Iterable, List
import blend_modes
@ -31,6 +30,7 @@ from synthesis.partitioner.page_partitioner import PagePartitioner
from synthesis.random import rnd
from synthesis.segment.content_rectangle import ContentRectangle
from synthesis.segment.random_content_rectangle import RandomContentRectangle
from synthesis.text.font import RandomFontPicker
from synthesis.text.line_formatter.identity import IdentityLineFormatter
from synthesis.text.line_formatter.paragraph import ParagraphLineFormatter
@ -50,13 +50,10 @@ from funcy import (
rest,
lsplit,
lfilter,
lzip,
keep,
repeatedly,
mapcat,
omit,
project,
complement,
chunks,
)
@ -674,78 +671,6 @@ def dump_plt_to_image(rectangle):
return image
class RandomFontPicker:
def __init__(self, font_dir=None, return_default_font=False):
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.return_default_font = return_default_font
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!
if self.return_default_font:
return ImageFont.load_default()
includes = [i.lower() for i in includes] if includes else []
excludes = [i.lower() for i in excludes] if excludes else []
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
font = first(fonts)
logger.info(f"Using font: {font.getname()}")
return font
def shuffle_fonts(self):
l = lzip(self.fonts, self.fonts_lower)
rnd.shuffle(l)
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"])
@lru_cache(maxsize=None)
def load_font(self, font: str):
logger.trace(f"Loading font: {font}")
try:
return ImageFont.truetype(font, size=11)
except OSError:
return None
@lru_cache(maxsize=None)
def font_is_renderable(self, font):
text_size = self.draw.textsize("Test String", font=font)
return text_size[0] > 0 and text_size[1]
def get_fonts(path: Path = None) -> List[str]:
path = path or Path("/usr/share/fonts")
fonts = list(path.rglob("*.ttf"))
fonts = [font.name for font in fonts]
return fonts
@lru_cache(maxsize=None)
def get_font_picker(**kwargs):
return RandomFontPicker(**kwargs, return_default_font=True)