"""Custom fraction glyphs for the TM-T88V receipt printer. Fractions are composed from small digit patterns stacked vertically: numerator on top, denominator on bottom — matching the old ticker tape style. To customize: - Edit DIGIT_PATTERNS to change how a digit looks in ALL fractions using it. - Or override a specific fraction in GLYPH_OVERRIDES with a full 12x24 grid. - '#' = pixel on, '.' = pixel off - Each digit pattern is 12 wide x 11 tall. - Each full glyph is 12 wide x 24 tall (11 + 2 gap + 11). """ # Character codes used on the printer for each fraction. # Must be 0x20-0x7E and not conflict with output chars (A-Z, 0-9, '.', 's', ' ') CHAR_MAP = { "1/8": "a", "1/4": "b", "3/8": "c", "1/2": "d", "5/8": "e", "3/4": "f", "7/8": "g", } # Map number of eighths (1-7) to fraction name EIGHTHS_TO_NAME = { 1: "1/8", 2: "1/4", 3: "3/8", 4: "1/2", 5: "5/8", 6: "3/4", 7: "7/8", } # ── Digit patterns (12 wide x 11 tall) ─────────────────────────────────────── # These are composed into the fraction glyphs below. # fmt: off DIGIT_PATTERNS = { 1: [ ".....##.....", "....###.....", "...####.....", ".....##.....", ".....##.....", ".....##.....", ".....##.....", ".....##.....", ".....##.....", ".....##.....", "...######...", ], 2: [ "..########..", ".##......##.", "..........##", "..........##", "........##..", "......##....", "....##......", "..##........", ".##.........", ".##.........", "############", ], 3: [ "..########..", ".##......##.", "..........##", "..........##", ".....####...", "..........##", "..........##", "..........##", ".##......##.", ".##......##.", "..########..", ], 4: [ ".......##...", "......###...", ".....####...", "....##.##...", "...##..##...", "..##...##...", ".##....##...", "############", ".......##...", ".......##...", ".......##...", ], 5: [ "############", "##..........", "##..........", "##..........", "##########..", "..........##", "..........##", "..........##", "..........##", ".##......##.", "..########..", ], 7: [ "############", "##........##", "..........##", ".........##.", "........##..", ".......##...", "......##....", ".....##.....", "....##......", "....##......", "....##......", ], 8: [ "..########..", ".##......##.", ".##......##.", ".##......##.", "..########..", ".##......##.", ".##......##.", ".##......##.", ".##......##.", ".##......##.", "..########..", ], } # fmt: on def _compose_fraction(num, den): """Build a 12x24 glyph: numerator digit stacked over denominator digit. Layout (24 rows): rows 0-10: numerator digit (11 rows) rows 11-12: gap (2 rows) rows 13-23: denominator digit (11 rows) """ blank = "." * 12 rows = list(DIGIT_PATTERNS[num]) rows.extend([blank] * 2) rows.extend(DIGIT_PATTERNS[den]) return rows # ── Composed glyphs (auto-generated from digit patterns) ──────────────────── GLYPHS = { "1/8": _compose_fraction(1, 8), "1/4": _compose_fraction(1, 4), "3/8": _compose_fraction(3, 8), "1/2": _compose_fraction(1, 2), "5/8": _compose_fraction(5, 8), "3/4": _compose_fraction(3, 4), "7/8": _compose_fraction(7, 8), } # ── Per-fraction overrides ────────────────────────────────────────────────── # To hand-craft a specific fraction, add it here as a 24-row list of 12-char # strings. It will replace the auto-composed version above. # # GLYPH_OVERRIDES = { # "1/2": [ # "............", # ... # 24 rows total # ], # } GLYPH_OVERRIDES = {} # Apply overrides GLYPHS.update(GLYPH_OVERRIDES) # ── ESC/POS conversion & upload ───────────────────────────────────────────── def glyph_to_escpos(rows): """Convert a visual glyph (24 rows of 12-char strings) to ESC/POS column data. ESC/POS user-defined chars are column-major: each column is 3 bytes (24 vertical pixels), MSB = topmost pixel. """ data = bytearray() for col in range(12): for byte_idx in range(3): val = 0 for bit in range(8): row = byte_idx * 8 + bit if rows[row][col] == "#": val |= 0x80 >> bit data.append(val) return bytes(data) def upload_fractions(printer): """Upload all custom fraction glyphs to the printer via ESC &.""" # ESC % 1: enable user-defined character set printer._raw(b"\x1b\x25\x01") for name, char in CHAR_MAP.items(): rows = GLYPHS[name] col_data = glyph_to_escpos(rows) code = ord(char) # ESC & 3 c1 c2 12 [column data] # 3 = bytes per column (24 dots), 12 = character width printer._raw(b"\x1b\x26\x03" + bytes([code, code, 12]) + col_data) def price_to_fraction(price): """Convert a decimal price to dollar amount + fraction character. Rounds to the nearest eighth of a dollar, like old ticker tapes. Returns e.g. "246" or "57d" where 'd' is the custom ½ glyph. """ dollars = int(price) eighths = round((price - dollars) * 8) if eighths == 8: dollars += 1 eighths = 0 if eighths == 0: return str(dollars) name = EIGHTHS_TO_NAME[eighths] return str(dollars) + CHAR_MAP[name]