mobitec-tools/extract-fonts.py

123 lines
3.9 KiB
Python

import json
import cv2
import numpy as np
"""
dd if=mobitec-1682-r7-bib-01979-r3.bin of=mobitec-1682-r7-bib-01979-r3-fonts.bin skip=64 bs=1024
dd if=mobitec-01682-r7-bib-01369-r9.bin of=mobitec-01682-r7-bib-01369-r9-fonts.bin skip=64 bs=1024
dhex mobitec-1682-r7-bib-01979-r3-fonts.bin mobitec-01682-r7-bib-01369-r9-fonts.bin
"""
src = "mobitec-1682-r7-bib-01979-r3"
src = "mobitec-01682-r7-bib-01369-r9"
with open("firmware/%s.bin" % src, "rb") as fh:
buf = fh.read()[65536:]
def scan_fonts(buf):
"""
Font start markers seem to be ascii character a..z followed by at least 60 nulls
"""
font_index = buf[0]
assert font_index == 0x61
prev = 0
done = False
while not done:
needle = b"%c" % (font_index + 1)
needle += 50 * b"\x00"
try:
font_offset = buf.index(needle)
except ValueError:
break
font_body = buf[prev:]
done = True
else:
font_body = buf[prev:font_offset]
if done:
break
font_marker_length = len(needle)
font_body = font_body[font_marker_length:]
while font_body.startswith(b"\x00"):
font_body = font_body[1:]
font_marker_length += 1
yield font_index, font_body, font_marker_length
prev = font_offset
font_index += 1
meta = []
for font_index, font_body, font_marker_length in scan_fonts(buf):
print()
print("Parsing font %02x" % font_index)
# Looks like yet another marker
assert font_body[0] == 3
assert font_body[1] == 1
# Look up table consists of 95 16-bit integers
glyph_offsets = np.frombuffer(font_body[2:192], dtype=np.uint16)
prev = 0
slices = []
errors = set()
for index, glyph_offset in enumerate(glyph_offsets):
if glyph_offset != 0:
glyph_offset -= font_marker_length
print("Found %d undecoded bytes between lookup table and first glyph" % len(font_body[192:192 + glyph_offset]))
break
characters_present = ""
for index, glyph_offset in enumerate(glyph_offsets):
if glyph_offset == 0:
characters_present += "_"
# No glyph
meta.append(None)
continue
else:
# The index goes from space (32) up to ~ (126)
character = chr(index + 32)
characters_present += "+"
# Glyphs are relative to the font marker start
glyph_offset -= font_marker_length
try:
height, width = font_body[glyph_offset], font_body[glyph_offset + 1]
except IndexError:
print("Failed to look up glyph for %s", repr(character))
else:
jah = np.frombuffer((font_body[glyph_offset + 2:])[::-1], dtype=np.uint8)[::-1]
a = np.unpackbits(jah, axis=0)
try:
img = a[:width * 16].reshape((width, 16, 1))
img = np.hstack([img[:, 8:, :], img[:, :8, :]])
meta.append({
"width": width,
"height": height,
"bitmap": img.tolist()
})
img = 255 * img
slices.append(img)
except ValueError:
pass
else:
blank_image = np.zeros((1, 16, 1), np.uint8)
blank_image[::] = 0
slices.append(blank_image)
if glyph_offset <= prev:
print("Glyph offsets not ordered")
prev = glyph_offset
print("Glyphs present:", characters_present)
img = np.vstack(slices)
img = np.flip(np.swapaxes(img, 1, 0), 0)
s = img.shape * np.array(5)
img = cv2.resize(img, dsize=(s[1], s[0]), interpolation=cv2.INTER_NEAREST)
cv2.imwrite("samples/%s-%02x.png" % (src, font_index), img)
with open("samples/%s-%02x.json" % (src, font_index), "w") as fh:
fh.write(json.dumps({"glyphs": meta}))