catprint/catclient.py
2022-09-10 08:40:26 +02:00

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())))