mirror of
https://github.com/slavfox/Cozette.git
synced 2024-10-04 04:17:08 +03:00
126 lines
3.7 KiB
Python
126 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import (
|
|
Dict,
|
|
Iterable,
|
|
List,
|
|
Literal,
|
|
NamedTuple,
|
|
TextIO,
|
|
Tuple,
|
|
Union,
|
|
)
|
|
|
|
import numpy as np # type: ignore
|
|
from fontTools.pens.ttGlyphPen import TTGlyphPen # type: ignore
|
|
|
|
Bit = Union[Literal[1], Literal[0]]
|
|
|
|
|
|
class BBX(NamedTuple):
|
|
w: int
|
|
h: int
|
|
x: int
|
|
y: int
|
|
|
|
|
|
class BdfGlyph:
|
|
def __init__(self, bits: np.array, meta: Dict[str, str]):
|
|
|
|
self.bbx = bbx = BBX(*[int(s) for s in meta["BBX"].split()])
|
|
# this is probably wrong
|
|
self.bits: np.array = bits[0 : bbx.h, 0 : bbx.w]
|
|
self.metadata = meta
|
|
|
|
@classmethod
|
|
def from_str(cls, s: str, meta: Dict[str, str]) -> BdfGlyph:
|
|
return cls.from_iterable((int(l, 16) for l in s.splitlines()), meta)
|
|
|
|
@classmethod
|
|
def from_iterable(
|
|
cls, values: Iterable[int], meta: Dict[str, str]
|
|
) -> BdfGlyph:
|
|
return BdfGlyph(
|
|
np.unpackbits(
|
|
np.array([[v] for v in values], dtype=np.uint8), axis=1
|
|
),
|
|
meta=meta,
|
|
)
|
|
|
|
def __str__(self) -> str:
|
|
return "\n".join(
|
|
"".join("#" if ch else " " for ch in line) for line in self.bits
|
|
)
|
|
|
|
def draw(self, ppp: float):
|
|
pen = TTGlyphPen(None)
|
|
for y, row in enumerate(reversed(self.bits)):
|
|
for x, col in enumerate(row):
|
|
if col:
|
|
pen.moveTo(
|
|
((x + self.bbx.x) * ppp, (y + self.bbx.y) * ppp)
|
|
)
|
|
pen.lineTo(
|
|
((x + self.bbx.x + 1) * ppp, (y + self.bbx.y) * ppp)
|
|
)
|
|
pen.lineTo(
|
|
(
|
|
(x + self.bbx.x + 1) * ppp,
|
|
(y + self.bbx.y + 1) * ppp,
|
|
)
|
|
)
|
|
pen.lineTo(
|
|
((x + self.bbx.x) * ppp, (y + self.bbx.y + 1) * ppp)
|
|
)
|
|
pen.lineTo(
|
|
((x + self.bbx.x) * ppp, (y + self.bbx.y) * ppp)
|
|
)
|
|
pen.closePath()
|
|
return pen.glyph()
|
|
|
|
def meta(self, k) -> List[str]:
|
|
return self.metadata[k].strip('"').strip().split()
|
|
|
|
|
|
def parse_char(bdfstream: TextIO) -> Tuple[Dict[str, str], List[int]]:
|
|
specs = {}
|
|
while not (line := bdfstream.readline()).startswith("BITMAP"):
|
|
parts = line.split(maxsplit=1)
|
|
specs[parts[0]] = parts[1].strip()
|
|
bitmap = []
|
|
while not (line := bdfstream.readline()).startswith("ENDCHAR"):
|
|
bitmap.append(int(line.strip(), 16))
|
|
return specs, bitmap
|
|
|
|
|
|
class BdfFont:
|
|
def __init__(self, metadata: Dict[str, str], glyphs: Dict[int, BdfGlyph]):
|
|
self.metadata: Dict[str, str] = metadata
|
|
self.glyphs: Dict[int, BdfGlyph] = glyphs
|
|
|
|
@classmethod
|
|
def from_bdf(cls, bdfstream: TextIO):
|
|
metadata = {}
|
|
while not (line := bdfstream.readline()).startswith("CHARS "):
|
|
try:
|
|
k, val = line.split(maxsplit=1)
|
|
metadata[k] = val.strip()
|
|
except ValueError:
|
|
pass
|
|
glyphs = {}
|
|
for i in range(int(line.split()[1])):
|
|
meta, char = parse_char(bdfstream)
|
|
glyphs[int(meta["ENCODING"])] = BdfGlyph.from_iterable(char, meta)
|
|
return cls(metadata, glyphs)
|
|
|
|
@property
|
|
def codepoints(self) -> Tuple[int, ...]:
|
|
return tuple(self.glyphs.keys())
|
|
|
|
@property
|
|
def str_codepoints(self) -> Tuple[str, ...]:
|
|
return tuple(f"U+{i:X}" for i in self.glyphs)
|
|
|
|
def meta(self, k) -> List[str]:
|
|
return self.metadata[k].strip('"').strip().split()
|