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
Exemple #2
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)
Exemple #3
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)
Exemple #4
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
Exemple #5
0
    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
Exemple #6
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))
     }
Exemple #7
0
    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)
Exemple #8
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)
 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()
Exemple #10
0
    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
Exemple #11
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
Exemple #12
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)
         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
Exemple #13
0
 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
Exemple #14
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
Exemple #15
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
Exemple #16
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
Exemple #17
0
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
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)
Exemple #19
0
 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
Exemple #20
0
    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
def wrap_text(text: str, width: int, font: ImageFont.FreeTypeFont):
    """Wrap text to new lines to meet a given width"""
    text_lines: List[str] = []
    text_line: List[str] = []
    text = text.replace("\n", " [br] ")
    words = text.split()

    for word in words:
        if word == "[br]":
            text_lines.append(" ".join(text_line))
            text_line = []
            continue

        text_line.append(word)
        text_width, _ = font.getsize(" ".join(text_line))
        if text_width > width:
            text_line.pop()
            text_lines.append(" ".join(text_line))
            text_line = [word]

    if text_line:
        text_lines.append(" ".join(text_line))

    return text_lines
Exemple #22
0
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
Exemple #23
0
def wrap_text(text: str, width: int,
              font: FreeTypeFont) -> Tuple[str, int, int]:
    text_lines = []
    text_line = []
    words = text.split()

    line_height = 0
    line_width = 0

    for word in words:
        text_line.append(word)
        w, h = font.getsize(' '.join(text_line))
        line_height = h
        line_width = max(line_width, w)
        if w > width:
            text_line.pop()
            text_lines.append(' '.join(text_line))
            text_line = [word]

    if len(text_line) > 0:
        text_lines.append(' '.join(text_line))

    text_height = line_height * len(text_lines)
    return "\n".join(text_lines), line_width, text_height
Exemple #24
0
def get_font_size(font: ImageFont.FreeTypeFont, text: str) -> (int, int):
    return font.getsize(text)
Exemple #25
0
def draw_bounding_box_on_image(
        image: Image.Image,
        ymin: int,
        xmin: int,
        ymax: int,
        xmax: int,
        color: Tuple[int],
        font: FreeTypeFont,
        thickness: int = 4,
        display_str_list: Iterable[Text] = (),
):
    """
    Adds a bounding box to an image.
    
    Adapted from:
    https://colab.research.google.com/github/tensorflow/hub/blob/master/examples/colab/object_detection.ipynb
    """
    draw = ImageDraw.Draw(image)  # type: ImageDraw.ImageDraw
    im_width, im_height = image.size
    (left, right, top, bottom) = (
        xmin * im_width,
        xmax * im_width,
        ymin * im_height,
        ymax * im_height,
    )
    draw.line(
        [(left, top), (left, bottom), (right, bottom), (right, top),
         (left, top)],
        width=thickness,
        fill=color,
    )

    # If the total height of the display strings added to the top of the bounding
    # box exceeds the top of the image, stack the strings below the bounding box
    # instead of above.
    display_str_heights = [font.getsize(ds)[1] for ds in display_str_list]
    # Each display_str has a top and bottom margin of 0.05x.
    total_display_str_height = (1 + 2 * 0.05) * sum(display_str_heights)

    if top > total_display_str_height:
        text_bottom = top
    else:
        text_bottom = bottom + total_display_str_height
    # Reverse list and print from bottom to top.
    for display_str in display_str_list[::-1]:
        text_width, text_height = font.getsize(display_str)
        margin = np.ceil(0.05 * text_height)
        draw.rectangle(
            [
                (left, text_bottom - text_height - 2 * margin),
                (left + text_width, text_bottom),
            ],
            fill=color,
        )
        draw.text(
            (left + margin, text_bottom - text_height - margin),
            display_str,
            fill="black",
            font=font,
        )
        text_bottom -= text_height - 2 * margin
 def get_char_box(self, font: ImageFont.FreeTypeFont) -> tuple:
     """
     Com base no tamanho da fonte (que é do tipo `monospace`), 
     calcula a largura e altura em pixels de um caractere."""
     return font.getsize("H")