Data Files

This commit is contained in:
khodowany
2026-09-16 22:54:03 -05:00
parent 01c6d4e786
commit a0eb8ba957
113 changed files with 54866 additions and 23 deletions
+192
View File
@@ -0,0 +1,192 @@
#!/usr/bin/env python3
import argparse
import difflib
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, features
REPO = Path(__file__).resolve().parents[1]
FONT_PATH = REPO / "package/assets/fonts/petaluma/PetalumaScript.otf"
FONT_SIZE = 32
FONT_HEIGHT = 36
FONT_MAX_WIDTH = 28
FIRST_CODEPOINT = 32
LAST_CODEPOINT = 126
def cpp_char_literal(value: str) -> str:
if value == "'":
return "'\\''"
if value == "\\":
return "'\\\\'"
return f"'{value}'"
def render_glyph(font: ImageFont.FreeTypeFont, value: str, y_offset: int) -> tuple[list[int], int, int]:
image = Image.new("L", (FONT_MAX_WIDTH, FONT_HEIGHT), 0)
draw = ImageDraw.Draw(image)
bbox = font.getbbox(value)
x = max(0, -bbox[0])
draw.text((x, y_offset), value, font=font, fill=255)
pixels = [min(15, max(0, round(pixel / 17))) for pixel in image.get_flattened_data()]
visible = image.getbbox()
width = 0 if visible is None else min(FONT_MAX_WIDTH, visible[2])
advance = min(255, max(0, round(font.getlength(value))))
return pixels, width, advance
def render_alpha_rows(values: list[int]) -> list[str]:
lines = []
for row in range(FONT_HEIGHT):
start = row * FONT_MAX_WIDTH
row_values = values[start: start + FONT_MAX_WIDTH]
lines.append(" " + ", ".join(f"0x{value:02x}" for value in row_values) + ",")
return lines
def pair_adjustments(font: ImageFont.FreeTypeFont) -> list[tuple[int, int]]:
chars = [chr(value) for value in range(FIRST_CODEPOINT, LAST_CODEPOINT + 1)]
adjustments = []
for left in chars:
left_width = font.getlength(left)
for right in chars:
adjustment = round(font.getlength(left + right) - left_width - font.getlength(right))
if adjustment == 0:
continue
key = (ord(left) << 8) | ord(right)
adjustments.append((key, int(adjustment)))
return sorted(adjustments)
def generate() -> str:
if not features.check_feature("raqm"):
raise RuntimeError("Font generation requires Pillow with Raqm support; install libfribidi0 on Debian/Ubuntu.")
font = ImageFont.truetype(str(FONT_PATH), FONT_SIZE, layout_engine=ImageFont.Layout.RAQM)
min_top = min(font.getbbox(chr(value))[1] for value in range(FIRST_CODEPOINT, LAST_CODEPOINT + 1))
y_offset = max(0, -min_top)
adjustments = pair_adjustments(font)
lines = [
"#include <vamp_style/font.hpp>",
"",
"#include <algorithm>",
"#include <array>",
"#include <cstdint>",
"#include <string_view>",
"",
"namespace {",
"",
"struct pair_adjustment {",
" std::uint16_t key{};",
" std::int8_t adjustment{};",
"};",
"",
f"constexpr auto pair_adjustments = std::array<pair_adjustment, {len(adjustments)}>{{{{",
]
for key, adjustment in adjustments:
lines.append(f" {{0x{key:04x}, {adjustment}}},")
lines.extend(
[
"}};",
"",
"auto pair_key(std::char_t left, std::char_t right) -> std::uint16_t {",
" auto l = static_cast<std::uint16_t>(static_cast<std::uint8_t>(left));",
" auto r = static_cast<std::uint16_t>(static_cast<std::uint8_t>(right));",
" return static_cast<std::uint16_t>((l << 8U) | r);",
"}",
"",
"auto empty_glyph() -> vamp_style::font_glyph {",
" return {",
" .alpha = {},",
" .width = 0,",
" .advance = 8,",
" };",
"}",
"",
"}",
"",
"auto vamp_style::ui_font_glyph(std::char_t value) -> vamp_style::font_glyph {",
" switch (value) {",
]
)
for codepoint in range(FIRST_CODEPOINT, LAST_CODEPOINT + 1):
value = chr(codepoint)
alpha, width, advance = render_glyph(font, value, y_offset)
lines.extend(
[
f" case {cpp_char_literal(value)}:",
" return {",
" .alpha = {",
]
)
lines.extend(render_alpha_rows(alpha))
lines.extend(
[
" },",
f" .width = {width},",
f" .advance = {advance},",
" };",
]
)
lines.extend(
[
" default:",
" return empty_glyph();",
" }",
"}",
"",
"auto vamp_style::ui_font_pair_adjust(std::char_t left, std::char_t right) -> std::int32_t {",
" auto key = pair_key(left, right);",
" auto found = std::lower_bound(pair_adjustments.begin(), pair_adjustments.end(), key, [](pair_adjustment const& item, std::uint16_t value) {",
" return item.key < value;",
" });",
" if (found == pair_adjustments.end() || found->key != key) {",
" return 0;",
" }",
" return found->adjustment;",
"}",
"",
"auto vamp_style::ui_text_width(std::string_view text, std::int32_t scale) -> std::int32_t {",
" if (text.empty() || scale <= 0) {",
" return 0;",
" }",
" auto width = std::int32_t{};",
" auto previous = std::char_t{};",
" for (auto ch : text) {",
" if (previous != std::char_t{}) {",
" width += vamp_style::ui_font_pair_adjust(previous, ch) * scale;",
" }",
" width += static_cast<std::int32_t>(vamp_style::ui_font_glyph(ch).advance) * scale;",
" previous = ch;",
" }",
" return std::max(std::int32_t{}, width);",
"}",
"",
]
)
return "\n".join(lines)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--check", action="store_true")
args = parser.parse_args()
generated = generate()
if args.check:
if not args.output.is_file():
parser.error(f"generated font is missing: {args.output}")
current = args.output.read_text(encoding="utf-8")
if current == generated:
print("pico font is up to date")
return 0
diff = difflib.unified_diff(
current.splitlines(),
generated.splitlines(),
fromfile=str(args.output),
tofile=f"{args.output} (generated)",
lineterm="",
)
print("\n".join(diff))
return 1
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(generated, encoding="utf-8")
print(f"generated {args.output}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+4
View File
@@ -0,0 +1,4 @@
Pillow==12.3.0
defusedxml==0.7.1
fonttools==4.65.0
aggdraw==1.4.1