Example #1
0
def draw_clean_text(draw: ImageDraw, text: str, lang: str, box: Box, fill: str = 'black', spacing: int = 0, pad: int = 0) -> None:
    '''Place the text at the center of the bounding box.
    Input:
        draw:    ImageDraw instance
        text:    Content to add
        lang:    Language
        box:     Bounding box
        fill:    Color of text
        spacing: Vertical spacing between lines
        pad:     Inner padding to add to the bounding box
    '''
    box.pad(p=pad)
    W, H = (box.R - box.L), (box.B - box.T)
    font_path = f'data/fonts/indic/{lang}.ttf'
    F = ImageFont.truetype(font=font_path, size=1)

    size = 1

    w, h = draw.multiline_textsize(text, font=F, spacing=spacing)
    while w < W and h < H:
        size += 1
        F = ImageFont.truetype(font=font_path, size=size)
        w, h = draw.multiline_textsize(text, font=F, spacing=spacing)

    # TODO: fix, uncomment this if you want a static size & not dynamic size for texts
    # size = 30 if size < 70 else 100

    F = ImageFont.truetype(font=font_path, size=size)
    w, h = F.getsize_multiline(text)

    cx = (W - w) / 2 + box.L
    cy = (H - h) / 2 + box.T - 10

    draw.multiline_text((cx, cy), text, fill=fill, font=F, align='center', spacing=spacing)
Example #2
0
def draw_text_on_image(
    draw: ImageDraw,
    text: str,
    font: ImageFont,
    height: int,
    width: int,
    heigh_offset: int,
    width_offset: int,
):
    """Draw given text on the given image.

    Args:
        draw (ImageDraw): The ImageDraw object using which text will be drawn
        text (str): Text to draw on the image
        font (ImageFont): Font to be used
        height (int): Height of the image
        width (int): Width of the image
        heigh_offset (int): Height offset from the centre
        width_offset (int): Width offset from the centre
    """
    w, h = draw.multiline_textsize(text, font, spacing=10)
    left = width_offset + ((width - w) * 0.5)
    top = heigh_offset + ((height - h) * 0.5)

    draw.text(
        (left, top),
        text,
        random.choice(COLOR_OPTIONS),
        font=font,
        spacing=10,
        stroke_width=3,
        stroke_fill="black",
    )
Example #3
0
def centeredText(draw: ImageDraw, font: ImageFont, x: int, y: int, text: str,
                 color: str):
    textSize = draw.multiline_textsize(text, font)
    draw.multiline_text((x - textSize[0] / 2, y - textSize[1] / 2),
                        text,
                        color,
                        font,
                        align="center")
Example #4
0
def try_better_aspect_ratio(img: ImageDraw, original_text: str,
                            modified_text: str):
    '''
    If the message text is too long, wrapping it in 25 character lines will result in an image with a big height and small width, making it difficult to read when resized to Telegram's limit of 512px.
    So if the wrapped text has a height more than two times its width, we increase the line width limit and re-wrap it until we get an aspect ratio closer to 1:1.
    '''
    line_width = LINE_WIDTH_LIMIT
    text_size = img.multiline_textsize(modified_text, font=FONTS['normal'])
    for _ in range(3):
        if text_size[1] > 2 * text_size[0]:
            line_width *= 2
            modified_text = wrapped_text(original_text,
                                         line_width=line_width,
                                         max_lines=MAX_NUMBER_OF_LINES)
            text_size = img.multiline_textsize(modified_text,
                                               font=FONTS['normal'])
        else:
            break
    return modified_text, text_size
def process_message(
        drawing: ImageDraw,
        message_text: str,
        font: ImageFont,
        line_spacing: int,
        max_width: int,
        max_lines: int):
    message_text = message_text.replace('\r\n', ' ')
    message_text = message_text.replace('\n', ' ')
    message_text = message_text.replace('  ', ' ')
    number_of_lines = 1
    lw, lh = drawing.multiline_textsize(text=message_text, font=font, spacing=line_spacing)

    if lw > max_width:
        message_words = message_text.split()
        message_text = ''
        has_more_text = True
        while has_more_text:
            for i in range(0 , len(message_words)):
                line = ' '.join(message_words[0:i + 1])
                lw, lh = drawing.multiline_textsize(text=line, font=font, spacing=line_spacing)
                if lw > max_width:
                    line = ' '.join(message_words[:i - 1])
                    message_text += f'{line}\n'
                    number_of_lines += 1
                    if number_of_lines > max_lines:
                        has_more_text = False
                        message_text = f'{message_text[:len(message_text) - 3]}...'
                        break
                    message_words = message_words[i - 1:]
                    line = ' '.join(message_words)
                    lw, lh = drawing.multiline_textsize(text=line, font=font, spacing=line_spacing)
                    if lw <= max_width:
                        has_more_text = False
                        message_text += f'{line}'
                        break
                    break

    lw, lh = drawing.multiline_textsize(text=message_text, font=font, spacing=line_spacing)
    number_of_lines = message_text.count('\n') + 1
    return message_text, number_of_lines, lw, lh
Example #6
0
def textException(txtDrawer: ImageDraw,
                  txt: str,
                  txtFieldWidth: int,
                  targetFontSize: int,
                  canLiningChange: bool,
                  pathToFont,
                  howMuchCanFontChange: int = None) -> [str, int]:
    fontSize = targetFontSize
    _font = ImageFont.truetype(pathToFont, fontSize)
    txtWidth = txtDrawer.multiline_textsize(txt, font=_font)[0]

    if howMuchCanFontChange:
        try:
            fontSize = tryToMatchTextWidthByFontSize(txt,
                                                     targetFontSize,
                                                     txtFieldWidth,
                                                     howMuchCanFontChange,
                                                     pathToFont=pathToFont)
            return txt, fontSize
        except:
            pass

    wordsCount = len(textPreparation(txt).split())
    if canLiningChange and wordsCount > 1:
        temp = minimizingTextWidthByLiningChange(txt,
                                                 targetFontSize,
                                                 txtFieldWidth,
                                                 wordsCount,
                                                 pathToFont=pathToFont)

        txt = temp[0]
        if temp[-1]:
            return txt, fontSize

    if howMuchCanFontChange and canLiningChange:
        try:
            fontSize = tryToMatchTextWidthByFontSize(txt,
                                                     targetFontSize,
                                                     txtFieldWidth,
                                                     howMuchCanFontChange,
                                                     pathToFont=pathToFont)
            return txt, fontSize
        except:
            pass

    if txtWidth > txtFieldWidth:
        raise ValueError("Слишком длинный текст")
Example #7
0
    def render_list(self, result: Image, draw: ImageDraw, draw_property: Mapping,
                    bounding_box: Tuple[Tuple[int, int], Tuple[int, int]], current_x: int, current_y: int,
                    backwards: bool) -> Tuple[int, int]:
        theme_font = None
        if "text" in draw_property["type"]:
            theme_font = ImageFont.truetype(draw_property["font"], draw_property["font_size"])
        items = self.properties.get(draw_property["property"])
        current_x, current_y = self.resolve_position(draw_property["position"], bounding_box, current_x, current_y)
        size_x, size_y = draw_property["size"]
        if size_x == "auto":
            size_x = bounding_box[1][0] - current_x + bounding_box[0][0]
            if backwards:
                size_x = current_x - bounding_box[0][0]
        if size_y == "auto":
            size_y = bounding_box[1][1] - current_y + bounding_box[0][1]
        print(current_x, size_x, backwards)
        if backwards:
            current_x = current_x - size_x
        orientation = draw_property["orientation"]
        internal_current_y = current_y
        internal_current_x = current_x
        if items is not None:
            cached_current_x = current_x
            cached_current_y = current_y
            spacing = draw_property["spacing"]
            x_border = current_x + size_x
            y_border = current_y + size_y
            rows = draw_property["rows"]
            columns = draw_property["columns"]
            first = True
            # CodeReview: Specify an order that all will conform to
            for item in items:
                resource_name = f"resources/icons/{item}.png"
                if "icon" in draw_property["type"] and os.path.isfile(resource_name):
                    icon = Image.open(resource_name)
                    icon_width, icon_height = icon.size
                    icon = icon.resize((self.format["icon_height"] * icon_width // icon_height,
                                        self.format["icon_height"]), Image.HAMMING)
                    icon_width, icon_height = icon.size
                    x_coord = internal_current_x
                    y_coord = internal_current_y
                    if orientation == "horizontal":
                        if not first:
                            x_coord += spacing
                        first = False
                        if draw_property["centered_height"]:
                            y_coord += (size_y // (rows or 1) - icon_height) // 2
                    if orientation == "vertical":
                        if not first:
                            y_coord += spacing
                        first = False
                        if draw_property["centered_width"]:
                            x_coord += (size_x // (columns or 1) - icon_width) // 2
                    if orientation == "horizontal" and x_coord + icon_width > x_border \
                            and rows is not None and internal_current_y < current_y + (rows - 1) * (size_y // rows):
                        internal_current_y += size_y // rows
                        internal_current_x = cached_current_x
                        x_coord = internal_current_x
                        y_coord += size_y // rows
                    elif orientation == "vertical" and y_coord + icon_height > y_border \
                            and columns is not None and \
                            internal_current_x < current_x + (columns - 1) * (size_y // columns):
                        internal_current_x += size_x // rows
                        internal_current_y = cached_current_y
                    result.paste(icon, (x_coord, y_coord))
                    if orientation == "horizontal":
                        internal_current_x = x_coord + icon_width
                    elif orientation == "vertical":
                        internal_current_y = y_coord + icon_height
                elif "text" in draw_property["type"]:
                    text_width, text_height = draw.textsize(item, theme_font)
                    if draw_property["wrap"] is not None:
                        item = textwrap.fill(item, draw_property["wrap"])
                        text_width, text_height = draw.multiline_textsize(item, theme_font)
                    x_coord = internal_current_x
                    y_coord = internal_current_y
                    text_color = self.properties.get(draw_property["text_color_property"]) or \
                        draw_property["text_color_default"]
                    if draw_property["bulleted"]:
                        draw.ellipse((internal_current_x + spacing,
                                      internal_current_y + (text_height - draw_property["font_size"]) // 2,
                                      internal_current_x + spacing + draw_property["font_size"],
                                      internal_current_y + (text_height + draw_property["spacing"]) // 2),
                                     fill=text_color)
                        x_coord += spacing*2 + draw_property["font_size"]
                    if orientation == "horizontal":
                        if not first:
                            x_coord += spacing
                        first = False
                        if draw_property["centered_height"]:
                            y_coord += (size_y // (rows or 1) - text_height) // 2
                    if orientation == "vertical":
                        if not first:
                            y_coord += spacing
                        first = False
                        if draw_property["centered_width"]:
                            x_coord += (size_x // (columns or 1) - text_width) // 2
                    print(x_coord, text_width, x_border, cached_current_x, internal_current_y)
                    if orientation == "horizontal" and x_coord + text_width > x_border \
                            and rows is not None and internal_current_y < current_y + (rows - 1) * (size_y // rows):
                        internal_current_y += size_y // rows
                        internal_current_x = cached_current_x
                        x_coord = internal_current_x
                        y_coord += size_y // rows
                    elif orientation == "vertical" and y_coord + text_height > y_border \
                            and columns is not None and \
                            internal_current_x < current_x + (columns - 1) * (size_y // columns):
                        internal_current_x += size_x // rows
                        internal_current_y = cached_current_y
                    if draw_property["wrap"] is None:
                        draw.text((x_coord, y_coord), item, fill=text_color, font=theme_font)
                    else:
                        draw.multiline_text((x_coord, y_coord), item, fill=text_color, font=theme_font)

                    if orientation == "horizontal":
                        internal_current_x = x_coord + text_width
                    elif orientation == "vertical":
                        internal_current_y = y_coord + text_height
                else:
                    raise Exception(f"Could not find handler for {item} for {draw_property['type']}")
            if not backwards:
                current_x += size_x
            if orientation == "vertical":
                current_y += size_y
        else:
            if draw_property["required"]:
                raise Exception(f"Missing required property: {draw_property['property']} from card {self.properties}")
        return current_x, current_y