def _draw_date_box(self, draw: ImageDraw.Draw, top_left_corner: Tuple[int, int], width: int, date: datetime) -> int: """ Draws a date/time clock onto the canvas at the given position. Args: draw: The ImageDraw instance to draw on the desired canvas. top_left_corner: Where to start the box. width: The fixed horizontal dimension of the box. date: The date/time to display on the clock. Returns: The bottom y coordinate of the box, so that further elements may be drawn after it """ time_str = format_time(date, 'HH:mm', locale='pt_BR') time_font = self.fonts['huge'] time_font_height = time_font.getsize('X')[1] date_str = format_date(date, "EEEE\ndd 'de' MMM 'de' yyyy", locale='pt_BR').capitalize() date_font = self.fonts['large'] date_font_height = date_font.getsize('X')[1] margin = 20 * self.scaler_value bottom_y = int(top_left_corner[1] + time_font_height + date_font_height * 2 + margin * 4) draw.rectangle( ( top_left_corner[0], top_left_corner[1], top_left_corner[0] + width, bottom_y ), fill=self.colors['date_box_background'] ) time_y = top_left_corner[1] + margin draw.text( (top_left_corner[0] + margin, time_y), time_str, fill=self.colors['date_box_text'], font=time_font, ) draw.multiline_text( (top_left_corner[0] + margin, time_y + time_font_height + margin), date_str, fill=self.colors['date_box_text'], font=date_font ) return bottom_y
def make_text( text: str, box=(0, 0), font_path='', init_font_size=76, color=(0, 0, 0, 255), stroke=None, # stroke can be a color tuple align='left', emojis={}, instance=''): if contains_emojis(text, emojis): # fancy rendering enabled # redirect to fit_text_with_emojis_in_box() return make_emoji_text(text, emojis=emojis, instance=instance, box=box, font_path=font_path, color=color, stroke=stroke, align=align) canvas = Image.new('RGBA', box, color=(255, 255, 255, 0)) draw = Draw(canvas) textsize = (box[0] + 1, box[1] + 1) font_size = init_font_size # max font size is 72; decrease by 4 until fit while textsize[0] > box[0] or textsize[1] > box[1]: # doesn't fit if 0 < font_size <= 16: font_size -= 1 elif 16 < font_size < 32: font_size -= 2 elif font_size >= 32: font_size -= 4 else: break font = truetype(font_path, size=font_size) # try to fit in the horizontal boundary wrapped = wrap_text(text, box[0], font) textsize = draw.multiline_textsize(wrapped, font=font) # when wrapped text fits in box, loop will exit, and font is remembered draw.multiline_text( (0, 0), wrapped, fill=color if stroke is None else WHITE, font=font, stroke_fill=stroke, stroke_width=(max(font_size // 20, 2) if stroke is not None else 0), align=align) return canvas
def _draw_text(draw: ImageDraw.Draw, words: Sequence[str], font: ImageFont.ImageFont, fill: Sequence[int], vertical_offset: float, picture_width: int) -> None: """ Нанесение текста на изображение """ y = vertical_offset for index, word in enumerate(words): word_width, word_height = _get_word_size_in_px(font, word) x = (picture_width - word_width[0]) / 2 draw.multiline_text((x, y), text=word, align='center', fill=fill, font=font) y += word_width[1] + max(abs(word_height[1]), int(word_width[1] / 5))
def draw_text_with_outline( draw: ImageDraw.Draw, xy: t.Tuple[int, int], text: str, font, color: t.Tuple[int, int, int], background_color: t.Tuple[int, int, int], ) -> None: """ Draw outlined text :param draw: target :param xy: location :param text: content :param font: font :param color: text color :param background_color: outline color :return: None """ _xy = np.asarray(xy) offset_range = (-1, 0, 1) for offset in ((x, y) for x in offset_range for y in offset_range): draw.multiline_text( xy=_xy + np.asarray(offset), text=text, font=font, fill=background_color, align='center', ) draw.multiline_text( xy=_xy, text=text, font=font, fill=color, align='center', )
def _draw_berthed_ship_box( self, draw: ImageDraw.Draw, top_left_corner: Tuple[int, int], width: int, ship: pd.Series ) -> int: """ Draws a berthed ship's information on a box. Args: draw: The ImageDraw instance to draw on the desired canvas. top_left_corner: Where to start the box. width: The fixed horizontal dimension of the box. ship: Source of the information for the ship, obtained from the LogKeeper. Returns: The bottom y coordinate of the box, so that further elements may be drawn after it """ berco = get_ship_berth_number(ship) # Alternate colors for different designated berthing numbers. if berco is None or berco % 2 == 0: background_color = self.colors['berthed_ship_box_background_even'] else: background_color = self.colors['berthed_ship_box_background_odd'] name_str = ship['Navio'] name_font = self.fonts['large'] # Resize the font until the name fits within the specified width. while (name_font.size > 12) and (name_font.getsize(name_str)[0] > (width - 30)): name_font = self._load_font(self.font_path, max(1, name_font.size - 1)) name_font_height = name_font.getsize('X')[1] berco_str = 'Berço ' + str(berco) berco_font = self.fonts['medium'] berco_font_height = berco_font.getsize('X')[1] margin = (20 * self.scaler_value) bottom_y = int(top_left_corner[1] + name_font_height + berco_font_height + margin * 3) draw.rectangle( ( top_left_corner[0], top_left_corner[1], top_left_corner[0] + width, bottom_y ), fill=background_color ) draw.multiline_text( (top_left_corner[0] + margin, top_left_corner[1] + margin), name_str, fill=self.colors['berthed_ship_box_text'], font=name_font ) if berco is not None: draw.multiline_text( (top_left_corner[0] + margin, top_left_corner[1] + name_font_height + margin * 2), berco_str, fill=self.colors['berthed_ship_box_text'], font=berco_font ) return bottom_y