Created
February 24, 2026 14:50
-
-
Save huynhbaoan/519b0fc8ee12d8d680318183ae4a8c5f to your computer and use it in GitHub Desktop.
Split
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Generate numbered QR PNGs from an existing base64 file (single line or multi-line). | |
| Workflow (you already have the .b64): | |
| 1) This script reads the .b64 text, strips ALL whitespace (handles Qrox+ newline/space behavior too), | |
| 2) Splits into chunks (default 2000 chars total payload INCLUDING header), | |
| 3) Prefixes each chunk with a fixed header: ###0000### (10 chars), | |
| 4) Generates QR images (Version 40, Error L) with screen→phone friendly sizing. | |
| Example: | |
| python3 qr_from_b64.py --in github-diff.txt.b64 --outdir qrs --chunk 2000 | |
| Outputs: | |
| qrs/qr_0000.png | |
| qrs/qr_0001.png | |
| ... | |
| """ | |
| import argparse | |
| import math | |
| import os | |
| import re | |
| from pathlib import Path | |
| import qrcode | |
| from qrcode.constants import ERROR_CORRECT_L | |
| HEADER_FMT = "###%04d###" # length = 10 | |
| HEADER_LEN = len(HEADER_FMT % 0) | |
| def read_b64_stripped(path: Path) -> str: | |
| # Strip ALL whitespace so the payload is deterministic. | |
| s = path.read_text(encoding="utf-8", errors="strict") | |
| # remove spaces, tabs, CRLF, etc. | |
| return re.sub(r"\s+", "", s) | |
| def chunk_with_headers(b64: str, chunk_total: int) -> list[str]: | |
| if chunk_total <= HEADER_LEN: | |
| raise ValueError(f"--chunk must be > {HEADER_LEN} because header is {HEADER_LEN} chars") | |
| payload_len = chunk_total - HEADER_LEN | |
| total = math.ceil(len(b64) / payload_len) | |
| frames = [] | |
| for i in range(total): | |
| start = i * payload_len | |
| end = start + payload_len | |
| header = HEADER_FMT % i | |
| frames.append(header + b64[start:end]) | |
| return frames | |
| def make_qr_png(data: str, out_path: Path, box_size: int, border: int) -> None: | |
| qr = qrcode.QRCode( | |
| version=40, # lock density (avoid auto changes) | |
| error_correction=ERROR_CORRECT_L, # L = max capacity | |
| box_size=box_size, # screen→phone friendly | |
| border=border, # quiet zone | |
| ) | |
| qr.add_data(data) | |
| qr.make(fit=False) # MUST be False since version is locked | |
| img = qr.make_image(fill_color="black", back_color="white") | |
| img.save(out_path) | |
| def main() -> None: | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--in", dest="infile", required=True, help="Input .b64 text file") | |
| ap.add_argument("--outdir", required=True, help="Output directory for PNGs") | |
| ap.add_argument("--chunk", type=int, default=2000, | |
| help="Total characters per QR INCLUDING header (default: 2000)") | |
| ap.add_argument("--box", type=int, default=6, help="QR box_size (default: 6)") | |
| ap.add_argument("--border", type=int, default=2, help="QR border (default: 2)") | |
| ap.add_argument("--dry-run", action="store_true", help="Only print counts, do not generate PNGs") | |
| args = ap.parse_args() | |
| infile = Path(args.infile) | |
| outdir = Path(args.outdir) | |
| outdir.mkdir(parents=True, exist_ok=True) | |
| b64 = read_b64_stripped(infile) | |
| frames = chunk_with_headers(b64, args.chunk) | |
| total = len(frames) | |
| payload_len = args.chunk - HEADER_LEN | |
| print(f"Input chars (whitespace stripped): {len(b64)}") | |
| print(f"Header len: {HEADER_LEN} | Payload per QR: {payload_len} | Total QRs: {total}") | |
| print(f"QR params: version=40, EC=L, box_size={args.box}, border={args.border}") | |
| print(f"Output dir: {outdir.resolve()}") | |
| if args.dry_run: | |
| return | |
| # Generate PNGs | |
| for i, frame in enumerate(frames): | |
| out_path = outdir / f"qr_{i:04d}.png" | |
| make_qr_png(frame, out_path, box_size=args.box, border=args.border) | |
| print("Done.") | |
| print("Tip: open all PNGs in Preview (spacebar) and use → arrow for fast scanning.") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment