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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With