277f742514
Change-Id: I3d6cc65a83b7da9428adf37804cf9cbd0df99492 Signed-off-by: Vojtech Bocek <vbocek@gmail.com>
199 lines
6.5 KiB
Python
Executable File
199 lines
6.5 KiB
Python
Executable File
#!/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)
|