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_ALL`       | all of the below | | ||||||
| | `PRTY_SYS_IFADDR`    | replace [stolen/ifaddr](./copyparty/stolen/ifaddr) with [upstream](https://pypi.org/project/ifaddr/) | | | `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 | 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 .__version__ import S_VERSION | ||||||
| from .authsrv import LEELOO_DALLAS, VFS  # typechk | from .authsrv import LEELOO_DALLAS, VFS  # typechk | ||||||
| from .bos import bos | from .bos import bos | ||||||
|  | from .qrkode import QrCode, qr2svg, qrgen | ||||||
| from .star import StreamTar | from .star import StreamTar | ||||||
| from .stolen.qrcodegen import QrCode, qr2svg |  | ||||||
| from .sutil import StreamArc, gfilter | from .sutil import StreamArc, gfilter | ||||||
| from .szip import StreamZip | from .szip import StreamZip | ||||||
| from .up2k import up2k_chunksize | from .up2k import up2k_chunksize | ||||||
|  | @ -4951,7 +4951,7 @@ class HttpCli(object): | ||||||
|             url += "#" + uhash |             url += "#" + uhash | ||||||
| 
 | 
 | ||||||
|         self.log("qrcode(%r)" % (url,)) |         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") |         self.reply(ret.encode("utf-8"), mime="image/svg+xml") | ||||||
|         return True |         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 | # https://github.com/nayuki/QR-Code-generator/blob/daa3114/python/qrcodegen.py | ||||||
| # the original ^ is extremely well commented so refer to that for explanations | # 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 | 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._apply_mask(msk)  # Apply the final choice of mask | ||||||
|         self._draw_format_bits(msk)  # Overwrite old format bits |         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: |     def _draw_function_patterns(self) -> None: | ||||||
|         # Draw horizontal and vertical timing patterns |         # Draw horizontal and vertical timing patterns | ||||||
|         for i in range(self.size): |         for i in range(self.size): | ||||||
|  | @ -613,20 +567,3 @@ def _get_bit(x: int, i: int) -> bool: | ||||||
| 
 | 
 | ||||||
| class DataTooLongError(ValueError): | class DataTooLongError(ValueError): | ||||||
|     pass |     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 .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode | ||||||
| from .cert import gencert | from .cert import gencert | ||||||
| from .stolen.qrcodegen import QrCode, qr2svg | from .qrkode import QrCode, qr2png, qr2svg, qr2txt, qrgen | ||||||
| from .util import ( | from .util import ( | ||||||
|     E_ACCESS, |     E_ACCESS, | ||||||
|     E_ADDR_IN_USE, |     E_ADDR_IN_USE, | ||||||
|  | @ -629,7 +629,7 @@ class TcpSrv(object): | ||||||
| 
 | 
 | ||||||
|         pad = self.args.qrp |         pad = self.args.qrp | ||||||
|         zoom = self.args.qrz |         zoom = self.args.qrz | ||||||
|         qrc = QrCode.encode_binary(btxt) |         qrc = qrgen(btxt) | ||||||
| 
 | 
 | ||||||
|         for zs in self.args.qr_file or []: |         for zs in self.args.qr_file or []: | ||||||
|             self._qr2file(qrc, zs) |             self._qr2file(qrc, zs) | ||||||
|  | @ -642,7 +642,7 @@ class TcpSrv(object): | ||||||
|             except: |             except: | ||||||
|                 zoom = 1 |                 zoom = 1 | ||||||
| 
 | 
 | ||||||
|         qr = qrc.render(zoom, pad) |         qr = qr2txt(qrc, zoom, pad) | ||||||
|         if self.args.no_ansi: |         if self.args.no_ansi: | ||||||
|             return "{}\n{}".format(txt, qr) |             return "{}\n{}".format(txt, qr) | ||||||
| 
 | 
 | ||||||
|  | @ -683,12 +683,12 @@ class TcpSrv(object): | ||||||
|             if zoom not in (1, 2): |             if zoom not in (1, 2): | ||||||
|                 raise Exception("invalid zoom for qr.txt; must be 1 or 2") |                 raise Exception("invalid zoom for qr.txt; must be 1 or 2") | ||||||
|             with open(ap, "wb") as f: |             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"): |         elif ap.endswith(".svg"): | ||||||
|             with open(ap, "wb") as f: |             with open(ap, "wb") as f: | ||||||
|                 f.write(qr2svg(qrc, pad).encode("utf-8")) |                 f.write(qr2svg(qrc, pad).encode("utf-8")) | ||||||
|         else: |         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): |     def _h2i(self, hs): | ||||||
|         try: |         try: | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ copyparty/metrics.py, | ||||||
| copyparty/mtag.py, | copyparty/mtag.py, | ||||||
| copyparty/multicast.py, | copyparty/multicast.py, | ||||||
| copyparty/pwhash.py, | copyparty/pwhash.py, | ||||||
|  | copyparty/qrkode.py, | ||||||
| copyparty/res, | copyparty/res, | ||||||
| copyparty/res/__init__.py, | copyparty/res/__init__.py, | ||||||
| copyparty/res/COPYING.txt, | copyparty/res/COPYING.txt, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue