Guide

Swift EPUB Text Extraction Library — Scribe (Open Source)

Last updated 11 September 2026 · 3 min read

Most PDF/EPUB extraction libraries you'll find for Swift either wrap a third-party parser or hand you raw text with the structure stripped out. If you're building a reading app — something that needs chapter boundaries, footnotes separated from body text, and a stable word index to sync a position against — raw text isn't enough, and shipping a GPU-backed extraction pipeline behind your app just to open a file a user already has on their phone is the wrong trade.

Scribe is a Swift package that does structured extraction for both PDF and EPUB entirely on-device, using only PDFKit, Vision, CoreGraphics and Foundation — no server, no network call, no bundled ML model.

Install

// Package.swift
        dependencies: [
            .package(url: "https://github.com/kegbenk/scribe.git", from: "0.1.0")
        ]

Usage

import Scribe
        if let result = ScribeProcessor.extractContent(from: pdfURL) {
            let text = result["text"] as! String
            let chapters = result["chapters"] as! [[String: Any]]
            for chapter in chapters {
                print(chapter["title"] as? String ?? "Untitled")
                print("Words: \(chapter["wordCount"] as? Int ?? 0)")
            }
        }

The same call signature works for EPUB — PDF and EPUB go through the same pipeline and emit the same contentStructure JSON contract (chapters, footnotes, images, table of contents, word-indexed), so a reading app writes one integration instead of two. There's also a CLI (swift run scribe-cli extract myfile.pdf --output content.json) if you want to inspect the output without wiring up the library first.

How it separates footnotes and finds chapters

This is the part that's actually hard, and it's the part most "extraction" libraries skip. Scribe classifies a document (digital-clean vs scanned, academic vs general) before doing anything else, because that classification picks the strategy for everything downstream: chapter detection tries the PDF outline first, falls back to parsing a Contents/TOC page, and falls back again to heading heuristics in the body. Footnote separation runs 6 adaptive strategies — gap detection, font-size differential, citation pattern matching — ranked by the document's profile, because an academic scan with heavy footnotes needs a different strategy than a clean digital novel with none.

What I actually verified

The repo ships its own reproducible benchmark doc (docs/benchmarks.md) with wall-clock latency and fidelity scores against hand-annotated ground truth, measured on an M4 Mac mini. The two numbers that matter most for "is this fast enough to run inline in an app": EPUB extraction on Jane Austen's Pride and Prejudice (132k words, 64 chapters, Gutenberg #1342) runs in 0.24 seconds at 100.0% fidelity against the book's own declared NCX/spine structure — because EPUB skips OCR and page-layout analysis entirely, it's roughly an order of magnitude faster than PDF. On the PDF side, a two-column academic paper extracts in 0.3 seconds (91.7% fidelity), while a 466k-word, 64 MB scanned 1868 book takes 21.5 seconds. Every number in that table lists the exact book, size and word count, and the doc explains its own reproduction steps (swift build -c release, then node eval/perf.js) rather than asking you to trust a claim with no receipt.

There's also an optional DocumentIntelligence layer (summarize, classify, ask-your-document) that sits on top of the same extraction, gated to iOS 26+/macOS 26+ and, for the Apple-Intelligence-backed methods, to devices where Apple Intelligence is actually available — it never gates the core extraction path, so you get full structured output either way.

Where it's actually used

Scribe is the on-device sibling of the PDF/EPUB pipeline running inside Velo, the RSVP and audiobook-style reading app it was extracted from — Velo uses this exact extraction path to turn an imported PDF or EPUB into the chapter- and word-indexed structure its reader renders and syncs a position against.

Known limitation

Peak memory scales with document size and OCR volume — large scanned PDFs (the 64 MB, 466k-word book above) peak around 1.19 GB even after the 0.4.x release added autorelease-pool draining to the per-page loop (down from 1.7 GB). That's comfortable on a Mac and tight against an iOS memory budget — worth knowing before pointing an iPhone at a 600-page scan rather than a Mac.

Apache-2.0 licensed. Full architecture, the benchmark methodology, and the test corpus are on GitHub.

Try it on a real book

Velo includes a library of free public-domain classics, so you can test both modes before importing anything of your own.

Get Velo Free

← All guides