"""Command-line interface for the GoojPrt PT-210 SDK.
Entry point: ``python -m goojprt <address> [flags]``. Flags are kept
stable with the historic standalone ``goojprt.py`` script so existing
shell snippets keep working.
"""
import argparse
import asyncio
from goojprt.enums import Align, CodePage, TextSize
from goojprt.printer import GoojPrtPT210
from goojprt.template import print_template
async def _demo_ble(address: str) -> None:
"""Self-contained BLE demo: text, QR, PDF417, diacritics (bitmap)."""
printer = GoojPrtPT210()
print(f"Connecting to {address} ...")
await printer.connect_ble(address)
print("Connected.")
await printer.initialize()
await printer.print_text("=== GoojPrt PT-210 ===", align=Align.CENTER, bold=True)
await printer.print_line()
await printer.print_text("Normal text")
await printer.print_text("Bold text", bold=True)
await printer.print_text("Large text", size=TextSize.DOUBLE_BOTH)
await printer.print_line()
await printer.print_text("QR code:", align=Align.CENTER)
await printer.print_qr("https://goojprt.com", size=5)
await printer.print_line()
await printer.print_text("PDF417:", align=Align.CENTER)
await printer.print_pdf417("GoojPrt-PT210-TEST-123")
await printer.print_line()
await printer.set_charset(CodePage.PC852)
await printer.print_text("CP852: Příliš žluťoučký kůň", encoding="cp852")
await printer.print_text_image(
"Příliš žluťoučký kůň\núpěl ďábelské ódy",
font_size=28, align=Align.LEFT,
)
await printer.feed(4)
await printer.disconnect()
print("Done, disconnected.")
def _demo_spp(address: str) -> None:
"""Self-contained SPP demo (Linux only)."""
printer = GoojPrtPT210()
print(f"Connecting (SPP) to {address} ...")
printer.connect_spp(address)
print("Connected.")
printer.initialize_spp()
printer.print_text_spp("SPP print test", align=Align.CENTER, bold=True)
printer.feed_spp(4)
printer.disconnect_spp()
print("Done, disconnected.")
async def _run_test(address: str, test_string: str) -> None:
"""Probe the firmware's code-page table using :meth:`probe_charsets`."""
printer = GoojPrtPT210()
print(f"Connecting to {address} ...")
await printer.connect_ble(address)
print("Connected. Running code-page probe...\n")
await printer.initialize()
await printer.probe_charsets(test_string)
await printer.disconnect()
print("Probe complete, disconnected.")
async def _run_test_cp1250(address: str, test_string: str) -> None:
"""Native ESC/POS print-quality test for the CP1250 code page.
Emits five variants of the input text (Font A/B, bold, raised heating
energy) so the paper output can be compared side by side.
"""
from goojprt import commands
def label(text: str) -> bytes:
"""Return an ASCII label used to separate variants on the paper."""
return f"--- {text} ---\n".encode("ascii", errors="replace")
printer = GoojPrtPT210()
print(f"Connecting to {address} ...")
await printer.connect_ble(address)
print("Connected. Running CP1250 quality test...\n")
encoded = test_string.encode("cp1250", errors="replace")
await printer.initialize()
await printer._ble.write(commands.charset(CodePage.WPC1250))
# 1) Normal
await printer._ble.write(label("1) Font A, normal"))
await printer._ble.write(commands.font(font_b=False))
await printer._ble.write(commands.bold(False))
await printer._ble.write(encoded + b"\n")
# 2) Bold
await printer._ble.write(label("2) Font A, bold"))
await printer._ble.write(commands.bold(True))
await printer._ble.write(encoded + b"\n")
await printer._ble.write(commands.bold(False))
# 3) Font B
await printer._ble.write(label("3) Font B, normal"))
await printer._ble.write(commands.font(font_b=True))
await printer._ble.write(encoded + b"\n")
await printer._ble.write(commands.font(font_b=False))
# 4) Font A with higher heating energy
await printer._ble.write(label("4) Font A + energy 120"))
await printer._ble.write(commands.energy(dots=7, heating=120, interval=2))
await printer._ble.write(encoded + b"\n")
# 5) Bold with higher heating energy
await printer._ble.write(label("5) Bold + energy 120"))
await printer._ble.write(commands.bold(True))
await printer._ble.write(encoded + b"\n")
await printer._ble.write(commands.bold(False))
# Restore default energy and feed paper out.
await printer._ble.write(commands.energy(dots=7, heating=80, interval=2))
await printer.feed(4)
await printer.disconnect()
print("CP1250 test complete, disconnected.")
[docs]
def build_parser() -> argparse.ArgumentParser:
"""Build the ``argparse`` parser with every supported CLI flag."""
parser = argparse.ArgumentParser(
prog="goojprt",
description="GoojPrt PT-210 Bluetooth driver",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# BLE demo print
python3 -m goojprt <addr>
# Bitmap text with antialiasing
python3 -m goojprt <addr> --print-image "Příliš žluťoučký kůň"
python3 -m goojprt <addr> --print-image "Headline" --font-size 48 --no-dither
# PDF417 barcode
python3 -m goojprt <addr> --pdf417 "1234567890"
python3 -m goojprt <addr> --pdf417 "data" --pdf417-scale 3 --pdf417-columns 7
# Code-page probe
python3 -m goojprt <addr> --test
python3 -m goojprt <addr> --test --test-string "Custom text"
""",
)
parser.add_argument("address", help="Bluetooth address / UUID of the printer")
parser.add_argument(
"mode", nargs="?", default="ble", choices=["ble", "spp"],
help="Transport: ble (default) or spp",
)
g_img = parser.add_argument_group("Bitmap text printing (--print-image)")
g_img.add_argument("--print-image", metavar="TEXT",
help="Print TEXT as a bitmap image (supports diacritics)")
g_img.add_argument("--font-size", type=int, default=24, metavar="PT",
help="Font size in points (default: 24)")
g_img.add_argument("--font", metavar="PATH",
help="Path to a .ttf/.ttc file (default: system font)")
g_img.add_argument("--align", choices=["left", "center", "right"], default="left",
help="Text alignment (default: left)")
g_img.add_argument("--supersample", type=int, default=3, metavar="N",
help="Antialiasing oversampling factor 1–4 (default: 3)")
g_img.add_argument("--no-dither", action="store_true",
help="Disable Floyd–Steinberg dithering, use pure threshold")
g_img.add_argument("--threshold", type=int, default=140, metavar="0-255",
help="Threshold for --no-dither (default: 140)")
g_pdf = parser.add_argument_group("PDF417 barcode (--pdf417)")
g_pdf.add_argument("--pdf417", metavar="DATA",
help="Print a PDF417 barcode encoding DATA")
g_pdf.add_argument("--pdf417-scale", type=int, default=2, metavar="N",
help="Module width in pixels (default: 2)")
g_pdf.add_argument("--pdf417-row-height", type=int, default=5, metavar="N",
help="Row height in modules (default: 5)")
g_pdf.add_argument("--pdf417-min-rows", type=int, default=None, metavar="N",
help="Minimum row count (auto-decreases column count)")
g_pdf.add_argument("--pdf417-columns", type=int, default=5, metavar="N",
help="Data column count 1–30 (default: 5)")
g_tmpl = parser.add_argument_group("TOML template (--template)")
g_tmpl.add_argument("--template", metavar="FILE",
help="Path to a .toml template (see examples/)")
g_tmpl.add_argument("--var", action="append", metavar="KEY=VALUE", default=[],
help="Template variable (may be repeated)")
g_test = parser.add_argument_group("Code-page probe (--test)")
g_test.add_argument("--test", action="store_true",
help="Probe each code page and print a bitmap reference")
g_test.add_argument("--test-string",
default="Příliš žluťoučký kůň úpěl ďábelské ódy",
metavar="TEXT", help="Text used by --test")
g_test.add_argument("--test-cp1250", action="store_true",
help="CP1250 quality test: 5 variants (Font A/B, bold, energy)")
return parser
[docs]
def main() -> None:
"""Entry point. Parses ``argv`` and dispatches to the selected command."""
parser = build_parser()
args = parser.parse_args()
align_map = {"left": Align.LEFT, "center": Align.CENTER, "right": Align.RIGHT}
if args.template:
extra = {}
for pair in args.var:
if "=" in pair:
k, v = pair.split("=", 1)
extra[k.strip()] = v.strip()
asyncio.run(print_template(args.address, args.template, extra or None))
elif args.test:
asyncio.run(_run_test(args.address, args.test_string))
elif args.test_cp1250:
asyncio.run(_run_test_cp1250(args.address, args.test_string))
elif args.print_image:
async def _cmd() -> None:
"""Bitmap text print: connect, initialise, print, disconnect."""
printer = GoojPrtPT210()
await printer.connect_ble(args.address)
await printer.initialize()
await printer.print_text_image(
args.print_image,
font_size=args.font_size,
font_path=args.font,
align=align_map[args.align],
supersample=args.supersample,
dither=not args.no_dither,
threshold=args.threshold,
)
await printer.feed(3)
await printer.disconnect()
asyncio.run(_cmd())
elif args.pdf417:
async def _cmd() -> None:
"""PDF417 print: connect, initialise, print, disconnect."""
printer = GoojPrtPT210()
await printer.connect_ble(args.address)
await printer.initialize()
await printer.print_pdf417(
args.pdf417,
scale=args.pdf417_scale,
row_height=args.pdf417_row_height,
columns=args.pdf417_columns,
min_rows=args.pdf417_min_rows,
)
await printer.feed(3)
await printer.disconnect()
asyncio.run(_cmd())
elif args.mode == "ble":
asyncio.run(_demo_ble(args.address))
else:
_demo_spp(args.address)