Convert PDF files into clean, semantic HTML — extracting all text, embedded images and vector figures (charts/diagrams), while preserving reading order and Unicode text. Built on PyMuPDF, works on Kali Linux / Debian without proprietary dependencies, and is MIT licensed.
Processed 30 files, 60 pages, 60 images extracted, 120 paragraphs, 0 pages OCR-ed.
The classic tools inspired the design:
| Tool | Technique used here |
|---|---|
Poppler pdftohtml |
Text runs sorted by (y, x) for reading order; images saved via drawImage hooks and referenced by src; fonts grouped by size/bold/italic. |
| PDFMiner.six | Page tree LTPage → LTFigure/LTTextBox → LTTextLine → LTChar; boxes re-ordered by a boxes_flow diagonal sort; paragraphs grouped by geometric analysis. |
| PyMuPDF | Our engine: get_text("dict", sort=True) for blocks/lines/spans with coordinates and font metrics; get_images + extract_image for raster images; get_pixmap(clip=…) to rasterise vector charts. |
pdfhtml re-implements the good ideas (reading-order sorting, flow-based
paragraph grouping, heading promotion by font size, image/figure extraction)
in ~800 lines of dependency-light Python.
- Single file, many files, a folder, or a wildcard:
pdfhtml input.pdf pdfhtml file1.pdf file2.pdf file3.pdf pdfhtml -d /path/to/pdf/folder/ pdfhtml *.pdf - All text preserved, paragraph breaks kept, headings promoted to
<h1>…<h4>from font sizes. - Images & figures extracted to PNG/JPEG side-files and embedded with
relative
<img>paths (<stem>_images/per PDF, so files never clash). Vector charts are rasterised at--figure-dpi. - Reading order maintained top-to-bottom, left-to-right (multi-column aware).
--mergecombines every PDF into onemerged.html.index.htmlgenerated automatically when converting multiple files.--ocrruns Tesseract on scanned / image-only pages (Bengali and other languages via--ocr-lang).- Unicode / Bengali handled natively; the output
<html lang="bn">is set automatically for Bengali documents. - Fast & memory-efficient for large batches (pages streamed, one document at a time, images deduplicated).
- Robust: corrupted and encrypted PDFs are reported and skipped without aborting the batch; missing Tesseract degrades gracefully.
Requires Python ≥ 3.9.
# From GitHub
pip install git+https://github.com/BL4CK570RM/pdfhtml.git
# Or from a local checkout
pip install .
# Or: install from PyPI-style requirements
pip install -r requirements.txt
# Optional extras
pip install .[progress] # prettier progress bars (tqdm)
pip install .[ocr] # pytesseract bindingsFor --ocr you also need the Tesseract binary:
sudo apt install tesseract-ocr # English
sudo apt install tesseract-ocr-ben # Bengali
export PDFHTML_OCR_LANG=ben # default languageusage: pdfhtml [-h] [-d DIRECTORY] [-o OUTPUT] [-m] [--ocr] [--ocr-lang OCR_LANG]
[--ocr-dpi OCR_DPI] [--password PASSWORD] [--min-image-size MIN_IMAGE_SIZE]
[--figure-dpi FIGURE_DPI] [--no-figures] [-v] [--version]
[pdf_files ...]
| Option | Description |
|---|---|
pdf_files ... |
PDF files to convert (shell wildcards expanded by your shell). |
-d, --directory DIR |
Process all *.pdf in DIR. |
-o, --output DIR |
Output directory (created if missing). Default ./output. |
-m, --merge |
Merge all input PDFs into a single merged.html. |
--ocr |
Run Tesseract OCR on pages with no extractable text. |
--ocr-lang LANG |
Tesseract language(s), e.g. ben or eng+ben. Default $PDFHTML_OCR_LANG or eng. |
--ocr-dpi N |
Raster DPI for OCR. Default 300. |
--password PW |
Password for encrypted PDFs. |
--min-image-size N |
Images smaller than N² pt² (area) are treated as noise. Default 12. |
--figure-dpi N |
DPI for vector figure rasterisation. Default 130. |
--no-figures |
Skip vector figure (chart/diagram) extraction. |
-v, --verbose |
Detailed per-page logging. |
-h, --help |
Show help. |
--version |
Show version. |
pdfhtml input.pdf # -> output/input.html + output/input_images/
pdfhtml file1.pdf file2.pdf file3.pdf # -> plus output/index.html
pdfhtml -d /home/user/documents/
pdfhtml *.pdf --merge -o ./converted/ # -> converted/merged.html + index.html
pdfhtml --ocr scanned.pdf # OCR the scanned pages
pdfhtml --ocr --ocr-lang ben paper.pdf # Bengali OCR
pdfhtml --password s3cret encrypted.pdfoutput/
├── index.html # generated when converting multiple PDFs
├── input.html # one file per PDF (or merged.html with --merge)
├── input_images/
│ ├── page001_000.png # embedded raster images
│ └── fig001_001.png # vector figures rasterised
├── other.html
└── other_images/
Images are referenced with relative paths, so the whole output/ folder is
portable.
Generate sample PDFs (needs pymupdf and pillow):
python tests/make_samples.pypython -m pdfhtml tests/samples/sample1.pdf -o /tmp/out_single
# expect:
# Processed 1 files, 2 pages, 2 images extracted, 4 paragraphs, 0 pages OCR-ed.
grep -o '<h1>[^<]*</h1>' /tmp/out_single/sample1.html # headings detected
ls /tmp/out_single/sample1_images/ # page001_000.png, fig001_001.pngpython -m pdfhtml tests/samples/sample1.pdf tests/samples/sample2.pdf -o /tmp/out_multi
# expect:
# Processed 2 files, 3 pages, 2 images extracted, 6 paragraphs, 0 pages OCR-ed.
ls /tmp/out_multi/index.html /tmp/out_multi/sample1_images /tmp/out_multi/sample2_imagespip install pytest
python -m pytest tests/ -vMD Mahmidul Hasan
- GitHub: @BL4CK570RM
- eJPT · ACP · CASA · CRTA · CNSP · MCRTA · ISO/IEC 27001:2022 LA · CSA1 · CTF Player · Bug Bounty Hunter · Penetration Tester · Ethical Hacker
- CVE-2026-57997 · CVE-2026-73033
MIT — see LICENSE.