Merge pull request #1755 from martinling/font-edit
Add script to extract font data to an image file for editing.
This commit is contained in:
commit
49bce311e2
1 changed files with 107 additions and 0 deletions
107
firmware/tools/edit-font.py
Normal file
107
firmware/tools/edit-font.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# Tool to extract font data, save to bitmap file, and apply modifications
|
||||
# to source code after editing the bitmap.
|
||||
|
||||
from __future__ import print_function
|
||||
from matplotlib.pyplot import imread, imsave, imshow, show
|
||||
import numpy as np
|
||||
import sys
|
||||
import re
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage: %s <font name> { show | extract | insert}" % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
font_name = sys.argv[1]
|
||||
mode = sys.argv[2]
|
||||
|
||||
# Where to find the font data - may need updated if code has changed.
|
||||
source_file = 'firmware/common/ui_portapack.c'
|
||||
data_marker = f'static const uint8_t {font_name}_glyph_data[] = {{'
|
||||
data_offset = 1 # Data starts this number of lines after start marker.
|
||||
end_pattern = '(.*)};$' # Indicates end of font data.
|
||||
def_marker = f'static const ui_font_t {font_name} ='
|
||||
def_offset = 1
|
||||
def_pattern = '^\t{{(?P<width>\\d+), (?P<height>\\d+)}'
|
||||
|
||||
# Image filename used in extract and insert modes.
|
||||
image_file = f'{font_name}.png'
|
||||
|
||||
# Get the literals used to populate the font array in the source file
|
||||
lines = [line.rstrip() for line in open(source_file).readlines()]
|
||||
try:
|
||||
start = lines.index(data_marker) + data_offset
|
||||
except ValueError:
|
||||
print(f"Could not find glyph data for font '{font_name}'")
|
||||
sys.exit(1)
|
||||
|
||||
data = str()
|
||||
for line in lines[start:]:
|
||||
if match := re.match(end_pattern, line):
|
||||
data += match.group(1)
|
||||
break
|
||||
else:
|
||||
data += line
|
||||
|
||||
# Get the width and height from the font definition.
|
||||
try:
|
||||
def_start = lines.index(def_marker)
|
||||
except ValueError:
|
||||
print(f"Could not find definition for font '{font_name}'")
|
||||
sys.exit(1)
|
||||
|
||||
def_match = re.match(def_pattern, lines[def_start + def_offset])
|
||||
|
||||
if def_match is None:
|
||||
print(f"Failed to parse definition of '{font_name}'")
|
||||
|
||||
width = int(def_match.group('width'))
|
||||
height = int(def_match.group('height'))
|
||||
|
||||
# Eval the literals to get the values into a numpy array
|
||||
packed = eval("np.array([%s], np.uint8)" % data)
|
||||
|
||||
# Reorganise into a monochrome image.
|
||||
chars_per_row = 5
|
||||
unpacked = np.unpackbits(packed)
|
||||
bitmaps = (unpacked
|
||||
.reshape(-1, height, width // 8, 8)[:,:,::-1,:]
|
||||
.reshape(-1, height, width)[:,:,::-1]
|
||||
)
|
||||
indices = np.arange(len(bitmaps)).reshape(-1, chars_per_row)
|
||||
image = np.block([[bitmaps[idx] for idx in row] for row in indices])
|
||||
|
||||
if mode == 'show':
|
||||
# Display font image
|
||||
imshow(image, cmap='binary')
|
||||
show()
|
||||
|
||||
elif mode == 'extract':
|
||||
# Save font image
|
||||
imsave(image_file, image, format='png', cmap='binary')
|
||||
|
||||
elif mode == 'insert':
|
||||
# Read in modified font image
|
||||
image = imread(image_file)[:,:,0].astype(bool)
|
||||
|
||||
# Reorganise back to original order
|
||||
rows = image.reshape(-1, height, image.shape[1])
|
||||
single_row_image = np.hstack(rows)
|
||||
num_chars = single_row_image.shape[1] // width
|
||||
bitmaps = np.array(np.split(single_row_image, num_chars, 1))
|
||||
unpacked = (bitmaps
|
||||
[:,:,::-1].reshape(-1, height, width // 8, 8)
|
||||
[:,:,::-1,:].reshape(-1)
|
||||
)
|
||||
packed = ~np.packbits(unpacked)
|
||||
|
||||
# Replace lines of file in same format as used before
|
||||
bytes_per_row = 13
|
||||
split_indices = np.arange(bytes_per_row, len(packed), bytes_per_row)
|
||||
for i, group in enumerate(np.split(packed, split_indices)):
|
||||
line = ("\t0x%02x," + " 0x%02x," * (len(group) - 1)) % tuple(group)
|
||||
if len(group) < bytes_per_row:
|
||||
line = line[:-1] + "};"
|
||||
lines[start + i] = line
|
||||
|
||||
# Write out modified source file
|
||||
open(source_file, 'w').writelines([line + "\n" for line in lines])
|
||||
Loading…
Add table
Add a link
Reference in a new issue