A Frappe app that turns any allowlisted document into a verifiable, signable PDF — with a Chromium-based PDF pipeline, QR-backed verification, optional content hashing, and real PAdES digital signatures recognised by Adobe Acrobat.
- Verified QR — a tamper-evident record per signed document. UUIDv4 + optional SHA-256 content hash + signatory metadata.
- Public verify page at
/verify_document/— camera scanner that returnsvalid/tampered/invalidfor any QR. - Chromium PDF pipeline — renders Frappe print formats via headless Chromium (Playwright) instead of wkhtmltopdf. Modern CSS, flex/grid, web fonts, all work.
- Live-preview print dialog — an in-browser preview iframe that regenerates on every option change.
- Multi-copy PDF — 1–5 copies in one PDF, each stamped with
ORIGINAL/DUPLICATE/ custom labels. - Per-page header/footer modes — all pages / first only / last only / first+last / none. Letterhead + copy badge respects the mode.
- Dialog-driven QR insertion — overlay a Verified QR in any of four corners. Auto-detects existing
scan-me-qrmarkers to avoid double-insertion. - Acrobat-style signature block — Verified QRs render as an Acrobat Sign-style card in the PDF with handwritten signature, signatory details, content hash, tamper status.
- PAdES digital signatures (PyHanko) — optional cryptographic signing that Adobe Reader recognises. Self-signed cert auto-generated on first use; drop a CA-issued PKCS#12 to graduate to the green "Signed and all signatures are valid" badge.
- Lockable audit trail — optional absolute lock on Verified QR records (no override, even for Administrator).
- Jinja helpers —
qr,barcode,qr_link,qr_img,qr_link_img,verify_qr,verify_qr_imgfor use in any print format. - Sales Invoice Scan Me — bundled print format covering company/customer info, item description, HSN, per-line discount + net rate, tax breakdown, totals, payment schedule, bank, terms, and the scan-to-verify QR.
cd $PATH_TO_YOUR_BENCH
bench get-app https://github.com/Tusharp21/scan_me.git
bench install-app scan_meThat's it — Playwright's Chromium browser (~200 MB) is downloaded automatically
by the after_install hook so PDF generation works immediately. The same hook
runs on bench migrate and only downloads if Chromium is missing, so upgrades
stay fast.
If the auto-download fails (restricted network, no write access to the browser cache, etc.), Scan Me logs the error and shows a message on first use. Run this once on the server to recover:
./env/bin/python -m playwright install chromiumThe app pins:
qrcode~=8.2— QR generationpython-barcode~=0.16.1— CODE128 and friendsplaywright~=1.47— Chromium PDF renderingpypdf~=6.1.3— PDF concatenation for multi-copypyHanko~=0.25— PAdES digital signaturescryptography>=41— self-signed cert generation
All install automatically via bench install-app.
Go to Scan Me Settings (desk) and:
- Under Doctype, add the DocTypes you want to expose for QR signing (e.g.
Sales Invoice,Purchase Order). Tickenablefor each; ticksignature_requiredif users must draw a signature before generating the QR. - Under Verification & Signing, decide:
- Allow Multiple Signers — N-party signing.
- Embed Content Hash in QR — tamper detection on doc edits.
- Lock Verified QR After Insert — absolute lock, no override.
- Use Stored User Signature Only — rejects client images; reads from a User custom field named
user_signature/signature/signature_image(in that order). - Apply Cryptographic PDF Signature (PAdES) — run every generated PDF through PyHanko.
- Under Print Dialog Features, toggle which sections of the print dialog your users see.
The first time PAdES signing runs, Scan Me auto-generates a 5-year self-signed PKCS#12 at sites/<site>/private/files/scan_me/signing.pfx and stores its password in site_config.json under scan_me_signing_password.
Adobe Reader will show a yellow "signer's identity is unknown" banner until users manually trust the cert — or you replace the PKCS#12 with a CA-issued bundle. To upgrade:
- Obtain a PKCS#12 bundle from a trusted CA (eMudhra, Sectigo, DigiCert, etc.).
- Replace
sites/<site>/private/files/scan_me/signing.pfxwith the new file. - Update
scan_me_signing_passwordinsite_config.jsonto the bundle's password. - No code change required — the next signed PDF will use the new cert and Adobe will show green.
- Open a doc of an allowlisted DocType (e.g. a Sales Invoice).
- Click Generate Verified QR in the form's menu.
- Draw a signature if prompted. A Verified QR record is created; content hash + signer identity captured per your settings.
- Menu → Print → select a print format (the bundled "Sales Invoice Scan Me" works out of the box).
- Click the blue Generate PDF button in the toolbar — opens the Scan Me dialog.
- Configure copies, header/footer, QR, signature block, PAdES signing. Watch the iframe preview update live.
- Download PDF.
- Open
https://<site>/verify_document/on a phone or laptop with a camera. - Scan the QR on the printed/PDF document.
- Page shows
valid(green),tampered(red, hash mismatch), orinvalid(UUID unknown).
{# Any string #}
{{ qr("encode-me") }} {# returns data URI #}
{{ barcode("12345", "code128") }} {# returns data URI #}
{# With class="scan-me-qr" marker — print dialog skips adding a second QR #}
{{ qr_link_img(doc.doctype, doc.name) }} {# desk URL #}
{{ verify_qr_img(doc.doctype, doc.name) }} {# Verified QR payload, empty if doc unsigned #}See docs/security.md for the full threat model. Short version:
- ✅ Rate-limited public verify endpoint.
- ✅ SHA-256 content hashing when enabled — edits after signing auto-invalidate.
- ✅ Absolute lock on Verified QR records when enabled — no role can override.
- ✅ Path-traversal safe: image URLs in print formats can't escape
sites/<site>/*/files/or benchassets/. - ✅ Size/format validation on client-supplied signature images (PNG/JPEG, max 2 MB).
- ✅ PAdES: real PDF signatures via PyHanko, Adobe-compatible.
- ⚠ Without CA-issued cert, Adobe shows "unknown signer" — manual trust required per reader.
- ⚠ No RFC-3161 trusted timestamp yet —
signed_onis the server clock.
Tier 2 (HMAC + revocation) and Tier 3 (Aadhaar eSign / hardware tokens) are on the roadmap.
This app uses pre-commit for formatting and linting:
cd apps/scan_me
pre-commit installTools: ruff, eslint, prettier, pyupgrade. CI runs Frappe Semgrep Rules + pip-audit on PRs.
MIT — see license.txt.