forked from vycdev/thermal-printer-scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbarcode_print.py
More file actions
96 lines (80 loc) · 3.83 KB
/
Copy pathbarcode_print.py
File metadata and controls
96 lines (80 loc) · 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/env python3
"""Print a scannable Code 39 barcode on the S01 thermal printer.
Code 39 encodes digits, uppercase letters, and a few symbols (- . space $ / + %).
Real barcode-scanner apps can read these. Handy for labelling cables, boxes,
shelves, or anything you want to track.
Examples:
python barcode_print.py --text "PRT-0042"
python barcode_print.py --text "SHELF A3"
python barcode_print.py # random sample code
python barcode_print.py --no-print
"""
from __future__ import annotations
import argparse
from pathlib import Path
import random
import string
from PIL import Image, ImageDraw
from print_common import ROOT, Card, font
# Code 39: 9 elements per char (bar/space alternating, starting bar); n=narrow, w=wide
CODE39 = {
"0": "nnnwwnwnn", "1": "wnnwnnnnw", "2": "nnwwnnnnw", "3": "wnwwnnnnn",
"4": "nnnwwnnnw", "5": "wnnwwnnnn", "6": "nnwwwnnnn", "7": "nnnwnnwnw",
"8": "wnnwnnwnn", "9": "nnwwnnwnn", "A": "wnnnnwnnw", "B": "nnwnnwnnw",
"C": "wnwnnwnnn", "D": "nnnnwwnnw", "E": "wnnnwwnnn", "F": "nnwnwwnnn",
"G": "nnnnnwwnw", "H": "wnnnnwwnn", "I": "nnwnnwwnn", "J": "nnnnwwwnn",
"K": "wnnnnnnww", "L": "nnwnnnnww", "M": "wnwnnnnwn", "N": "nnnnwnnww",
"O": "wnnnwnnwn", "P": "nnwnwnnwn", "Q": "nnnnnnwww", "R": "wnnnnnwwn",
"S": "nnwnnnwwn", "T": "nnnnwnwwn", "U": "wwnnnnnnw", "V": "nwwnnnnnw",
"W": "wwwnnnnnn", "X": "nwnnwnnnw", "Y": "wwnnwnnnn", "Z": "nwwnwnnnn",
"-": "nwnnnnwnw", ".": "wwnnnnwnn", " ": "nwwnnnwnn", "$": "nwnwnwnnn",
"/": "nwnwnnnwn", "+": "nwnnnwnwn", "%": "nnnwnwnwn", "*": "nwnnwnwnn",
}
def build_segments(text: str):
"""Return list of (is_bar, width_units) for *text* framed by start/stop '*'."""
chars = ["*"] + list(text) + ["*"]
segs = []
for ci, ch in enumerate(chars):
for ei, el in enumerate(CODE39[ch]):
segs.append((ei % 2 == 0, 3 if el == "w" else 1)) # even index = bar
if ci != len(chars) - 1:
segs.append((False, 1)) # narrow inter-character space
return segs
def render(text: str, target_w: int, height: int = 90) -> Image.Image:
segs = build_segments(text)
total = sum(w for _, w in segs)
quiet = 12 # quiet-zone units each side
u = max(1, target_w // (total + 2 * quiet))
bar_px = total * u
w = bar_px + 2 * quiet * u
img = Image.new("L", (w, height), 255)
d = ImageDraw.Draw(img)
x = quiet * u
for is_bar, width in segs:
if is_bar:
d.rectangle((x, 0, x + width * u - 1, height - 1), fill=0)
x += width * u
return img
def main() -> int:
parser = argparse.ArgumentParser(description="Print a scannable Code 39 barcode on the S01 thermal printer.")
parser.add_argument("--text", default=None, help="Text to encode (default: random sample code).")
parser.add_argument("--out", type=Path, default=ROOT / "barcode.png")
parser.add_argument("--darkness", type=int, choices=range(1, 6), default=3)
parser.add_argument("--bottom-feed", type=int, default=24)
parser.add_argument("--no-print", action="store_true")
args = parser.parse_args()
text = (args.text or "PRT-" + "".join(random.choice(string.digits) for _ in range(4))).upper()
bad = [c for c in text if c not in CODE39 or c == "*"]
if bad:
raise SystemExit(f"Code 39 can't encode: {''.join(sorted(set(bad)))}. "
"Allowed: 0-9 A-Z space - . $ / + %")
print(f"Encoding Code 39: {text}")
card = Card()
card.title("BARCODE")
card.line("Code 39", size=12, bold=False, center=True)
card.gap(8).image(render(text, card.inner_w), border=False)
card.gap(4).line(text, size=18, bold=True, center=True)
card.footer("code 39", "scan me")
return card.finish(args.out, args.bottom_feed, args.darkness, do_print=not args.no_print)
if __name__ == "__main__":
raise SystemExit(main())