206 lines
7.3 KiB
Python
206 lines
7.3 KiB
Python
|
#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))
|