PDF to Markdown
Create a validated Markdown sibling while preserving the PDF as the visual
authority. Default to report.pdf plus report.md; do not retain parser JSON,
coordinates, extracted assets, or chunks unless the user names a consumer for
them.
Convert safely
Resolve each source PDF and intended output path. Process multiple PDFs one
at a time so each result can be validated independently.
If the intended output exists, use --overwrite only when the user has
explicitly authorized replacing that exact file. If that authority is
missing, stop before conversion. Do not invent an alternate destination;
use another path only when the user selects or approves it.
Check for the xberg executable. If it is missing, identify the current
official installation method for the host platform and use applicable user
approval, requesting it only when absent. Do not run an install script,
package manager, or model download prewarming beyond that approval.
Resolve scripts/convert_pdf.py relative to this SKILL.md and run:
python3 scripts/convert_pdf.py /absolute/path/report.pdf
The wrapper writes report.md atomically, preserves the PDF, rejects empty
or structurally inconsistent output, and never invokes a shell. It asks
Xberg for JSON so it can validate every physical page, but publishes only
Xberg's whole-document Markdown and retains no parser JSON.
The wrapper's ordinary Xberg extraction uses these core flags:
xberg extract /absolute/path/report.pdf \
--no-config-discovery \
--format json \
--content-format markdown \
--extract-pages true \
--page-markers true
When page markers are enabled, the wrapper also supplies a randomized
private marker through --config-json; it later converts only those private
markers to <!-- PAGE n -->. A direct fallback must use an equivalently
collision-resistant private marker rather than public page comments during
validation.
The wrapper validates that Xberg's page records form a contiguous 1-based
sequence and agree with its declared page count. If Xberg omits markers for
pages it classifies as blank, the wrapper restores those markers from page
metadata while preserving Xberg's Markdown body unchanged.
It refuses publication when marker and page metadata cannot be reconciled.
Do not rebuild the document by concatenating per-page Markdown because that
can reset lists and damage structures spanning page boundaries. Do not
replace the wrapper with a direct shell redirect when creating the final
artifact.
For a PDF that contains scanned Korean pages, add --korean-ocr. This uses
PaddleOCR with explicit Korean selection and OCRs only pages classified as
scans while retaining native text elsewhere:
python3 scripts/convert_pdf.py /absolute/path/report.pdf --korean-ocr
The corresponding Xberg flags are:
xberg extract /absolute/path/report.pdf \
--no-config-discovery \
--format json \
--content-format markdown \
--extract-pages true \
--page-markers true \
--ocr true \
--ocr-backend paddle-ocr \
--ocr-language korean \
--ocr-scanned-pages
The first PaddleOCR use may download models as part of the requested
conversion. Tell the user before starting when network use or model storage
is material in the current environment.
Do not OCR a born-digital PDF merely because its language is Korean. Use
--force-ocr only when the entire document is image-only or its text layer is
demonstrably broken; combine it with --korean-ocr for Korean documents.
If Python is unavailable but Xberg is present, reproduce the wrapper's exact
flags with a command runner while preserving the same non-overwrite, temporary
output, nonempty-result, and atomic-finalization guarantees. If those guarantees
cannot be preserved, stop and report the missing capability.
Validate and escalate
After every conversion:
Inspect the beginning, middle, and end of the Markdown. Check headings,
paragraph order, tables, lists, code, Hangul where expected, numbers, dates,
missing pages, suspicious repetition, mojibake, and abrupt density changes.
Read the wrapper's validation summary. It reports physical, nonblank, and
blank page counts and, when nonzero, how many blank-page markers it restored.
Compare that total with an independent PDF page count when a PDF inspection
capability is available. A blank page can legitimately contain no text, but
it must still have a marker in the final Markdown unless --no-page-markers
was requested.
Repeated pdf_oxide dictionary-as-stream warnings are summarized rather than
dumped; they mean malformed or unusual PDF objects were treated as empty
streams, so inspect nearby visual content when fidelity is uncertain. The
wrapper also reports Xberg's structured processing warnings with bounded
output; treat them as page or pipeline-specific fidelity risks to inspect.
If ordinary extraction loses reading order, headings, tables, lists, or
figure placement, rerun to a temporary comparison file with --layout:
python3 scripts/convert_pdf.py /absolute/path/report.pdf \
--output /absolute/path/report.layout.md \
--layout
This adds --layout --layout-strategy auto --use-layout-for-markdown to the ordinary Xberg command.
Compare content fidelity, then replace the intended output only with the
user's overwrite authority. Do not assume that the larger output is better.
If layout extraction reports an ONNX Runtime API mismatch, treat that as the
cause even if a later worker message says Mutex poisoned. On Windows, check
whether an older C:\Windows\System32\onnxruntime.dll was loaded. Point
ORT_DYLIB_PATH at a compatible runtime only with user authority; do not
silently install or replace a system library. The wrapper publishes no
partial layout output after this failure.
If Korean OCR still omits or corrupts content, retry with --force-ocr only
when the native text layer is the cause. For complex visual pages that remain
unreliable, report the failing pages and offer a Docling or document-VLM
comparison rather than silently switching tools or installing another
parser.
Finish
Finish only when the Markdown exists, is nonempty, passed representative source
checks, and the source PDF remains unchanged. Report the PDF and Markdown paths,
Xberg version, OCR or layout options used, physical/nonblank/blank page counts,
restored marker count, summarized parser warnings, any overwritten file
explicitly authorized by the user, and any pages or structures that remain
uncertain.
1---2name: pdf-to-markdown3description: Convert PDF inputs into clean, reusable whole-document Markdown for agents, LLM ingestion, search, or archival text, using Xberg with safe output handling, Korean scan OCR, page markers, validation, and layout escalation. Use when the user asks to create, save, generate, normalize, reconvert, or extract a Markdown or whole-document agent-readable text artifact from a PDF. Do not use for summaries or questions with no converted artifact, PDF editing or rendering, parser research, or targeted extraction into CSV, JSON, images, or other structured formats.4---56# PDF to Markdown78Create a validated Markdown sibling while preserving the PDF as the visual9authority. Default to `report.pdf` plus `report.md`; do not retain parser JSON,10coordinates, extracted assets, or chunks unless the user names a consumer for11them.1213## Convert safely14151. Resolve each source PDF and intended output path. Process multiple PDFs one16 at a time so each result can be validated independently.172. If the intended output exists, use `--overwrite` only when the user has18 explicitly authorized replacing that exact file. If that authority is19 missing, stop before conversion. Do not invent an alternate destination;20 use another path only when the user selects or approves it.213. Check for the `xberg` executable. If it is missing, identify the current22 official installation method for the host platform and use applicable user23 approval, requesting it only when absent. Do not run an install script,24 package manager, or model download prewarming beyond that approval.254. Resolve `scripts/convert_pdf.py` relative to this `SKILL.md` and run:2627 ```bash28 python3 scripts/convert_pdf.py /absolute/path/report.pdf29 ```3031 The wrapper writes `report.md` atomically, preserves the PDF, rejects empty32 or structurally inconsistent output, and never invokes a shell. It asks33 Xberg for JSON so it can validate every physical page, but publishes only34 Xberg's whole-document Markdown and retains no parser JSON.3536 The wrapper's ordinary Xberg extraction uses these core flags:3738 ```bash39 xberg extract /absolute/path/report.pdf \40 --no-config-discovery \41 --format json \42 --content-format markdown \43 --extract-pages true \44 --page-markers true45 ```4647 When page markers are enabled, the wrapper also supplies a randomized48 private marker through `--config-json`; it later converts only those private49 markers to `<!-- PAGE n -->`. A direct fallback must use an equivalently50 collision-resistant private marker rather than public page comments during51 validation.5253 The wrapper validates that Xberg's page records form a contiguous 1-based54 sequence and agree with its declared page count. If Xberg omits markers for55 pages it classifies as blank, the wrapper restores those markers from page56 metadata while preserving Xberg's Markdown body unchanged.57 It refuses publication when marker and page metadata cannot be reconciled.58 Do not rebuild the document by concatenating per-page Markdown because that59 can reset lists and damage structures spanning page boundaries. Do not60 replace the wrapper with a direct shell redirect when creating the final61 artifact.625. For a PDF that contains scanned Korean pages, add `--korean-ocr`. This uses63 PaddleOCR with explicit Korean selection and OCRs only pages classified as64 scans while retaining native text elsewhere:6566 ```bash67 python3 scripts/convert_pdf.py /absolute/path/report.pdf --korean-ocr68 ```6970 The corresponding Xberg flags are:7172 ```bash73 xberg extract /absolute/path/report.pdf \74 --no-config-discovery \75 --format json \76 --content-format markdown \77 --extract-pages true \78 --page-markers true \79 --ocr true \80 --ocr-backend paddle-ocr \81 --ocr-language korean \82 --ocr-scanned-pages83 ```8485 The first PaddleOCR use may download models as part of the requested86 conversion. Tell the user before starting when network use or model storage87 is material in the current environment.8889Do not OCR a born-digital PDF merely because its language is Korean. Use90`--force-ocr` only when the entire document is image-only or its text layer is91demonstrably broken; combine it with `--korean-ocr` for Korean documents.9293If Python is unavailable but Xberg is present, reproduce the wrapper's exact94flags with a command runner while preserving the same non-overwrite, temporary95output, nonempty-result, and atomic-finalization guarantees. If those guarantees96cannot be preserved, stop and report the missing capability.9798## Validate and escalate99100After every conversion:1011021. Inspect the beginning, middle, and end of the Markdown. Check headings,103 paragraph order, tables, lists, code, Hangul where expected, numbers, dates,104 missing pages, suspicious repetition, mojibake, and abrupt density changes.1052. Read the wrapper's validation summary. It reports physical, nonblank, and106 blank page counts and, when nonzero, how many blank-page markers it restored.107 Compare that total with an independent PDF page count when a PDF inspection108 capability is available. A blank page can legitimately contain no text, but109 it must still have a marker in the final Markdown unless `--no-page-markers`110 was requested.111 Repeated `pdf_oxide` dictionary-as-stream warnings are summarized rather than112 dumped; they mean malformed or unusual PDF objects were treated as empty113 streams, so inspect nearby visual content when fidelity is uncertain. The114 wrapper also reports Xberg's structured processing warnings with bounded115 output; treat them as page or pipeline-specific fidelity risks to inspect.1163. If ordinary extraction loses reading order, headings, tables, lists, or117 figure placement, rerun to a temporary comparison file with `--layout`:118119 ```bash120 python3 scripts/convert_pdf.py /absolute/path/report.pdf \121 --output /absolute/path/report.layout.md \122 --layout123 ```124125 This adds `--layout --layout-strategy auto126 --use-layout-for-markdown` to the ordinary Xberg command.127128 Compare content fidelity, then replace the intended output only with the129 user's overwrite authority. Do not assume that the larger output is better.130 If layout extraction reports an ONNX Runtime API mismatch, treat that as the131 cause even if a later worker message says `Mutex poisoned`. On Windows, check132 whether an older `C:\Windows\System32\onnxruntime.dll` was loaded. Point133 `ORT_DYLIB_PATH` at a compatible runtime only with user authority; do not134 silently install or replace a system library. The wrapper publishes no135 partial layout output after this failure.1364. If Korean OCR still omits or corrupts content, retry with `--force-ocr` only137 when the native text layer is the cause. For complex visual pages that remain138 unreliable, report the failing pages and offer a Docling or document-VLM139 comparison rather than silently switching tools or installing another140 parser.141142## Finish143144Finish only when the Markdown exists, is nonempty, passed representative source145checks, and the source PDF remains unchanged. Report the PDF and Markdown paths,146Xberg version, OCR or layout options used, physical/nonblank/blank page counts,147restored marker count, summarized parser warnings, any overwritten file148explicitly authorized by the user, and any pages or structures that remain149uncertain.