Source code for dartwork_mpl.font

"""Font management utilities for Matplotlib.

Registers custom fonts from the package's asset/font directory with
matplotlib's internal font manager.
"""

import threading
import warnings
from pathlib import Path

from matplotlib import font_manager

__all__ = ["ensure_loaded"]

# Rough sanity floor for the bundled font set. We expect at least a
# handful of files spanning Roboto + Paperlogy + NotoSansCJK +
# NotoSansMath cores. Falling below this hints that the install is
# missing assets — likely a slim install or accidental deletion.
_EXPECTED_MIN_FONTS: int = 5


def _add_fonts() -> None:
    """Register bundled custom fonts with matplotlib's font manager.

    Scans the ``asset/font`` directory for font files and registers them
    with matplotlib's font manager so they can be used in charts. Emits
    a :class:`UserWarning` when the bundle looks emptied so that the
    Korean/CJK fallback chain degradation is visible to users.

    Notes
    -----
    This function is called automatically once when the library is
    imported; users do not need to call it directly.
    """
    font_dir: list[Path] = [Path(__file__).parent / "asset/font"]
    found = font_manager.findSystemFonts(font_dir)
    for font in found:
        font_manager.fontManager.addfont(font)

    # Graceful warning if the bundle looks emptied. This catches
    # accidental asset deletion and any future slim-install variant
    # (e.g. a [fonts] extra) that shipped without the bundled corpus.
    if len(found) < _EXPECTED_MIN_FONTS:
        warnings.warn(
            f"dartwork-mpl found only {len(found)} bundled font file(s) "
            f"in {font_dir[0]}. The Korean/CJK fallback chain may "
            f"degrade to system fonts. Reinstall the package to "
            f"restore the bundled assets.",
            UserWarning,
            stacklevel=3,
        )


_loaded: bool = False
_lock: threading.Lock = threading.Lock()


[docs] def ensure_loaded() -> None: """Ensure custom fonts are loaded and registered. Thread-safe: uses double-checked locking to avoid duplicate font registration when called concurrently from multiple threads. """ global _loaded # Fast path: skip lock once already loaded. if _loaded: return with _lock: if _loaded: return _add_fonts() _loaded = True