feat: initial commit — local services (backend + manager dashboard + waiter PWA)
Includes all work to date: - local_backend: FastAPI backend with products, orders, tables, shifts, cloud sync - manager_dashboard: React manager UI with product/category management, reports, settings - waiter_pwa: React PWA for waiter devices - Category reparent endpoint and UI - Waiter domain: local_ip sent on heartbeat, waiter_domain persisted from cloud response - QR code modal in AppInfoTab for waiter domain - Product form: number input spinners removed, category pre-selected on new product - Category row: count badge moved to far right Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
137
local_backend/print_size_test.py
Normal file
137
local_backend/print_size_test.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
Font size comparison test — Jolimark TP850UE
|
||||
Usage: python print_size_test.py [IP] [PORT]
|
||||
Default: 10.98.20.25:9100
|
||||
|
||||
Prints a single page showing all available size options side by side,
|
||||
to help decide which sizes to expose in the settings UI.
|
||||
|
||||
Hardware facts:
|
||||
ESC ! (0x1B 0x21 n):
|
||||
0x10 = double-height only (tall + narrow — breaks aspect ratio)
|
||||
0x20 = double-width only (short + wide — breaks aspect ratio)
|
||||
0x30 = double-height + double-width (2x in both axes — correct aspect ratio)
|
||||
There is NO 1.5x in ESC/POS hardware.
|
||||
GS ! (0x1D 0x21 n) can go 3x, 4x … 8x but they are extremely large.
|
||||
"""
|
||||
import sys
|
||||
|
||||
PRINTER_IP = sys.argv[1] if len(sys.argv) > 1 else "10.98.20.25"
|
||||
PRINTER_PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 9100
|
||||
|
||||
try:
|
||||
from escpos.printer import Network
|
||||
except ImportError:
|
||||
print("escpos not installed. Run: pip install python-escpos")
|
||||
sys.exit(1)
|
||||
|
||||
def gr(text):
|
||||
return text.encode('cp737', errors='replace')
|
||||
|
||||
def raw(p, b):
|
||||
p._raw(b)
|
||||
|
||||
def section(p, title):
|
||||
raw(p, b'\x1b\x21\x00')
|
||||
raw(p, b'\x1b\x45\x00')
|
||||
raw(p, b'\x1b\x61\x01')
|
||||
p._raw(gr(f"--- {title} ---\n"))
|
||||
raw(p, b'\x1b\x61\x00')
|
||||
|
||||
def print_sample(p, esc_bang, gs_size, label_en, label_gr):
|
||||
"""Print one size sample with label."""
|
||||
# Label at normal size
|
||||
raw(p, b'\x1b\x21\x00')
|
||||
raw(p, b'\x1b\x45\x00')
|
||||
p._raw(gr(f"{label_en}:\n"))
|
||||
|
||||
# Apply size via ESC ! and/or GS !
|
||||
if gs_size is not None:
|
||||
raw(p, bytes([0x1d, 0x21, gs_size]))
|
||||
raw(p, bytes([0x1b, 0x21, esc_bang]))
|
||||
|
||||
p._raw(gr(f"Club Sandwich. x1\n"))
|
||||
p._raw(gr(f"* Χωρις αλατι\n"))
|
||||
p._raw(gr(f"+ Extra Bacon x2\n"))
|
||||
|
||||
# Reset
|
||||
raw(p, b'\x1d\x21\x00')
|
||||
raw(p, b'\x1b\x21\x00')
|
||||
raw(p, b'\n')
|
||||
|
||||
def divider(p):
|
||||
raw(p, b'\x1b\x21\x00')
|
||||
p._raw(gr("-" * 48 + "\n"))
|
||||
|
||||
print(f"Connecting to {PRINTER_IP}:{PRINTER_PORT}...")
|
||||
p = Network(PRINTER_IP, PRINTER_PORT, timeout=10)
|
||||
raw(p, b'\x1b\x40') # ESC @ reset
|
||||
raw(p, b'\x1b\x74\x1d') # CP737 Greek
|
||||
|
||||
raw(p, b'\x1b\x61\x01')
|
||||
raw(p, b'\x1b\x21\x30')
|
||||
raw(p, b'\x1b\x45\x01')
|
||||
p._raw(gr("SIZE COMPARISON TEST\n"))
|
||||
raw(p, b'\x1b\x21\x00')
|
||||
raw(p, b'\x1b\x45\x00')
|
||||
raw(p, b'\x1b\x61\x00')
|
||||
p._raw(gr("Which sizes look good for ticket printing?\n\n"))
|
||||
|
||||
# ── Section 1: The two aspect-ratio-correct options ───────────────────────
|
||||
section(p, "CORRECT ASPECT RATIO")
|
||||
p._raw(gr("\n"))
|
||||
|
||||
print_sample(p,
|
||||
esc_bang=0x00, gs_size=None,
|
||||
label_en="[1] SMALL (1x1 — normal)",
|
||||
label_gr="")
|
||||
|
||||
print_sample(p,
|
||||
esc_bang=0x30, gs_size=None,
|
||||
label_en="[2] LARGE (2x2 — double height+width)",
|
||||
label_gr="")
|
||||
|
||||
# ── Section 2: The broken single-axis options (for comparison) ────────────
|
||||
divider(p)
|
||||
section(p, "BROKEN ASPECT RATIO (for comparison)")
|
||||
p._raw(gr("These scale only ONE axis — shown so\nyou can confirm they look wrong.\n\n"))
|
||||
|
||||
print_sample(p,
|
||||
esc_bang=0x10, gs_size=None,
|
||||
label_en="[3] Tall only (2x height, 1x width)",
|
||||
label_gr="")
|
||||
|
||||
print_sample(p,
|
||||
esc_bang=0x20, gs_size=None,
|
||||
label_en="[4] Wide only (1x height, 2x width)",
|
||||
label_gr="")
|
||||
|
||||
# ── Section 3: GS ! options — 3x and beyond ──────────────────────────────
|
||||
divider(p)
|
||||
section(p, "GS! LARGER SIZES (3x3, 4x4)")
|
||||
p._raw(gr("These are technically available but\nvery large. Shown for completeness.\n\n"))
|
||||
|
||||
print_sample(p,
|
||||
esc_bang=0x00, gs_size=0x22,
|
||||
label_en="[5] GS! 3x3",
|
||||
label_gr="")
|
||||
|
||||
print_sample(p,
|
||||
esc_bang=0x00, gs_size=0x33,
|
||||
label_en="[6] GS! 4x4",
|
||||
label_gr="")
|
||||
|
||||
# ── Conclusion ────────────────────────────────────────────────────────────
|
||||
divider(p)
|
||||
raw(p, b'\x1b\x61\x01')
|
||||
raw(p, b'\x1b\x21\x00')
|
||||
p._raw(gr("CONCLUSION:\n"))
|
||||
p._raw(gr("[1] Small = use for modifiers/notes\n"))
|
||||
p._raw(gr("[2] Large = use for item names/headers\n"))
|
||||
p._raw(gr("No true 1.5x exists in hardware.\n"))
|
||||
p._raw(gr("GS! 3x3/4x4 available if desired.\n"))
|
||||
|
||||
raw(p, b'\n\n\n')
|
||||
p.cut()
|
||||
p.close()
|
||||
print("Done.")
|
||||
Reference in New Issue
Block a user