added rounding to 1/8th of a dollar for price
This commit is contained in:
parent
ff248a9b68
commit
6efed3be72
2 changed files with 274 additions and 2 deletions
267
glyphs.py
Normal file
267
glyphs.py
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
"""Custom fraction glyphs for the TM-T88V receipt printer.
|
||||
|
||||
Each glyph is a 12-wide x 24-tall pixel bitmap defined as a list of 24 strings.
|
||||
'#' = pixel on, '.' = pixel off.
|
||||
|
||||
To customize a glyph, edit its pattern below. Each row must be exactly 12
|
||||
characters wide and there must be exactly 24 rows per glyph.
|
||||
"""
|
||||
|
||||
# Map each fraction to a character code on the printer.
|
||||
# Must be in 0x20-0x7E range 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",
|
||||
}
|
||||
|
||||
# fmt: off
|
||||
GLYPHS = {
|
||||
"1/8": [
|
||||
"............", # 0
|
||||
".........#..", # 1 ── numerator "1"
|
||||
"........##..", # 2
|
||||
".........#..", # 3
|
||||
".........#..", # 4
|
||||
".........#..", # 5
|
||||
".........#..", # 6
|
||||
".........#..", # 7
|
||||
"........#...", # 8 ── slash
|
||||
".......#....", # 9
|
||||
"......#.....", # 10
|
||||
".....#......", # 11
|
||||
"....#.......", # 12
|
||||
"...#........", # 13
|
||||
"..#.........", # 14
|
||||
".#..........", # 15
|
||||
"..##........", # 16 ── denominator "8"
|
||||
".#..#.......", # 17
|
||||
".#..#.......", # 18
|
||||
"..##........", # 19
|
||||
".#..#.......", # 20
|
||||
".#..#.......", # 21
|
||||
"..##........", # 22
|
||||
"............", # 23
|
||||
],
|
||||
"1/4": [
|
||||
"............", # 0
|
||||
".........#..", # 1 ── "1"
|
||||
"........##..", # 2
|
||||
".........#..", # 3
|
||||
".........#..", # 4
|
||||
".........#..", # 5
|
||||
".........#..", # 6
|
||||
".........#..", # 7
|
||||
"........#...", # 8 ── slash
|
||||
".......#....", # 9
|
||||
"......#.....", # 10
|
||||
".....#......", # 11
|
||||
"....#.......", # 12
|
||||
"...#........", # 13
|
||||
"..#.........", # 14
|
||||
".#..........", # 15
|
||||
"....#.......", # 16 ── "4"
|
||||
"...##.......", # 17
|
||||
"..#.#.......", # 18
|
||||
".#..#.......", # 19
|
||||
".####.......", # 20
|
||||
"....#.......", # 21
|
||||
"....#.......", # 22
|
||||
"............", # 23
|
||||
],
|
||||
"3/8": [
|
||||
"............", # 0
|
||||
"........##..", # 1 ── "3"
|
||||
".......#..#.", # 2
|
||||
"..........#.", # 3
|
||||
"........##..", # 4
|
||||
"..........#.", # 5
|
||||
".......#..#.", # 6
|
||||
"........##..", # 7
|
||||
"........#...", # 8 ── slash
|
||||
".......#....", # 9
|
||||
"......#.....", # 10
|
||||
".....#......", # 11
|
||||
"....#.......", # 12
|
||||
"...#........", # 13
|
||||
"..#.........", # 14
|
||||
".#..........", # 15
|
||||
"..##........", # 16 ── "8"
|
||||
".#..#.......", # 17
|
||||
".#..#.......", # 18
|
||||
"..##........", # 19
|
||||
".#..#.......", # 20
|
||||
".#..#.......", # 21
|
||||
"..##........", # 22
|
||||
"............", # 23
|
||||
],
|
||||
"1/2": [
|
||||
"............", # 0
|
||||
".........#..", # 1 ── "1"
|
||||
"........##..", # 2
|
||||
".........#..", # 3
|
||||
".........#..", # 4
|
||||
".........#..", # 5
|
||||
".........#..", # 6
|
||||
".........#..", # 7
|
||||
"........#...", # 8 ── slash
|
||||
".......#....", # 9
|
||||
"......#.....", # 10
|
||||
".....#......", # 11
|
||||
"....#.......", # 12
|
||||
"...#........", # 13
|
||||
"..#.........", # 14
|
||||
".#..........", # 15
|
||||
"..##........", # 16 ── "2"
|
||||
".#..#.......", # 17
|
||||
"....#.......", # 18
|
||||
"...#........", # 19
|
||||
"..#.........", # 20
|
||||
".#..........", # 21
|
||||
".####.......", # 22
|
||||
"............", # 23
|
||||
],
|
||||
"5/8": [
|
||||
"............", # 0
|
||||
".......####.", # 1 ── "5"
|
||||
".......#....", # 2
|
||||
".......###..", # 3
|
||||
"..........#.", # 4
|
||||
"..........#.", # 5
|
||||
".......#..#.", # 6
|
||||
"........##..", # 7
|
||||
"........#...", # 8 ── slash
|
||||
".......#....", # 9
|
||||
"......#.....", # 10
|
||||
".....#......", # 11
|
||||
"....#.......", # 12
|
||||
"...#........", # 13
|
||||
"..#.........", # 14
|
||||
".#..........", # 15
|
||||
"..##........", # 16 ── "8"
|
||||
".#..#.......", # 17
|
||||
".#..#.......", # 18
|
||||
"..##........", # 19
|
||||
".#..#.......", # 20
|
||||
".#..#.......", # 21
|
||||
"..##........", # 22
|
||||
"............", # 23
|
||||
],
|
||||
"3/4": [
|
||||
"............", # 0
|
||||
"........##..", # 1 ── "3"
|
||||
".......#..#.", # 2
|
||||
"..........#.", # 3
|
||||
"........##..", # 4
|
||||
"..........#.", # 5
|
||||
".......#..#.", # 6
|
||||
"........##..", # 7
|
||||
"........#...", # 8 ── slash
|
||||
".......#....", # 9
|
||||
"......#.....", # 10
|
||||
".....#......", # 11
|
||||
"....#.......", # 12
|
||||
"...#........", # 13
|
||||
"..#.........", # 14
|
||||
".#..........", # 15
|
||||
"....#.......", # 16 ── "4"
|
||||
"...##.......", # 17
|
||||
"..#.#.......", # 18
|
||||
".#..#.......", # 19
|
||||
".####.......", # 20
|
||||
"....#.......", # 21
|
||||
"....#.......", # 22
|
||||
"............", # 23
|
||||
],
|
||||
"7/8": [
|
||||
"............", # 0
|
||||
".......####.", # 1 ── "7"
|
||||
"..........#.", # 2
|
||||
".........#..", # 3
|
||||
".........#..", # 4
|
||||
"........#...", # 5
|
||||
"........#...", # 6
|
||||
"........#...", # 7
|
||||
"........#...", # 8 ── slash
|
||||
".......#....", # 9
|
||||
"......#.....", # 10
|
||||
".....#......", # 11
|
||||
"....#.......", # 12
|
||||
"...#........", # 13
|
||||
"..#.........", # 14
|
||||
".#..........", # 15
|
||||
"..##........", # 16 ── "8"
|
||||
".#..#.......", # 17
|
||||
".#..#.......", # 18
|
||||
"..##........", # 19
|
||||
".#..#.......", # 20
|
||||
".#..#.......", # 21
|
||||
"..##........", # 22
|
||||
"............", # 23
|
||||
],
|
||||
}
|
||||
# fmt: on
|
||||
|
||||
|
||||
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 "57a" where 'a' 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]
|
||||
|
|
@ -14,6 +14,8 @@ import sys
|
|||
|
||||
from escpos.printer import Dummy
|
||||
|
||||
from glyphs import price_to_fraction, upload_fractions
|
||||
|
||||
# TM-T88V with 90° rotation: Font A chars are 24 dots wide (instead of 12),
|
||||
# so 512 printable dots / 24 = 21 chars fit across the 80mm paper width.
|
||||
LINE_WIDTH = 21
|
||||
|
|
@ -21,10 +23,11 @@ LINE_WIDTH = 21
|
|||
|
||||
def print_trade(p, symbol, lots, price):
|
||||
"""Print a single trade as ticker tape characters, one per row."""
|
||||
frac = price_to_fraction(price)
|
||||
if lots > 1:
|
||||
price_str = f"{lots}s{price:.2f}"
|
||||
price_str = f"{lots}s{frac}"
|
||||
else:
|
||||
price_str = f"{price:.2f}"
|
||||
price_str = frac
|
||||
|
||||
# Symbol characters at the top (right-aligned)
|
||||
for ch in symbol:
|
||||
|
|
@ -49,6 +52,8 @@ def main():
|
|||
# ESC 3 n: set line spacing to n/180 inch. Font A chars are 12 dots wide,
|
||||
# so n=12 makes rotated characters sit flush with no gap.
|
||||
p._raw(b"\x1b\x33\x0c")
|
||||
# Upload custom fraction glyphs (⅛ ¼ ⅜ ½ ⅝ ¾ ⅞)
|
||||
upload_fractions(p)
|
||||
|
||||
with open(csv_file) as f:
|
||||
reader = csv.reader(f)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue