Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

exam2md

Convert annotated exam PDFs into Markdown question sheets.

exam2md renders PDF pages, suppresses saturated red and blue handwriting, extracts printed text, formulas, and figures as separate tracks, then writes a structured Markdown package for each PDF.

The review workflow is goal-driven: every page is evaluated against an explicit target state, and the result is recorded as pass, needs_fix, or uncertain. This keeps accepted output separate from draft material and makes partial failures auditable.

Install

npm install -g exam2md

For local development:

npm install
npm link

Requirements

brew install poppler

Install a supported reviewer CLI and make sure it is available in PATH.

Check your environment:

exam2md doctor

Usage

exam2md ./pdfs
exam2md ./paper.pdf -o markdown_output
exam2md ./paper.pdf --reviewer auto --review-rounds 3
exam2md ./paper.pdf --effort high
exam2md ./paper.pdf --timeout 60
exam2md ./paper.pdf --no-context-pages
exam2md review markdown_output/paper/.exam2md/manifest.json --audit-passed
exam2md ./paper.pdf --accept-last-fix
exam2md review markdown_output/paper/.exam2md/manifest.json

Recommended settings for annotated exam scans:

exam2md ./pdfs -o markdown_output --reviewer auto --effort medium --review-rounds 2 --timeout 180 --dpi 240 --accept-last-fix

Output

Each Markdown file gets its own sibling assets folder:

markdown_output/
└── paper/
    ├── paper.md
    ├── paper.assets/
    │   ├── q01_fig01.png
    │   └── q02_fig01.png
    └── .exam2md/
        ├── manifest.json
        ├── paper_draft.md
        ├── paper_qa.md
        └── paper_evolution.json

Markdown image paths are always relative, for example:

![问题 1 图 1](paper.assets/q01_fig01.png)

Math is emitted with standard Markdown/LaTeX delimiters:

  • inline: $...$
  • display: $$...$$

Goal-Driven Verification

Each page is processed with a fixed goal-driven verification procedure:

  1. Define the target: printed exam content in Markdown, with required figures beside the relevant question.
  2. Compare the original page with the cleaned page.
  3. Extract text, formulas, and figures separately.
  4. Reconstruct the original question order and logical flow.
  5. Associate each figure crop with the relevant question.
  6. Exclude handwriting, selected answers, derivations, marks, and platform UI.
  7. Check text, formulas, options, units, and image references.
  8. Return one of three page states: pass, needs_fix, or uncertain.

Only pages marked pass are written to the final Markdown file. Pages marked needs_fix are written to the draft file and documented in the QA report. A page is sent to review when the first-pass extraction is usable, even if it includes minor uncertainty notes.

Localized uncertainty is handled in the body instead of being hidden: the uncertain span is italicized when it can be located, and a small blockquote note is placed under the relevant question. Fully unreadable or unsupported content remains out of the trusted final Markdown.

exam2md also writes an evolution report next to each manifest inside .exam2md/. It records page states, observed failure modes, applied recovery mechanisms, figure statistics, and recommended settings for the next run. Each run appends a compact practiceLog entry, capped to the latest 20 runs, so repeated conversions leave an auditable trail without turning the project into a log dump.

The evolution loop is deliberately conservative. It can carry forward concrete failure patterns such as undersized figure crops, reviewer failures, draft pages, or localized uncertainty. It does not silently rewrite prompts, invent missing content, or hide failed pages.

Reports are compacted before writing. Repeated crop diagnostics are normalized into short reasons such as used readable question-section fallback, which keeps QA and evolution files readable after multiple retries.

If a page is too blurry, physically occluded, or exceeds the configured review timeout, exam2md records the available draft and does not attempt further reconstruction for that page.

Figure-internal labels are preserved visually through crops. A page is not downgraded only because tiny labels inside a captured figure are not separately transcribed. exam2md applies low-cost deterministic crop cleanup for duplicated stem text and neighboring question fragments. If a diagram crop is too small to be readable, it is replaced with a readable question-section fallback rather than shipping a misleading fragment. If removing an answer mark would damage the diagram, the complete diagram is kept and the event is recorded in QA.

Neighboring cleaned pages are attached by default during extraction to resolve continuation and question numbering. Use --no-context-pages to disable this.

Use --accept-last-fix when you want the final corrected Markdown from the reviewer to be accepted if no uncertainty is reported. Without it, pages that still return needs_fix remain in the draft package.

Use --audit-passed with exam2md review to force already accepted pages through another visual audit.

The defaults are conservative. For production conversion, use the recommended command above: higher DPI improves small formulas, neighboring pages improve continuation handling, and --accept-last-fix reduces manual work when the reviewer has already produced a corrected page without uncertainty.

Limits

Printed text that is fully covered or illegible cannot be recovered reliably. Such cases are reported as uncertain and excluded from the final Markdown until corrected.

About

Turn annotated exam PDFs into clean Markdown question sheets with figure crops and review.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages