Pro grafické aplikace jsem začal používat grafickou knihovnu, kterou jsem našel zde http://herptronix.blogspot.fr/2013/12/open-source-smart-display-part-3.html. Uvažoval jsem o použití uGFX, ale vzhledem k licenčním podmínkám (možnosti i komerčního použití), jsem posléze raději naportoval knihovnu od Herptronixe. Nástroje, které jsou dostupné pro tvorbu resource souborů s fonty ale bohužel mají dost problém s češtinou a proto jsem napsal vlastní konvertor fontů v Pythonu.Program vychází z původních zdrojových kódů, jen je napsán tak, aby používal grafickou knihovnu PIL/PILLOW, určen je pro Python 2.7, umožňuje určit kódovou stránku výsledného fontu, umožňuje vytvořit 1,2 a 4 bitové fonty, počítá šířku pro případné monospace zobrazení, dokáže oříznout výsledek na minimální velikost atd.
Zdrojový kód:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
#http://stackoverflow.com/questions/5747739/python-render-non-anti-aliased-font-to-internal-image 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") 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.png', 'PNG') pix = image.load() firstimageline = -1 lastimageline = imageysize for y in range(imageysize): linepix = 0 for x in range(imagexsize): 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), linepix |= outpix if linepix: if -1 == firstimageline: firstimageline = y lastimageline = y if -1 == firstimageline: firstimageline = imageysize / 2 lastimageline = firstimageline return imagexsize, imageysize, firstimageline, lastimageline, result def encodechar(chardata, bitsperpixel, firstline, height): """ Encode given character map """ result = [] w, h, rawdata = chardata #print w, h, rawdata index = firstline * w outdata = 0 if 4 == bitsperpixel: bppx = 4 elif 2 == bitsperpixel: bppx = 2 else: bppx = 1 posBit = 8 - bppx for y in range(height): for x in range(w): data = rawdata[index] >> (4 - bppx) mask = data << posBit outdata |= mask posBit -= bppx index += 1 if posBit < 0: result.append(outdata) posBit = 8 - bppx outdata = 0 if posBit != (8 - bppx): result.append(outdata) 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) if 4 == bitsperpixel: result += "4bpp" elif 2 == bitsperpixel: result += "2bpp" else: result += "1bpp" if reduceheight: result += "_R" result += "_" + codepage return result def createfont(fontname, ptsize, charfrom, charto, bitsperpixel, reduceheight, codepage): """ Write Font file """ fontminline = 65535 fontmaxline = 0 fontrawdata = [] fontheight = 0 fontvaliddata = [] fontoffsets = [] actualoffset = 0 fontmonospacewidth = 0 for c in xrange(charfrom, charto + 1): encchar = unicode(chr(c), codepage, 'replace') #encchar = chr(c) #try: # encchar.decode(codepage, 'replace') # print unicode(chr(c), codepage, 'replace') # #encchar.decode('unicode', 'replace').encode(codepage, 'replace') #except: # encchar = ' ' # pass fchar = character(fontname, ptsize, bitsperpixel, encchar) w,h,ymin,ymax,dummy = fchar if w > fontmonospacewidth: fontmonospacewidth = w fontrawdata.append((w, h, dummy)) if ymin < fontminline: fontminline = ymin if ymax > fontmaxline: fontmaxline = ymax if reduceheight: fontheight = fontmaxline - fontminline + 1 else: fontminline = 0 fontheight = h for c in fontrawdata: fontoffsets.append(actualoffset) fontchar = encodechar(c, bitsperpixel, fontminline, fontheight) fontvaliddata.append(fontchar) actualoffset += len(fontchar) OutFile = open(buildfontname(fontname, ptsize, charfrom, charto, bitsperpixel, reduceheight, codepage) + ".c", 'w', 0) OutFile.write("/* Font resource created by fontconv.py (c) 2014 by Pavel Brychta */\n") OutFile.write('#include "resources.h"\n\n') OutFile.write("const uint8_t " + buildfontname(fontname, ptsize, charfrom, charto, bitsperpixel, reduceheight, codepage) + "[] =\n{\n") if 4 == bitsperpixel: OutFile.write("\t2, /* font type 4bppx */\n") elif 2 == bitsperpixel: OutFile.write("\t3, /* font type 2bppx */\n") else: OutFile.write("\t1, /* font type 1bppx */\n") OutFile.write("\t{0}, /* first char ID */\n".format(charfrom)) OutFile.write("\t{0}, /* last char ID */\n".format(charto)) OutFile.write("\t{0}, /* font height */\n".format(fontheight)) OutFile.write("/*\t{0}, // width for monospace output */\n".format(fontmonospacewidth)) OutFile.write("\t/* index table: glyph width, offset hl, lh, ll */\n") ptr = 0 for o in fontoffsets: f = fontrawdata[ptr] OutFile.write("\t0x{0:02X},".format(f[0])) OutFile.write("0x{0:02X},".format((fontoffsets[ptr] >> 16) & 0xff)) OutFile.write("0x{0:02X},".format((fontoffsets[ptr] >> 8) & 0xff)) OutFile.write("0x{0:02X},".format(fontoffsets[ptr] & 0xff)) 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] fontsize = int(argv[2]) charfrom = ast.literal_eval(argv[3]) charto = ast.literal_eval(argv[4]) bitsperpixel = int(argv[5]) reduceheight = int(argv[6]) codepage = argv[7] createfont(fontname, fontsize, charfrom, charto, bitsperpixel, reduceheight, codepage) return 0 if __name__ == "__main__": sys.exit(main(sys.argv)) |
Syntaxe pro použití je následující:
1 |
c:\Python27\python.exe fontconv.py OpenSans_CondBold.ttf 18 0x20 0xff 2 1 cp852 |
Kde OpenSans_… je jméno souboru s TTF fontem, 18 je velikost (POZOR – udávají se pt, ne px!), 0x20 je číslo prvního generovaného znaku, 0xff je číslo posledního generovaného znaku, 1 znamená ořezávat na minimální výšku a cp852 je výsledná kódová stránka.
Tímto článkem bych zároveň chtěl poděkovat Herpovi za výbornou knihovnu, se kterou mám zatím ty nejlepší zkušenosti…