简体   繁体   中英

Converting a .txt file to an image in Python

I have some code that converts an image into an ascii art image. Currently it outputs it as a .txt file, but the file can have several hundred thousand characters. How can I convert the file to an image such as a .png file?

Currently it builds a character vector based on the pixel density, and then writes the vector into a .txt image.

If I understand correctly, you want an image that looks as if someone took a screenshot of the ascii art as it would look in a giant unlimited text editor.

I have done something similar to generate text programmatically with PILLOW. Here is an example modified from this code of mine. Hopefully this code will help you and others avoid the fiddling I had to do to figure out how to make things look reasonable.

Here is an example result made from the code below.

在此处输入图片说明

The code is a straightforward modification of the linked library to work with a text file instead of a string. You should be able to paste this into a console or a file and run it directly.

from math import ceil

from PIL import (
    Image,
    ImageFont,
    ImageDraw,
)

PIL_GRAYSCALE = 'L'
PIL_WIDTH_INDEX = 0
PIL_HEIGHT_INDEX = 1
COMMON_MONO_FONT_FILENAMES = [
    'DejaVuSansMono.ttf',  # Linux
    'Consolas Mono.ttf',   # MacOS, I think
    'Consola.ttf',         # Windows, I think
]


def main():
    image = textfile_to_image('content.txt')
    image.show()
    image.save('content.png')


def textfile_to_image(textfile_path):
    """Convert text file to a grayscale image.

    arguments:
    textfile_path - the content of this file will be converted to an image
    font_path - path to a font file (for example impact.ttf)
    """
    # parse the file into lines stripped of whitespace on the right side
    with open(textfile_path) as f:
        lines = tuple(line.rstrip() for line in f.readlines())

    # choose a font (you can see more detail in the linked library on github)
    font = None
    large_font = 20  # get better resolution with larger size
    for font_filename in COMMON_MONO_FONT_FILENAMES:
        try:
            font = ImageFont.truetype(font_filename, size=large_font)
            print(f'Using font "{font_filename}".')
            break
        except IOError:
            print(f'Could not load font "{font_filename}".')
    if font is None:
        font = ImageFont.load_default()
        print('Using default font.')

    # make a sufficiently sized background image based on the combination of font and lines
    font_points_to_pixels = lambda pt: round(pt * 96.0 / 72)
    margin_pixels = 20

    # height of the background image
    tallest_line = max(lines, key=lambda line: font.getsize(line)[PIL_HEIGHT_INDEX])
    max_line_height = font_points_to_pixels(font.getsize(tallest_line)[PIL_HEIGHT_INDEX])
    realistic_line_height = max_line_height * 0.8  # apparently it measures a lot of space above visible content
    image_height = int(ceil(realistic_line_height * len(lines) + 2 * margin_pixels))

    # width of the background image
    widest_line = max(lines, key=lambda s: font.getsize(s)[PIL_WIDTH_INDEX])
    max_line_width = font_points_to_pixels(font.getsize(widest_line)[PIL_WIDTH_INDEX])
    image_width = int(ceil(max_line_width + (2 * margin_pixels)))

    # draw the background
    background_color = 255  # white
    image = Image.new(PIL_GRAYSCALE, (image_width, image_height), color=background_color)
    draw = ImageDraw.Draw(image)

    # draw each line of text
    font_color = 0  # black
    horizontal_position = margin_pixels
    for i, line in enumerate(lines):
        vertical_position = int(round(margin_pixels + (i * realistic_line_height)))
        draw.text((horizontal_position, vertical_position), line, fill=font_color, font=font)

    return image


if __name__ == '__main__':
    main()

By the way, all that code should not be stuffed in one function, but I think it makes it easier to follow for example code.

Have a look at Pillow (documentation here )

From the above linked documentation:

Here's a simple example:

 import ImageFont, ImageDraw draw = ImageDraw.Draw(image) # use a bitmap font font = ImageFont.load("arial.pil") draw.text((10, 10), "hello", font=font) # use a truetype font font = ImageFont.truetype("arial.ttf", 15) draw.text((10, 25), "world", font=font)

I liked that the ImageFont-ImageDraw solution above, because it was much shorter. But it did not work as I tried it. Therefore I figured something out which produced the desired output.

In the following example code 1st a white image is generated. Afterwards the test is written on the image:

from PIL import Image, ImageFont, ImageDraw
img = Image.new('RGB', (200, 50), color = (255,255,255))
fnt = ImageFont.truetype("Pillow/Tests/fonts/FreeMono.ttf", 30)
ImageDraw.Draw(img).text((0,0), "hello world", font=fnt, fill=(0,0,0))
img

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM