#!/usr/bin/env python3
# hangulmath-metrics.py -- part of the hangulmath package
# Copyright (C) 2026 Inkyu Park <icpark00@gmail.com>
# Distributed under the LaTeX Project Public License 1.3c or later.
"""Generate a hangulmath metrics file for a given font.

Requires Python 3.10+ and fontTools (pip install fonttools).

Usage:
    python3 hangulmath-metrics.py "Nanum Gothic" -o nanum.def   # font name (fc-match)
    python3 hangulmath-metrics.py /path/to/font.otf -o myfont.def

The output (default: hangulmath-metrics.def) records, for each of the
24 jamo, the left/right ink bounds in em, plus a common baseline shift
and a recommended scale. Load it with
    \\usepackage[font={...}, metrics=myfont]{hangulmath}
"""
import argparse
import statistics
import subprocess
import sys
from pathlib import Path

from fontTools.pens.boundsPen import BoundsPen
from fontTools.ttLib import TTCollection, TTFont

CONSONANTS = [0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145,
              0x3147, 0x3148, 0x314A, 0x314B, 0x314C, 0x314D, 0x314E]
VOWELS = [0x314F, 0x3151, 0x3153, 0x3155, 0x3157,
          0x315B, 0x315C, 0x3160, 0x3161, 0x3163]
DOUBLE_CONSONANTS = [0x3132, 0x3138, 0x3143, 0x3146, 0x3149]
COMPOUND_VOWELS = [0x3150, 0x3152, 0x3154, 0x3156, 0x3158, 0x3159,
                   0x315A, 0x315D, 0x315E, 0x315F, 0x3162]

# Target height of a consonant, in em of the surrounding text font
# (Latin Modern: x-height 0.43em, cap height 0.68em).
TARGET_CONSONANT_HEIGHT = 0.60


def resolve(font: str) -> tuple[str, str | None]:
    """Return (file path, family name to pick inside a .ttc)."""
    if Path(font).exists():
        return font, None
    path = subprocess.run(["fc-match", "-f", "%{file}", font],
                          capture_output=True, text=True, check=True).stdout
    return path, font


def load(path: str, family: str | None) -> TTFont:
    if path.lower().endswith((".ttc", ".otc")):
        fonts = TTCollection(path).fonts
        for f in fonts:
            if family and f["name"].getDebugName(1) == family:
                return f
        return fonts[0]
    return TTFont(path)


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("font", help="font name or file path")
    ap.add_argument("-o", "--output", default="hangulmath-metrics.def")
    args = ap.parse_args()

    path, family = resolve(args.font)
    font = load(path, family)
    upm = font["head"].unitsPerEm
    cmap = font.getBestCmap()
    glyphs = font.getGlyphSet()

    bounds = {}
    for code in CONSONANTS + VOWELS + DOUBLE_CONSONANTS + COMPOUND_VOWELS:
        if code not in cmap:
            sys.exit(f"U+{code:04X} not in font {args.font}")
        pen = BoundsPen(glyphs)
        glyphs[cmap[code]].draw(pen)
        x0, y0, x1, y1 = (v / upm for v in pen.bounds)
        bounds[code] = (x0, y0, x1, y1, glyphs[cmap[code]].width / upm)

    shift = statistics.median(bounds[c][1] for c in CONSONANTS)
    height = statistics.median(bounds[c][3] - bounds[c][1] for c in CONSONANTS)
    scale = TARGET_CONSONANT_HEIGHT / height

    lines = [
        f"%% hangulmath metrics for: {args.font}",
        f"%% generated by hangulmath-metrics.py from {path}",
        f"\\def\\hm@baseshift{{{shift:.4f}}}",
        f"\\def\\hm@defaultscale{{{scale:.3f}}}",
    ]
    for code, (x0, _, x1, _, adv) in bounds.items():
        lines.append(f"\\hm@metric{{{code:04X}}}{{{x0:.4f}}}{{{adv - x1:.4f}}}"
                     f"% {chr(code)}")
    Path(args.output).write_text("\n".join(lines) + "\n\\endinput\n",
                                 encoding="utf-8")
    print(f"wrote {args.output}  (shift={shift:.3f}em, scale={scale:.3f})")


if __name__ == "__main__":
    main()
