Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

folio

A PDF viewer that is only pages and scrolling, and a PDF reader for agents that never makes them swallow the document.

folio, n. — a leaf of a manuscript or book; the page number printed on it.

Two binaries, one engine:

for does
folio people Opens a PDF. Scrolls. That is the whole feature list.
folioq agents Answers questions about a PDF by location, so reading costs tens of tokens instead of tens of thousands.

Install

curl -fsSL https://raw.githubusercontent.com/pub-struct/folio/main/install.sh | bash

Installs to ~/.local/bin, with the runtime libraries in ~/.cache/folio. No root, nothing touched system-wide. Prebuilt binaries are used where they exist; otherwise it builds from source with cargo.

Options and manual install
FOLIO_VERSION=v0.1.0 …| bash   # pin a version
FOLIO_FROM_SOURCE=1  …| bash   # always build from source
FOLIO_NO_OCR=1       …| bash   # skip the ONNX Runtime download (~70 MB)
FOLIO_BIN_DIR=~/bin  …| bash   # install somewhere else

From a clone:

git clone https://github.com/pub-struct/folio && cd folio
./install.sh

To uninstall: rm ~/.local/bin/folio{,q} && rm -rf ~/.cache/folio.

The idea

A 65-page manual is about 31,000 tokens of Markdown. The answer you want is usually on two of them. Every tool in this space converts the document — folioq is built to avoid converting the document.

$ folioq info manual.pdf
pages       65
type        TextBased (confidence 1.00)
est_tokens  30885 for the whole document
headings    164
tables      18-20,25,32-37,39,65
columns     11,27,33-35,37,39,40,46-49,51-53,55,58
needs_ocr   31,46-48
ocr_applied 46-48

That is ~60 tokens, and it is everything you need to plan: how big the document is, which pages hold tables, which pages have no trustworthy text. Then you locate:

$ folioq search manual.pdf "sampling rate"
p6    …Speex can encode wideband speech (16 kHz sampling rate) in addition to…
p7    …### Sampling rate The sampling rate expressed in Hertz (Hz) is the…
p9    …useful to convert audio from one sampling rate to another. There are…

~100 tokens. And only then do you read:

$ folioq read manual.pdf -p 7
[p7]
# 2 Codec description

Two pages instead of sixty-five. ~1,100 tokens instead of 30,885.

Measure it yourself

The numbers above are from one document; run it on yours:

$ ./scripts/benchmark.sh manual.pdf "jitter buffer"

$ folioq info manual.pdf
pages       65
est_tokens  30885 for the whole document
needs_ocr   31,46-48
...
$ folioq search manual.pdf "jitter buffer"
p9    …ectly provided in the codec. # 2.4 Adaptive Jitter Buffer When…
p22   …th the playback or recording process. # 6.3 Jitter Buffer The…
...
$ folioq read manual.pdf -p 22
[p22]
## 6 Speech Processing API ( libspeexdsp)
...
──────────────────────────────────────────────────────────────
  read the whole document        123,879 chars   ~30,969 tokens
  info → search → read -p 22       5,439 chars   ~1,359 tokens

  same answer, 23x less context

It runs the real commands, picks the page with the most matches, and counts characters exactly (tokens are the usual ~4-chars-per-token estimate). On a short document it will tell you plainly that you may as well read the whole thing — folio earns its keep on documents worth navigating, not on every PDF.

Usage

folioq info    paper.pdf                # what is this, and what would it cost
folioq outline paper.pdf                # headings, with page numbers
folioq search  paper.pdf "batch size"   # which pages mention it, with context
folioq read    paper.pdf -p 12-14       # only those pages
folioq render  paper.pdf -p 7           # a page as a PNG, to actually look at

Every command takes --json. Page selection accepts ranges and lists everywhere: -p 3, -p 1-5, -p 1-5,8,12-.

Useful flags: -r for regex search, --max-chars on read to cap output (anything dropped is reported in truncated_pages, never silently), --max-level on outline to see only top-level headings.

Caching. Extraction happens once per file and is keyed by content hash. The first question about a 65-page PDF takes ~3s; every later one takes ~15ms. So ask several narrow questions rather than one broad one. folioq cache path locates it, folioq cache clear empties it. The key covers the file's contents, the OCR feature and the folio version, so neither editing a file nor upgrading folio can serve you a stale extract. Set FOLIO_CACHE_DIR to put it somewhere else.

Use it from your agent

folioq speaks MCP, exposing the same five operations as tools: pdf_info, pdf_outline, pdf_search, pdf_read, pdf_render. pdf_render returns the image inline, so a scanned page can simply be looked at.

Claude Code

claude mcp add --scope user folio -- ~/.local/bin/folioq mcp

Claude Desktop, Cursor, Zed, or any MCP client — add to the client's config:

{
  "mcpServers": {
    "folio": {
      "command": "/home/you/.local/bin/folioq",
      "args": ["mcp"]
    }
  }
}

Make it the default for PDFs. Registering the server gives the agent the tools; a line in your instructions file makes it reach for them. In ~/.claude/CLAUDE.md (or your client's equivalent):

# PDFs

Read PDFs with `folio`, never by dumping the whole file into context.
Work in this order: `pdf_info` first, then `pdf_outline` or `pdf_search`
to locate, then `pdf_read` with explicit pages. Only read a whole PDF
when `pdf_info` shows it is small and you genuinely need all of it.
If a page appears in `needs_ocr`, its text is unreliable — use
`pdf_render` and look at it instead.

Agents without MCP can just shell out to folioq … --json.

The viewer

folio paper.pdf

No toolbar, no sidebar, no page counter. The window is the document.

j / k / arrows     scroll                g / G      first / last page
space / shift      page down / up        + / - / 0  zoom in, out, reset
PageUp / PageDown  page up / down        q / Esc    quit

Pages lay out from their dimensions, which PDFium reports without parsing page content, so a 500-page file has a correct scrollbar the instant it opens. Only pages near the viewport are rasterised — on a background thread, at your display's real pixel density — and they are released once they scroll away. Memory tracks the window, not the document.

Scanned PDFs

Pages whose text cannot be trusted — scanned images, broken font encodings, garbled extraction — are reported in needs_ocr rather than quietly returned as garbage.

With ONNX Runtime present (the installer fetches it), OCR runs automatically on exactly those pages and nothing else, so a text PDF pays nothing for the feature. Without it, extraction still succeeds, info says why OCR did not run, and those pages stay flagged so you know to render and read them visually.

Build with --no-default-features to drop the OCR stack entirely.

How it works

crates/foliocore   document model: rasterisation, page-addressed text, cache
crates/folio       the viewer
crates/folioq      the CLI and MCP server

Run the tests with cargo test --workspace, and again with --no-default-features. Both are needed: the OCR and text-only paths take page numbers from different upstream APIs, one 1-indexed and one 0-indexed, so a mistake in the text-only conversion is invisible when OCR is on.

Page numbers are 1-indexed throughout the public API, matching what a reader sees at the bottom of the page.

PDFium and ONNX Runtime are loaded at runtime rather than linked, and are discovered from $PDFIUM_LIB_PATH / $ORT_DYLIB_PATH, the executable's directory, ./target/, or ~/.cache/folio.

Built on

folio is a thin layer over other people's hard work:

  • pdf-inspector (firecrawl) — text extraction, layout and table detection, OCR routing
  • firecrawl-pdfium (firecrawl) — safe PDFium bindings
  • GPUI (Zed) — the GPU-accelerated UI framework
  • PDFium (Google) — rasterisation
  • ONNX Runtime (Microsoft) — OCR inference
  • rmcp — the MCP SDK

It is not a fork of any of them; they are all upstream dependencies.

License

MIT

About

A PDF viewer that is only pages and scrolling, and a PDF reader for agents that never makes them swallow the document.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages