def renderLine(self, raw: str, image: Image, y: int, height: int, font: ImageFont.FreeTypeFont) -> ImageDraw: """ Render a single line """ # Get the number of blanks blanks = raw.count("_blank_") no_line = raw.replace("_blank_", "") ascent, descent = font.getmetrics() w, h = font.getsize(no_line) (width, baseline), (offset_x, offset_y) = font.font.getsize(no_line) if height < h: line = ascent else: line = height - descent / 2 y = y - height + h ascent, descent = font.getmetrics() draw = ImageDraw.Draw(image) # draw.line((0, y, w, y), fill=(255, 0, 0)) # draw.line((0, y + height, w, y + height), fill=(0, 255, 0)) # draw.line((0, y + h, w, y + h), fill=(0, 0, 255)) if blanks > 0: # Get the size of each blank. blank_size = (image.size[0] - w) / blanks groups = raw.split("_blank_") # Render each group x = 0 for group in groups: # Draw the text draw.text((x, y), group, self.color, font) w, _ = font.getsize(group) x += w if blanks > 0: # Draw the line draw.line( ((x + 5, y + line), (x + blank_size - 5, y + line)), fill=self.color, width=2) x += blank_size blanks -= 1 return draw.text((0, y), raw, self.color, font)
def __get_x_centered(text_line: str, width_avail: int, font: ImageFont.FreeTypeFont) -> int: """Get the X coordinate at which to draw a text line horizontally-centered. Parameters ---------- text_line : str Text line to draw. width_avail : int Width available to draw (essentially the image's width). font : ImageFont.FreeTypeFont Font used to draw. Returns ------- int The X coordinate at which to draw the text line horizontally-centered. """ # https://stackoverflow.com/a/46220683/9263761 ascent, descent = font.getmetrics() # Width needed to draw the line width_text = font.getmask(text_line).getbbox()[2] + font.font.getsize( text_line)[1][0] # Calculate the centered X coordinate x = (width_avail - width_text) // 2 # Return the first Y coordinate and a list with the height of each line return x
def __get_y_and_heights(text_wrapped: List[str], height_avail: int, margin: float, font: ImageFont.FreeTypeFont) -> Tuple[int, List[int]]: """Calculate the height needed to draw all text lines, vertically centered. Return the vertical coordinate to draw the first line at and a list of the heights for each line (margin included). Parameters ---------- text_wrapped : List[str] Lines of text to draw. height_avail : int Height available to draw in (essentially the image's height). margin : float Vertical margin between text lines. font : ImageFont.FreeTypeFont Font used to draw. Returns ------- Tuple[int, List[int]] The vertical coordinate of the first text line and a list with the heights of all lines. """ # https://stackoverflow.com/a/46220683/9263761 ascent, descent = font.getmetrics() # Calculate the height needed to draw each line of text height_lines = [ font.getmask(text_line).getbbox()[3] + font.font.getsize(text_line)[1][1] + margin for text_line in text_wrapped ] # The last line doesn't have a bottom margin height_lines[-1] -= margin # Total height needed height_text = sum(height_lines) # Calculate the Y coordinate at which to draw the first line of text y = (height_avail - height_text) // 2 # Return the first Y coordinate and a list with the height of each line return (y, height_lines)
def getsize(font: FreeTypeFont, text_str: str) -> Tuple[int, int]: """get the size of text""" if USE_PIL: # original behavior # size = font.getsize(text_str) # new behavior width, _ = font.getsize(text_str) ascent, descent = font.getmetrics() height = ascent + descent size = (width, height) else: info = text_scala.get_info(text_str, _font_to_tuple(font), 0, BORDER_DEFAULT) # Important note! Info height and width may not include some antialiasing pixels! # import numpy as np # column_sums = np.sum(img, axis=0) # present = np.where(column_sums > 0)[0] # calculated_width = (present[-1] - present[0]) + 1 # print("metrics width:", info["width"]) # print("calculated width:", calculated_width) size = (info["width"], info["height"]) return size