pdfboss-cli 1.2.0

PDF command line: PDF to text, Markdown and PNG, image extraction, PDF creation, and the json/hex/q/tui explorer
---
name: pdfboss
description: Use when reading, extracting, rendering, creating, or exploring PDF files with pdfboss, the from-scratch Rust PDF engine with a CLI and Python bindings. Triggers include extracting text or Markdown from a PDF, rasterizing pages to PNG, PPM, BMP or JPEG, pulling embedded images, composing a new PDF (blank, text, images, Markdown, TOML manifest, or the pdfboss.write API), watermarking an existing PDF, inspecting PDF internals (objects, xref, hexdump, jq-style queries), reading PDFs over HTTP without downloading them whole, and opening encrypted PDFs.
---

# pdfboss

A PDF engine written from scratch in safe Rust: parse, extract text and Markdown, rasterize to PNG, PPM, BMP or JPEG, extract embedded images, and create PDFs. One core behind the `pdfboss` CLI and the `pdfboss` Python package. Clean-room from ISO 32000, no C dependencies. The reader is lenient: broken cross-reference tables are reconstructed, wrong stream lengths tolerated, garbage operators skipped, and every dropped or approximated item is reported instead of silently lost.

## Install

```bash
pip install pdfboss            # abi3 wheels, CPython 3.12+, no toolchain needed
pip install 'pdfboss[full]'    # adds the OFL substitute faces (pdfboss-fonts)
cargo install pdfboss-cli      # the `pdfboss` binary
```

Coding agents can install this skill with `pdfboss skill install` (writes it into the agent's skill directory; `pdfboss skill print` writes it to stdout) or with `npx skills add 4thel00z/pdfboss`.

## CLI

```bash
pdfboss info    doc.pdf                     # version, page count, sizes, metadata
pdfboss text    doc.pdf --page 2            # omit --page for all pages
pdfboss md      doc.pdf                     # Markdown: headings, lists, tables from layout, pages read in content order
pdfboss render  doc.pdf --page 1 -o p.png --scale 2.0   # -o extension picks .png/.ppm/.bmp/.jpg; --jpeg-quality 1-100
pdfboss images  doc.pdf -o out/             # embedded images as native-size PNGs
pdfboss tui     doc.pdf                     # interactive terminal explorer
```

Every command takes a local path or an `http(s)://` URL; remote files are fetched in byte ranges rather than downloaded whole (a server that ignores `Range` costs one full download). When stderr is a terminal, a ranged open draws a two-line coverage minimap of the byte ranges fetched so far, erased once the document is open; non-interactive runs never see it. Encrypted files take `--password` (an empty user password opens transparently).

Creation:

```bash
pdfboss create blank  -o out.pdf --pages 3
pdfboss create text   notes.txt -o out.pdf
pdfboss create images a.png b.jpg -o out.pdf
pdfboss create md     notes.md -o out.pdf        # CommonMark+GFM, CSS-themable
pdfboss create manifest doc.toml -o out.pdf      # [meta] plus [[page]] text/paragraph/image/link
```

Explorer:

```bash
pdfboss json doc.pdf                 # the document as a JSON value tree (--layout adds page blocks)
pdfboss q    doc.pdf '.header.version'
pdfboss obj  doc.pdf 5               # pretty-print object 5
pdfboss hex  doc.pdf obj:5           # hexdump the file or one element
```

In the TUI, `y` opens a yank menu on the selected element (jq-style query, equivalent CLI command, hexdump, raw bytes, decoded value, object reference), `y e` copies the element and `y m` the current page's Markdown; panes resize with Alt+arrows.

Fonts: `--fonts embedded-only|all-embedded|full`. The default resolves to `full` when substitute faces are available (the compiled-in OFL set or `--font-dir`), otherwise `all-embedded`. Text a tier leaves unpainted still advances, so the rest of the page keeps its layout.

## Python

```python
import pdfboss

doc  = pdfboss.Document("doc.pdf")            # or Document(data=raw_bytes), password=""
text = doc.extract_text()                     # pages fan out across cores
md   = doc.extract_markdown()
png  = doc[0].render(scale=2.0)               # PNG bytes; format="ppm"/"bmp" for raw RGB pixels, "jpeg" (quality=90) for lossy
png, warnings = doc[0].render_reporting()     # warnings list every drop or approximation
images = doc[0].extract_images()              # each: .data (PNG bytes), .width, .height
spans  = list(doc.spans())                    # styled spans: font, weight, color, position

# async, over files or HTTP, range-fetched
doc = await pdfboss.AsyncDocument.open_url("https://example.com/doc.pdf")

# composition: pages and elements join with |, singleton slots raise on duplicates
from pdfboss.write import Pdf, Page, Text, Paragraph, Metadata, Standard14
page = (
    Page(size="a4")
    | Text("Title", at=(72, 770), font=Standard14.HELVETICA_BOLD, size=28)
    | Paragraph("Body text.", rect=(72, 100, 451, 640))
)
data = (Pdf() | Metadata(title="Title") | page).to_bytes()

# watermark an existing file: overlay's first page drawn over every page, as an
# incremental update appended to the original bytes (rewrite=True writes a fresh, compressed file)
stamped = pdfboss.write.watermark(original_bytes, overlay_bytes)

# markdown to PDF: returns the file bytes
data = pdfboss.md.to_pdf("# Hello\n\nWorld", theme=None, size="a4")
```

`fonts=` on the render methods defaults to `None`, resolving to `"full"` when `font_dir=` is given or the `pdfboss-fonts` package is importable, else `"all-embedded"`; an explicit `fonts="full"` with no face source raises `ValueError`. `format=` is `"png"` (default), `"ppm"`, `"bmp"` or `"jpeg"`: PPM and BMP are the pixels behind a header (no encode cost, alpha dropped), JPEG is the lossy one with `quality=` 1 to 100. The type stubs in `pdfboss/_pdfboss.pyi` are the authoritative signatures.

## Rust

Library crates on crates.io: `pdfboss-core` (the reader), `pdfboss-text`, `pdfboss-output` (plain text and Markdown), `pdfboss-render` (rasterizer; `RenderOptions` fields `glyph_painting`, `substitutes`, `oc`, `cache`), `pdfboss-write` (creation), `pdfboss-markdown` (Markdown to PDF), `pdfboss-aio` (async range-fetching I/O), `pdfboss-jpx` and `pdfboss-icc` (its own codecs), `pdfboss-tui`.

## Gotchas

- Rendering never fails on unreadable content; when fidelity matters, use `render_reporting` and check the warnings instead of assuming a clean render.
- `/Symbol` and `/ZapfDingbats` have no license-clean substitute and stay blank at every tier.
- Scanned PDFs (JBIG2, CCITT) carry no text layer: `text` and `md` return little or nothing there; render the pages instead.
- `extract_markdown` drops repeated page headers and footers by design.
- A whole-document Rust render walk should share one `RenderCache` through `RenderOptions::cache` so fonts and ICC profiles load once.

## Links

- User guide: https://pdfboss.dev/docs/
- Site and benchmarks: https://pdfboss.dev
- Race it in a browser: https://pdfarena.tahrioui.de
- Repository: https://github.com/4thel00z/pdfboss