コード例 #1
0
def text_wrap(text: str, font: ImageFont.FreeTypeFont, max_width: int) -> str:
    """
    Text wrapper
    @param text: Text to wrap
    @type text: str
    @param font: Font that the text will be displayed with
    @type font: ImageFont.FreeTypeFont
    @param max_width: The maximal width of the text
    @type max_width: int
    @return: Wrapped text
    @rtype: str
    """
    lines: list[str] = []
    # TODO: implement emoji
    # If the text width is smaller than the image width, then no need to split
    # just add it to the line list and return
    if font.getsize(text)[0] <= max_width:
        return text
    # split the line by spaces to get words
    words: list[str] = text.split(' ')
    i = 0
    # append every word to a line while its width is shorter than the image width
    while i < len(words):
        line = ''
        while i < len(words) and font.getsize(line + words[i])[0] <= max_width:
            line = line + words[i] + " "
            i += 1
        if not line:
            line = words[i]
            i += 1
        lines.append(line)

    return '\n'.join(lines)
コード例 #2
0
    def __text_wrap(self, text: str, font: PIL_ImgFont.FreeTypeFont,
                    max_width: int) -> List[str]:
        """
        Wrap the into multiple lines
        """
        lines = []
        # If the width of the text is smaller than image width
        # we don't need to split it, just add it to the lines array
        # and return
        if font.getsize(text)[0] <= max_width:
            lines.append(text)
        else:
            # split the line by spaces to get words
            words = text.split(' ')
            i = 0

            while i < len(words):
                line = ''
                while i < len(words) and font.getsize(
                        line + words[i])[0] <= max_width:
                    line = line + words[i] + " "
                    i += 1
                if not line:
                    line = words[i]
                    i += 1

                lines.append(line)
        return lines
コード例 #3
0
ファイル: text.py プロジェクト: romanyakovlev/image-pattern
 def draw_text(self, image: PillowImage, text: List[str],
               font: PillowImageFont):
     draw = ImageDraw.Draw(image)
     for line_index, line in enumerate(text):
         font_width, _ = font.getsize(line)
         _, height_offset = font.getoffset(line)
         x = self._get_x(font_width)
         y = self._get_y(line_index, self.line_height, height_offset)
         for c in line:
             if not is_emoji(c) and c.isprintable():
                 draw.text((x, y), c, font=font, fill=self.font_color)
                 x += math.ceil(draw.textsize(c, font)[0])
             elif self.get_emoji_content:
                 emoji_content = self.get_emoji_content(c)
                 if emoji_content:
                     with tempfile.NamedTemporaryFile(mode='wb') as f:
                         temp_filename = f.name
                         f.write(emoji_content)
                         emoji_img = Image.open(temp_filename)
                         resized_emoji_img = emoji_img.resize(
                             (font.size, font.size))
                         image.paste(resized_emoji_img, (x, y),
                                     resized_emoji_img)
                     x += math.ceil(resized_emoji_img.width)
     return image
コード例 #4
0
ファイル: graphics.py プロジェクト: Ze1598/quotespy
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
コード例 #5
0
    def draw_text(self, color: Color, size, font: ImageFont.FreeTypeFont):
        im = Image.new('RGB', size, color.rgb)

        draw = ImageDraw.Draw(im)
        name = str(color)
        text_size = font.getsize(name)
        text_color = self.text_color(color)
        if text_size[0] > size[0]:
            all_lines = []
            lines = split_string(name,
                                 maxlen=len(name) // (text_size[0] / size[0]))
            margin = 2
            total_y = 0
            for line in lines:
                line = line.strip()
                if not line:
                    continue

                if text_size[1] + total_y > size[1]:
                    break

                x = (size[0] - font.getsize(line)[0]) // 2
                all_lines.append((line, x))
                total_y += margin + text_size[1]

            y = (size[1] - total_y) // 2
            for line, x in all_lines:
                draw.text((x, y), line, font=font, fill=text_color)
                y += margin + text_size[1]

        else:
            x = (size[0] - text_size[0]) // 2
            y = (size[1] - text_size[1]) // 2
            draw.text((x, y), name, font=font, fill=text_color)
        return im
コード例 #6
0
def wrap_new(canv: ImageDraw.Draw, box: Tuple[Tuple[int, int],
                                              Tuple[int, int]], text: str, *,
             font: ImageFont.FreeTypeFont):
    _, h = font.getsize('yA')

    max_width = box[1][0] - box[0][0]
    max_height = box[1][1]

    def write(x: int, y: int, line: List[str]):
        text_ = ' '.join(line)
        font_ = auto_font(font, text_, max_width)
        w, h = font_.getsize(text_)
        xy = (x + center(w, max_width), y)
        canv.text(xy, text_, fill='black', font=font_)

    x, y = box[0]
    line = []
    for word in text.split():
        w, _ = font.getsize(' '.join(line + [word]))

        if w > max_width:
            write(x, y, line)

            y += h
            if y > max_height:
                return

            line = [word]
        else:
            line.append(word)

    if line:
        write(x, y, line)
コード例 #7
0
ファイル: utils.py プロジェクト: fixator10/Fixator10-Cogs
 def _get_character_pixel_width(font: ImageFont.FreeTypeFont,
                                char: str) -> int:
     """Use getlength over using getsize for character pixel width, if available in PIL."""
     try:
         write_pos = int(font.getlength(char))
     except AttributeError:
         write_pos = font.getsize(char)[0]
     return write_pos
コード例 #8
0
ファイル: cah_config.py プロジェクト: ttocsneb/cahGenerator
    def renderPick(self, font: ImageFont.FreeTypeFont) -> Image:
        """
        Render Pick 2, or draw 2/pick 3
        """

        # Figure out the size of the image
        pickIm = self.renderCircle(self.blanks, font)
        w, h = pickIm.size

        pickT = "PICK "
        pw, ph = font.getsize(pickT)
        w += pw

        if self.blanks > 2:
            h = round(h * 1.75)

            drawIm = self.renderCircle(self.blanks - 1, font)
            drawT = "DRAW "
            dw, dh = font.getsize(drawT)
            h += dh

            w = max(w, drawIm.width + dw)
        else:
            drawIm = None

        im = Image.new("RGB", (w, h), (0, 0, 0))
        draw = ImageDraw.Draw(im)

        # Draw the Pick
        nw, nh = pickIm.size
        im.paste(pickIm, (w - nw, h - nh))
        draw.text((w - nw - pw, h - nh // 2 - ph // 2),
                  pickT,
                  fill=(255, 255, 255),
                  font=font)

        if drawIm:
            # Draw the Draw
            nw, nh = drawIm.size
            im.paste(drawIm, (w - nw, 0))
            draw.text((w - nw - dw, nh // 2 - dh // 2),
                      drawT,
                      fill=(255, 255, 255),
                      font=font)

        # Close the temporary images
        pickIm.close()
        if drawIm:
            drawIm.close()

        newIm = im.resize((round(w * 0.75), round(h * 0.75)),
                          resample=Image.LANCZOS)
        im.close()

        return newIm
コード例 #9
0
 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))
     }
コード例 #10
0
    def draw_text(self, image: PillowImage, text: List[str],
                  font: PillowImageFont):
        draw = ImageDraw.Draw(image)

        for line_index, line in enumerate(text):
            font_width, _ = font.getsize(line)
            _, height_offset = font.getoffset(line)
            x = self._get_x(font_width)
            y = self._get_y(line_index, self.line_height, height_offset)
            draw.text((x, y), line, font=font, fill=self.font_color)

        return image
コード例 #11
0
ファイル: cah_config.py プロジェクト: ttocsneb/cahGenerator
    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)
コード例 #12
0
def draw_right_text(text: str, draw: ImageDraw, font: FreeTypeFont,
                    f_width: int, x: int, y: int, color: Tuple[int, int, int],
                    outline_percentage, outline_color,
                    fontsize) -> Tuple[int, int]:
    text_width = font.getsize(text)[0]
    off_x = f_width - text_width
    draw.text((x + off_x, y),
              text,
              color,
              font,
              stroke_width=round(outline_percentage * 0.01 * fontsize),
              stroke_fill=outline_color)
    return font.getsize(text)
コード例 #13
0
 def wrap_line_with_font(inputstr: str, max_width: int,
                         font: ImageFont.FreeTypeFont) -> str:
     if font.getsize(inputstr)[0] <= max_width:
         return inputstr
     tokens = inputstr.split(" ")
     output = ""
     segments = []
     for token in tokens:
         combo = output + " " + token
         if font.getsize(combo)[0] > max_width:
             segments.append(output + "\n")
             output = token
         else:
             output = combo
     return "".join(segments + [output]).strip()
コード例 #14
0
ファイル: cah_config.py プロジェクト: ttocsneb/cahGenerator
    def renderText(self,
                   font: ImageFont.FreeTypeFont,
                   width: int = 0,
                   maxwidth: int = 0) -> Image:
        """
        Render the text of the card
        """
        lines = list()

        # split the words that are too long
        for match in re.finditer(r"(?:\\n|\s*)(.*?(?=\\n)|.{0,20}(?=\s|$))",
                                 self.raw):
            if match[1]:
                lines.append(match[1])

        ws, hs = zip(*[font.getsize(l) for l in lines])

        mult = 1.5

        if not width:
            width = max(ws)

        padding = (maxwidth - width) // 2

        h = round(sum(hs) / len(hs))
        height = round(h * (len(hs) - 1) * mult + hs[-1])
        im = Image.new("RGB", (width, height + padding), self.background)

        y = padding
        for l in lines:
            self.renderLine(l, im, y, h, font)
            y += round(h * mult)

        return im
コード例 #15
0
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
コード例 #16
0
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
コード例 #17
0
def get_text_pos(size: Tuple[int, int],
                 text: Text,
                 font: ImageFont.FreeTypeFont,
                 relative: bool = False) -> Tuple[int, int]:
    min_x = round(text.x_range[0] * size[0])
    max_x = round(text.x_range[1] * size[0])
    min_y = round(text.y_range[0] * size[1])
    max_y = round(text.y_range[1] * size[1])
    text_size = font.getsize_multiline(text.text,
                                       stroke_width=text.stroke_width *
                                       font.size)

    if int(text.position.value) // 3 == 0:
        pos_y = min_y
    elif int(text.position.value) // 3 == 1:
        pos_y = round((min_y + max_y) / 2 - text_size[1] / 2)
    else:
        pos_y = max_y - text_size[1]

    if int(text.position.value) % 3 == 0:
        pos_x = min_x
    elif int(text.position.value) % 3 == 1:
        pos_x = round((min_x + max_x) / 2 - text_size[0] / 2)
    else:
        pos_x = max_x - text_size[0]
    if relative:
        return pos_x - min_x, pos_y - min_y
    else:
        return pos_x, pos_y
コード例 #18
0
    def _get_multiline_text(text, font: PillowImageFont,
                            width: int) -> List[str]:
        font_width, _ = font.getsize(text)
        line_length = int((width / (font_width / len(text))))
        text_lines = wrap(text, line_length)

        return text_lines
コード例 #19
0
def center_text(text: str, x_pos: int, y_pos: int, font: ImageFont.FreeTypeFont) -> Tuple[int, int]:

    text_x, text_y = font.getsize(text)

    x = (x_pos - text_x//2)  # / 2
    y = (y_pos - text_y//2)  # / 2

    return x, y
コード例 #20
0
ファイル: blimp_text.py プロジェクト: bdzimmer/symbols
def getoffset(font: FreeTypeFont, text_str: str) -> Tuple[int, int]:
    """get the offset of text"""
    if USE_PIL:
        offset = font.getoffset(text_str)
    else:
        # FOR NOW...because I don't think this is truly necessary
        offset = (0, 0)
    return offset
コード例 #21
0
 def get_final_text_lines(text: str, text_width: int, font: ImageFont.FreeTypeFont) -> int:
     lines = text.split("\n")
     line_count = 0
     for line in lines:
         if not line:
             line_count += 1
             continue
         line_count += int(math.ceil(float(font.getsize(line)[0]) / float(text_width)))
     return line_count + 1
コード例 #22
0
ファイル: graphics.py プロジェクト: Ze1598/quotespy
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)
コード例 #23
0
ファイル: data_source.py プロジェクト: MeetWq/mybot
def wrap_text(text: str, font: FreeTypeFont, max_width: float) -> List[str]:
    line = ""
    lines = []
    for t in text:
        if t == "\n":
            lines.append(line)
            line = ""
        elif font.getsize(line + t)[0] > max_width:
            lines.append(line)
            line = t
        else:
            line += t
    lines.append(line)
    return lines
コード例 #24
0
def does_label_needs_to_be_wrapped(font: ImageFont.FreeTypeFont,
                                   label_text: str,
                                   label_width: int) -> Tuple[bool, int]:
    """

    :param font:
    :param label_text: single-line text to be checked if it needs to wrapped
    :param label_width:
    :return: True if text needs to be wrapped else False
    """
    w, h = font.getsize(label_text)
    per_char_pixels = w / len(label_text)
    can_fit_n_chars_in_single_line = label_width // per_char_pixels
    return (len(label_text) > can_fit_n_chars_in_single_line,
            can_fit_n_chars_in_single_line)
コード例 #25
0
ファイル: blimp_text.py プロジェクト: bdzimmer/symbols
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
コード例 #26
0
ファイル: memes.py プロジェクト: nullqwertyuiop/sagiri-bot
 def wrap_text(text: str,
               font: FreeTypeFont,
               max_width: float,
               stroke_width: int = 0) -> List[str]:
     line = ''
     lines = []
     for t in text:
         if t == '\n':
             lines.append(line)
             line = ''
         elif font.getsize(line + t,
                           stroke_width=stroke_width)[0] > max_width:
             lines.append(line)
             line = t
         else:
             line += t
     lines.append(line)
     return lines
コード例 #27
0
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
コード例 #28
0
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
コード例 #29
0
ファイル: cah_config.py プロジェクト: ttocsneb/cahGenerator
    def renderCircle(self, txt: str, font: ImageFont.FreeTypeFont) -> Image:
        """
        Render text inside a circle
        """
        txt = str(txt)

        # Get the size of the circle
        w, h = font.getsize(txt)
        diam = round(math.sqrt(w**2 + h**2) * 1.25)

        im = Image.new("RGB", (diam, diam), (0, 0, 0))
        draw = ImageDraw.Draw(im)

        draw.ellipse((0, 0, diam, diam), fill=(255, 255, 255))
        draw.text((diam // 2 - w // 2, diam // 2 - h // 2),
                  txt,
                  fill=(0, 0, 0),
                  font=font)

        return im
コード例 #30
0
ファイル: circle.py プロジェクト: ilonachan/runic-babble
def circle_text(img,
                text,
                base_circle,
                rotation,
                font: ImageFont.FreeTypeFont,
                color,
                angle=None,
                upscale=4):
    if angle is None:
        angle = 360 / len(text)
    angle_rad = angle / 360 * 2 * math.pi

    bigger_size = (img.size[0] * upscale, img.size[1] * upscale)
    bigger_font = font.font_variant(size=font.size * upscale)

    cx, cy, r = _extract_circle(base_circle)
    ucx, ucy = cx * upscale, cy * upscale
    rad = r * upscale + bigger_font.size / 2

    img2 = Image.new("RGBA", bigger_size, (0, 0, 0, 0))
    for i, c in enumerate(text):
        angled = angled_text(c,
                             fill=color,
                             font=bigger_font,
                             angle=-90 - angle * i)
        text_origin = (ucx + rad * math.cos(angle_rad * i),
                       ucy + rad * math.sin(angle_rad * i))
        img2.paste(angled,
                   box=(int(text_origin[0] - angled.width / 2),
                        int(text_origin[1] - angled.height / 2)))

    img2 = img2.rotate(rotation + 90,
                       center=(ucx, ucy)).resize(img.size,
                                                 resample=Image.LANCZOS)
    img.paste(img2, mask=img2)

    return r + font.size + 4