start working on vectorization

This commit is contained in:
Slavfox 2020-02-09 01:23:17 +01:00
parent 821aa08664
commit 2969e98636
6 changed files with 109 additions and 49 deletions

View File

@ -1,55 +1,22 @@
from pathlib import Path from pathlib import Path
from cozette_builder.imagegen import save_sample, save_charlist, Sample from cozette_builder.imagegen import save_sample, save_charlist, read_sample
from cozette_builder.ttfbuilder import TTFBuilder
SAMPLE_TEXT = """
$BLUE$
$MAGENTA$$NC$ $FG$Cozette$NC$ $MAGENTA$$BLUE$
$WHITE$A B C D E F G H I J K L M $BLUE$
$WHITE$N O P Q R S T U V W X Y Z $BLUE$
1 2 3 4 5 6 7 8 9 0
$WHITE$a b c d e f g h i j k l m $BLUE$
$WHITE$n o p q r s t u v w x y z $BLUE$
$RED$() [] {} <> ? / \ : " ' ;$BLUE$ │
$RED$! @ # $ % ^ & * _ = + - |$BLUE$ │
, . ` ~$BLUE$
$YELLOW$
$BLUE$ $FG$
$BLUE$$FG$ $GREEN$ $YELLOW$ $MAGENTA$ $FG$ $BLUE$
$FG$ $BLUE$ $FG$$BLUE$
$FG$$BLUE$
$GREEN$Pchnąć w łódź jeża lub
ósm skrzyń fig.
Eĥoŝanĝo ĉiuĵaŭde.
Příliš žluťoučký kůň
úpěl ďábelské ódy.
$FG$ $MAGENTA$
"""
REPO_ROOT = Path(__file__).resolve().parent REPO_ROOT = Path(__file__).resolve().parent
bdfpath = REPO_ROOT / "Cozette8" / "Cozette-10.bdf"
def save_images(): def save_images():
bdfpath = REPO_ROOT / "Cozette8" / "Cozette-10.bdf"
save_charlist(bdfpath, REPO_ROOT / "img" / "characters.png") save_charlist(bdfpath, REPO_ROOT / "img" / "characters.png")
save_sample( save_sample(
"Clozette", "Clozette",
Sample(SAMPLE_TEXT, 30, 32), read_sample(REPO_ROOT / "img" / "sample.txt"),
REPO_ROOT / "img" / "sample.png", REPO_ROOT / "img" / "sample.png",
) )
if __name__ == "__main__": if __name__ == "__main__":
save_images() # save_images()
TTFBuilder.from_bdf_path(bdfpath).build("cloze.ttf")

View File

@ -9,7 +9,8 @@ from typing import (
TextIO, TextIO,
Tuple, Tuple,
) )
import numpy as np import numpy as np # type: ignore
from fontTools.pens.ttGlyphPen import TTGlyphPen
Bit = Union[Literal[1], Literal[0]] Bit = Union[Literal[1], Literal[0]]
@ -29,7 +30,7 @@ class BdfGlyph:
self.bits: np.array = np.pad(bits, [[0, 0], [bbx.x, 0]])[ self.bits: np.array = np.pad(bits, [[0, 0], [bbx.x, 0]])[
bbx.y : bbx.y + bbx.h, bbx.x : bbx.x + bbx.w bbx.y : bbx.y + bbx.h, bbx.x : bbx.x + bbx.w
] ]
self.meta = meta self.metadata = meta
@classmethod @classmethod
def from_str(cls, s: str, meta: Dict[str, str]) -> BdfGlyph: def from_str(cls, s: str, meta: Dict[str, str]) -> BdfGlyph:
@ -51,8 +52,24 @@ class BdfGlyph:
"".join("#" if ch else " " for ch in line) for line in self.bits "".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 reversed(enumerate(self.bits)):
for x, col in enumerate(row):
if col:
pen.moveTo((x * ppp, y * ppp))
pen.lineTo(((x + 1) * ppp, y * ppp))
pen.lineTo(((x + 1) * ppp, (y + 1) * ppp))
pen.lineTo((x * ppp, (y + 1) * ppp))
pen.lineTo((x * ppp, y * ppp))
pen.closePath()
return pen.glyph()
def parse_char(bdfstream: TextIO): def meta(self, k) -> List[str]:
return self.metadata[k].strip().strip('"').strip().split()
def parse_char(bdfstream: TextIO) -> Tuple[Dict[str, str], List[int]]:
specs = {} specs = {}
while not (line := bdfstream.readline()).startswith("BITMAP"): while not (line := bdfstream.readline()).startswith("BITMAP"):
parts = line.split(maxsplit=1) parts = line.split(maxsplit=1)
@ -64,15 +81,19 @@ def parse_char(bdfstream: TextIO):
class BdfFont: class BdfFont:
def __init__(self, metadata: List[str], glyphs: Dict[int, BdfGlyph]): def __init__(self, metadata: Dict[str, str], glyphs: Dict[int, BdfGlyph]):
self.metadata: List[str] = metadata self.metadata: Dict[str, str] = metadata
self.glyphs: Dict[int, BdfGlyph] = glyphs self.glyphs: Dict[int, BdfGlyph] = glyphs
@classmethod @classmethod
def from_bdf(cls, bdfstream: TextIO): def from_bdf(cls, bdfstream: TextIO):
metadata = [] metadata = {}
while not (line := bdfstream.readline()).startswith("CHARS "): while not (line := bdfstream.readline()).startswith("CHARS "):
metadata.append(line) try:
meta, val = line.split(maxsplit=1)
metadata[meta] = val
except ValueError:
pass
glyphs = {} glyphs = {}
for i in range(int(line.split()[1])): for i in range(int(line.split()[1])):
meta, char = parse_char(bdfstream) meta, char = parse_char(bdfstream)
@ -86,3 +107,6 @@ class BdfFont:
@property @property
def str_codepoints(self) -> Tuple[str, ...]: def str_codepoints(self) -> Tuple[str, ...]:
return tuple(f"U+{i:X}" for i in self.glyphs) return tuple(f"U+{i:X}" for i in self.glyphs)
def meta(self, k) -> List[str]:
return self.metadata[k].strip().strip('"').strip().split()

View File

@ -3,7 +3,7 @@ from typing import Tuple, Dict, Optional, NamedTuple
from shlex import quote from shlex import quote
from pathlib import Path from pathlib import Path
from unicodedata import east_asian_width as charwidth from unicodedata import east_asian_width as charwidth
from cozette_builder.bdf.bdffont import BdfFont from cozette_builder.bdffont import BdfFont
import platform import platform
import tempfile import tempfile

View File

@ -0,0 +1,69 @@
from pathlib import Path
from fontTools.fontBuilder import FontBuilder
from fontTools.pens.ttGlyphPen import TTGlyphPen
from cozette_builder.bdffont import BdfFont, BdfGlyph
class TTFBuilder:
@classmethod
def from_bdf_path(cls, path: Path):
with path.open() as f:
bdf = BdfFont.from_bdf(f)
return cls(bdf)
def __init__(self, bdf: BdfFont):
self.bdf = bdf
upm = 1000
self.fb = FontBuilder(unitsPerEm=upm)
self.ppp = upm / int(self.bdf.meta("SIZE")[0])
w, h, startx, starty = self.bdf.meta("FONTBOUNDINGBOX")
self.ascent = int(self.bdf.meta("FONT_ASCENT")[0])
self.descent = int(self.bdf.meta("FONT_DESCENT")[0])
self.w = int(w)
self.h = int(h)
self.startx = int(startx)
self.starty = int(starty)
def build(self, output_path: Path):
glyph_names = {
k: glyph.meta("STARTCHAR")[0] for k, glyph in
self.bdf.glyphs.items()
}
ascent = round(self.ascent * self.ppp)
descent = round(self.descent * self.ppp)
self.fb.setupGlyphOrder(list(glyph_names.values()))
self.fb.setupCharacterMap(glyph_names)
advance_widths = {
name: 700 for name in glyph_names.values()
}
# copied from fontbuilder for now
familyName = "HelloTestFont"
styleName = "TotallyNormal"
version = "0.1"
nameStrings = dict(
familyName=dict(en=familyName, nl="HalloTestFont"),
styleName=dict(en=styleName, nl="TotaalNormaal"),
uniqueFontIdentifier="fontBuilder: " + familyName + "." + styleName,
fullName=familyName + "-" + styleName,
psName=familyName + "-" + styleName,
version="Version " + version,
)
self.fb.setupGlyf(
{name: self.bdf.glyphs[k].draw(self.ppp) for k, name in
glyph_names.items()}
)
metrics = {}
glyphTable = self.fb.font["glyf"]
for name in glyph_names.values():
metrics[name] = (700, glyphTable[name].xMin)
self.fb.setupHorizontalMetrics(metrics)
self.fb.setupHorizontalHeader(ascent=ascent, descent=-descent)
self.fb.setupNameTable(nameStrings)
self.fb.setupOS2(sTypoAscender=ascent, usWinAscent=ascent,
usWinDescent=descent)
self.fb.setupPost()
self.fb.save(str(output_path))

View File

@ -31,4 +31,4 @@ $BLUE$┏━━━━━━━━━━━━━━━━━━━━━━━
Příliš žluťoučký kůň Příliš žluťoučký kůň
úpěl ďábelské ódy. úpěl ďábelské ódy.
$FG$♠ $MAGENTA$♥ ♦ $FG$♠ $MAGENTA$♥ $FG$♣ $MAGENTA$