226 lines
6.1 KiB
Python
226 lines
6.1 KiB
Python
"""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]
|