Skip to content

Instantly share code, notes, and snippets.

@sigsergv
Last active August 11, 2025 21:05
Show Gist options
  • Save sigsergv/9309ad0e3b3c2be65aa58c37d259f580 to your computer and use it in GitHub Desktop.
Save sigsergv/9309ad0e3b3c2be65aa58c37d259f580 to your computer and use it in GitHub Desktop.
fonts thumbnailer

Install PILLOW:

pip install pillow

Execute script:

./draw-fonts-samples --fonts-dir ~/tmp/Fonts --output-dir thumbnails

After script execution you may want to compress resulting PNG files using optipng.

#!/usr/bin/env python3
"""
Create samples of each truetype font from the specified directory as
a single html page with png images representing glyphs from each font.
Original source: https://gist.github.com/sigsergv/9309ad0e3b3c2be65aa58c37d259f580
Author: Sergey Stolyarov <[email protected]>
License: Public Domain
Requirements: PILLOW
"""
import argparse
import os
from sys import exit
from PIL import Image, ImageDraw, ImageFont
from datetime import datetime
p = argparse.ArgumentParser()
p.add_argument('--fonts-dir', dest='fonts_dir', required=True, help='Path to directory with truetype fonts.')
p.add_argument('--output-dir', dest='output_dir', required=True,
help='Path to output directory where index.html and a set of .png images to be created.')
args = p.parse_args()
SAMPLE_STRING = '''0123456789!@#$%^&*(){}[]
THE FIVE BOXING WIZARDS JUMP QUICKLY.
the five boxing wizards jump quickly.
ЮЖНО-ЭФИОПСКИЙ ГРАЧ УВЁЛ МЫШЬ ЗА ХОБОТ НА СЪЕЗД ЯЩЕРИЦ.
южно-эфиопский грач увёл мышь за хобот на съезд ящериц.
'''
fonts_dir = args.fonts_dir
out_dir = args.output_dir
if not os.path.isdir(fonts_dir):
print(f'Fonts directory `{fonts_dir}` not found!')
exit(1)
if not os.path.isdir(out_dir):
print(f'Output directory `{out_dir}` not found!')
exit(1)
text_color = (0, 0, 0)
bg_color = (255, 255, 255)
font_size = 48
html = '''<html>
<head>
<title>Fonts samples</title>
<style>
body {
background-color: white;
color: black;
font-family: sans-serif;
}
h3 {
border-style: none none solid none;
border-color: black;
border-width: 3px;
}
</style>
</head>
<body>
'''
html += '<h1>Index of truetype fonts</h1>\n'
html += f'<div>TrueType directory: {fonts_dir}</div>\n'
today = datetime.today().isoformat()
html += f'<div>Generated on: {today}</div>\n'
fonts_html_block = ''
ttf_num = 0
for ind, font_file in enumerate(os.listdir(fonts_dir)):
font_file = os.path.join(fonts_dir, font_file)
if not os.path.isfile(font_file):
# skipping non-files
continue
try:
font = ImageFont.truetype(font_file, font_size)
except:
print(f'`{font_file}` is not a valid truetype font! skipping')
continue
font_name, font_style = font.getname()
fonts_html_block += f'<h3>{font_name} {font_style}</h3>\n'
# calculate text box
dummy_image = Image.new('RGB', (1, 1))
dummy_draw = ImageDraw.Draw(dummy_image)
bbox = dummy_draw.textbbox((0, 0), SAMPLE_STRING, font=font)
width = bbox[2] - bbox[0]
height = bbox[3] - bbox[1]
# create target image
image_file = f'img_{ind}.png'
image_path = os.path.join(out_dir, image_file)
image = Image.new('RGB', (width + 20, height + 20), color=bg_color)
draw = ImageDraw.Draw(image)
draw.text((0, 0), SAMPLE_STRING, font=font, fill=text_color)
image.save(image_path, 'png')
# append to html
fonts_html_block += f'<div><img src="{image_file}"></div>\n'
ttf_num += 1
html += f'<div>Total files: {ttf_num}</div>\n'
html += fonts_html_block
html += '</body></html>'
index_html = os.path.join(out_dir, 'index.html')
with open(index_html, 'w') as ifp:
ifp.write(html)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment