# How pdf-inspector Determines Heading Levels from Font-Size Ratios

> Discover how pdf-inspector uses font-size ratios to determine heading levels. Learn about base font size comparison, bold text tiers, and fallback thresholds.

- Repository: [Firecrawl/pdf-inspector](https://github.com/firecrawl/pdf-inspector)
- Tags: how-to-guide
- Published: 2026-09-02

---

**The pdf-inspector markdown module determines heading tiers by comparing each line's font size against the document's base font size, using bold text with ratios between 1.05–1.20 for tier detection and fallback thresholds of 2.0, 1.5, and 1.25 when no tiers are found.**

The `pdf-inspector` repository by Firecrawl provides a Rust-based PDF-to-markdown conversion pipeline that automatically infers document structure without relying on explicit PDF heading tags. Understanding how **pdf-inspector heading levels from font-size ratios** work is essential for debugging conversion output or customizing the toolchain for specialized document formats.

## Heading Tier Detection Through Font-Size Ratios

The core algorithm resides in [`src/markdown/analysis.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/analysis.rs) within the `compute_heading_tiers` function. This function establishes a **base font size**—the most frequently occurring size in the document—then evaluates candidate headings against it.

### Collecting Candidate Tiers

The tier detection logic applies a strict filter to identify potential heading levels:

```rust
let ratio = font_size / base_size;
(1.05..1.2).contains(&ratio) && is_bold

```

Only **bold text** with font sizes **5% to 20% larger** than the base size qualifies as a heading tier candidate. The pdf-inspector markdown module collects all distinct font sizes meeting this criteria, sorts them from largest to smallest, and assigns hierarchical levels—the largest becomes **H1**, the next **H2**, and so forth through the sequence.

## Fallback Thresholds for Unstructured Documents

When `compute_heading_tiers` finds no qualifying tiers, the system falls back to hard-coded ratio thresholds defined in [`src/markdown/analysis.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/analysis.rs):

| Ratio Threshold | Heading Level |
|-----------------|---------------|
| `ratio >= 2.0` | **H1** |
| `ratio >= 1.5` | **H2** |
| `ratio >= 1.25` | **H3** |

Any text below the 1.25 threshold renders as normal body text. The `bold_heading_level` helper function handles edge cases where bold formatting exists without meeting ratio requirements—assigning such lines one level below the lowest discovered tier (defaulting to **H2** when no tiers exist).

## Applying Tiers During Markdown Conversion

The `effective_heading_level` function—invoked from `merge_heading_lines` in [`src/markdown/preprocess.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/preprocess.rs) and the main conversion orchestrator in [`src/markdown/convert.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/convert.rs)—performs the final level assignment:

```rust
use pdf_inspector::markdown::convert::process_pdf;

// Extract markdown with automatic heading detection
let md = process_pdf("paper.pdf");

// The detector has already computed the tiers:
//   – Font size 24 pt (ratio ≈ 2.0) → # H1

//   – Font size 18 pt (ratio ≈ 1.5) → ## H2

//   – Font size 15 pt (ratio ≈ 1.25) → ### H3

println!("{}", md);

```

Given a base size of 12 pt, detected tiers of 24.0, 18.0, and 15.0 would map directly to H1, H2, and H3 respectively. The tier index plus one yields the markdown heading level.

## Examining Tier Computation Directly

For debugging or custom tooling, you can invoke `compute_heading_tiers` independently:

```rust
use pdf_inspector::markdown::analysis::compute_heading_tiers;

// Assume a base size of 12 pt (most common size)
let lines = /* … parsed TextLine collection … */;
let tiers = compute_heading_tiers(&lines, 12.0);

// Example result
// tiers == [24.0, 18.0, 15.0]   // → H1, H2, H3 respectively
println!("Detected heading tiers: {:?}", tiers);

```

This interface allows validation of how specific PDF documents will translate to markdown structure before full conversion.

## Key Source Files for Heading Level Logic

| File | Purpose |
|------|---------|
| [`src/markdown/analysis.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/analysis.rs) | Computes font-size rarity, builds `heading_tiers`, and provides fallback logic including `bold_heading_level` and ratio checks |
| [`src/markdown/convert.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/convert.rs) | Orchestrates conversion; calls `compute_heading_tiers` and merges heading lines |
| [`src/markdown/preprocess.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/preprocess.rs) | Merges consecutive heading lines using tiers and determines effective heading level through `merge_heading_lines` |

## Summary

- ** pdf-inspector heading levels from font-size ratios** rely on bold text with sizes **1.05× to 1.20×** the base size for tier detection
- Discovered tiers sort by descending size and map sequentially to H1, H2, H3...
- **Fallback thresholds** of **2.0, 1.5, and 1.25** apply when automatic tier detection fails
- The `compute_heading_tiers`, `effective_heading_level`, and `bold_heading_level` functions in [`src/markdown/analysis.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/analysis.rs) implement the core logic
- Pipeline integration occurs through [`src/markdown/convert.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/convert.rs) and [`src/markdown/preprocess.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/preprocess.rs)

## Frequently Asked Questions

### Why does pdf-inspector require bold formatting for heading detection?

The `is_bold` requirement in `(1.05..1.2).contains(&ratio) && is_bold` prevents false positives from minor font size variations in body text. Bold formatting serves as a semantic signal that distinguishes intentional headings from incidental sizing differences, aligning with conventional document authoring practices where headings typically receive weight emphasis.

### What happens when a document has more than three heading levels?

The tier-based system scales beyond three levels dynamically. `compute_heading_tiers` collects all distinct bold font sizes in the 1.05–1.20 ratio window and assigns sequential levels. A document with five qualifying tiers would produce H1 through H5 automatically, with each tier's index plus one determining its markdown level.

### How does pdf-inspector handle documents with no detectable heading structure?

When no tiers satisfy the bold-and-ratio criteria, the fallback logic in [`src/markdown/analysis.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/analysis.rs) activates. Lines with ratios at or above 2.0, 1.5, and 1.25 receive H1, H2, and H3 tags respectively. Bold lines that miss all ratio gates default to H2 via `bold_heading_level`, ensuring some structural output even from poorly formatted source PDFs.

### Can I customize the ratio thresholds for specialized document types?

The current implementation uses compile-time constants for both the tier detection window (1.05–1.20) and fallback thresholds (2.0, 1.5, 1.25). Modifying [`src/markdown/analysis.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/markdown/analysis.rs) and recompiling would allow adaptation for domain-specific conventions—such as academic papers with consistently larger heading ratios or compact technical manuals with tighter size gradations.