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 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
|
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")
|
||||||
|
|
||||||
|
@ -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()
|
@ -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
|
||||||
|
|
||||||
|
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říliš žluťoučký kůň
|
||||||
úpěl ďábelské ódy.
|
úpěl ďábelské ódy.
|
||||||
|
|
||||||
$FG$♠ ♣ $MAGENTA$♥ ♦
|
$FG$♠ $MAGENTA$♥ $FG$♣ $MAGENTA$♦
|
||||||
|
Loading…
Reference in New Issue
Block a user