The sprites are 6×7 including a 1px margin on the right and bottom edges. The margin is occasionally tapped into by descenders (g, j, p, q, ƒ, Ç, φ, џ, comma, semicolon, etc.), box-drawing characters, and other symbols that just wouldn't consistently compress below 6×7 (↔, ↨, √, etc.).
I have crafted thus far all characters in IBM 437, Windows-1252, and the Macintosh Cyrillic codepage.
(Original work; hereby published under a dual license CC0+WTFPL.)
Could be useful for very small LCD displays, or for fitting an entire movie script on your monitor at once.
It is not finished yet; my next goal is to figure out how to create one of these so I can actually use the thing… then I'll worry about adding more characters. (Fontforge is waay too sophisticated for this use-case, though, so it has ended up screwing up the font every time I tried to render an .obt
file: the letters all had "shaved tops", and the UI element to set their height manually was greyed-out. Hmm…)
Appendices
Compiler
from PIL import Image
import struct, csv, gzip
# USAGE:
# python3 make_psf.py 6x7 cp437_tinyfont.gif unicode_cp437.tsv 437-Handmade5x6.psf2.gz && setfont -v 437-Handmade5x6.psf2.gz
# python3 make_psf.py 6x7 cp437_tinyfont.gif unicode_x1252.tsv x1252-Handmade5x6.psf2.gz && setfont -v x1252-Handmade5x6.psf2.gz
PSF2_MAGIC = b'\x72\xb5\x4a\x86'
PSF2_HAS_UNICODE_TABLE = 0b1
PSF2_MAXVERSION = 0
PSF2_SEPARATOR = b'\xFF'
PSF2_STARTSEQ = b'\xFE'
psf2_header = struct.Struct('<4sIiIII2I')
# PCF IS WIP NOT YET IMPLEMENTED #
# https://fontforge.org/docs/techref/pcf-format.html
PCF_MAGIC = b'\x01FCP'
PCF_PROPERTIES = 0b000000001
PCF_ACCELERATORS = 0b000000010
PCF_METRICS = 0b000000100
PCF_BITMAPS = 0b000001000
PCF_INK_METRICS = 0b000010000
PCF_BDF_ENCODINGS = 0b000100000
PCF_SWIDTHS = 0b001000000
PCF_GLYPH_NAMES = 0b010000000
PCF_BDF_ACCELERATORS = 0b100000000
PCF_DEFAULT_FORMAT = 0x000
PCF_INKBOUNDS = 0x200
PCF_ACCEL_W_INKBOUNDS = 0x100
PCF_COMPRESSED_METRICS = 0x100
PCF_GLYPH_PAD_MASK = 0b000011
PCF_BYTE_MASK = 0b000100
PCF_BIT_MASK = 0b001000
PCF_SCAN_UNIT_MANK = 0b110000
pcf_header_1 = struct.Struct('<4sI')
pcf_header_2 = struct.Struct('<IIII')
def im2glyphs(im, w, h):
for y in range(0, im.height, h):
for x in range(0, im.width, w):
yield im.crop((x, y, x+w, y+h))
def _makeheader(glyphs, *, ver=PSF2_MAXVERSION, flags=0):
n = len(glyphs)
assert len(set(glyph.size for glyph in glyphs)) == 1
w, h = glyphs[0].size
glyphsize = h * ((w + 7) // 8)
#glyphsize = -(w * h // -8)
return psf2_header.pack(PSF2_MAGIC, ver, psf2_header.size, flags, n, glyphsize, h, w)
def _makebody(glyphs):
assert len(set(glyph.size for glyph in glyphs)) == 1
for glyph in glyphs:
yield glyph.convert('1').tobytes()
def _makeuctable(unicodemap):
for position in unicodemap:
for sequence in position:
yield PSF2_STARTSEQ
yield sequence.encode("utf-8")
yield PSF2_SEPARATOR
def makepsf2(size, im, out, unicodemap=None):
global glyphs # for debugging, run python -i
glyphs = list(im2glyphs(im, *size))
flags = 0
if unicodemap is not None:
flags |= PSF2_HAS_UNICODE_TABLE
else:
assert len(glyphs) == 256
out.write(_makeheader(glyphs, flags=flags))
for chunk in _makebody(glyphs):
out.write(chunk)
if unicodemap is not None:
for chunk in _makeuctable(unicodemap):
out.write(chunk)
if __name__ == '__main__':
import sys
size = map(int, sys.argv[1].split('x'))
im = Image.open(sys.argv[2])
with open(sys.argv[3], 'r') as f:
r = csv.reader(f, delimiter='\t')
unicodemap = list(r)
if unicodemap[0][0] == '':
unicodemap[0][0] = '\u0000'
if unicodemap[255][0] == '\x20' and unicodemap[0x20][0] == '\x20':
unicodemap[255][0] = '\u00A0'
with gzip.open(sys.argv[4], 'wb') as f:
makepsf2(size, im, f, unicodemap)
datafiles
CP437 Unicode Table
"" ☺ ☻ ♥ ♦ ♣ ♠ • ◘ ○ ◙ ♂ ♀ ♪ 𝅘𝅥𝅮 ♫ ☼ ► ▶ ▸ ◄ ↕ ᛨ ‼ ¶ § ▬ ↨ ↑ ᛏ ↓ → ← ∟ ↔ ▲ ▼ " " ! ǃ ⵑ """" # $ % ٪ ⁒ & ' ʻ ʹ ˈ ʹ ᑊ ꞌ ( ❲ ) ❳ * ∗ 𐌟 + , ¸ ؍ ‚ ꓹ - ˗ ‐ ‑ . ܁ ܂ ․ ꓸ 𝅭 / ᜵ ∕ 〳 𝈺 0 1 2 3 З Ӡ 4 5 6 б 7 8 9 : ː ˸ ։ ׃ ܃ ܄ ः ઃ ᛬ ᠃ ᠉ ⁚ ∶ ꓽ ꞉ ︰ : ; ; < ᐸ 𝈶 = > ᐳ 𖼿 𝈷 ? @ A Α А B Β В C Ϲ С D E Ε Е F G H Η Н I Ι І Ӏ J Ј K Κ К L M Μ М N Ν O Ο О P Ρ Р Q Ԛ R S Ѕ Ꚃ T Τ Т U V W Ԝ X Χ Х Y Υ Ү Z Ζ [ \ ] ^ ˄ ˆ _ ` a а b c с d e е f g h һ i і j ј k κ l m n o ο о p р q ԛ r s ѕ t u v w ԝ x х y γ у ү z { | ӏ } ~ ⌂ 🏠 Ç ü é â ä ӓ à å ç ς ҫ ê ë ё è ѐ ï ϊ ї î ì Ä Ӓ Å Å É æ ӕ Æ Ӕ ô ö ӧ ò û ù ÿ ӱ Ö Ӧ Ü ¢ £ ¥ ₧ ƒ á í ó ú ñ Ñ ª º ¿ ⌐ ¬ ½ ¼ ¡ « » ░ ▒ ▓ │ ┤ ╡ ╢ ╖ ╕ ╣ ║ ╗ ╝ ╜ ╛ ┐ └ ┴ ┬ ├ ─ ┼ ╞ ╟ ╚ ╔ ╩ ╦ ╠ ═ ╬ ╧ ╨ ╤ ╥ ╙ ╘ ╒ ╓ ╫ ╪ ┘ ┌ █ ▄ ▌ ▐ ▀ α ß β Γ Г Г π Σ σ µ μ τ ꚍ Φ Ф Θ Ѳ Ө Ω Ω δ ∞ ꚙ φ ε є ɛ ∩ ≡ Ξ ± ≥ ≤ ⌠ ⌡ ÷ ≈ ° ∙ · 𐩐 ٠ ۰ √ ⁿ ² ■ " "
x1252 Unicode Table
○ ■ ↑ ᛏ ↓ → ← ║ ═ ╔ ╗ ╚ ╝ ░ ▒ ► ▶ ▸ ◄ │ ─ ┌ ┐ └ ┘ ├ ┤ ┴ ┬ ♦ ┼ █ ▄ ▀ ▬ " " ! ǃ ⵑ """" # $ % ٪ ⁒ & ' ʻ ʹ ˈ ʹ ᑊ ꞌ ( ❲ ) ❳ * ∗ 𐌟 + , ¸ ؍ ꓹ - ˗ ‐ ‑ . ܁ ܂ ․ ꓸ 𝅭 / ᜵ ∕ 〳 𝈺 0 1 2 3 З Ӡ 4 5 6 б 7 8 9 : ː ˸ ։ ׃ ܃ ܄ ः ઃ ᛬ ᠃ ᠉ ⁚ ∶ ꓽ ꞉ ︰ : ; ; < ᐸ 𝈶 = > ᐳ 𖼿 𝈷 ? @ A Α А B Β В C Ϲ С D E Ε Е F G H Η Н I Ι І Ӏ J Ј K Κ К L M Μ М N Ν O Ο О P Ρ Р Q Ԛ R S Ѕ Ꚃ T Τ Т U V W Ԝ X Χ Х Y Υ Ү Z Ζ [ \ ] ^ _ ` a а b c с d e е f g h һ i і j ј k κ l m n o ο о p р q ԛ r s ѕ t u v w ԝ x х y γ у ү z { | ӏ } ~ € ‚ ƒ „ … † ‡ ˆ ˄ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ "" ® ¯ ° ± ² ³ ´ µ μ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Ӓ Å Å Æ Ӕ Ç È É Ê Ë Ì Í Î Ï Đ Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã å ä ӓ æ ӕ ç ς ҫ è ѐ é ê ë ì í î ï ð ∂ ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ
Might have to reverse-engineer the format https://gitlab.freedesktop.org/xorg/app/fonttosfnt