from collections.abc import Generator, Iterable
from copy import deepcopy
from more_itertools import chunked
from bitarray import bitarray
from bdflib.model import Font as BDFLibFont
from ibm437 import to_ibm437
import bdflib.writer
import bdflib.xlfd
import os.path
class Font:
def __init__(
self,
filename: str,
strict=True,
bits_per_scanline_byte: int = 8,
bytes_per_scanline: int = 1,
):
self.filename = filename
self.strict = strict
self.bpsb = bits_per_scanline_byte
self.bpsl = bytes_per_scanline
self.populate_filename_info()
self.fontbuf = bitarray()
with open(filename, "rb") as f:
self.fontbuf.fromfile(f)
self.populate_glyphs()
def populate_filename_info(self) -> int:
temp = self.filename.split(".F")
assert len(temp) == 2, "Bad filename!"
fontname, self.scanlines_per = temp
assert all(
c.isdigit() for c in self.scanlines_per
), "Non-digits in scanline specifier!"
self.scanlines_per = int(self.scanlines_per)
assert (
0 < self.scanlines_per <= 32
), f"Scanline count {self.scanlines_per} not between 0…32!"
assert self.bpsl in (1, 2), f"Only 1 or 2 bytes per scanline allowed!"
self.family = fontname.split(os.path.sep)[-1]
return self.scanlines_per
def _glyphs_gen(self) -> Generator[Generator[list[int]]]:
return chunked(
list(chunked(list(self.fontbuf), self.bpsb * self.bpsl, strict=True)),
self.scanlines_per,
strict=False,
)
def populate_glyphs(self) -> None:
self.glyphs = list()
for glyph in self._glyphs_gen():
self.glyphs.append(glyph)
@staticmethod
def expand_glyph(
glyph: Iterable[Iterable[list[int]]], boxdrawing: bool = True
) -> Generator[list[str]]:
if boxdrawing:
chars = " █"
else:
chars = "01"
def expand_line(line: list[int]) -> str:
return "".join([chars[1] if i else chars[0] for i in line])
for l in glyph:
yield expand_line(l)
def expanded_glyphs(self, **kwargs) -> Generator[str]:
for g in self.glyphs:
yield "\n".join(Font.expand_glyph(g, **kwargs))
def pretty(self) -> str:
return "\n\n".join(
[
"Glyph 0x{i:X} ({c}):\n\n{g}".format(c=to_ibm437(i), i=i, g=g)
for i, g in enumerate(self.expanded_glyphs())
]
)
class BDF(BDFLibFont):
@staticmethod
def _convert_glyph_data(glyph):
return list(reversed([int("".join([str(bb) for bb in b]), 2) for b in glyph]))
def __init__(self, font: Font):
self.name = font.family.replace("&", "").encode("ascii")
self.ptSize = font.scanlines_per
self.xdpi = self.ydpi = 100
super().__init__(self.name, self.ptSize, self.xdpi, self.ydpi)
for i, glyph in enumerate(font.glyphs):
cp = ord(to_ibm437(i))
glyph_data = dict(
codepoint=cp, bbW=font.bpsb, bbH=font.scanlines_per, advance=font.bpsb
)
gname = "uni{:04X}".format(cp).encode("ascii")
gdata = BDF._convert_glyph_data(glyph)
self.new_glyph_from_data(gname, gdata, **glyph_data)
def main():
import sys
_, FONT = sys.argv
assert ".F" in FONT
parsed = Font(FONT)
# print(parsed.pretty())
bdf = BDF(parsed)
bdflib.xlfd.fix(bdf)
bdf.properties[bdflib.xlfd.CHARSET_REGISTRY] = b"Unicode"
bdflib.writer.write_bdf(bdf, sys.stdout.buffer)
if __name__ == "__main__":
main()