Parsing PDFs with PyMuPDF4LLM

Most RAG tutorials start with plain text files. Real documents are PDFs with tables, headers, multi-column layouts, and broken words across page boundaries. If your parser loses that structure, your retrieval was doomed before the first embedding.

document_utils.py
python
import pymupdf4llm
import docx

class DocumentParser:
    """Handles parsing of PDF (PyMuPDF4LLM), Docx, etc."""

    @staticmethod
    def parse_pdf(file_path: str) -> str:
        """Parse PDF using PyMuPDF4LLM (layout-aware markdown)."""
        md_text = pymupdf4llm.to_markdown(file_path)
        return md_text

    @staticmethod
    def parse_docx(file_path: str) -> str:
        """Parse DOCX file."""
        doc = docx.Document(file_path)
        return "\n\n".join([p.text for p in doc.paragraphs if p.text.strip()])

PyMuPDF4LLM is the key call. It returns markdown with headers preserved, tables formatted, and reading order intact, which matters later when we split by header.

PyPDF2 gives you a wall of text with no structural signals. pdfplumber preserves layout but outputs raw positional data. PyMuPDF4LLM was designed specifically for LLM pipelines. It produces markdown, keeps headers as # and ##, formats tables, and preserves reading order across multi-column pages. That structure becomes the scaffold for smart chunking.

Quiz: Quiz

Loading practice…