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 __calculate_username_width(user_name: str, user_pic: str, font: ImageFont.FreeTypeFont) -> float: """Calculate the width of the username. The width is given as the width of the largest username line (in case it is broken up into multiple lines). user_name : str User name. user_pic : str Path to the profile picture. font : ImageFont.FreeTypeFont Font to be used for the username (header). Returns ------- float Width needed to draw the username. """ # Calculate username character limit per line based on the presence of\ # the profile picture username_char_limit = 19 if user_pic != "" else 38 # Break the text into multiple lines based on the character limit username_wrapped = wrap(user_name, username_char_limit) # The username's width is set by the largest username text line width_username = max([ font.getmask(text_line).getbbox()[2] + font.font.getsize(text_line)[1][0] for text_line in username_wrapped ]) return width_username
def __calculate_text_width(tweet_info: TweetInfo, graphic_settings: GraphicSettings, font: ImageFont.FreeTypeFont) -> float: """Calculate the width of the tweet content. The width is given as the width of the largest tweet text line. Parameters ---------- tweet_info : TweetInfo Dictionary with all the tweet's information. graphic_settings : GraphicSettings Dictionary with the graphic's settings. font : ImageFont.FreeTypeFont Font to be used for the text. Returns ------- float Width needed to draw the text. """ # Break the text into multiple lines based on the character limit text_wrapped = wrap(tweet_info["tweet_text"], graphic_settings["wrap_limit"]) # The text's width is set by the largest text line width_text = max([ font.getmask(text_line).getbbox()[2] + font.font.getsize(text_line)[1][0] for text_line in text_wrapped ]) return width_text
def _get_metrics_map( text: str, font: ImageFont.FreeTypeFont, with_vertical_metrics: bool = True ) -> typing.Union[typing.Dict[str, typing.Tuple[int, int]], typing.Dict[ str, typing.Tuple[int, int, int, int]], ]: return { char: (*size, size[1] - height, size[1]) if with_vertical_metrics and (height := font.getmask(char).size[1]) is not None else size for char in set(text) if (size := font.getsize(char)) }
def __calculate_header_height( tweet_info: TweetInfo, graphic_settings: GraphicSettings, font: ImageFont.FreeTypeFont, ) -> float: """Calculate the header height: username, user tag and profile picture. Parameters ---------- tweet_info : TweetInfo Dictionary with all the tweet's information. graphic_settings : GraphicSettings Dictionary with the graphic's settings. font : ImageFont.FreeTypeFont Font to be used for the header. Returns ------- float Height needed for the header (pixels). """ user_pic = tweet_info["user_pic"] height_margin = graphic_settings["margin_bottom"] profile_pic_size = graphic_settings["profile_pic_size"] user_name = tweet_info["user_name"] user_tag = tweet_info["user_tag"] # Calculate the height of the header's text: user name and user tag height_user_name = __calculate_username_height(user_name, user_pic, height_margin, font) height_usertag = font.getmask(user_tag).getbbox()[3] + font.font.getsize( user_tag)[1][1] height_header_text = height_user_name + height_usertag + height_margin # If the header's text is taller than the profile picture, than that's\ # the header height if (height_header_text + height_margin) > profile_pic_size[1]: height_header = height_header_text # Otherwise, the profile picture sets the header height else: height_header = profile_pic_size[1] return height_header
def __calculate_header_width(tweet_info: TweetInfo, graphic_settings: GraphicSettings, font_header: ImageFont.FreeTypeFont) -> float: """Calculate the width of the header. Parameters ---------- tweet_info : TweetInfo Dictionary with all the tweet's information. graphic_settings : GraphicSettings Dictionary with the graphic's settings. font : ImageFont.FreeTypeFont Font to be used for the text. Returns ------- float Width needed to draw the header (pixels). """ # Calculate the header of the profile picture profile_pic_width = graphic_settings["profile_pic_size"][0] # Calculate the user name width username_width = __calculate_username_width(tweet_info["user_name"], tweet_info["user_pic"], font_header) # Calculate the user tag width user_tag = tweet_info["user_tag"] usertag_width = font_header.getmask(user_tag).getbbox()[2] +\ font_header.font.getsize(user_tag)[1][0] # The width of the header's text is set by the largest of the user\ # name and user tag if username_width > usertag_width: text_width = username_width else: text_width = usertag_width # The header width is given as the sum of the profile picture and\ # the header text width width_header = profile_pic_width + \ graphic_settings["margin_bottom"] + text_width return width_header
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 __calculate_text_height( tweet_info: TweetInfo, graphic_settings: GraphicSettings, font: ImageFont.FreeTypeFont, ) -> float: """Calculate the height needed to draw the tweet text. Parameters ---------- tweet_info : TweetInfo Dictionary with all the tweet's information. graphic_settings : GraphicSettings Dictionary with the graphic's settings. font : ImageFont.FreeTypeFont Font to be used for the text. Returns ------- float Height needed to draw the text (pixels). """ # Wrap the tweet's text based on the line character limit text_wrapped = wrap(tweet_info["tweet_text"], graphic_settings["wrap_limit"]) height_margin = graphic_settings["margin_bottom"] # Total text height is the sum of height of each text line heights_text = [ font.getmask(line).getbbox()[3] + font.font.getsize(line)[1][1] + height_margin for line in text_wrapped ] # Last line does not have bottom margin heights_text[-1] -= height_margin total_text_height = sum(heights_text) return total_text_height
def __calculate_username_height(user_name: str, user_pic: str, height_margin: int, font: ImageFont.FreeTypeFont) -> float: """Calculate the height of the username. The height is given as the sum of the heights each line of username (when the username exceeds the limit of characters per line). Parameters ---------- user_name : str User name. user_pic : str Path to the profile picture. height_margin: int Vertical margin between lines of text. font : ImageFont.FreeTypeFont Font to be used for the username (header). Returns ------- float Height needed to draw the username. """ # Wrap the username into multiple lines as needed username_char_limit = 19 if user_pic != "" else 38 user_name = wrap(user_name, username_char_limit) # Total username height is the sum of height of each username line heights_username = [ font.getmask(line).getbbox()[3] + font.font.getsize(line)[1][1] + height_margin for line in user_name ] # Last line does not have bottom margin heights_username[-1] -= height_margin total_height_username = sum(heights_username) return total_height_username