diff --git a/tools/fontconv.py b/tools/fontconv.py new file mode 100644 index 0000000..8b8f222 --- /dev/null +++ b/tools/fontconv.py @@ -0,0 +1,205 @@ +#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)) diff --git a/tools/requirements.txt b/tools/requirements.txt new file mode 100644 index 0000000..b338169 --- /dev/null +++ b/tools/requirements.txt @@ -0,0 +1 @@ +PIL diff --git a/tools/test_fontconv.sh b/tools/test_fontconv.sh new file mode 100644 index 0000000..ab94dd9 --- /dev/null +++ b/tools/test_fontconv.sh @@ -0,0 +1,7 @@ +# python3 fontconv.py "built titling lt.ttf" 25 0x30 0xff 1 1 cp852 +# python3 fontconv.py "built_titling_lt.ttf" 25 0x20 0x3f 1 1 cp852 +# python3 fontconv.py "built_titling_lt.ttf" 60 0x30 0x39 1 1 cp852 +# python3 fontconv.py "built_titling_sb.ttf" 60 0x30 0x39 1 1 cp852 +#python3 fontconv.py "built_titling_sb.ttf" 60 0x20 0x23 1 1 cp852 +#python3 fontconv.py "built_titling_sb.ttf" 60 0x23 0x24 1 1 cp852 +python3 fontconv.py "built_titling_sb.ttf" 60 0x30 0x39 1 1 cp852