---
name: read_pdf
description: "Extract text and metadata from PDF files — local or remote URLs. Returns page-by-page text, metadata, and full-text concatenation."
---

# read_pdf

Extract text content and metadata from PDF files — local files or remote URLs. Returns page-by-page text, document metadata, and full-text concatenation. Handles encrypted PDFs gracefully and supports page range selection.

## Install

```bash
pip install pymupdf
# or
pip install "fliiq[pdf]"
```

## Usage

### Read a local PDF

```python
read_pdf(path="/path/to/paper.pdf")
```

### Read from a URL

```python
read_pdf(url="https://arxiv.org/pdf/1706.03762")
```

### Specific pages only

```python
read_pdf(path="paper.pdf", pages=[1, 2, 3])   # pages 1, 2, and 3
```

### Full-text mode (concatenated string)

```python
read_pdf(path="paper.pdf", mode="full_text")
```

### Structured mode (per-page, default)

```python
read_pdf(path="paper.pdf", mode="structured")
# Returns pages: [{page_num: 1, text: "..."}, ...]
```

## Return Format

```json
{
  "title": "Attention Is All You Need",
  "author": "Vaswani et al.",
  "page_count": 15,
  "pages_extracted": 15,
  "full_text": "The dominant sequence transduction models...",
  "pages": [
    {"page_num": 1, "text": "..."},
    {"page_num": 2, "text": "..."}
  ],
  "source": "/path/to/paper.pdf"
}
```

## Error Handling

| Situation | Behavior |
|-----------|----------|
| File not found | Returns `{"error": "File not found: ..."}` |
| Encrypted/password-protected PDF | Returns `{"error": "This PDF is encrypted..."}` |
| URL returns HTML instead of PDF | Returns `{"error": "URL returned HTML, not a PDF..."}` |
| File exceeds size limit | Returns `{"error": "File too large: X MB (limit: 50 MB)"}` |
| Wrong Content-Type from URL | Returns `{"error": "URL does not appear to be a PDF..."}` |

## Size Limits

- Default maximum: **50 MB** per PDF (configurable via `max_size_mb` parameter)
- For large documents, use `pages=` to extract specific sections

## Pairing with fetch_arxiv

```python
# Step 1: Find the paper
result = fetch_arxiv(paper_id="1706.03762", download_pdf=True, output_path="./papers/")

# Step 2: Read it
pdf_result = read_pdf(path="./papers/1706_03762.pdf")
print(pdf_result["full_text"][:2000])  # First 2000 chars
```

## Notes

- Uses **PyMuPDF** (fitz) — faster and more accurate than pdfminer for multi-column academic papers
- URL fetching follows redirects (arXiv PDF URLs redirect before serving)
- Page numbers are **1-based** (page 1 = first page)
- Empty pages (blank or image-only) return empty text strings — not an error
- For scanned PDFs (image-only), text extraction will return empty strings; OCR is not included in v1
