write qrcode to file

This commit is contained in:
ed 2025-08-21 23:06:39 +00:00
parent cc4f4aef99
commit 202ddeac0d
4 changed files with 52 additions and 2 deletions

View file

@ -1115,6 +1115,7 @@ def add_qr(ap, tty):
ap2.add_argument("--qrz", metavar="N", type=int, default=0, help="[\033[32m1\033[0m]=1x, [\033[32m2\033[0m]=2x, [\033[32m0\033[0m]=auto (try [\033[32m2\033[0m] on broken fonts)") ap2.add_argument("--qrz", metavar="N", type=int, default=0, help="[\033[32m1\033[0m]=1x, [\033[32m2\033[0m]=2x, [\033[32m0\033[0m]=auto (try [\033[32m2\033[0m] on broken fonts)")
ap2.add_argument("--qr-pin", metavar="N", type=int, default=0, help="sticky/pin the qr-code to always stay on-screen; [\033[32m0\033[0m]=disabled, [\033[32m1\033[0m]=with-url, [\033[32m2\033[0m]=just-qr") ap2.add_argument("--qr-pin", metavar="N", type=int, default=0, help="sticky/pin the qr-code to always stay on-screen; [\033[32m0\033[0m]=disabled, [\033[32m1\033[0m]=with-url, [\033[32m2\033[0m]=just-qr")
ap2.add_argument("--qr-wait", metavar="SEC", type=float, default=0, help="wait \033[33mSEC\033[0m before printing the qr-code to the log") ap2.add_argument("--qr-wait", metavar="SEC", type=float, default=0, help="wait \033[33mSEC\033[0m before printing the qr-code to the log")
ap2.add_argument("--qr-file", metavar="TXT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m write qr-code to file.\n └─To create txt or svg, \033[33mTXT\033[0m is Filepath:Zoom:Pad, for example [\033[32mqr.txt:1:2\033[0m]\n └─To create png or gif, \033[33mTXT\033[0m is Filepath:Zoom:Pad:Foreground:Background, for example [\033[32mqr.png:8:2:333333:ffcc55\033[0m], or [\033[32mqr.png:8:2::ffcc55\033[0m] for transparent")
def add_fs(ap): def add_fs(ap):

View file

@ -200,6 +200,25 @@ class QrCode(object):
return "\n".join(rows) 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):

View file

@ -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 from .stolen.qrcodegen import QrCode, qr2svg
from .util import ( from .util import (
E_ACCESS, E_ACCESS,
E_ADDR_IN_USE, E_ADDR_IN_USE,
@ -621,6 +621,10 @@ 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 = QrCode.encode_binary(btxt)
for zs in self.args.qr_file or []:
self._qr2file(qrc, zs)
if zoom == 0: if zoom == 0:
try: try:
tw, th = termsize() tw, th = termsize()
@ -656,3 +660,29 @@ class TcpSrv(object):
t = t.replace("\n", "`\n`") t = t.replace("\n", "`\n`")
return txt + t return txt + t
def _qr2file(self, qrc: QrCode, txt: str):
if ".txt:" in txt or ".svg:" in txt:
ap, zs1, zs2 = txt.rsplit(":", 2)
bg = fg = ""
else:
ap, zs1, zs2, bg, fg = txt.rsplit(":", 4)
zoom = int(zs1)
pad = int(zs2)
if ap.endswith(".txt"):
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"))
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)
def _h2i(self, hs):
try:
return tuple(int(hs[i : i + 2], 16) for i in (0, 2, 4))
except:
return None

View file

@ -170,7 +170,7 @@ class Cfg(Namespace):
ex = "ban_403 ban_404 ban_422 ban_pw ban_pwc ban_url spinner" ex = "ban_403 ban_404 ban_422 ban_pw ban_pwc ban_url spinner"
ka.update(**{k: "no" for k in ex.split()}) ka.update(**{k: "no" for k in ex.split()})
ex = "ext_th grp idp_h_usr idp_hm_usr ipr on403 on404 xac xad xar xau xban xbc xbd xbr xbu xiu xm" ex = "ext_th grp idp_h_usr idp_hm_usr ipr on403 on404 qr_file xac xad xar xau xban xbc xbd xbr xbu xiu xm"
ka.update(**{k: [] for k in ex.split()}) ka.update(**{k: [] for k in ex.split()})
ex = "exp_lg exp_md" ex = "exp_lg exp_md"