1#!/usr/bin/env -S uv run --quiet --with pillow --with nanoemoji --with fonttools --with brotli python 2"""Builds pixel-emoji.woff2: a few of SerenityOS's emoji as a small colour font. 3 4 build-pixel-emoji.py <serenity checkout>/Base/res/emoji pixel-emoji.woff2 5 6Each glyph is SerenityOS's own PNG, one SVG path per colour made of a rectangle for each run of equal pixels, put through 7nanoemoji exactly as linusg/serenityos-emoji-font does (glyf_colr_1). The set is GLYPHS below; 8a new codepoint is added here, to `PixelEmoji` in src/components.rs and to the `unicode-range` 9of the `Pixel Emoji` face in ui.css, and a test holds the last two together. 10""" 11import subprocess, sys, tempfile 12from pathlib import Path 13from PIL import Image 14from fontTools.ttLib import TTFont 15 16GLYPHS = ["25B6", "23F8", "23F3", "231B"] 17 18 19def svg(png: Path) -> str: 20 """One path per colour, a rectangle for each run of equal pixels in a row: the runs are one 21 shape, so no hairline shows where two of them meet.""" 22 im = Image.open(png).convert("RGBA") 23 paths: dict[str, list[str]] = {} 24 for y in range(im.height): 25 x = 0 26 while x < im.width: 27 p = im.getpixel((x, y)) 28 if p[3] == 0: 29 x += 1 30 continue 31 x2 = x 32 while x2 + 1 < im.width and im.getpixel((x2 + 1, y)) == p: 33 x2 += 1 34 paths.setdefault(f"#{p[0]:02x}{p[1]:02x}{p[2]:02x}", []).append(f"M{x} {y}h{x2 - x + 1}v1h-{x2 - x + 1}z") 35 x = x2 + 1 36 body = "".join(f'<path fill="{color}" d="{"".join(runs)}"/>' for color, runs in paths.items()) 37 return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {im.width} {im.height}" width="{im.width}" height="{im.height}">{body}</svg>' 38 39 40def main(emoji_dir: str, dest: str) -> None: 41 with tempfile.TemporaryDirectory() as tmp: 42 tmp = Path(tmp) 43 svgs = [] 44 for cp in GLYPHS: 45 path = tmp / f"{cp}.svg" 46 path.write_text(svg(Path(emoji_dir) / f"U+{cp}.png")) 47 svgs.append(str(path)) 48 subprocess.check_call( 49 ["nanoemoji", "--family", "Pixel Emoji", "--output_file", "PixelEmoji.ttf", "--color_format", "glyf_colr_1", *svgs], 50 cwd=tmp, 51 ) 52 font = TTFont(tmp / "build" / "PixelEmoji.ttf") 53 font.flavor = "woff2" 54 font.save(dest) 55 56 57if __name__ == "__main__": 58 main(sys.argv[1], sys.argv[2])