Code cleanup for fonttool.py (#739, fixes #738)

This commit is contained in:
xcodz-dot 2020-10-24 12:39:55 +05:30 committed by GitHub
parent 02a3dcbaa2
commit 0df9743c9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,28 +1,37 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import math import math
import sys
import os
import re import re
import argparse
CP_MAX = 0x10FFFF CP_MAX = 0x10FFFF
FONT_CPP = "data/font.cpp" FONT_CPP = "data/font.cpp"
FONT_HEIGHT = 12 FONT_HEIGHT = 12
PTRS_PER_LINE = 8 PTRS_PER_LINE = 8
class ReadBDFError(RuntimeError):
def __init__(self, line_number, message):
super().__init__(self, 'line %i: %s' % (line_number, message))
class FontTool: class FontTool:
def __init__(self): def __init__(self):
with open(FONT_CPP) as font_cpp: with open(FONT_CPP) as font_cpp:
self.font_cpp_data = font_cpp.read() self.font_cpp_data = font_cpp.read()
font_data = ([ int(s, 16) for s in re.findall(r'\w+', re.search(r'font_data[^{]*{([^;]+);', self.font_cpp_data, re.MULTILINE | re.DOTALL)[1]) ]) font_data = ([int(s, 16) for s in re.findall(r'\w+', re.search(r'font_data[^{]*{([^;]+);', self.font_cpp_data,
font_ptrs = ([ int(s, 16) for s in re.findall(r'\w+', re.search(r'font_ptrs[^{]*{([^;]+);', self.font_cpp_data, re.MULTILINE | re.DOTALL)[1]) ]) re.MULTILINE | re.DOTALL)[1])])
font_ranges = ([ int(s, 16) for s in re.findall(r'\w+', re.search(r'font_ranges[^{]*{([^;]+);', self.font_cpp_data, re.MULTILINE | re.DOTALL)[1]) ]) font_ptrs = ([int(s, 16) for s in re.findall(r'\w+', re.search(r'font_ptrs[^{]*{([^;]+);', self.font_cpp_data,
self.code_points = [ False for _ in range(CP_MAX + 2) ] re.MULTILINE | re.DOTALL)[1])])
font_ranges = ([int(s, 16) for s in re.findall(r'\w+',
re.search(r'font_ranges[^{]*{([^;]+);', self.font_cpp_data,
re.MULTILINE | re.DOTALL)[1])])
self.code_points = [False for _ in range(CP_MAX + 2)]
ptrs_ptr = 0 ptrs_ptr = 0
for i in range(len(font_ranges) // 2 - 1): for i in range(len(font_ranges) // 2 - 1):
for cp in range(font_ranges[i * 2], font_ranges[i * 2 + 1] + 1): for cp in range(font_ranges[i * 2], font_ranges[i * 2 + 1] + 1):
base = font_ptrs[ptrs_ptr] base = font_ptrs[ptrs_ptr]
self.code_points[cp] = font_data[base : (base + math.ceil(font_data[base] * FONT_HEIGHT / 4) + 1)] self.code_points[cp] = font_data[base: (base + math.ceil(font_data[base] * FONT_HEIGHT / 4) + 1)]
ptrs_ptr += 1 ptrs_ptr += 1
def commit(self): def commit(self):
@ -34,10 +43,12 @@ class FontTool:
in_range = False in_range = False
elif not in_range and data: elif not in_range and data:
in_range = True in_range = True
new_ranges.append([ i ]) new_ranges.append([i])
font_data_lines_hex = [ [ '0x%02X' % v for v in d ] for d in filter(lambda x: x, self.code_points) ] font_data_lines_hex = [['0x%02X' % v for v in d] for d in filter(lambda x: x, self.code_points)]
font_data_lines = [ len(h) > 1 and h[0] + ', ' + ', '.join(h[1 :]) + ',' or '0x00, ' for h in font_data_lines_hex ] font_data_lines = [len(h) > 1 and h[0] + ', ' + ', '.join(h[1:]) + ',' or '0x00, ' for h in
font_cpp_data = re.sub(r'font_data[^{]*{([^;]+);', 'font_data[] = {\n ' + '\n '.join(font_data_lines) + '\n};', self.font_cpp_data) font_data_lines_hex]
font_cpp_data = re.sub(r'font_data[^{]*{([^;]+);',
'font_data[] = {\n ' + '\n '.join(font_data_lines) + '\n};', self.font_cpp_data)
font_ptrs_blocks = [] font_ptrs_blocks = []
data_ptr = 0 data_ptr = 0
for ran in new_ranges: for ran in new_ranges:
@ -47,11 +58,14 @@ class FontTool:
data_ptr += math.ceil(self.code_points[cp][0] * FONT_HEIGHT / 4) + 1 data_ptr += math.ceil(self.code_points[cp][0] * FONT_HEIGHT / 4) + 1
font_ptrs_wrapped = [] font_ptrs_wrapped = []
for i in range(0, len(block), PTRS_PER_LINE): for i in range(0, len(block), PTRS_PER_LINE):
font_ptrs_wrapped.append(', '.join([ '0x%08X' % v for v in block[i : (i + PTRS_PER_LINE)] ])) font_ptrs_wrapped.append(', '.join(['0x%08X' % v for v in block[i: (i + PTRS_PER_LINE)]]))
font_ptrs_blocks.append(',\n '.join(font_ptrs_wrapped)) font_ptrs_blocks.append(',\n '.join(font_ptrs_wrapped))
font_cpp_data = re.sub(r'font_ptrs[^{]*{([^;]+);', 'font_ptrs[] = {\n ' + ',\n\n '.join(font_ptrs_blocks) + ',\n};', font_cpp_data) font_cpp_data = re.sub(r'font_ptrs[^{]*{([^;]+);',
font_ranges_lines = [ '{ 0x%06X, 0x%06X },' % ( r[0], r[1] ) for r in new_ranges ] 'font_ptrs[] = {\n ' + ',\n\n '.join(font_ptrs_blocks) + ',\n};', font_cpp_data)
font_cpp_data = re.sub(r'font_ranges[^{]*{([^;]+);', 'font_ranges[][2] = {\n ' + '\n '.join(font_ranges_lines) + '\n { 0, 0 },\n};', font_cpp_data) font_ranges_lines = ['{ 0x%06X, 0x%06X },' % (r[0], r[1]) for r in new_ranges]
font_cpp_data = re.sub(r'font_ranges[^{]*{([^;]+);',
'font_ranges[][2] = {\n ' + '\n '.join(font_ranges_lines) + '\n { 0, 0 },\n};',
font_cpp_data)
with open(FONT_CPP, 'w') as font_cpp: with open(FONT_CPP, 'w') as font_cpp:
font_cpp.write(font_cpp_data) font_cpp.write(font_cpp_data)
@ -60,10 +74,10 @@ class FontTool:
for row in cp_matrix: for row in cp_matrix:
if width < len(row): if width < len(row):
width = len(row) width = len(row)
cp_data = [ width ] cp_data = [width]
bits = 8 bits = 8
for row in cp_matrix: for row in cp_matrix:
padded = row + [ 0 ] * (width - len(row)) padded = row + [0] * (width - len(row))
for cv in padded: for cv in padded:
if bits == 8: if bits == 8:
cp_data.append(0) cp_data.append(0)
@ -89,11 +103,12 @@ class FontTool:
bits -= 2 bits -= 2
return cp_matrix return cp_matrix
class RawReader: class RawReader:
def __init__(self, path): def __init__(self, path):
self.code_points = [ False for _ in range(CP_MAX + 2) ] self.code_points = [False for _ in range(CP_MAX + 2)]
with open(path) as raw: with open(path) as raw:
items = [ int(v) for v in re.findall(r'[0-9]+', raw.read()) ] items = [int(v) for v in re.findall(r'[0-9]+', raw.read())]
ptr = 0 ptr = 0
while ptr <= len(items) - 2: while ptr <= len(items) - 2:
cp = items[ptr] cp = items[ptr]
@ -101,17 +116,15 @@ class RawReader:
ptr += 2 ptr += 2
matrix = [] matrix = []
for i in range(ptr, ptr + width * FONT_HEIGHT, width): for i in range(ptr, ptr + width * FONT_HEIGHT, width):
matrix.append(items[i : (i + width)]) matrix.append(items[i: (i + width)])
ptr += width * FONT_HEIGHT ptr += width * FONT_HEIGHT
self.code_points[cp] = FontTool.pack(matrix) self.code_points[cp] = FontTool.pack(matrix)
class BDFReader: class BDFReader:
class ReadBDFError:
def __init__(line_number, message):
super(RuntimeError, self).__init__('line %i: %s' % ( line_number, message ))
def __init__(self, path, xoffs, yoffs): def __init__(self, path, xoffs, yoffs):
self.code_points = [ False for _ in range(CP_MAX + 2) ] self.code_points = [False for _ in range(CP_MAX + 2)]
item_re = re.compile(r'[^ \n\r]+') item_re = re.compile(r'[^ \n\r]+')
with open(path) as bdf: with open(path) as bdf:
global_dw = False global_dw = False
@ -136,7 +149,8 @@ class BDFReader:
cv = 0 cv = 0
xx = x + xoffs xx = x + xoffs
yy = FONT_HEIGHT - 1 - y + yoffs yy = FONT_HEIGHT - 1 - y + yoffs
if xx >= char_bbx[2] and xx < char_bbx[0] + char_bbx[2] and yy >= char_bbx[3] and yy < char_bbx[1] + char_bbx[3]: if char_bbx[2] <= xx < char_bbx[0] + char_bbx[2] and char_bbx[3] <= yy < char_bbx[1] + \
char_bbx[3]:
cv = bitmap[char_bbx[1] - 1 - (yy - char_bbx[3])][xx - char_bbx[2]] * 3 cv = bitmap[char_bbx[1] - 1 - (yy - char_bbx[3])][xx - char_bbx[2]] * 3
cp_matrix[-1].append(cv) cp_matrix[-1].append(cv)
self.code_points[char_cp] = FontTool.pack(cp_matrix) self.code_points[char_cp] = FontTool.pack(cp_matrix)
@ -151,8 +165,8 @@ class BDFReader:
bits = [] bits = []
for ch in items[0]: for ch in items[0]:
cv = int(ch, 16) cv = int(ch, 16)
bits += [ cv & 8 and 1 or 0, cv & 4 and 1 or 0, cv & 2 and 1 or 0, cv & 1 and 1 or 0 ] bits += [cv & 8 and 1 or 0, cv & 4 and 1 or 0, cv & 2 and 1 or 0, cv & 1 and 1 or 0]
bitmap.append(bits[ : char_bbx[0]]) bitmap.append(bits[: char_bbx[0]])
elif items[0] == 'SIZE': elif items[0] == 'SIZE':
if len(items) != 4: if len(items) != 4:
raise ReadBDFError(line_number, "invalid directive") raise ReadBDFError(line_number, "invalid directive")
@ -171,7 +185,7 @@ class BDFReader:
elif startchar and items[0] == 'BBX': elif startchar and items[0] == 'BBX':
if len(items) != 5: if len(items) != 5:
raise ReadBDFError(line_number, "invalid directive") raise ReadBDFError(line_number, "invalid directive")
char_bbx = [ int(items[1]), int(items[2]), int(items[3]), int(items[4]) ] char_bbx = [int(items[1]), int(items[2]), int(items[3]), int(items[4])]
elif startchar and items[0] == 'ENCODING': elif startchar and items[0] == 'ENCODING':
if len(items) != 2: if len(items) != 2:
raise ReadBDFError(line_number, "invalid directive") raise ReadBDFError(line_number, "invalid directive")
@ -190,77 +204,79 @@ class BDFReader:
if not startchar: if not startchar:
global_dw = char_dw global_dw = char_dw
if __name__ == "__main__": if __name__ == "__main__":
def print_usage_and_exit():
print("""Usage:
* fonttool.py addbdf FIRST LAST BDFFILE [XOFFS YOFFS]
* fonttool.py addraw FIRST LAST RAWFILE
* fonttool.py remove FIRST [LAST]
* fonttool.py inspect FIRST [LAST]
LAST defaults to FIRST, XOFFS and YOFFS default to 0. BDF is an parser = argparse.ArgumentParser("fonttool.py", description="font tools for managing fonts, this script can be"
archaic bitmap font format; look it up. " imported as a module",
fromfile_prefix_chars="@")
command = parser.add_subparsers(dest="command", required=True)
"Raw" files are simply ASCII-encoded white-space delimited lists addbdf = command.add_parser("addbdf", help="Adds BDF Formated Font")
addbdf.add_argument("first", metavar="FIRST", type=int)
addbdf.add_argument("last", metavar="LAST", type=int)
addbdf.add_argument("bdffile", metavar="BDFFILE", help="BDF is an archaic bitmap font format")
addbdf.add_argument("xoffs", metavar="XOFFS", nargs="?", default=0, type=int, help="Defaults to 0")
addbdf.add_argument("yoffs", metavar="YOFFS", nargs="?", default=0, type=int, help="Defaults to 0")
addraw = command.add_parser("addraw", help="Adds a Raw Formated Font")
addraw.add_argument("first", metavar="FIRST", type=int)
addraw.add_argument("last", metavar="LAST", type=int)
addraw.add_argument("rawfile", metavar="RAWFILE", help=""""Raw" files are simply ASCII-encoded white-space delimited \
lists
of decimal integer constants. These lists of integers encode of decimal integer constants. These lists of integers encode
characters as any number of consecutive character description characters as any number of consecutive character description
structures laid out as follows: structures laid out as follows:
* the code point corresponding to the character being described; * the code point corresponding to the character being described;
* the width in pixels of the character being described; * the width in pixels of the character being described;
* width times %i brightness levels between 0 and 3, a row-major matrix. * width times %i brightness levels between 0 and 3, a row-major matrix.""")
This script is also an importable module.""" % FONT_HEIGHT) remove = command.add_parser("remove", help="Remove")
exit(1) remove.add_argument("first", metavar="FIRST", type=int)
remove.add_argument("last", metavar="LAST", type=int, default=None, nargs="?", help="Defaults to FIRST")
if len(sys.argv) < 3: inspect = command.add_parser("inspect", help="Inspect")
print_usage_and_exit() inspect.add_argument("first", metavar="FIRST", type=int)
inspect.add_argument("last", metavar="LAST", type=int, default=None, nargs="?", help="Defaults to FIRST")
cp_first = int(sys.argv[2]) args = parser.parse_args()
if len(sys.argv) < 4:
cp_first = args.first
if args.last is None:
cp_last = cp_first cp_last = cp_first
else: else:
cp_last = int(sys.argv[3]) cp_last = args.last
if cp_first < 0 or cp_last > CP_MAX or cp_first > cp_last: if cp_first < 0 or cp_last > CP_MAX or cp_first > cp_last:
print('invalid range') print('invalid range')
exit(1) exit(1)
ft = FontTool() ft = FontTool()
if sys.argv[1] == 'addbdf': if args.command == "addbdf":
if len(sys.argv) < 5: xoffs = args.xoffs
print_usage_and_exit() yoffs = args.yoffs
xoffs = 0 bdfr = BDFReader(args.bdffile, xoffs, yoffs)
yoffs = 0
if len(sys.argv) >= 6:
xoffs = int(sys.argv[5])
if len(sys.argv) >= 7:
yoffs = int(sys.argv[6])
bdfr = BDFReader(sys.argv[4], xoffs, yoffs)
for i in range(cp_first, cp_last + 1): for i in range(cp_first, cp_last + 1):
if bdfr.code_points[i] and not ft.code_points[i]: if bdfr.code_points[i] and not ft.code_points[i]:
ft.code_points[i] = bdfr.code_points[i] ft.code_points[i] = bdfr.code_points[i]
ft.commit() ft.commit()
elif sys.argv[1] == 'addraw': elif args.command == 'addraw':
if len(sys.argv) < 5: rr = RawReader(args.rawfile)
print_usage_and_exit()
rr = RawReader(sys.argv[4])
for i in range(cp_first, cp_last + 1): for i in range(cp_first, cp_last + 1):
if rr.code_points[i] and not ft.code_points[i]: if rr.code_points[i] and not ft.code_points[i]:
ft.code_points[i] = rr.code_points[i] ft.code_points[i] = rr.code_points[i]
ft.commit() ft.commit()
elif sys.argv[1] == 'remove': elif args.command == 'remove':
for i in range(cp_first, cp_last + 1): for i in range(cp_first, cp_last + 1):
ft.code_points[i] = False ft.code_points[i] = False
ft.commit() ft.commit()
elif sys.argv[1] == 'inspect': elif args.command == 'inspect':
lut = [ ' ', '░░', '▒▒', '▓▓' ] lut = [' ', '░░', '▒▒', '▓▓']
for i in range(cp_first, cp_last + 1): for i in range(cp_first, cp_last + 1):
if ft.code_points[i]: if ft.code_points[i]:
print('code point %i (%c)' % ( i, i )) print('code point %i (%c)' % (i, i))
print('') print('')
print('\n'.join([ ''.join([ lut[ch] for ch in row ]) for row in FontTool.unpack(ft.code_points[i]) ])) print('\n'.join([''.join([lut[ch] for ch in row]) for row in FontTool.unpack(ft.code_points[i])]))
print('') print('')
else: else:
print('code point %i (%c) is not available' % ( i, i )) print('code point %i (%c) is not available' % (i, i))
else:
print_usage_and_exit()