Home / All Skills / Docs & Knowledge / photo-to-scanned-pdf
Docs & Knowledge · daymade/claude-code-skills

photo-to-scanned-pdf

Two pipelines ending at a scanner-look PDF. (1) Phone photos of paper documents (contracts, stamped certificates, receipts, forms, handwritten notes) → clean scanner-quality PDF: perspective rectification + noteshrink whitening + A4 assembly + mandatory whole-document check. Trigger: "把照片 做成扫描件", "photos to scanned PDF", "make this look scanned", "手机拍的 文档转 PDF", "盖章文件扫描", replacing pages in an existing scanned PDF, any CamScanner-like request. (2) A digital document with no signature yet (rendered docx/PDF, confirmation form, contract draft) → make it look hand-signed and scanned: synthesize a handwriting-style signature, composite it onto the signature line, apply the same scan-look post-processing. Trigger: "帮我做个手写签名", "生成签名盖到这份文件上", "做成签过字的扫描件", "synthesize a signature", any request for a document that needs to look signed without a real photographed signature. Do NOT hand-roll levels/contrast enhancement for scan-look — tried and rejected twice; this skill's pipeline is the proven one.

L4 High-Privilege · Graded auth & full auditV1 Commit-PinnedExecute CommandData ExfiltrationNetwork AccessWrite FileDual-pass Audit
Author daymadeGitHub daymade/claude-code-skills ↗Stars 1385License MIT(仓库根 LICENSE;Copyright (c) 2025 daymade;GitHub API spdx MIT)Commit d5c4678cb5
Control levels describe skill capabilities and suggested control measures; they do not represent quality or safety certifications, nor do they dictate usability. Usage decisions rest entirely with users and administrators. Different agent hosts apply varying constraints; do not assume hosts provide automatic protection. Third-party skill copyrights belong to their original authors. Use at your own risk.

1How It Works & Mechanism

Both pipelines share the same target aesthetic and design philosophy: scripts strictly handle execution, while the agent retains two non-automatable human judgments—content-based page sorting and full-document verification.

daymade-docs/photo-to-scanned-pdf/SKILL.md
**Division of labor**: scripts carry execution; you (the agent) carry the two judgment steps — content-based page ordering, and whole-document verification. Neither can be automated away: filenames lie about order, and per-page spot checks miss wrong-slot bugs.
Main pipeline: rectify (photo_to_scan.py --raw) → content-based sorting + paper color judgment (agent) → noteshrink enhancement → assemble_pdf.py → contact sheet full-document verification.

Perspective rectification and white balance use in-house OpenCV scripts: photo_to_scan.py detects the largest bright quad and warps it to A4 (200 dpi); --raw mode only rectifies, while default mode additionally divides by the paper background color for pre-whitening colored pages. EXIF rotation is pre-fixed via PIL exif_transpose (cv2.imread ignores EXIF).

daymade-docs/photo-to-scanned-pdf/SKILL.md
The script handles EXIF rotation internally (`cv2.imread` ignores EXIF; phone photos come rotated — this silently produces sideways pages if you rectify with raw OpenCV).
The script is a PEP 723 single-file script run via uv run (auto-resolving opencv-python-headless/numpy/pillow); colored paper must be white-balanced before noteshrink, otherwise black ink turns blue-violet and red stamps turn crimson.

The 'scanned look' is achieved via palette quantization with noteshrink (uvx external tool): white paper batches use -w -g -K once for a global palette to unify ink/stamp colors, and -c "true" skips its internal PDF generation step so this skill handles assembly.

daymade-docs/photo-to-scanned-pdf/SKILL.md
uvx noteshrink -w -g -K -q -b ns -c "true" page_03.jpg page_01.jpg page_07.jpg ...
The SKILL repeatedly emphasizes not manually tuning levels/curves—a rule derived from two real user rejections; shell pitfalls such as zsh not word-splitting variables and requiring explicit filenames are also documented.

A4 assembly includes margin trimming: assemble_pdf.py crops 24px top / 12px sides (200 dpi) per page to remove desktop edges introduced by rectification, and img2pdf embeds indexed-color PNGs directly without re-encoding, outputting ~0.05MB/page.

daymade-docs/photo-to-scanned-pdf/SKILL.md
Edge crop (default 24px top / 12px sides at 200 dpi) removes the sliver of desk surface that rectification drags in along page borders; document margins dwarf it.
Replacing a page in an existing scanned PDF = keep the ns*/nc* working set, rerun that single page, locate the slot by content identity, and re-verify the full document.

Verification discipline requires 'mandatory contact sheet review of the full document': make_contact_sheet.py generates an overview sheet where the agent inspects page order, color cast, stamps, and signatures page-by-page, followed by spot-checking 1–2 pages at full resolution for sharpness.

daymade-docs/photo-to-scanned-pdf/SKILL.md
**"I verified the pages I changed" is not verification.**
Two real shipped-bug incidents back this rule: page-replacement off-by-one errors overwriting adjacent pages, and palette contamination affecting unedited pages—both would be missed if inspecting only modified pages.

The digital signature branch (digital-signature-synthesis.md) is executed via synthesize_signature.py across subcommands (fonts/candidates/generate/locate/composite/scanify); fonts must be picked by the user from a candidates sheet (agent must not choose), with --seed ensuring reproducibility.

daymade-docs/photo-to-scanned-pdf/references/digital-signature-synthesis.md
**Send the candidates sheet to the user and get a number back — do not pick one yourself.** Which style reads as a plausible signature is a taste call, not something derivable from the document or the font's technical properties.
The generator uses fc-list to detect CJK handwriting fonts (Hannotate/HanziPen/Xingkai/Kaiti/STKaiti families), applies per-character independent random rotation (-6°..3°) + tightened kerning (0.70–0.78×); scanify adds ±0.3° rotation, noise, brightness gradient, and JPEG q86 aging.

Synthesized signature positioning relies on pdftotext -bbox to locate signature line labels, with built-in fail-safes: when a label and underline merge into a single bounding box word, it prints a WARNING and guides scanning the right edge by pixels to prevent the signature from floating.

daymade-docs/photo-to-scanned-pdf/references/digital-signature-synthesis.md
This means `pdftotext` tokenized your label together with whatever sits right after it on the same line — most commonly a signature blank authored as underscores immediately following the label with no space (`簽名:_____`, an entirely ordinary way to type this in Word).
This WARNING scenario was 'verified to place a signature floating in empty page space, silently'—documenting an actual past bug as an explicit inspection checkpoint.

2Core Capabilities

01Phone photo → A4 perspective-rectified page (quad detection + EXIF correction + FULLFRAME fallback flag)
02noteshrink scan-look enhancement: white paper global palette / colored paper white-balance pre-wash separated pipelines
03A4 PDF assembly (img2pdf zero-reencoding + border trimming)
04Full-document contact sheet verification (page-by-page check for order, color cast, and stamps)
05Replacing/inserting pages in existing scanned PDFs by content identity (slots not inferred from filenames)
06Synthetic handwritten signature: font candidate comparison sheet → user selection → reproducible generation (--seed)
07Signature line pixel positioning + composite overlay + full-page scanify aging (scan visual consistency)

3External Dependencies

KindDependency / Endpoint
cliuvx noteshrink(扫描感增强,PEP 723 首跑 ~30s 构建)
clipoppler pdftoppm / pdftotext(contact sheet / -bbox 定位)
packageopencv-python-headless / numpy / pillow / img2pdf(PEP 723 内联依赖)
cliLibreOffice soffice(签名分支把 docx 渲染成页图)
clifc-list(fontconfig,发现手写体字体)

4Recommended Governance & Evidence

Suggested Controls for Safe Operation
For Agent Runtime
Graded/per-use authorization, sandbox recommended
For Administrators
Approval workflow, rollback support, full audit
Evidence & Triggers (File Paths & Excerpts)
  • scripts/: Execute local scripts or commands: python photo_to_scan.py / assemble_pdf.py / make_contact_sheet.py / synthes
  • 外部端点: Exfiltrate local content or conversation to third-party cloud storage
  • 本地工作目录: File read/write and artifact output: rectify/enhance intermediate PNGs, assembled scanned.pdf, contact.png written to user-specified working directory; signature branch writes signatures
Audit Notes: The code attack surface belongs to the blue tier (purely local image tools, no network egress, no credentials, no destructive deletion). However, the second pipeline deliberately generates realistic scanned documents that appear hand-signed—high-fidelity output from synthetic signatures and scan-aging carries deceptive side effects with real risks of misuse (faking the appearance of contracts, confirmation forms, or authorization letters), warranting a yellow rating under the 'when in doubt, bump up a tier' rule. There are no authorization or usage guardrails within the documentation, so the host environment must strictly regulate trigger scenarios and ensure recipient awareness.

5Second-Pass Verification

  • [ok] 主流水线 rectify→noteshrink→assemble→contact sheet — 四个环节命令与脚本均在;photo_to_scan.py --raw 与默认白平衡模式分支存在。
  • [ok] EXIF/quad/白平衡实现 — load_exif_corrected 用 ImageOps.exif_transpose;find_page_quad/warp_a4/flatten_whitebalance 函数在 photo_to_scan.py 源码可定位。
  • [ok] 边缘裁剪默认值 24/12px — assemble_pdf.py argparse default crop_top=24/crop_side=12 与 SKILL 一致。
  • [ok] noteshrink 参数 -w -g -K -c "true" — SKILL 命令与外部工具语义一致(noteshrink 为 uvx 外部包,未在本仓捆绑,命令调用方式可核)。
  • [ok] 『只查改动页不算验证』纪律 — SKILL 原文 + 两个 shipped-bug 叙事(错槽覆盖邻页、调色板污染)在 Step 5。
  • [ok] 签名分支六子命令 — synthesize_signature.py subparsers fonts/candidates/generate/locate/composite/scanify 均实现;candidates→用户挑选→generate 流程与 reference 一致。
  • [ok] 候选人选门禁(agent 不自选字体) — reference 原文 'do not pick one yourself';脚本 generate 需 --font/--face/--ink 显式参数。
  • [ok] 无网络/凭证/删除面 — 全脚本扫描零网络代码、零 env 密钥读取;无删除用户文件逻辑;子进程均列表参数。

6Verdict & Scenarios

  • The photo-to-scan quality pipeline is battle-tested (relying on noteshrink rather than manual curve adjustments); separated handling of white/colored paper prevents color-cast issues.
  • The full-document verification discipline prevents two real classes of shipped bugs (page overwrites and palette contamination) at the minimal cost of a single contact sheet.
  • High engineering quality in signature synthesis: human-in-the-loop font selection, reproducible seeds, and bbox-merge WARNING guards against floating signatures, turning critical failure points into explicit checks.
  • Systematic defense against untrusted filenames (content-based ordering, explicit filenames, content-identity slot matching) is consistently enforced across both pipelines.
  • Best for: Ideal for routine office tasks converting mobile photos of paper documents (contracts, stamped certificates, receipts, handwritten notes) into clean scanned PDFs. The signature branch is suitable for users who hold signing authority and need a digitized handwritten-signature appearance, or for demonstration/educational scenarios.
    Not for: Not suitable for forging signatures or tampering with document authenticity in any scenario (usage risk, not technical limitation); not suitable for archival requirements requiring true scanner-grade DPI/color accuracy; not suitable in environments lacking poppler/noteshrink.
    Install Agent-Ready
    ① Verified Mirror 2026-09-09
    Option A · Direct DownloadDownload photo-to-scanned-pdf.tar.gz
    sha256: 0e8f936e0a123f3a…
    Option B · Copy Guide for Agent
    install.json
    Agents automatically download and verify mirrors from this JSON guide.
    ② Upstream GitHub
    GitHub Repo ↗
    Pinned to commit d5c4678cb5.