Skip to main content

Cross-platform font discovery and resolution library for Python

Project description

JustMyType

CI codecov

A precise, lightweight, and extensible font discovery library for Python. JustMyType provides a robust "Font Atlas" for the Python ecosystem—a definitive map of every font available to an application, whether installed on the system or bundled in a Python package.

Features

  • Cross-platform: Unified API across macOS, Linux, and Windows
  • Extensible: Font packs can be added via standard Python EntryPoints mechanism
  • Efficient: Lazy discovery with in-memory caching
  • Flexible: Supports font matching by family, weight, style, and width with intelligent fallback
  • W3C Compliant: Implements CSS Fonts Level 4 matching algorithm for browser-like behavior
  • Precise: Uses fonttools to parse binary font tables—never guesses from filenames

Installation

pip install justmytype

For Pillow support (optional, for loading fonts):

pip install justmytype[pillow]

Quick Start

from justmytype import FontRegistry, get_default_registry

# Get default registry
registry = get_default_registry()

# Find a font (lazy discovery happens automatically)
font_info = registry.find_font("Arial", weight=700, style="normal")
if font_info:
    # Load as PIL ImageFont (requires Pillow)
    font = font_info.load(size=16)

    # Or use the path directly
    print(f"Found font at: {font_info.path}")

Basic Usage

Finding Fonts

from justmytype import FontRegistry

registry = FontRegistry()

# Find by family name
font_info = registry.find_font("Roboto", weight=400)

# Find with style
font_info = registry.find_font("Open Sans", weight=700, style="italic")

# Find with width
font_info = registry.find_font("Arial", width="condensed")

# List all available families
for family in registry.list_families():
    print(family)

Loading Fonts with Pillow

from justmytype import get_default_registry
from PIL import Image, ImageDraw, ImageFont

registry = get_default_registry()
font_info = registry.find_font("Arial", weight=700)

if font_info:
    # Load as PIL ImageFont
    font = font_info.load(size=24)

    # Use with PIL
    img = Image.new("RGB", (200, 100), "white")
    draw = ImageDraw.Draw(img)
    draw.text((10, 10), "Hello", font=font, fill="black")
    img.save("output.png")

Blocking Font Packs

# Block system fonts
registry = FontRegistry(blocklist={"system-fonts"})

# Block via environment variable
# FONT_DISCOVERY_BLOCKLIST="system-fonts,broken-pack" python app.py

Command-Line Interface

JustMyType includes a CLI for discovering and inspecting fonts from the command line.

Find vs Info

  • find — Resolves a single font (best match for family/weight/style/width). Use it when you need a path or a quick answer: one font file, with pack and license when available.
  • info — Rich view of a font family: pack metadata (version, description, licenses) and all variants. Use it when you want to see where the family comes from and what styles exist.

Family names

Matching is by exact family name (case-insensitive). Use the name as it appears in the font, not a shortened or partial form. For example, use "Noto Sans" (from justmytype list), not "noto" or "notosans".

List All Fonts

# List families with pack and license (family | pack | license)
justmytype list

# Sort by number of variants
justmytype list --sort count

# Output as JSON (includes family_details with pack and licenses)
justmytype list --json

Find a Font

# Resolve one font by family name; shows path, pack, license
justmytype find "Roboto"
justmytype find "Noto Sans"

# Find with specific weight and style
justmytype find "Roboto" --weight 400 --style normal
justmytype find "Inter" --weight 700 --style italic

# Output as JSON
justmytype find "Roboto" --json

Show Font Information

# Rich family view: pack metadata and default variant
justmytype info "Roboto"
justmytype info "Noto Sans"

# Show all variants of the font
justmytype info "Roboto" --all-variants

# Output as JSON
justmytype info "Roboto" --json

List Font Packs

# List registered font packs (names only)
justmytype packs

# Show description, version, and licenses
justmytype packs --verbose

# Output as JSON (includes manifest when present)
justmytype packs --json

Blocking Font Packs (CLI)

# Block specific font packs
justmytype list --blocklist "system-fonts"
justmytype find "Roboto" --blocklist "system-fonts,broken-pack"

Creating Font Packs

Creating a font pack involves: (1) layout — organizing font files in a directory (or directories) the pack will expose, and optionally adding pack_manifest.json; (2) building (optional) — using tools such as pack-tools to fetch and assemble families from upstream; (3) registering — exposing the pack via the justmytype.packs entry point so applications or third-party packages can provide fonts.

Pack layout and file organization

The entry point factory returns one or more directory paths (font directories). The registry scans each font directory for font files (e.g. .ttf, .otf).

  • Where to put files: Put font files and optionally pack_manifest.json in the directory (or directories) you return. Two common layouts: (a) a Python package that is the font directory (e.g. myapp.fontsmyapp/fonts/); or (b) a package with a fonts/ subdirectory (e.g. my_pack/fonts/). Ensure your package build (e.g. pyproject.toml / hatchling) includes those files in the wheel.
  • Optional manifest: If present, pack_manifest.json in a font directory is read by JustMyType for pack metadata (e.g. justmytype packs --verbose). Not required for discovery.

Building packs with pack-tools

For a standard workflow to build packs from selected families (e.g. from Google Fonts), use justmytype-pack-tools from the justmytype-essentials repo. It lets you configure families and upstream in upstream.toml, fetch families from the upstream repo into a cache, build into the pack's font directory (with license resolution and pack_manifest.json), and optionally run manifest only when fonts are already in place. Usable from the essentials mono-repo or standalone; see the pack-tools README for usage and upstream.toml schema.

First-Party Font Pack (Application's Own Fonts)

Register a first-party font pack so your application's bundled fonts are discovered. The factory must return the font directory (or directories) that contain the font files.

# myapp/fonts.py
from pathlib import Path
from importlib.resources import files

def get_font_directories():
    """Entry point factory: returns font directory paths for this pack."""
    package = files("myapp.fonts")
    return [Path(str(package))]
# pyproject.toml
[project.entry-points."justmytype.packs"]
"myapp-fonts" = "myapp.fonts:get_font_directories"

Third-Party Font Pack

Register a third-party font pack so the package can provide fonts to any application using JustMyType. The factory must return the font directory (or directories) that contain the font files.

# my_font_pack/__init__.py
from pathlib import Path
from importlib.resources import files

def get_font_directories():
    """Entry point factory: returns font directory paths for this font pack."""
    package = files("my_font_pack.fonts")
    return [Path(str(package))]

Architecture

JustMyType follows a unified "Font Pack" architecture where all font sources (system fonts, bundled fonts, third-party fonts) implement the same FontPack protocol. This ensures:

  • Consistency: All fonts are discovered and resolved the same way
  • Extensibility: New font sources can be added via EntryPoints
  • Priority: Font packs (priority 100) override system fonts (priority 0)

See docs/architecture.md for detailed architecture documentation.

Development

Setup

# Clone the repository
git clone https://github.com/yourusername/justmytype.git
cd justmytype

# Install with development dependencies
pip install -e ".[pillow]"
pip install -e ".[dev]"  # If dev dependencies are configured

Running Tests

# Run tests with coverage
pytest

# Run with coverage report
pytest --cov=justmytype --cov-report=html

Code Quality

# Format code
ruff format .

# Lint code
ruff check .

# Type checking
mypy src/

Requirements

  • Python 3.10+
  • fonttools (required)
  • Pillow (optional, for FontInfo.load())

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please read the architecture documentation in docs/architecture.md and follow the project philosophy outlined in AGENTS.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

justmytype-0.4.0rc2.tar.gz (106.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

justmytype-0.4.0rc2-py3-none-any.whl (26.0 kB view details)

Uploaded Python 3

File details

Details for the file justmytype-0.4.0rc2.tar.gz.

File metadata

  • Download URL: justmytype-0.4.0rc2.tar.gz
  • Upload date:
  • Size: 106.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for justmytype-0.4.0rc2.tar.gz
Algorithm Hash digest
SHA256 6fcc8eed85d4b525c121a53a45438c8dee5c119ed8ef2f75f6d3884db14fee4c
MD5 a3e6a951de6f3d3ab92a0bbb43f1a45b
BLAKE2b-256 9fa028a160eace05c9e249ff0c5c34721397f9df888d5dbf0d6036a44be0143c

See more details on using hashes here.

File details

Details for the file justmytype-0.4.0rc2-py3-none-any.whl.

File metadata

  • Download URL: justmytype-0.4.0rc2-py3-none-any.whl
  • Upload date:
  • Size: 26.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for justmytype-0.4.0rc2-py3-none-any.whl
Algorithm Hash digest
SHA256 b09922f93ab35c044dab603792aac3fd407d945c1e034c9e7dc02fbcca3bb003
MD5 44214e6e5b6e328c3bc03e6e7a1421aa
BLAKE2b-256 5e52e195054feddcd08957045d0899ce6db169681c541240f619b8effa01e9ac

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page