OLEDUiPlus/tools/fontconv.py

206 lines
7.3 KiB
Python
Raw Permalink Normal View History

#http://stackoverflow.com/questions/5747739/python-render-non-anti-aliased-font-to-internal-image
#Author: Pavel Brychta
from PIL import Image
from PIL import ImageFont, ImageDraw
import sys
import ast
def character(fontname, fontsize, bitsperpixel, char):
""" Vykresli znak do vnitrniho povrchu a nasledne ho prevede do pole elementu intenzit,
ktere ma vzdy 4 bity/bod (intenzita 0-15).
Vraci [rozmer obrazku v ose X, rozmer obrazku v ose Y, Prvni radek obrazku, ktery nese obrazovou informaci (offset od 0), Posledni radek obrazku, ktery nese obrazovou informaci, [pole elementu]
"""
result = []
usr_font = ImageFont.truetype(fontname, fontsize)
imagexsize, imageysize = usr_font.getsize(char)
image = Image.new("RGB", (imagexsize, imageysize), "Black")
#image = Image.new("RGB", (320, 320), "Black")
# print(imagexsize, imageysize)
d_usr = ImageDraw.Draw(image)
if bitsperpixel > 1:
d_usr.fontmode = "L" # Zapneme antialiasovani
else:
d_usr.fontmode = "1" # Vypneme antialiasovani (pro fonty o hloubce 1bit/pixel)
#imagexsize, imageysize = d_usr.textsize(char, usr_font)
d_usr.text((0, 0), char, "White", font=usr_font)
#print "Imageinfosize:", ord(char), imagexsize, imageysize
#image.save('test_' + str(ord(char)) + '.png', 'PNG')
pix = image.load()
firstimageline = 1000
lastimageline = -1
for x in range(imagexsize):
for y in range(imageysize):
R, G, B = pix[x, y]
if R < 0x08:
outpix = 0
elif R < 0x18:
outpix = 1
elif R < 0x28:
outpix = 2
elif R < 0x38:
outpix = 3
elif R < 0x48:
outpix = 4
elif R < 0x58:
outpix = 5
elif R < 0x68:
outpix = 6
elif R < 0x78:
outpix = 7
elif R < 0x88:
outpix = 8
elif R < 0x98:
outpix = 9
elif R < 0xa8:
outpix = 10
elif R < 0xb8:
outpix = 11
elif R < 0xc8:
outpix = 12
elif R < 0xd8:
outpix = 13
elif R < 0xe8:
outpix = 14
else:
outpix = 15
result.append(outpix),
if outpix != 0:
if firstimageline > y:
firstimageline = y
if lastimageline < y:
lastimageline = y
if firstimageline == 1000:
firstimageline = imageysize >> 1
lastimageline = firstimageline
return imagexsize, imageysize, firstimageline, lastimageline, result
def encodechar(chardata, bitsperpixel, firstline, height):
""" Prevede zadanou bitovou mapu s rozsahem 0-15 do binarniho tvaru
s orezem na viditelnou cast za pomoci firstline a height parametru
"""
result = []
w, h, rawdata = chardata
# print(rawdata)
# print(chardata)
# print(firstline, w, len(rawdata))
# print(firstline, h, height, len(rawdata))
index = 0
for x in range(w):
posBit = 0
outdata = 0
for y in range(h):
if (y >= firstline) and (y <= (height + firstline)):
data = rawdata[index] >> 3
mask = data << posBit
outdata |= mask
posBit += 1
if posBit > 7:
result.append(outdata)
posBit = 0
outdata = 0
index += 1
if posBit != 0:
result.append(outdata)
# optimalizace dat (odstraneni poslednich bytu, ktere jsou nulove)
while len(result) > 1 and result[-1] == 0:
del result[-1]
return result
def buildfontname(fontname, ptsize, charfrom, charto, bitsperpixel, reduceheight, codepage):
""" Build ID of the font
"""
result = "Font"
result += fontname.split(".")[0] + "_"
result += "{0}pt_".format(ptsize)
result += "{0:02X}_".format(charfrom)
result += "{0:02X}_".format(charto)
return result
def createfont(fontname, ptsize, charfrom, charto, bitsperpixel, reduceheight, codepage):
""" Write Font file
"""
fontminline = 65535
fontmaxline = 0
fontrawdata = []
fontheight = 0
fontvaliddata = []
fontoffsets = []
fontsizes = []
actualoffset = 0
fontmonospacewidth = 0
for c in range(charfrom, charto + 1):
encchar = chr(c)
fchar = character(fontname, ptsize, bitsperpixel, encchar)
w, h, ymin, ymax, dummy = fchar
if w > fontmonospacewidth:
fontmonospacewidth = w # znak je zatim nejsirsi - zapamatujeme si jeho sirku pro monospace vystup
fontrawdata.append((w, h, dummy))
if ymin < fontminline:
fontminline = ymin # znak zacina drive nez predchozi (ve vyssim bode), zapamatujeme si pocatek
if ymax > fontmaxline:
fontmaxline = ymax # znak konci pozdeji nez predchozi (v nizsim bode), zapamatujeme si konec
if reduceheight:
fontheight = fontmaxline - fontminline + 1 # vypocitame si vysku skutecneho pismena
else:
fontminline = 0
fontheight = h
for c in fontrawdata:
fontoffsets.append(actualoffset)
fontchar = encodechar(c, bitsperpixel, fontminline, fontheight)
fontvaliddata.append(fontchar)
fontsizes.append(len(fontchar))
actualoffset += len(fontchar)
OutFile = open(buildfontname(fontname, ptsize, charfrom, charto, 1, reduceheight, codepage) + ".c", 'w')
OutFile.write("/* Font resource created by fontconv.py (c) 2020 by Pavel Brychta (1 bit per pixel)*/\n")
OutFile.write("const uint8_t " + buildfontname(fontname, ptsize, charfrom, charto, 1, reduceheight, codepage) + "[] PROGMEM =\n{\n")
OutFile.write("\t{0}, /* width for monospace output */\n".format(fontmonospacewidth))
OutFile.write("\t{0}, /* font height */\n".format(fontheight))
OutFile.write("\t{0}, /* first char ID */\n".format(charfrom))
OutFile.write("\t{0}, /* number of characters */\n".format(charto - charfrom + 1))
OutFile.write("\t/* index table: offset MSB, offset LSB, length, width */\n")
ptr = 0
for o in fontoffsets:
f = fontrawdata[ptr]
OutFile.write("\t0x{0:02X},".format((o >> 8) & 0xff)) # MSB
OutFile.write("0x{0:02X},".format(o & 0xff)) # LSB
OutFile.write("0x{0:02X},".format(fontsizes[ptr])) # length
OutFile.write("0x{0:02X},".format(f[0])) # width
OutFile.write("\n")
ptr += 1
OutFile.write("\t/* glyph raw */")
count = 0
for c in fontvaliddata:
for u in c:
if 0 == count:
OutFile.write("\n\t")
OutFile.write("0x{0:02X}".format(u))
count += 1
if 20 == count:
count = 0
OutFile.write(",")
OutFile.write("\n};\n")
OutFile.close()
def main(argv):
if len(argv) != 8:
sys.stderr.write("Invalid number of parameters.")
return 1
else:
fontname = argv[1] # jmeno fontu
fontsize = int(argv[2]) # velikost fontu (vyska v bodech)
charfrom = ast.literal_eval(argv[3]) # kod prvniho znaku, ktery bude ve vyslednem fontu ulozeny
charto = ast.literal_eval(argv[4]) # kod posledniho znaku, ktery bude ve vyslednem fontu ulozeny
bitsperpixel = int(argv[5]) # pocet bitu/pixel. Platne hodnoty jsou 1, 2, 4
reduceheight = int(argv[6]) # priznak, zda do vysledneho fontu neukladat "mrtve" oblasti (spocita skutecnou vysku nejvyssiho znaku a na tuto vysku prepocita vsechny ostatni znaky)
codepage = argv[7] # pouzita kodova stranka pro narodni znaky (diakritiku)
createfont(fontname, fontsize, charfrom, charto, bitsperpixel, reduceheight, codepage)
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))