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_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