pdftohtml.py
PDF Invoice → HTML Table Converter
What Is It?
pdftohtml.py is a command-line Python script that reads an invoice-style PDF file and produces a clean, styled HTML file containing the same table — faithfully reproducing every row, column, header block, and grand total exactly as they appear in the original document.
It is designed for PDFs that have no embedded table grid lines (i.e. the table is plain text laid out with spaces). The script uses a regex parser tuned to the column order of the invoice rather than relying on pdfplumber's automatic table detector.
Requirements
Python Version
Python 3.10 or newer is required (uses the built-in list[...] type hint syntax).
Python Libraries
Install both libraries with pip:
pip install pdfplumber pypdf
| Package | Purpose |
|---|---|
pdfplumber | Extracts raw text from each PDF page with layout awareness. Also attempts automatic table detection as a primary strategy. |
pypdf | Reads PDF metadata (title, author, creation date) from the file header. |
No Other Dependencies
The script uses only Python's built-in modules (sys, os, re) alongside the two packages above. No OCR engine, no browser, no external tools required.
Installation
No installation step is needed. Simply download the script and run it directly:
# 1. Download / save the script
# 2. Install dependencies
pip install pdfplumber pypdf
# 3. Run
python pdf_to_html.py your_invoice.pdf
Usage
Basic — auto-named output
python pdf_to_html.py input.pdf
Saves the result as input_table.html in the same directory.
Custom output path
python pdf_to_html.py input.pdf output.html
Saves the result to the path you specify.
Examples
python pdf_to_html.py examples.pdf
python pdf_to_html.py invoices/march.pdf reports/march_table.html
Output
The generated HTML file contains:
- A header block with Auftraggeber, date range, Baustellenname, Teilrechnung number, and Kst.Nr.
- A full data table with all rows extracted from the PDF.
- Right-aligned, monospace numeric columns (Menge, Einheitspreis, Betrag).
- A bold Summe footer row with the grand total.
- Hover highlighting on rows for easy reading.
Open the .html file in any web browser to view or print it. No internet connection is needed — all styling is self-contained.
How It Works
Step 1 — extract_metadata()
Reads the first page of the PDF as plain text and uses regular expressions to locate the Auftraggeber line (splitting off the date range), Baustellenname, Teilrechnung number, and Kst.Nr.
Step 2 — extract_rows()
Reads every page's text and applies a single multi-group regex that matches the invoice row format:
01 Positionstext description 1,00 Stk € 500,00 € 500,00 Address note
Important: the
€symbol appears before each number in this PDF (€ 500,00), not after. The regex is written to match that exact format.
Step 3 — extract_total()
Scans the full text for the Summe line and extracts the euro total.
Step 4 — build_html()
Assembles the metadata, rows, and total into a complete, self-contained HTML document with embedded CSS. Numeric columns are detected by index and rendered right-aligned in a monospace font.
Terminal Output
When you run the script you will see:
📄 Reading: examples.pdf
Rows found : 27
Total : € 12 325,00
✅ Saved: examples.html
If no rows are found the script prints a warning and exits with code 1. This typically means the PDF uses scanned/image-based text and would require OCR to process.
Limitations
- Scanned PDFs: Image-based PDFs cannot be parsed. An OCR library such as
pytesseractwould be needed. - Different formats: The regex is tuned to the column order and
€placement of this specific invoice format. Different invoice layouts may need regex adjustments. - Multi-page tables: The script reads all pages and combines the text, so multi-page tables are handled correctly as long as the row format is consistent.
Quick Reference
| Task | Command |
|---|---|
| Install dependencies | pip install pdfplumber pypdf |
| Run (auto output name) | python pdftohtml.py file.pdf |
| Run (custom output) | python pdftohtml.py file.pdf out.html |
| Open result | Open the .html file in any browser |
| Python version | 3.10 or newer |