Skip to content

I wrote a library to convert to BDF #6

@ctrlcctrlv

Description

@ctrlcctrlv

Is this of interest? If so I can publish it

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()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions