Scanned PDF Text Repair
Use this skill for image-only PDFs where text extraction returns empty or does not contain the target text. The goal is visual repair: rebuild the page image with a small localized patch, not semantic PDF text editing.
Workflow
Inspect the PDF before editing.
- Count pages with PyMuPDF.
- Extract text with
page.get_text().
- If the target text is absent and the page has one or more images, treat it as scanned/image-only.
- Render the page at 2x or 3x scale and inspect the target area.
- Prefer
scripts/inspect_pdf.py for this step.
Locate the exact patch.
- Use rendered pixel coordinates, not guessed PDF point coordinates.
- Crop the target line and identify dark-pixel runs for each character.
- Patch only the old character/phrase plus a tiny margin.
- Do not repaint surrounding text unless the user explicitly wants phrase-level replacement.
- Use
scripts/find_dark_runs.py on the rendered page or crop when exact character bounds are unclear.
Rebuild from the original PDF every time.
- Never edit on top of a previous repaired PDF during iterations.
- Re-render the original page, apply one patch, and create the final PDF from that result.
- This prevents cumulative blur, halos, and dirty paper blocks.
Match visual style.
- Use nearby original characters as the reference for font family, size, baseline, weight, and gray level.
- For Chinese official documents, test
simfang.ttf, STFANGSO.TTF, simsun.ttc, STSONG.TTF, and simkai.ttf when available.
- Prefer FangSong/simfang when the body text resembles Chinese official-document FangSong.
- Match the replacement glyph's bottom alignment to adjacent characters before judging top alignment.
- If the user says the glyph is too thin, increase stroke weight or opacity before changing font family.
- If the user says the glyph is too small, increase font size and adjust baseline downward as needed.
- Use
scripts/font_variants.py to generate a contact sheet when visual matching is not obvious.
Preserve the paper background.
- Build the cover patch from local bright pixels around the target area.
- Add slight deterministic noise instead of a flat white rectangle.
- Apply small Gaussian blur to replacement text only when needed to match scan softness.
Verify output.
- Render the repaired PDF.
- Crop the target line and inspect it visually.
- Check page count and page size are unchanged.
- For gray/weight checks, compare dark-pixel count and mean grayscale against adjacent original characters.
- Report the output file and the specific text that changed.
Script
Use these scripts instead of rewriting one-off Python snippets:
scripts/inspect_pdf.py: inspect text layer, image count, page size, render a page, and make a crop.
scripts/find_dark_runs.py: locate dark-pixel runs inside a rendered image area to estimate character boxes.
scripts/font_variants.py: generate font/size preview sheets on the real target crop.
scripts/repair_scanned_pdf_text.py: apply the final localized repair. It preserves other PDF pages by default; pass --single-page only when a one-page output is desired.
Do not preserve every exploratory command as a script. Keep only reusable steps: inspect, locate, preview, repair, verify.
Repair Script
Typical use:
py -X utf8 C:\Users\zhouh\.codex\skills\pdf-patch\scripts\repair_scanned_pdf_text.py `
--input .\source.pdf `
--output .\source_已修改.pdf `
--page 1 `
--cover 651,518,688,561 `
--text 为 `
--position 654,525 `
--font C:\Windows\Fonts\simfang.ttf `
--size 30 `
--fill 22,25,30,250 `
--blur 0.16
Render and crop after writing:
py -X utf8 C:\Users\zhouh\.codex\skills\pdf-patch\scripts\repair_scanned_pdf_text.py `
--input .\source.pdf `
--output .\source_已修改.pdf `
--page 1 `
--cover 651,518,688,561 `
--text 为 `
--position 654,525 `
--font C:\Windows\Fonts\simfang.ttf `
--size 30 `
--render-check .\check.png `
--crop-check .\check-crop.png `
--crop 620,505,1015,585
Inspect and crop before editing:
py -X utf8 C:\Users\zhouh\.codex\skills\pdf-patch\scripts\inspect_pdf.py `
--input .\source.pdf `
--page 1 `
--render .\page-1.png `
--crop 620,505,1015,585 `
--crop-out .\target-crop.png
Find candidate character runs:
py -X utf8 C:\Users\zhouh\.codex\skills\pdf-patch\scripts\find_dark_runs.py `
--image .\page-1.png `
--box 620,505,1015,585 `
--threshold 180
Generate font/size previews:
py -X utf8 C:\Users\zhouh\.codex\skills\pdf-patch\scripts\font_variants.py `
--image .\page-1.png `
--output .\font-variants.png `
--preview-crop 620,505,1015,585 `
--cover 651,518,688,561 `
--text 为 `
--position 654,525 `
--fonts C:\Windows\Fonts\simfang.ttf C:\Windows\Fonts\STFANGSO.TTF C:\Windows\Fonts\simsun.ttc `
--sizes 28 29 30 31
Coordinate Notes
--cover, --position, and --crop are rendered image coordinates, not PDF points.
- The default render scale is
2, matching a standard A4-ish PDF render near 1191 x 1685.
- If the source page has a different resolution or orientation, render first and locate coordinates on that rendered image.
- For single-character Chinese repair, cover about the original character bounding box plus 2-5 pixels of margin.
Failure Modes
- Visible font mismatch: test nearby system Chinese fonts and use adjacent characters to score shape similarity.
- Flat white block: use local paper sampling and noise; reduce cover size.
- Glyph too high or low: adjust only
--position y first.
- Glyph too thin or light: increase alpha, add
--stroke-width 1, or use a darker fill.
- Glyph too bold: remove stroke, lower alpha, or add a small blur.
- Repeated artifacts after iterations: confirm the script input is the original PDF, not the previous output.
1---2name: pdf-patch3description: Repair or replace short text in scanned or image-only PDF pages while preserving the visual appearance. Use when Codex needs to edit a PDF that has no text layer, replace one Chinese character or a short phrase, cover old pixels, match font size, weight, baseline, gray level, blur, and verify the rendered result with screenshots.4license: MIT5---67# Scanned PDF Text Repair89Use this skill for image-only PDFs where text extraction returns empty or does not contain the target text. The goal is visual repair: rebuild the page image with a small localized patch, not semantic PDF text editing.1011## Workflow12131. Inspect the PDF before editing.14 - Count pages with PyMuPDF.15 - Extract text with `page.get_text()`.16 - If the target text is absent and the page has one or more images, treat it as scanned/image-only.17 - Render the page at 2x or 3x scale and inspect the target area.18 - Prefer `scripts/inspect_pdf.py` for this step.19202. Locate the exact patch.21 - Use rendered pixel coordinates, not guessed PDF point coordinates.22 - Crop the target line and identify dark-pixel runs for each character.23 - Patch only the old character/phrase plus a tiny margin.24 - Do not repaint surrounding text unless the user explicitly wants phrase-level replacement.25 - Use `scripts/find_dark_runs.py` on the rendered page or crop when exact character bounds are unclear.26273. Rebuild from the original PDF every time.28 - Never edit on top of a previous repaired PDF during iterations.29 - Re-render the original page, apply one patch, and create the final PDF from that result.30 - This prevents cumulative blur, halos, and dirty paper blocks.31324. Match visual style.33 - Use nearby original characters as the reference for font family, size, baseline, weight, and gray level.34 - For Chinese official documents, test `simfang.ttf`, `STFANGSO.TTF`, `simsun.ttc`, `STSONG.TTF`, and `simkai.ttf` when available.35 - Prefer FangSong/simfang when the body text resembles Chinese official-document FangSong.36 - Match the replacement glyph's bottom alignment to adjacent characters before judging top alignment.37 - If the user says the glyph is too thin, increase stroke weight or opacity before changing font family.38 - If the user says the glyph is too small, increase font size and adjust baseline downward as needed.39 - Use `scripts/font_variants.py` to generate a contact sheet when visual matching is not obvious.40415. Preserve the paper background.42 - Build the cover patch from local bright pixels around the target area.43 - Add slight deterministic noise instead of a flat white rectangle.44 - Apply small Gaussian blur to replacement text only when needed to match scan softness.45466. Verify output.47 - Render the repaired PDF.48 - Crop the target line and inspect it visually.49 - Check page count and page size are unchanged.50 - For gray/weight checks, compare dark-pixel count and mean grayscale against adjacent original characters.51 - Report the output file and the specific text that changed.5253## Script5455Use these scripts instead of rewriting one-off Python snippets:5657- `scripts/inspect_pdf.py`: inspect text layer, image count, page size, render a page, and make a crop.58- `scripts/find_dark_runs.py`: locate dark-pixel runs inside a rendered image area to estimate character boxes.59- `scripts/font_variants.py`: generate font/size preview sheets on the real target crop.60- `scripts/repair_scanned_pdf_text.py`: apply the final localized repair. It preserves other PDF pages by default; pass `--single-page` only when a one-page output is desired.6162Do not preserve every exploratory command as a script. Keep only reusable steps: inspect, locate, preview, repair, verify.6364## Repair Script6566Typical use:6768```powershell69py -X utf8 C:\Users\zhouh\.codex\skills\pdf-patch\scripts\repair_scanned_pdf_text.py `70 --input .\source.pdf `71 --output .\source_已修改.pdf `72 --page 1 `73 --cover 651,518,688,561 `74 --text 为 `75 --position 654,525 `76 --font C:\Windows\Fonts\simfang.ttf `77 --size 30 `78 --fill 22,25,30,250 `79 --blur 0.1680```8182Render and crop after writing:8384```powershell85py -X utf8 C:\Users\zhouh\.codex\skills\pdf-patch\scripts\repair_scanned_pdf_text.py `86 --input .\source.pdf `87 --output .\source_已修改.pdf `88 --page 1 `89 --cover 651,518,688,561 `90 --text 为 `91 --position 654,525 `92 --font C:\Windows\Fonts\simfang.ttf `93 --size 30 `94 --render-check .\check.png `95 --crop-check .\check-crop.png `96 --crop 620,505,1015,58597```9899Inspect and crop before editing:100101```powershell102py -X utf8 C:\Users\zhouh\.codex\skills\pdf-patch\scripts\inspect_pdf.py `103 --input .\source.pdf `104 --page 1 `105 --render .\page-1.png `106 --crop 620,505,1015,585 `107 --crop-out .\target-crop.png108```109110Find candidate character runs:111112```powershell113py -X utf8 C:\Users\zhouh\.codex\skills\pdf-patch\scripts\find_dark_runs.py `114 --image .\page-1.png `115 --box 620,505,1015,585 `116 --threshold 180117```118119Generate font/size previews:120121```powershell122py -X utf8 C:\Users\zhouh\.codex\skills\pdf-patch\scripts\font_variants.py `123 --image .\page-1.png `124 --output .\font-variants.png `125 --preview-crop 620,505,1015,585 `126 --cover 651,518,688,561 `127 --text 为 `128 --position 654,525 `129 --fonts C:\Windows\Fonts\simfang.ttf C:\Windows\Fonts\STFANGSO.TTF C:\Windows\Fonts\simsun.ttc `130 --sizes 28 29 30 31131```132133## Coordinate Notes134135- `--cover`, `--position`, and `--crop` are rendered image coordinates, not PDF points.136- The default render scale is `2`, matching a standard A4-ish PDF render near `1191 x 1685`.137- If the source page has a different resolution or orientation, render first and locate coordinates on that rendered image.138- For single-character Chinese repair, cover about the original character bounding box plus 2-5 pixels of margin.139140## Failure Modes141142- Visible font mismatch: test nearby system Chinese fonts and use adjacent characters to score shape similarity.143- Flat white block: use local paper sampling and noise; reduce cover size.144- Glyph too high or low: adjust only `--position` y first.145- Glyph too thin or light: increase alpha, add `--stroke-width 1`, or use a darker fill.146- Glyph too bold: remove stroke, lower alpha, or add a small blur.147- Repeated artifacts after iterations: confirm the script input is the original PDF, not the previous output.