from PIL import ImageDraw, ImageFont from . import Layout from .internal.textlayouter import TextLayouter from .internal.helpers import min_with_none, max_with_none, get_line_height from .enums import TextAlign, TextVerticalAlign, TextWrap class Text(Layout): def __init__( self, text=None, text_align=TextAlign.LEFT, vertical_text_align=TextVerticalAlign.TOP, text_wrap=TextWrap.NO_WRAP, line_spacing=0, **kwargs ): super().__init__(**kwargs) self.text_layouter = None self.text = text self.text_align = text_align self.vertical_text_align = vertical_text_align self.text_wrap = text_wrap self.line_spacing = line_spacing def complete_init(self, container): super().complete_init(container) if self.is_empty(): lines = [] elif isinstance(self.text, list): lines = self.text else: lines = self.text.splitlines() self.text_layouter = TextLayouter(lines=lines, align=self.text_align, vertical_align=self.vertical_text_align, wrap=self.text_wrap, font=self.get_font(), line_height=self.get_line_height_with_spacing()) def is_empty(self): return self.text is None or self.text == '' or (isinstance(self.text, list) and len(self.text) == 0) def get_min_inner_width(self, max_height=None): if self.is_empty(): width = 0 else: if max_height is not None: max_height += self.line_spacing width = self.text_layouter.get_dimensions(0, max_height)[0] return max_with_none(width, self.min_width) def get_min_inner_height(self, max_width=None): if self.is_empty(): height = 0 else: # no spacing after the last line height = self.text_layouter.get_dimensions(max_width, 0)[1] - self.line_spacing return max_with_none(height, self.min_height) def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None): available_width = min_with_none(available_width, self.width) available_height = min_with_none(available_height, self.height) width, height = self.text_layouter.get_dimensions(available_width, available_height) height -= self.line_spacing # no spacing after the last line if self.width is not None: width = self.width if self.height is not None: height = self.height width = max_with_none(width, self.min_width) height = max_with_none(height, self.min_height) width = min_with_none(width, self.max_width) height = min_with_none(height, self.max_height) return width, height def render_content(self, rect): image = self.make_canvas() d = ImageDraw.Draw(image) x1, y1, x2, y2 = rect width, height = x2 - x1 + 1, y2 - y1 + 1 if self.max_width is not None: width = min(width, self.max_width) if self.min_width is not None: width = max(width, self.min_width) if self.max_height is not None: height = min(height, self.max_height) if self.min_height is not None: height = max(height, self.min_height) lines = self.text_layouter.get_lines(width, height) color = self.get_fg_color() font = self.get_font() for posx, posy, text in lines: d.text((x1 + posx, y1 + posy), text, font=font, fill=color) return image def get_line_height_with_spacing(self): return get_line_height(self.get_font()) + self.line_spacing