Very early IPP support, works with cups on unix

This commit is contained in:
NaitLee 2021-09-09 17:33:19 +08:00
parent 3123548ab5
commit 64d1c3528b

View File

@ -1,6 +1,6 @@
from http.server import HTTPServer, BaseHTTPRequestHandler from http.server import HTTPServer, BaseHTTPRequestHandler
import socketserver, threading, urllib, os, asyncio import socketserver, threading, urllib, os, asyncio, tempfile, platform
from printer import PrinterDriver from printer import PrinterDriver
import bleak import bleak
@ -76,6 +76,10 @@ class PrinterServer(BaseHTTPRequestHandler):
self.wfile.write(b'Not Found') self.wfile.write(b'Not Found')
return return
def do_POST(self): def do_POST(self):
if self.headers.get('Content-Type', '') == 'application/ipp':
# https://datatracker.ietf.org/doc/html/rfc8010
self.handle_ipp()
return
path = urllib.parse.unquote(self.path) path = urllib.parse.unquote(self.path)
v = urlvar(path) v = urlvar(path)
path = path.split('?')[0] path = path.split('?')[0]
@ -90,7 +94,6 @@ class PrinterServer(BaseHTTPRequestHandler):
try: try:
content_length = int(self.headers.get('Content-Length')) content_length = int(self.headers.get('Content-Length'))
data = self.rfile.read(content_length) data = self.rfile.read(content_length)
assert content_length == len(data), 'Post data not fully read'
asyncio.run(self.driver.print_data(data, v['address'])) asyncio.run(self.driver.print_data(data, v['address']))
self.send_response(200) self.send_response(200)
self.send_header('Content-Type', 'text/plain') self.send_header('Content-Type', 'text/plain')
@ -106,6 +109,92 @@ class PrinterServer(BaseHTTPRequestHandler):
self.send_header('Content-Type', 'text/plain') self.send_header('Content-Type', 'text/plain')
self.end_headers() self.end_headers()
self.wfile.write(b'Bad Request') self.wfile.write(b'Bad Request')
def handle_ipp(self):
path = urllib.parse.unquote(self.path)
printer_name = path[1:]
data = self.rfile.read(int(self.headers.get('Content-Length', 0)))
# len_data = len(data)
# ipp_version_number = data[0:2]
# ipp_operation_id = data[2:4]
# ipp_request_id = data[4:8]
ipp_operation_attributes_tag = data[8]
attributes = {}
data_to_print = b''
# b'\x01'[0] == int(1)
if ipp_operation_attributes_tag == b'\x01'[0]:
pointer = 9
next_name_length_at = 10
next_value_length_at = 10
name = b''
value = b''
while data[pointer] != b'\x03'[0]:
tag = data[pointer:pointer + 1]
pointer += 1
if tag[0] < 0x10: # delimiter-tag
continue
next_name_length_at = pointer + data[pointer] * 0x0100 + data[pointer + 1] + 2
pointer += 2
while pointer < next_name_length_at:
name = name + data[pointer:pointer + 1]
pointer += 1
next_value_length_at = pointer + data[pointer] * 0x0100 + data[pointer + 1] + 2
pointer += 2
while pointer < next_value_length_at:
value = value + data[pointer:pointer + 1]
pointer += 1
attributes[name] = (tag, value)
name = b''
value = b''
pointer += 1
data_to_print = data[pointer:]
if data_to_print == b'':
self.send_response(200)
self.send_header('Content-Type', 'application/ipp')
self.end_headers()
self.wfile.write(
b'\x01\x01\x00\x00\x00\x00\x00\x01\x01\x03'
)
return
try:
devices = asyncio.run(bleak.BleakScanner.discover())
target_device = ''
for i in devices:
if i.name == printer_name:
target_device = i.address
if target_device != '':
platform_system = platform.system()
temp_dir = tempfile.mkdtemp()
temp_file_ps = os.path.join(temp_dir, 'temp.ps')
temp_file_pbm = os.path.join(temp_dir, 'temp.pbm')
f = open(temp_file_ps, 'wb')
f.write(data_to_print)
f.close()
# https://ghostscript.com/doc/9.54.0/Use.htm#Output_device
ghostscript_exe = 'gs'
if platform_system == 'Windows':
ghostscript_exe = 'gswin32c.exe'
elif platform_system == 'Linux':
ghostscript_exe = 'gs'
elif platform_system == 'OS/2':
ghostscript_exe = 'gsos2'
return_code = os.system('%s -q -sDEVICE=pbmraw -dNOPAUSE -dBATCH -dSAFER -dFIXEDMEDIA -g384x543 -r46.4441219158x46.4441219158 -dFitPage -sOutputFile="%s" "%s"' % (ghostscript_exe, temp_file_pbm, temp_file_ps))
if return_code == 0:
asyncio.run(self.driver.print_file(temp_file_pbm, target_device))
else:
raise Exception('Error on invoking Ghostscript')
# print(data_to_print)
self.send_response(200)
self.send_header('Content-Type', 'application/ipp')
self.end_headers()
self.wfile.write(
b'\x01\x01\x00\x00\x00\x00\x00\x01\x01\x03'
)
except Exception as _:
self.send_response(500)
self.send_header('Content-Type', 'application/ipp')
self.end_headers()
self.wfile.write(b'')
class ThreadedHTTPServer(socketserver.ThreadingMixIn, HTTPServer): class ThreadedHTTPServer(socketserver.ThreadingMixIn, HTTPServer):
""" Handle requests in a separate thread. """ """ Handle requests in a separate thread. """