mirror of
				https://github.com/9001/copyparty.git
				synced 2025-10-31 04:32:20 -06:00 
			
		
		
		
	unvendorable qrcodegen (#887);
move copyparty-original code to qrkode.py stolen/qrcodegen.py can be deleted and replaced with system lib this is safe and has minimal affect on functionality; performance will be a tiny bit slower without the vendored copy
This commit is contained in:
		
							parent
							
								
									656f0a6c39
								
							
						
					
					
						commit
						08ebb0b4c9
					
				|  | @ -3000,6 +3000,7 @@ force use of system modules  instead of the vendored versions: | |||
| | -------------------- | ------------ | | ||||
| | `PRTY_SYS_ALL`       | all of the below | | ||||
| | `PRTY_SYS_IFADDR`    | replace [stolen/ifaddr](./copyparty/stolen/ifaddr) with [upstream](https://pypi.org/project/ifaddr/) | | ||||
| | `PRTY_SYS_QRCG`      | replace [stolen/qrcodegen.py](./copyparty/stolen/qrcodegen.py) with [upstream](https://github.com/nayuki/QR-Code-generator/blob/master/python/qrcodegen.py) | | ||||
| 
 | ||||
| to debug, run copyparty with `PRTY_MODSPEC=1` to see where it's getting each module from | ||||
| 
 | ||||
|  |  | |||
|  | @ -34,8 +34,8 @@ from .__init__ import ANYWIN, RES, TYPE_CHECKING, EnvParams, unicode | |||
| from .__version__ import S_VERSION | ||||
| from .authsrv import LEELOO_DALLAS, VFS  # typechk | ||||
| from .bos import bos | ||||
| from .qrkode import QrCode, qr2svg, qrgen | ||||
| from .star import StreamTar | ||||
| from .stolen.qrcodegen import QrCode, qr2svg | ||||
| from .sutil import StreamArc, gfilter | ||||
| from .szip import StreamZip | ||||
| from .up2k import up2k_chunksize | ||||
|  | @ -4951,7 +4951,7 @@ class HttpCli(object): | |||
|             url += "#" + uhash | ||||
| 
 | ||||
|         self.log("qrcode(%r)" % (url,)) | ||||
|         ret = qr2svg(QrCode.encode_binary(url.encode("utf-8")), 2) | ||||
|         ret = qr2svg(qrgen(url.encode("utf-8")), 2) | ||||
|         self.reply(ret.encode("utf-8"), mime="image/svg+xml") | ||||
|         return True | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										112
									
								
								copyparty/qrkode.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								copyparty/qrkode.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,112 @@ | |||
| # coding: utf-8 | ||||
| from __future__ import print_function, unicode_literals | ||||
| 
 | ||||
| import os | ||||
| 
 | ||||
| try: | ||||
|     if os.getenv("PRTY_SYS_ALL") or os.getenv("PRTY_SYS_QRCG"): | ||||
|         raise ImportError() | ||||
|     from .stolen.qrcodegen import QrCode | ||||
| 
 | ||||
|     qrgen = QrCode.encode_binary | ||||
|     VENDORED = True | ||||
| except ImportError: | ||||
|     VENDORED = False | ||||
|     from qrcodegen import QrCode | ||||
| 
 | ||||
| if os.getenv("PRTY_MODSPEC"): | ||||
|     from inspect import getsourcefile | ||||
| 
 | ||||
|     print("PRTY_MODSPEC: qrcode:", getsourcefile(QrCode)) | ||||
| 
 | ||||
| if True:  # pylint: disable=using-constant-test | ||||
|     import typing | ||||
|     from typing import Any, Optional, Sequence, Union | ||||
| 
 | ||||
| 
 | ||||
| if not VENDORED: | ||||
| 
 | ||||
|     def _qrgen(data: Union[bytes, Sequence[int]]) -> "QrCode": | ||||
|         ret = None | ||||
|         V = QrCode.Ecc | ||||
|         for e in [V.HIGH, V.QUARTILE, V.MEDIUM, V.LOW]: | ||||
|             qr = QrCode.encode_binary(data, e) | ||||
|             qr.size = qr._size | ||||
|             qr.modules = qr._modules | ||||
|             if not ret or ret.size > qr.size: | ||||
|                 ret = qr | ||||
|         return ret | ||||
| 
 | ||||
|     qrgen = _qrgen | ||||
| 
 | ||||
| 
 | ||||
| def qr2txt(qr: QrCode, zoom: int = 1, pad: int = 4) -> str: | ||||
|     tab = qr.modules | ||||
|     sz = qr.size | ||||
|     if sz % 2 and zoom == 1: | ||||
|         tab.append([False] * sz) | ||||
| 
 | ||||
|     tab = [[False] * sz] * pad + tab + [[False] * sz] * pad | ||||
|     tab = [[False] * pad + x + [False] * pad for x in tab] | ||||
| 
 | ||||
|     rows: list[str] = [] | ||||
|     if zoom == 1: | ||||
|         for y in range(0, len(tab), 2): | ||||
|             row = "" | ||||
|             for x in range(len(tab[y])): | ||||
|                 v = 2 if tab[y][x] else 0 | ||||
|                 v += 1 if tab[y + 1][x] else 0 | ||||
|                 row += " ▄▀█"[v] | ||||
|             rows.append(row) | ||||
|     else: | ||||
|         for tr in tab: | ||||
|             row = "" | ||||
|             for zb in tr: | ||||
|                 row += " █"[int(zb)] * 2 | ||||
|             rows.append(row) | ||||
| 
 | ||||
|     return "\n".join(rows) | ||||
| 
 | ||||
| 
 | ||||
| def qr2png( | ||||
|     qr: QrCode, | ||||
|     zoom: int, | ||||
|     pad: int, | ||||
|     bg: Optional[tuple[int, int, int]], | ||||
|     fg: Optional[tuple[int, int, int]], | ||||
|     ap: str, | ||||
| ) -> None: | ||||
|     from PIL import Image | ||||
| 
 | ||||
|     tab = qr.modules | ||||
|     sz = qr.size | ||||
|     psz = sz + pad * 2 | ||||
|     if bg: | ||||
|         img = Image.new("RGB", (psz, psz), bg) | ||||
|     else: | ||||
|         img = Image.new("RGBA", (psz, psz), (0, 0, 0, 0)) | ||||
|         fg = (fg[0], fg[1], fg[2], 255) | ||||
|     for y in range(sz): | ||||
|         for x in range(sz): | ||||
|             if tab[y][x]: | ||||
|                 img.putpixel((x + pad, y + pad), fg) | ||||
|     if zoom != 1: | ||||
|         img = img.resize((sz * zoom, sz * zoom), Image.Resampling.NEAREST) | ||||
|     img.save(ap) | ||||
| 
 | ||||
| 
 | ||||
| def qr2svg(qr: QrCode, border: int) -> str: | ||||
|     parts: list[str] = [] | ||||
|     for y in range(qr.size): | ||||
|         sy = border + y | ||||
|         for x in range(qr.size): | ||||
|             if qr.modules[y][x]: | ||||
|                 parts.append("M%d,%dh1v1h-1z" % (border + x, sy)) | ||||
|     t = """\ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 {0} {0}" stroke="none"> | ||||
| <rect width="100%" height="100%" fill="#F7F7F7"/> | ||||
| <path d="{1}" fill="#111111"/> | ||||
| </svg> | ||||
| """ | ||||
|     return t.format(qr.size + border * 2, " ".join(parts)) | ||||
|  | @ -4,7 +4,7 @@ | |||
| # https://github.com/nayuki/QR-Code-generator/blob/daa3114/python/qrcodegen.py | ||||
| # the original ^ is extremely well commented so refer to that for explanations | ||||
| 
 | ||||
| # hacks: binary-only, auto-ecc, render, py2-compat | ||||
| # hacks: binary-only, auto-ecc, py2-compat | ||||
| 
 | ||||
| from __future__ import print_function, unicode_literals | ||||
| 
 | ||||
|  | @ -173,52 +173,6 @@ class QrCode(object): | |||
|         self._apply_mask(msk)  # Apply the final choice of mask | ||||
|         self._draw_format_bits(msk)  # Overwrite old format bits | ||||
| 
 | ||||
|     def render(self, zoom=1, pad=4) -> str: | ||||
|         tab = self.modules | ||||
|         sz = self.size | ||||
|         if sz % 2 and zoom == 1: | ||||
|             tab.append([False] * sz) | ||||
| 
 | ||||
|         tab = [[False] * sz] * pad + tab + [[False] * sz] * pad | ||||
|         tab = [[False] * pad + x + [False] * pad for x in tab] | ||||
| 
 | ||||
|         rows: list[str] = [] | ||||
|         if zoom == 1: | ||||
|             for y in range(0, len(tab), 2): | ||||
|                 row = "" | ||||
|                 for x in range(len(tab[y])): | ||||
|                     v = 2 if tab[y][x] else 0 | ||||
|                     v += 1 if tab[y + 1][x] else 0 | ||||
|                     row += " ▄▀█"[v] | ||||
|                 rows.append(row) | ||||
|         else: | ||||
|             for tr in tab: | ||||
|                 row = "" | ||||
|                 for zb in tr: | ||||
|                     row += " █"[int(zb)] * 2 | ||||
|                 rows.append(row) | ||||
| 
 | ||||
|         return "\n".join(rows) | ||||
| 
 | ||||
|     def to_png(self, zoom, pad, bg, fg, ap) -> None: | ||||
|         from PIL import Image | ||||
| 
 | ||||
|         tab = self.modules | ||||
|         sz = self.size | ||||
|         psz = sz + pad * 2 | ||||
|         if bg: | ||||
|             img = Image.new("RGB", (psz, psz), bg) | ||||
|         else: | ||||
|             img = Image.new("RGBA", (psz, psz), (0, 0, 0, 0)) | ||||
|             fg = (fg[0], fg[1], fg[2], 255) | ||||
|         for y in range(sz): | ||||
|             for x in range(sz): | ||||
|                 if tab[y][x]: | ||||
|                     img.putpixel((x + pad, y + pad), fg) | ||||
|         if zoom != 1: | ||||
|             img = img.resize((sz * zoom, sz * zoom), Image.Resampling.NEAREST) | ||||
|         img.save(ap) | ||||
| 
 | ||||
|     def _draw_function_patterns(self) -> None: | ||||
|         # Draw horizontal and vertical timing patterns | ||||
|         for i in range(self.size): | ||||
|  | @ -613,20 +567,3 @@ def _get_bit(x: int, i: int) -> bool: | |||
| 
 | ||||
| class DataTooLongError(ValueError): | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| def qr2svg(qr: QrCode, border: int) -> str: | ||||
|     parts: list[str] = [] | ||||
|     for y in range(qr.size): | ||||
|         sy = border + y | ||||
|         for x in range(qr.size): | ||||
|             if qr.modules[y][x]: | ||||
|                 parts.append("M%d,%dh1v1h-1z" % (border + x, sy)) | ||||
|     t = """\ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 {0} {0}" stroke="none"> | ||||
| <rect width="100%" height="100%" fill="#F7F7F7"/> | ||||
| <path d="{1}" fill="#111111"/> | ||||
| </svg> | ||||
| """ | ||||
|     return t.format(qr.size + border * 2, " ".join(parts)) | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ import time | |||
| 
 | ||||
| from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode | ||||
| from .cert import gencert | ||||
| from .stolen.qrcodegen import QrCode, qr2svg | ||||
| from .qrkode import QrCode, qr2png, qr2svg, qr2txt, qrgen | ||||
| from .util import ( | ||||
|     E_ACCESS, | ||||
|     E_ADDR_IN_USE, | ||||
|  | @ -629,7 +629,7 @@ class TcpSrv(object): | |||
| 
 | ||||
|         pad = self.args.qrp | ||||
|         zoom = self.args.qrz | ||||
|         qrc = QrCode.encode_binary(btxt) | ||||
|         qrc = qrgen(btxt) | ||||
| 
 | ||||
|         for zs in self.args.qr_file or []: | ||||
|             self._qr2file(qrc, zs) | ||||
|  | @ -642,7 +642,7 @@ class TcpSrv(object): | |||
|             except: | ||||
|                 zoom = 1 | ||||
| 
 | ||||
|         qr = qrc.render(zoom, pad) | ||||
|         qr = qr2txt(qrc, zoom, pad) | ||||
|         if self.args.no_ansi: | ||||
|             return "{}\n{}".format(txt, qr) | ||||
| 
 | ||||
|  | @ -683,12 +683,12 @@ class TcpSrv(object): | |||
|             if zoom not in (1, 2): | ||||
|                 raise Exception("invalid zoom for qr.txt; must be 1 or 2") | ||||
|             with open(ap, "wb") as f: | ||||
|                 f.write(qrc.render(zoom, pad).encode("utf-8")) | ||||
|                 f.write(qr2txt(qrc, zoom, pad).encode("utf-8")) | ||||
|         elif ap.endswith(".svg"): | ||||
|             with open(ap, "wb") as f: | ||||
|                 f.write(qr2svg(qrc, pad).encode("utf-8")) | ||||
|         else: | ||||
|             qrc.to_png(zoom, pad, self._h2i(bg), self._h2i(fg), ap) | ||||
|             qr2png(qrc, zoom, pad, self._h2i(bg), self._h2i(fg), ap) | ||||
| 
 | ||||
|     def _h2i(self, hs): | ||||
|         try: | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ copyparty/metrics.py, | |||
| copyparty/mtag.py, | ||||
| copyparty/multicast.py, | ||||
| copyparty/pwhash.py, | ||||
| copyparty/qrkode.py, | ||||
| copyparty/res, | ||||
| copyparty/res/__init__.py, | ||||
| copyparty/res/COPYING.txt, | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue