Source code for goojprt.encoding

"""Text encoding helpers and cross-platform font discovery.

This module is the bridge between Python ``str`` objects and the byte
streams that ESC/POS commands expect, plus a tiny utility for locating
a usable truetype font on the host system without relying on
system-specific APIs.

Exports:
    - ``CODEPAGE_TO_ENCODING``: map from :class:`~goojprt.enums.CodePage`
      to the name of the matching Python codec.
    - :func:`text_to_bytes`: encode a string with a graceful UTF-8 fallback.
    - :func:`find_system_font`: return the first existing font path from
      :data:`~goojprt.constants.FONT_CANDIDATES`.
"""

from pathlib import Path

from goojprt.constants import FONT_CANDIDATES
from goojprt.enums import CodePage


#: ESC/POS code page → Python codec name (as accepted by ``str.encode``).
CODEPAGE_TO_ENCODING: dict[int, str] = {
    CodePage.PC437:   "cp437",
    CodePage.PC850:   "cp850",
    CodePage.PC852:   "cp852",
    CodePage.WPC1250: "cp1250",
    CodePage.PC866:   "cp866",
    CodePage.PC858:   "cp858",
}


[docs] def text_to_bytes(text: str, encoding: str = "gb2312") -> bytes: """Encode ``text`` into bytes suitable for the printer's data stream. The PT-210 firmware usually expects GB2312/GBK (Chinese) for native text output, but plain ASCII also works with ``"ascii"`` or ``"utf-8"``. When the chosen codec cannot represent a character (``UnicodeEncodeError``) or is unknown to Python (``LookupError``) the function falls back to UTF-8 with ``errors="replace"`` so callers never receive an exception. :param text: Input string. :param encoding: Name of the Python codec to try first, e.g. ``"gb2312"``, ``"cp1250"`` or ``"ascii"``. :returns: Raw bytes for inclusion in an ESC/POS payload. """ try: return text.encode(encoding) except (UnicodeEncodeError, LookupError): return text.encode("utf-8", errors="replace")
[docs] def find_system_font() -> str | None: """Return the path to the first available fallback system font. Iterates :data:`~goojprt.constants.FONT_CANDIDATES` in order (macOS → Linux → Windows) and returns the first path that exists on disk. :returns: Absolute path to a usable ``.ttf``/``.ttc`` file, or ``None`` when no candidate exists (in which case callers should fall back to :func:`PIL.ImageFont.load_default`). """ for path in FONT_CANDIDATES: if Path(path).exists(): return path return None