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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…