PK! ezprinting/__init__.py__version__ = '0.2.5' from ezprinting.print_job import PrintJob from ezprinting.print_server import PrintServer from ezprinting.printer import Printer PK! 5c\SSezprinting/print_job.py# -*- coding: utf-8 -*- from __future__ import division, print_function, unicode_literals import json from .printer import Printer from .print_server import PrintServer, GCP_BASE_URI STATE0_DRAFT = "DRAFT" STATE1_HELD = "HELD" STATE2_QUEUED = "QUEUED" STATE3_IN_PROGRESS = "IN_PROGRESS" STATE4_STOPPED = "STOPPED" STATE5_DONE = "DONE" STATE6_ABORTED = "ABORTED" GENERIC_TITLE = "A print job" DEFAULT_CONTENT_TYPE = 'application/pdf' class PrintJob: def __init__(self, printer: Printer, content, content_type: str = DEFAULT_CONTENT_TYPE, title: str = GENERIC_TITLE, options=None): self.printer = printer self.print_server = self.printer.print_server self.content = content self.content_type = content_type self.title = title self.job_id = None self.submitted = False self.gcp_submit_response = None self.gcp_submit_response_content = None self.gcp_last_message = "" self.state = STATE0_DRAFT if not options: if self.print_server.is_gcp: options = {"version": "1.0", "print": {}} elif self.print_server.is_cups: options = {} self.options = options def print(self): conn = self.print_server.open_connection() if self.print_server.is_cups: self.job_id = conn.createJob(self.printer.id, self.title, self.options) conn.startDocument(self.printer.id, self.job_id, self.title, self.content_type, 1) # Requires pycups >= 1.9.74, lower versions were buggy status = conn.writeRequestData(self.content, len(self.content)) if (conn.finishDocument(self.printer.id)==0): self.submitted = True self.status = STATE2_QUEUED if status==100 else STATE0_DRAFT return self.submitted elif self.print_server.is_gcp: # Must NOT set content-type as part of payload below payload = \ {"printerid": self.printer.id, \ "title": self.title, \ "ticket": json.dumps(self.options) } # Here, the second 'content' could be anything, it is there because # requests library requires a 'filename' but we need a filename without # extension to not mess up content-type files = {'content': ('content', self.content, self.content_type)} response = conn.post('{}/submit'.format(GCP_BASE_URI), data=payload, files=files) if response.status_code == 200: self.submitted = True self.gcp_submit_response = response self.gcp_submit_response_content = json.loads(response.content, strict=False) self.gcp_last_success = self.gcp_submit_response_content["success"] if self.gcp_last_success: self.job_id = self.gcp_submit_response_content["job"]["id"] self.state = self.gcp_submit_response_content["job"]["semanticState"]["state"]["type"] self.gcp_last_message = self.gcp_submit_response_content["message"] else: pass return self.gcp_last_success @classmethod def new_cups(cls, printer_name, content, content_type: str = DEFAULT_CONTENT_TYPE, host: str = "localhost:631", username: str = None, password: str = None, title: str = GENERIC_TITLE, options=None): ps = PrintServer.cups(host=host, username=username, password=password) p = Printer(print_server=ps, name_or_id=printer_name) pj = cls(printer=p, content=content, content_type=content_type, title=title, options=options) return pj @classmethod def new_gcp(cls, service_account: str, printer_id: str, content, content_type: str = DEFAULT_CONTENT_TYPE, title: str = GENERIC_TITLE, ticket=None): ps = PrintServer.gcp(name="GCP", service_account=service_account) p = Printer(ps, printer_id) pj = cls(printer=p, content=content, content_type=content_type, title=title, options=ticket) return pj PK!Iezprinting/print_server.py# -*- coding: utf-8 -*- from __future__ import division, print_function, unicode_literals import cups import json from google.oauth2 import service_account from google.auth.transport.requests import AuthorizedSession SERVER_TYPE_CUPS = "CUPS" SERVER_TYPE_GOOGLE_CLOUD_PRINT = "GCP" PRINT_SERVER_GENERIC_NAME="A Print Server" DEFAULT_CUPS_HOST="localhost:631" GCP_SCOPEs = ['https://www.googleapis.com/auth/cloudprint'] GCP_BASE_URI = 'https://www.google.com/cloudprint' class PrintServer: def __init__(self, name, server_type, params_dict): self._name = name self._type = server_type self.is_cups = False self.is_gcp = False if (self._type == SERVER_TYPE_CUPS): self.is_cups = True self._cups_host = params_dict["cups_host"] self._cups_user = params_dict["cups_username"] self._cups_passwd = params_dict["cups_password"] self._cups_user = "" if not self._cups_user else self._cups_user self._cups_passwd = "" if not self._cups_passwd else self._cups_password elif (self._type == SERVER_TYPE_GOOGLE_CLOUD_PRINT): self.is_gcp = True self._service_account_json = params_dict else: raise ValueError("Invalid server_type.") @classmethod def cups(cls, host=DEFAULT_CUPS_HOST, username: str = None, password: str = None, name=PRINT_SERVER_GENERIC_NAME): d = dict() d["cups_host"] = host d["cups_username"] = username d["cups_password"] = password d["name"] = name return cls(name=name, server_type=SERVER_TYPE_CUPS, params_dict=d) @classmethod def gcp(cls, service_account: str, name: str=PRINT_SERVER_GENERIC_NAME): return cls(name=name, server_type=SERVER_TYPE_GOOGLE_CLOUD_PRINT, params_dict=json.loads(service_account, strict=False)) def test_connection(self): success = True error_message = "Connection OK!" if self.is_cups: try: conn = self.open_connection() except RuntimeError as e: success = False error_message = str(e) elif self.is_gcp: try: session = self.open_connection() r = session.get("{}{}".format(GCP_BASE_URI, '/search?use_cdd=true&connection_status=ALL')) except Exception as e: success = False error_message = str(e) else: if not r.ok: success = False error_message = r.reason session.close() return success, error_message def open_connection(self): if (self.is_cups): cups.setServer(self._cups_host) cups.setUser(self._cups_user) cups.setPasswordCB(lambda a: self._cups_passwd) return cups.Connection() elif (self.is_gcp): credentials = service_account.Credentials.from_service_account_info(self._service_account_json, scopes=GCP_SCOPEs) return AuthorizedSession(credentials) else: raise RuntimeError("Server type error!") def get_printers(self): if self.is_cups: conn = self.open_connection() return conn.getPrinters() elif self.is_gcp: session = self.open_connection() r = session.get(GCP_BASE_URI + '/search?use_cdd=true&extra_fields=connectionStatus&q=&type=&connection_status=ALL') j = json.loads(r.content, strict=False) return j['printers']PK!1p.ezprinting/printer.py# -*- coding: utf-8 -*- from __future__ import division, print_function, unicode_literals import json import cups from ezprinting.print_server import PrintServer, GCP_BASE_URI class Printer: def __init__(self, print_server: PrintServer, name_or_id: str): self.print_server = print_server self.id = name_or_id def check_printer_exists(self, conn=None): if not conn: conn = self.print_server.open_connection() if self.print_server.is_cups: try: p = self.get_printer_attributes(conn=conn) except cups.IPPError: return False return True if p['printer-name'] == self.id else False elif self.print_server.is_gcp: p = self.get_printer_attributes(conn=conn) if not p['success']: self.enable_printer(conn=conn) p = self.get_printer_attributes(conn=conn) return p['success'] def get_printer_attributes(self, conn=None): if not conn: conn = self.print_server.open_connection() if self.print_server.is_cups: return conn.getPrinterAttributes(self.id) elif self.print_server.is_gcp: r = conn.get('{}/printer?printerid={}'.format(GCP_BASE_URI, self.id)) j = json.loads(r.content, strict=False) return j def enable_printer(self, conn=None): if not conn: conn = self.print_server.open_connection() if self.print_server.is_cups: conn.enablePrinter(self.id) elif self.print_server.is_gcp: conn.get('{}/processinvite?accept=true&printerid={}'.format(GCP_BASE_URI, self.id)) PK!ﰰ"ezprinting-0.2.6.dist-info/LICENSECopyright 2018 Marcelo Bello Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!HWY ezprinting-0.2.6.dist-info/WHEEL A н#Z;/"b&F]xzwC;dhfCSTֻ0*Ri.4œh6-]{H, JPK!Hš ʅ='#ezprinting-0.2.6.dist-info/METADATAZms_q?ԐPl'NKWRul%t:88w MwPrδ{ٗgw];d.Y5Vj.ײTs>֍VII,mR6ٻD-\)Pb/x'OՈTKj lQڭPw7ªf_/YJ\ mMoMf5.s])ʅ* s֓Teɟ~gѼx:;㢅f M #^%e!K&"sZ\ucPn׃-7Y5,qx%UKFwf_O>w}gx/?{Wg:AFٕ.w&81o_L{_򛇗4EzJ O*sw?TmvO*]; $*7{S>3&7*I#fE]-yyy#B[!VғStEfZN&6:حux.Q*hEg:ɋ}e |3gfARVIfզ'JVVM+v(Dp @,̡j']3ɲtKM4 w"I+Q#l[צqx N5[cF-5N 'G⺲NQRYɦIj\2 ~K*9AVUlK:[Q,W[1vd` Yn۳+в ƶ́u |l#na^i6NaSfmӪodaU4 ˺Pf9RA+xie6l;AL轼һ|4#8o8`5O~DŽap%Cf'!z'Ad*ND圷-b6JI?-Gݧu|̧Vj)vŵDIM;O+[eHӘ5t~~ {sH$9?Iq]k *|JD8@=?Gxƺ榔J]ϟ=}Jt7WpLg/9俎VryCU3X,%*p4\N&xzg6S[kwP-^3(ŵt㇯诔;"[+TU,v-.^Q2݆#Ww[:&!کF}ER٤dҿ$P5]q\`L(+1''M<>HwI?27U_/ }/  zަ|ݰpuLC2̿On:q%v$ M@ |?EQzqg~$ RqH?>Pϊa' >`K.JpE ǬD5'qTby!V/cfI- ؂T2jzpƺ/J@#9sTDTa]) 7TB( _1*cK w۵i(a/4]6`8`Y%WwM.MIWT ?^F(+:l*PCS[x5Rv'Tߩc`%ol^MS_@ԃl[(4qߚTڎsxe'G}wڥ=&V4*S 56GےGDFs: +Re"ce-!Ἆ̗´Η]cǩե+Rʿt¦5Ov\O_BqR.CVEMU>k%}q g 3VwѮO ž]}3zcJG/AsX`hEF윈V*>C~ Ԇ0j:%41R7e9oիa͈qJ'a{)tFM?*1~h\F) }M[Q97q+*,~'1tZsAڪ`XOϓ'}IC†*{vD7n(MBih9h" $,AP!^|wxmGI錿i,3A 7*C]%O0. .A>9; ކ jy^`R}*RCʑUټ}.&0i'r/dQ8M0(T!;P[FW tHV * Yqo Tr5ROX,j^C}G)7*p~@RŖ؂iWk'E{摣N 2I#w?Kw *舚 hۋ My" "WJx{GXa4od;Оm$^fDmǗA1Kz;<./ S0WTq*{@;*!F,ssY*2RMl{ӿ5^$`َ{O"3Q5!H`G;}'MGOn/X p~g~5}܅ zbR7q_4xqs-3Jy ۋZ]E?ۈ -z7OL⷇$]4Lû͟n,4Bǵ JgUM  |GIOPK!Hp݇r!ezprinting-0.2.6.dist-info/RECORD}n@E} ,bBoJ.)j)G{G֮ϛ1og&1t 5d-ǿ^beT'HodV ' }(>/Eq' {ZtWcN' *|C-2{7F;ҋuiq٫LQhw3[5p`So@ Zb ur8k MƝX9g;·Λ}6ukVv[嚛]xAl }ȧDt\!47G!)jp`Q∀"vD}flxQc8kA_:H)[^8H f Rq&%qZ!=ߤɀ}x (PK! ezprinting/__init__.pyPK! 5c\SSezprinting/print_job.pyPK!ITezprinting/print_server.pyPK!1p.ezprinting/printer.pyPK!ﰰ"&ezprinting-0.2.6.dist-info/LICENSEPK!HWY *ezprinting-0.2.6.dist-info/WHEELPK!Hš ʅ='#+ezprinting-0.2.6.dist-info/METADATAPK!Hp݇r!F;ezprinting-0.2.6.dist-info/RECORDPKR =