pdfmux/blog
python

PDF to DOCX in Python: Convert PDFs to Editable Word Documents

TL;DRConvert a PDF to an editable Word .docx in Python with pdf2docx: layout, tables, and images preserved. Covers scanned-PDF limits and when you want markdown instead.

Direct answer: Convert a born-digital PDF to an editable Word document with the open-source pdf2docx library — Converter("file.pdf").convert("file.docx") reconstructs paragraphs, tables, and images from the PDF’s text and vector objects, no Word installation required. It works well on PDFs that have a real text layer and reasonably regular layout; it does not work on scanned PDFs (no text layer to reconstruct from) and it struggles with complex multi-column or rotated layouts, the same structural problems every PDF-to-structured-format tool runs into. If your actual destination is an LLM, embeddings model, or RAG pipeline rather than a human editor, skip DOCX entirely — PDF to markdown for RAG gets you there in one lossless step instead of two lossy ones.


What “PDF to DOCX” actually requires

A PDF stores glyphs at (x, y) coordinates plus some drawn lines and embedded images — it has no concept of a paragraph, a table, or a heading the way .docx’s Office Open XML format does. Converting PDF to DOCX means reconstructing that structure from scratch:

  • Paragraph boundaries have to be inferred from line spacing and indentation, since a PDF doesn’t mark where one paragraph ends and the next begins.
  • Tables need the same geometry-based detection as any PDF table extraction — find lines or aligned whitespace, group cells, infer rows and columns.
  • Fonts and styling (bold, italic, size) are attached to individual glyph runs in the PDF and have to be mapped onto DOCX’s run-level formatting.
  • Images are embedded objects that need to be extracted and re-embedded at the right position in the new document flow.

pdf2docx does all four by parsing the PDF’s internal structure with PyMuPDF and rebuilding the equivalent DOCX structure with python-docx. It’s actively maintained, MIT-licensed, and the standard answer in the Python ecosystem for this specific direction of conversion.


Step 1: Basic conversion

pip install pdf2docx
from pdf2docx import Converter

def pdf_to_docx(pdf_path: str, docx_path: str) -> None:
    cv = Converter(pdf_path)
    cv.convert(docx_path, start=0, end=None)
    cv.close()

pdf_to_docx("report.pdf", "report.docx")

start and end take zero-indexed page numbers if you only need part of the document — useful for a long PDF where you only care about a specific section, since conversion time scales with page count and layout complexity, not file size. cv.close() matters: it releases the underlying PyMuPDF document handle, and skipping it leaks file handles in a long-running batch job.

For a single page or a short range, you can skip the full-document pass entirely:

cv = Converter("report.pdf")
cv.convert("summary-page.docx", start=4, end=5)  # page 5 only (zero-indexed)
cv.close()

Step 2: Tables and multi-page documents

Tables are the part most likely to need a second look after conversion. pdf2docx detects table boundaries from line geometry by default, which is reliable for tables with visible grid lines and shakier for tables that rely on whitespace alignment alone.

from pdf2docx import Converter

cv = Converter("financial-report.pdf")
cv.convert(
    "financial-report.docx",
    multi_processing=True,   # parallelize page parsing on multi-core machines
    cpu_count=4,
)
cv.close()

multi_processing is worth enabling for anything past a few dozen pages — each page is parsed independently, so there’s no correctness trade-off, only a speed one. After conversion, specifically check any table that spans a page break and any table without visible borders; those are the two cases most likely to merge cells or drop a row incorrectly, regardless of how clean the rest of the document comes out.


Step 3: Batch conversion

A realistic pipeline converts a folder of PDFs, not one file, and needs to keep going when one document fails instead of stopping the whole batch.

from pathlib import Path
from pdf2docx import Converter

def batch_convert(input_dir: str, output_dir: str) -> dict[str, str]:
    in_dir = Path(input_dir)
    out_dir = Path(output_dir)
    out_dir.mkdir(parents=True, exist_ok=True)

    results: dict[str, str] = {}
    for pdf_path in in_dir.glob("*.pdf"):
        docx_path = out_dir / f"{pdf_path.stem}.docx"
        try:
            cv = Converter(str(pdf_path))
            cv.convert(str(docx_path))
            cv.close()
            results[pdf_path.name] = "ok"
        except Exception as e:
            results[pdf_path.name] = f"failed: {e}"
    return results

report = batch_convert("incoming_pdfs/", "converted_docs/")
for name, status in report.items():
    print(f"{name}: {status}")

Wrapping each file’s conversion in its own try/except means one malformed or password-protected PDF doesn’t take down an overnight batch job — you get a per-file status report instead of a stack trace three files before the end.


Why scanned PDFs don’t work here

pdf2docx reconstructs structure from the PDF’s actual text objects and vector drawing commands. A scanned PDF — an image of a page, possibly with an invisible OCR text layer bolted on by the scanning software — has no native paragraph or table structure to reconstruct. Running pdf2docx on one of two things happens: it produces an empty or near-empty document if there’s no text layer at all, or it produces a document with the OCR text but none of the original visual layout, since that OCR layer typically carries word positions but not paragraph and table semantics.

The honest path for a scanned PDF is OCR first, then build the DOCX from scratch with python-docx:

import pytesseract
from pdf2image import convert_from_path
from docx import Document

def scanned_pdf_to_docx(pdf_path: str, docx_path: str) -> None:
    pages = convert_from_path(pdf_path, dpi=300)
    doc = Document()

    for i, page_img in enumerate(pages):
        text = pytesseract.image_to_string(page_img)
        doc.add_paragraph(text)
        if i < len(pages) - 1:
            doc.add_page_break()

    doc.save(docx_path)

This gets you editable text, not a layout-faithful reconstruction — tables become plain paragraphs, columns run together, and headers lose their formatting. It’s the right tool for “I need the words in an editable file,” and the wrong one for “I need this scanned contract to look like the original in Word.”


When you don’t actually want DOCX

If the PDF’s final destination is an LLM prompt, an embeddings model, or a RAG pipeline rather than a human opening Word, converting to DOCX is an unnecessary and lossy detour. DOCX preserves visual layout — exact font runs, page breaks, headers and footers repeated on every page — almost none of which a language model benefits from and some of which actively hurts retrieval quality (a repeated header or footer showing up as a near-duplicate chunk on every page, for instance).

pdfmux skips straight from PDF to the shape a machine reader wants:

from pdfmux import process

result = process("report.pdf", quality="standard", output_format="markdown")
print(result.text[:500])

This returns markdown with headings, tables as markdown tables, and reading order preserved — no DOCX round trip, no stripping headers and footers back out of a Word document after the fact. The two tools solve genuinely different problems: pdf2docx is for “a person needs to edit this in Word,” and pdfmux is for “a machine needs to read this reliably.” Converting PDF to DOCX and then DOCX back to plain text to feed a pipeline is strictly more lossy than going directly from PDF to markdown or JSON, since each conversion step is its own opportunity to mangle structure.


pdf2docx vs. commercial alternatives vs. pdfmux

ToolOutputScanned PDFsBest for
pdf2docx.docxNo (needs OCR pre-step)Free, local, editable Word doc from a born-digital PDF
Adobe PDF Services API.docxPartial (built-in OCR)Highest layout fidelity, paid, cloud dependency
Aspose.PDF.docxPartial (built-in OCR)Enterprise, paid, also handles PDF → PPTX/XLSX
pdfmuxMarkdown / JSON / CSV / LLM-ready textYes (auto-routes to OCR)Feeding an LLM, RAG pipeline, or structured-data pipeline — not editing in Word

If the deliverable is a Word file a colleague will open and edit, pdf2docx (or a commercial API if layout fidelity on scanned documents matters enough to pay for it) is the right call. If the deliverable is text or structured data for a machine, the DOCX step is pure overhead.

Keep reading

Last updated: October 2026.

Frequently asked questions

Can I convert a scanned PDF to an editable Word document?

Not directly and not well. pdf2docx reconstructs layout from a PDF's existing text objects and vector graphics — a scanned PDF has neither, only a flat image. You have to OCR it first (pytesseract or similar) to get a text layer, then build the docx from that OCR output with python-docx. You'll get editable text, but you lose the original visual layout almost entirely; OCR gives you words and rough positions, not the paragraph and table structure pdf2docx reconstructs from a born-digital PDF.

Why do my tables come out broken or merged in the converted docx?

pdf2docx infers table structure from line geometry and text alignment, the same underlying problem every PDF table extractor faces — a PDF has no native table object, only glyphs and drawn lines. Tables with real grid lines convert cleanly most of the time. Tables that rely on whitespace alone, or that span page breaks, are the common failure points — check those specifically rather than assuming a clean conversion elsewhere means the whole document converted correctly.

Does pdfmux convert PDFs to DOCX?

No — pdfmux outputs markdown, JSON, CSV, or LLM-ready chunked text, never DOCX, and that's a deliberate scope boundary, not a missing feature on the roadmap. DOCX preserves visual layout for a human editor; pdfmux preserves semantic structure (headings, tables, reading order) for a machine reader. If you actually need an editable Word document, use pdf2docx below. If you're feeding the output to an LLM, embeddings model, or RAG pipeline, you want pdfmux's markdown or JSON output instead — converting to DOCX and back to text just to get there adds a lossy round trip for no benefit.

Is there a way to do this without installing Microsoft Word?

Yes — pdf2docx builds the .docx file format directly with python-docx under the hood; it never opens or shells out to Word, so it runs the same on Linux, macOS, or a headless server. The alternative some tutorials suggest — automating Word via COM on Windows — is unnecessary for this direction of conversion and won't work on Linux CI at all.