mirror of
https://github.com/slavfox/Cozette.git
synced 2024-10-04 04:17:08 +03:00
start working on vectorization
This commit is contained in:
parent
821aa08664
commit
2969e98636
47
build.py
47
build.py
@ -1,55 +1,22 @@
|
||||
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 tę łó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
|
||||
|
||||
bdfpath = REPO_ROOT / "Cozette8" / "Cozette-10.bdf"
|
||||
|
||||
def save_images():
|
||||
bdfpath = REPO_ROOT / "Cozette8" / "Cozette-10.bdf"
|
||||
|
||||
save_charlist(bdfpath, REPO_ROOT / "img" / "characters.png")
|
||||
|
||||
save_sample(
|
||||
"Clozette",
|
||||
Sample(SAMPLE_TEXT, 30, 32),
|
||||
read_sample(REPO_ROOT / "img" / "sample.txt"),
|
||||
REPO_ROOT / "img" / "sample.png",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
save_images()
|
||||
# save_images()
|
||||
TTFBuilder.from_bdf_path(bdfpath).build("cloze.ttf")
|
||||
|
||||
|
@ -9,7 +9,8 @@ from typing import (
|
||||
TextIO,
|
||||
Tuple,
|
||||
)
|
||||
import numpy as np
|
||||
import numpy as np # type: ignore
|
||||
from fontTools.pens.ttGlyphPen import TTGlyphPen
|
||||
|
||||
Bit = Union[Literal[1], Literal[0]]
|
||||
|
||||
@ -29,7 +30,7 @@ class BdfGlyph:
|
||||
self.bits: np.array = np.pad(bits, [[0, 0], [bbx.x, 0]])[
|
||||
bbx.y : bbx.y + bbx.h, bbx.x : bbx.x + bbx.w
|
||||
]
|
||||
self.meta = meta
|
||||
self.metadata = meta
|
||||
|
||||
@classmethod
|
||||
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
|
||||
)
|
||||
|
||||
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 = {}
|
||||
while not (line := bdfstream.readline()).startswith("BITMAP"):
|
||||
parts = line.split(maxsplit=1)
|
||||
@ -64,15 +81,19 @@ def parse_char(bdfstream: TextIO):
|
||||
|
||||
|
||||
class BdfFont:
|
||||
def __init__(self, metadata: List[str], glyphs: Dict[int, BdfGlyph]):
|
||||
self.metadata: List[str] = metadata
|
||||
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 = []
|
||||
metadata = {}
|
||||
while not (line := bdfstream.readline()).startswith("CHARS "):
|
||||
metadata.append(line)
|
||||
try:
|
||||
meta, val = line.split(maxsplit=1)
|
||||
metadata[meta] = val
|
||||
except ValueError:
|
||||
pass
|
||||
glyphs = {}
|
||||
for i in range(int(line.split()[1])):
|
||||
meta, char = parse_char(bdfstream)
|
||||
@ -86,3 +107,6 @@ class BdfFont:
|
||||
@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('"').strip().split()
|
@ -3,7 +3,7 @@ from typing import Tuple, Dict, Optional, NamedTuple
|
||||
from shlex import quote
|
||||
from pathlib import Path
|
||||
from unicodedata import east_asian_width as charwidth
|
||||
from cozette_builder.bdf.bdffont import BdfFont
|
||||
from cozette_builder.bdffont import BdfFont
|
||||
import platform
|
||||
import tempfile
|
||||
|
||||
|
69
cozette_builder/ttfbuilder.py
Normal file
69
cozette_builder/ttfbuilder.py
Normal 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))
|
||||
|
||||
|
||||
|
@ -31,4 +31,4 @@ $BLUE$┏━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Příliš žluťoučký kůň
|
||||
úpěl ďábelské ódy.
|
||||
|
||||
$FG$♠ ♣ $MAGENTA$♥ ♦
|
||||
$FG$♠ $MAGENTA$♥ $FG$♣ $MAGENTA$♦
|
||||
|
Loading…
Reference in New Issue
Block a user