This article is part of the **PDF Parsing for RAG* series. In Part 1, we analyzed multi-column reading order braiding. In Part 2, we reconstructed borderless table grids. Today we examine what happens when mathematical formulas are flattened into raw character strings.*
When building retrieval-augmented generation (RAG) systems over scientific preprints, financial risk whitepapers, or engineering specifications, mathematical queries exhibit the lowest recall in the entire corpus. A user submits a precise query: "What is the scaling factor in equation 4 for policy entropy regularization?" The retrieval model returns unrelated descriptive paragraphs, or the generator produces hallucinated coefficients.
The root failure does not originate in the vector store or the embedding dimensionality. It occurs at the document ingestion boundary: converting two-dimensional mathematical notation into a one-dimensional character stream strips the semantic hierarchy of equations, leaving downstream embedding models with disjointed character fragments.
How Untagged PDFs Render Mathematical Equations
Standard PDFs lack semantic tags for mathematical expressions unless generated under specialized PDF/UA profiles. Instead, a formula is assembled using two separate rendering layers:
- Font Elevation and Micro-Offsets: Subscripts and superscripts do not carry explicit exponent markers (such as
^or_). Instead, the PDF engine shifts the cursor vertically via text matrix offsets (Tm) and reduces font size (Tf). A superscript2in $x^2$ is simply a small digit placed at an elevated Y-coordinate. - Vector Graphic Connectors: Horizontal fraction bars, square root radicals, and grouping brackets are frequently drawn as vector graphic paths (
m,l,re, or curved Bézier strokes) rather than font characters.
Because standard text extractors scan only text operators (Tj, TJ) and ignore vector drawing paths, fraction bars disappear entirely. The numerator and denominator collapse into adjacent lines of text without operational delimiters.
The Three Failure Modes of Naive Formula Extraction
When naive text extraction processes mathematical blocks, three distinct structural corruptions destroy embedding vectors:
1. Subscript and Superscript Flattening
Because font elevation deltas are discarded during plain text output, superscripts fall onto the baseline. The mathematical expression $e^{-\frac{(x-\mu)^2}{2\sigma^2}}$ degrades into e - (x - μ) 2 / 2 σ 2. The exponent 2 becomes an ordinary multiplicative constant, changing the mathematical semantics completely.
2. Disconnected Fraction and Radical Paths
When fraction bars are rendered as vector strokes, naive parsers see the numerator on line $N$ and the denominator on line $N+1$. If a column break or paragraph margin coincides with the equation, the denominator joins the following body paragraph, creating orphan numbers in downstream chunks.
3. Chunk Window Boundary Slicing
Standard recursive character splitters treat math punctuation (=, +, /) as arbitrary text dividers. Splitting a 4-line display derivation at token 512 leaves variable definitions in chunk A and the resulting differential relation in chunk B. Neither chunk retains sufficient context for standalone vector matching.
Practical Reconstruction: Extracting and Preserving LaTeX Blocks
To provide clean embeddings and accurate LLM reasoning, technical ingestion pipelines must isolate formula bounding boxes, identify font elevation shifts, and serialize equations into standardized LaTeX delimiters ($...$ for inline math and $$...$$ for display math).
Here is an end-to-end Python implementation using PyMuPDF (fitz) character dictionaries to detect font elevation deltas and preserve equation syntax:
import fitz # PyMuPDF import re def extract_page_with_math_blocks(page, font_size_ratio_threshold=0.85): """ Extracts text from a PDF page while detecting font elevation shifts (superscripts/subscripts) and formatting math expressions into valid LaTeX. Args: page: fitz.Page object font_size_ratio_threshold: Font size drop ratio indicating sub/superscript. """ # Extract detailed character layout dictionary page_dict = page.get_text("dict") extracted_blocks = [] for block in page_dict.get("blocks", []): if block.get("type") != 0: # Skip image blocks continue block_text = [] for line in block.get("lines", []): spans = line.get("spans", []) if not spans: continue # Determine dominant font size for the line base_size = max(span["size"] for span in spans) line_str = "" for span in spans: text = span["text"] size = span["size"] flags = span["flags"] # bit 0: superscript flag in some PDF writers y_coord = span["bbox"][1] # Check if this span represents a subscript or superscript is_smaller = (size / base_size) <= font_size_ratio_threshold if is_smaller and text.strip(): clean_text = text.strip() # Heuristic: elevation vs line baseline line_baseline = line["bbox"][3] span_center_y = (span["bbox"][1] + span["bbox"][3]) / 2.0 if span_center_y < (line["bbox"][1] + line_baseline) / 2.0: # Elevated: Superscript line_str += f"^{{{clean_text}}}" else: # Depressed: Subscript line_str += f"_{{{clean_text}}}" else: line_str += text block_text.append(line_str) full_block = "\n".join(block_text).strip() if full_block: # Normalize display math: if block consists predominantly of math operators, wrap in $$ math_symbols = r'[=\+\-\*/\^_\{\}\(\)∫∑√≤≥≠±]' math_matches = len(re.findall(math_symbols, full_block)) word_count = max(1, len(full_block.split())) if math_matches >= 3 and (math_matches / word_count) > 0.4: # Wrap display math cleanly extracted_blocks.append(f"$$\n{full_block}\n$$") else: extracted_blocks.append(full_block) return "\n\n".join(extracted_blocks) def validate_latex_brackets(latex_str): """ Validates structural bracket balancing for extracted LaTeX blocks. """ pairs = {'{': '}', '(': ')', '[': ']'} stack = [] for char in latex_str: if char in pairs: stack.append(pairs[char]) elif char in pairs.values(): if not stack or stack.pop() != char: return False, "Unmatched delimiter" return len(stack) == 0, "Balanced" Serialization Tradeoffs: LaTeX vs. Unicode Math vs. Image OCR
When selecting an equation representation for retrieval and generation, consider the structural tradeoffs across formats:
| Format | Parsing Compute | Search Precision | LLM Reasoning Fidelity | Recommended Scenario |
|---|---|---|---|---|
Standard LaTeX ($$...$$) | Low (text-layer analysis) | High (exact structural notation) | Excellent (high pretraining density) | Born-digital academic PDFs and technical reports |
| Unicode Approximation | Lowest (raw character mapping) | Moderate (loses positional exponents) | Fair (struggles with fractions/matrices) | Fast keyword search without symbolic parsing needs |
| Multimodal Vision OCR | High (GPU inference / API) | High (reconstructs complex 2D graphs) | Excellent | Scanned PDFs and rasterized formula figures |
For enterprise RAG pipelines, Standard LaTeX yields the highest retrieval fidelity and generation accuracy without imposing heavy GPU inference costs on every page.
Mathematical RAG Audit Checklist
Before releasing a technical corpus into your production index, validate your mathematical parsing pipeline against this five-point audit:
- Verify Exponent Retention: Inspect sample extractions of quadratic and exponential terms; confirm that $x^2$ is preserved as
x^{2}rather thanx 2. - Audit Fraction Delimiters: Confirm that multi-line fractions retain explicit division operators (
\frac{a}{b}or(a) / (b)) instead of collapsing into two separated lines. - Atomic Math Chunking: Configure chunking rules so that display math blocks (
$$...$$) are never severed mid-equation by arbitrary token splitters. - Delimited Symbol Search: Test retrieval against 10 symbol-dense queries (e.g., specific variable names with Greek letters like
\sigma_{reg}). Verify that vector search returns exact equation passages rather than generic text mentions. - Layout Confidence Flags: Emit an
extraction_confidence: lowmetadata flag for equation regions containing unmatched brackets or ambiguous vertical baseline shifts, routing them to human review or high-precision vision parsing.