| #!/usr/bin/env python |
| # -*- coding: utf8 -*- |
| import codecs,os,gzip,ctypes,ctypes.util,sys |
| from struct import * |
| from PIL import Image, ImageDraw, ImageFont |
| |
| # ====== Python script to convert TrueTypeFonts to TWRP's .dat format ====== |
| # This script was originally made by https://github.com/suky for his chinese version of TWRP |
| # and then translated to English by feilplane at #twrp of irc.freenode.net. |
| # However, it was not compatible with vanilla TWRP, so https://github.com/Tasssadar rewrote |
| # most of it and it now has very little in common with the original script. |
| |
| class Reference(): |
| def __init__(self, val): |
| self.__value = val |
| |
| def get(self): |
| return self.__value |
| |
| def set(self, val): |
| self.__value = val |
| |
| quiet = Reference(False) |
| |
| def log(text): |
| if not quiet.get(): |
| sys.stdout.write(text) |
| |
| def write_data(f, width, height, offsets, data): |
| f.write(pack("<I", width)) |
| f.write(pack("<I", height)) |
| for off in offsets: |
| f.write(pack("<I", off)) |
| f.write(data) |
| |
| if __name__ == "__main__": |
| fontsize = Reference(20) |
| out_fname = Reference("font.dat") |
| voffset = Reference(None) |
| padding = Reference(0) |
| font_fname = Reference(None) |
| preview = Reference(None) |
| |
| arg_parser = [ |
| ["-s", "--size=", fontsize, int], |
| ["-o", "--output=", out_fname, str], |
| ["-p", "--preview=", preview, str], |
| [None, "--padding=", padding, int], |
| ["-q", "--quiet", quiet, None], |
| [None, "--voffset=", voffset, int] |
| ] |
| |
| argv = sys.argv |
| argc = len(argv) |
| i = 1 |
| while i < argc: |
| arg = argv[i] |
| arg_next = argv[i+1] if i+1 < argc else None |
| |
| if arg == "--help" or arg == "-h": |
| print ("This script converts TrueTypeFonts to .dat file for TWRP recovery.\n\n" |
| "Usage: %s [SWITCHES] [TRUETYPE FILE]\n\n" |
| " -h, --help - print help\n" |
| " -o, --output=[FILE] - output file or '-' for stdout (default: font.dat)\n" |
| " -p, --preview=[FILE] - generate font preview to png file\n" |
| " --padding=[PIXELS] - horizontal padding around each character (default: 0)\n" |
| " -q, --quiet - Do not print any output\n" |
| " -s, --size=[SIZE IN PIXELS] - specify font size in points (default: 20)\n" |
| " --voffset=[PIXELS] - vertical offset (default: font size*0.25)\n\n" |
| "Example:\n" |
| " %s -s 40 -o ComicSans_40.dat -p preview.png ComicSans.ttf\n") % ( |
| sys.argv[0], sys.argv[0] |
| ) |
| exit(0) |
| |
| found = False |
| for p in arg_parser: |
| if p[0] and arg == p[0] and (arg_next or not p[3]): |
| if p[3]: |
| p[2].set(p[3](arg_next)) |
| else: |
| p[2].set(True) |
| i += 1 |
| found = True |
| break |
| elif p[1] and arg.startswith(p[1]): |
| if p[3]: |
| p[2].set(p[3](arg[len(p[1]):])) |
| else: |
| p[2].set(True) |
| found = True |
| break |
| |
| if not found: |
| font_fname.set(arg) |
| |
| i += 1 |
| |
| if not voffset.get(): |
| voffset.set(int(fontsize.get()*0.25)) |
| |
| if out_fname.get() == "-": |
| quiet.set(True) |
| |
| log("Loading font %s...\n" % font_fname.get()) |
| font = ImageFont.truetype(font_fname.get(), fontsize.get(), 0, "utf-32be") |
| cwidth = 0 |
| cheight = font.getsize('A')[1] |
| offsets = [] |
| renders = [] |
| data = bytes() |
| |
| # temp Image and ImageDraw to get access to textsize |
| res = Image.new('L', (1, 1), 0) |
| res_draw = ImageDraw.Draw(res) |
| |
| # Measure each character and render it to separate Image |
| log("Rendering characters...\n") |
| for i in range(32, 128): |
| w, h = res_draw.textsize(chr(i), font) |
| w += padding.get()*2 |
| offsets.append(cwidth) |
| cwidth += w |
| if h > cheight: |
| cheight = h |
| ichr = Image.new('L', (w, cheight*2)) |
| ichr_draw = ImageDraw.Draw(ichr) |
| ichr_draw.text((padding.get(), 0), chr(i), 255, font) |
| renders.append(ichr) |
| |
| # Twice the height to account for under-the-baseline characters |
| cheight *= 2 |
| |
| # Create the result bitmap |
| log("Creating result bitmap...\n") |
| res = Image.new('L', (cwidth, cheight), 0) |
| res_draw = ImageDraw.Draw(res) |
| |
| # Paste all characters into result bitmap |
| for i in range(len(renders)): |
| res.paste(renders[i], (offsets[i], 0)) |
| # uncomment to draw lines separating each character (for debug) |
| #res_draw.rectangle([offsets[i], 0, offsets[i], cheight], outline="blue") |
| |
| # crop the blank areas on top and bottom |
| (_, start_y, _, end_y) = res.getbbox() |
| res = res.crop((0, start_y, cwidth, end_y)) |
| cheight = (end_y - start_y) + voffset.get() |
| new_res = Image.new('L', (cwidth, cheight)) |
| new_res.paste(res, (0, voffset.get())) |
| res = new_res |
| |
| # save the preview |
| if preview.get(): |
| log("Saving preview to %s...\n" % preview.get()) |
| res.save(preview.get()) |
| |
| # Pack the data. |
| # The "data" is a B/W bitmap with all 96 characters next to each other |
| # on one line. It is as wide as all the characters combined and as |
| # high as the tallest character, plus padding. |
| # Each byte contains info about eight pixels, starting from |
| # highest to lowest bit: |
| # bits: | 7 6 5 4 3 2 1 0 | 15 14 13 12 11 10 9 8 | ... |
| # pixels: | 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15 | ... |
| log("Packing data...\n") |
| bit = 0 |
| bit_itr = 0 |
| for c in res.tostring(): |
| # FIXME: How to handle antialiasing? |
| # if c != '\x00': |
| # In Python3, c is int, in Python2, c is string. Because of reasons. |
| try: |
| fill = (ord(c) >= 127) |
| except TypeError: |
| fill = (c >= 127) |
| if fill: |
| bit |= (1 << (7-bit_itr)) |
| bit_itr += 1 |
| if bit_itr >= 8: |
| data += pack("<B", bit) |
| bit_itr = 0 |
| bit = 0 |
| |
| # Write them to the file. |
| # Format: |
| # 000: width |
| # 004: height |
| # 008: offsets of each characters (96*uint32) |
| # 392: data as described above |
| log("Writing to %s...\n" % out_fname.get()) |
| if out_fname.get() == "-": |
| write_data(sys.stdout, cwidth, cheight, offsets, data) |
| else: |
| with open(out_fname.get(), 'wb') as f: |
| write_data(f, cwidth, cheight, offsets, data) |
| |
| exit(0) |