mirror of
https://github.com/9001/copyparty.git
synced 2025-10-10 02:22:21 -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