Files
MeshTerm/meshterm/emulator/fetch.py
T
Jean-Pierre MartineauandClaude Opus 5.5 3e251a9707 Docstrings and comments of meshterm/emulator are in ASD-STE100
The same rewrite as for the core package. The physical screen of a
handheld is now the display, and the computer is the handheld. The
comment on FONT_ENV now says that it is the environment variable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 03:51:55 -04:00

71 lines
2.8 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Download the Terminus 12-pixel fonts that the emulator draws in: one time, only on request.
Terminus has the SIL Open Font License. Thus MeshTerm never ships it. This module
downloads the upstream release and checks that it is the correct archive. Then it
extracts ``ter-u12n.bdf``, ``ter-u12b.bdf``, and the licence next to them into the
directory that the emulator reads (:func:`~.font.font_dir`). ``meshterm emulate
--fetch-fonts`` runs it.
Python can fail to download the archive, for example when a certificate store rejects the
mirror. Then you can download the archive in a different way and give it with
``--archive``. In both cases, the module checks the archive against the same digest.
"""
from __future__ import annotations
import hashlib
import io
import tarfile
import urllib.request
from collections.abc import Callable
from pathlib import Path
from .font import BOLD_BDF, REGULAR_BDF, font_dir
URL = (
"https://sourceforge.net/projects/terminus-font/files/terminus-font-4.49/"
"terminus-font-4.49.1.tar.gz/download"
)
#: The SHA-256 of the release archive, so that a mirror cannot return a different file.
SHA256 = "d961c1b781627bf417f9b340693d64fc219e0113ad3a3af1a3424c7aa373ef79"
_ROOT = "terminus-font-4.49.1/"
_WANTED = (REGULAR_BDF, BOLD_BDF, "OFL.TXT")
def install_terminus(archive: Path | None = None, say: Callable[[str], None] = print) -> Path:
"""Download (or take) Terminus 4.49.1, check it, and install the fonts and the licence.
Args:
archive: The release archive, already downloaded. ``None`` downloads it.
say: Where the progress lines go.
Returns:
The directory that the fonts were installed into.
Raises:
ValueError: The archive is not the release (its digest is different), or a file
is missing from it.
OSError: The download or a write failed.
"""
target = font_dir()
if archive is not None:
say(f"installing Terminus 4.49.1 from {archive} into {target}")
data = archive.read_bytes()
else:
say(f"fetching Terminus 4.49.1 into {target}")
with urllib.request.urlopen(URL, timeout=60) as response: # noqa: S310 - fixed https URL
data = response.read()
digest = hashlib.sha256(data).hexdigest()
if digest != SHA256:
raise ValueError(f"refusing the archive: sha256 {digest} is not the release's {SHA256}")
target.mkdir(parents=True, exist_ok=True)
with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as tar:
for name in _WANTED:
member = tar.extractfile(_ROOT + name)
if member is None:
raise ValueError(f"the archive has no {name}")
(target / name).write_bytes(member.read())
say(f" {name}")
return target