149 lines
4.3 KiB
Python
149 lines
4.3 KiB
Python
import argparse
|
|
import asyncio
|
|
import os
|
|
import socket
|
|
import sys
|
|
import tempfile
|
|
|
|
import PIL.Image
|
|
import PIL.ImageDraw
|
|
import PIL.ImageFont
|
|
import qrcode
|
|
|
|
HERE = os.path.dirname(__file__)
|
|
font = PIL.ImageFont.truetype(os.path.join(HERE, 'inconsolata.ttf'), 20)
|
|
|
|
DEFAULT_SOCKET = '/tmp/catprint.s'
|
|
|
|
|
|
def text2img(text):
|
|
maxwidth=384
|
|
height=21
|
|
img = PIL.Image.new('1', (maxwidth, height))
|
|
d = PIL.ImageDraw.Draw(img)
|
|
tw, th = d.textsize(text, font=font)
|
|
img = PIL.Image.new('1', (tw, height))
|
|
d = PIL.ImageDraw.Draw(img)
|
|
print(f'({tw}, {th}) {text}')
|
|
d.text((0, 0), text, font=font, fill=(0xff,))
|
|
dat = list(img.getdata())
|
|
if tw == 0:
|
|
return height * [[]]
|
|
return [dat[i:i+tw] for i in range(0, len(dat), tw)]
|
|
|
|
|
|
def qr2img(payload):
|
|
img = qrcode.make(payload, box_size=3).get_image()
|
|
width, _ = img.size
|
|
dat = [0 if x else 1 for x in img.getdata()]
|
|
return [dat[i:i+width] for i in range(0, len(dat), width)]
|
|
|
|
|
|
def get_template0(qr_payload, line0, line1, line2=''):
|
|
qr = qrcode.make(qr_payload, box_size=3).get_image()
|
|
qr.putdata([0 if x else 1 for x in qr.getdata()])
|
|
qrw, qrh = qr.size
|
|
width = 384
|
|
height = qrh
|
|
textheight = 21
|
|
texttop = (height - 3 * textheight) / 3
|
|
textleft = qrw + 20
|
|
img = PIL.Image.new('1', (width, height))
|
|
img.paste(qr)
|
|
d = PIL.ImageDraw.Draw(img)
|
|
d.text((textleft, texttop + 0 * textheight), line0, font=font, fill=(0xff,))
|
|
d.text((textleft, texttop + 1 * textheight), line1, font=font, fill=(0xff,))
|
|
d.text((textleft, texttop + 2 * textheight), line2, font=font, fill=(0xff,))
|
|
dat = list(img.getdata())
|
|
return [dat[i:i+width] for i in range(0, len(dat), width)]
|
|
|
|
|
|
def img2printable(img):
|
|
for line in img:
|
|
out = []
|
|
for i in range(0, len(line), 8):
|
|
val = 0
|
|
for j in range(8):
|
|
if i+j >= len(line):
|
|
break
|
|
if line[i+j]:
|
|
val |= (1 << j)
|
|
out.append(val)
|
|
yield out
|
|
|
|
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(description='Print stuff on a cat.')
|
|
parser.add_argument('--socket', dest='socket_path',
|
|
help='socket of the daemon',
|
|
default=DEFAULT_SOCKET)
|
|
parser.add_argument('--text', action='store_true', help='print text from stdin')
|
|
parser.add_argument('--feed', action='store_true', help='feed paper')
|
|
parser.add_argument('--debug', action='store_true', help='only for debug')
|
|
parser.add_argument('--template0', help='print template0')
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
# XXX: not async
|
|
class CatClient:
|
|
def __init__(self, socket_path):
|
|
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
|
self.clsock = tempfile.mktemp()
|
|
self.sock.bind(self.clsock)
|
|
self.socket_path = socket_path
|
|
|
|
def flush(self):
|
|
try:
|
|
self.sock.setblocking(False)
|
|
while self.sock.recvfrom(1024):
|
|
pass
|
|
except BlockingIOError:
|
|
pass
|
|
finally:
|
|
self.sock.setblocking(True)
|
|
|
|
def feedpaper(self):
|
|
self.flush()
|
|
self.sock.sendto(b'f', self.socket_path)
|
|
dat, addr = self.sock.recvfrom(1024)
|
|
|
|
def scanline(self, data):
|
|
self.flush()
|
|
self.sock.sendto(b's' + data, self.socket_path)
|
|
dat, addr = self.sock.recvfrom(1024)
|
|
|
|
|
|
async def main(socket_path=None, text=None, feed=False, debug=False, template0=None):
|
|
if debug:
|
|
for line in sys.stdin:
|
|
img = text2img(line)
|
|
print('\n'.join(''.join('x' if x else ' ' for x in y) for y in img))
|
|
return
|
|
|
|
client = CatClient(socket_path)
|
|
|
|
if text:
|
|
for line in sys.stdin:
|
|
if line.startswith('qr:'):
|
|
img = qr2img(line[3:])
|
|
else:
|
|
img = text2img(line)
|
|
for data in img2printable(img):
|
|
if data:
|
|
client.scanline(bytes(data))
|
|
else:
|
|
client.feedpaper(lines=1)
|
|
elif template0 is not None:
|
|
qr, line0, line1, line2 = template0.split(';')
|
|
img = get_template0(qr, line0, line1, line2)
|
|
for data in img2printable(img):
|
|
client.scanline(bytes(data))
|
|
if feed:
|
|
client.feedpaper()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main(**vars(parse_args())))
|