mirror of
https://git.pumpin.online/funkpower/catena.git
synced 2026-10-22 18:57:14 +00:00
138 lines
3.7 KiB
Markdown
138 lines
3.7 KiB
Markdown
# tocfind
|
||||
|
|
|
|||
|
|
A book's own table of contents.
|
|||
|
|
|
|||
|
|
Part of [catena](../../README.md).
|
|||
|
|
|
|||
|
|
## What it does
|
|||
|
|
|
|||
|
|
A book usually knows its own chapters. An EPUB keeps them in a navigation
|
|||
|
|
document, a PDF in an outline, and both are put there by the publisher — which
|
|||
|
|
makes them better than any heading guessed at from converted text.
|
|||
|
|
|
|||
|
|
Entries come out in reading order, each carrying how deeply it is nested, so a
|
|||
|
|
flat stream still has the shape of the book.
|
|||
|
|
|
|||
|
|
## When you would use it
|
|||
|
|
|
|||
|
|
- **Real chapter titles** for chunks, instead of the extractive ones inferred
|
|||
|
|
from converted markdown.
|
|||
|
|
- **Splitting a book** at the seams its publisher declared.
|
|||
|
|
- **Judging a conversion**: a book whose contents list forty chapters and
|
|||
|
|
whose markdown has three was converted badly.
|
|||
|
|
|
|||
|
|
## Install
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
cargo install --git https://git.pumpin.online/funkpower/catena tocfind
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Example
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
$ tocfind enlightenment.epub --outline
|
|||
|
|
Cover page
|
|||
|
|
Halftitle page
|
|||
|
|
Title page
|
|||
|
|
Copyright page
|
|||
|
|
Contents
|
|||
|
|
1. Introduction: Radical Enlightenment and “Modernity” (1650–1850)
|
|||
|
|
1.1. Basic Argument and Scope
|
|||
|
|
1.2. Definitions and Categories
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
As records:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
$ tocfind enlightenment.epub | head -2
|
|||
|
|
{"v":1,"idx":0,"depth":0,"title":"Cover page","target":"000_ACover.xhtml"}
|
|||
|
|
{"v":1,"idx":1,"depth":0,"title":"Halftitle page","target":"002_FM_halftitle.xhtml"}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
A PDF's outline carries page numbers instead of documents:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
$ tocfind history.pdf --outline
|
|||
|
|
Part One (1)
|
|||
|
|
I. The Setting (3)
|
|||
|
|
II. The Second Estate (41)
|
|||
|
|
Part Two (118)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Only the real chapters, skipping the front matter every book opens with:
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
tocfind book.epub | jq -r 'select(.depth == 0) | .title' | tail -n +5
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The books whose conversion lost the structure:
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
for f in *.epub; do
|
|||
|
|
declared=$(tocfind "$f" | wc -l)
|
|||
|
|
found=$(grep -c '^# ' "converted/${f%.epub}.md")
|
|||
|
|
[ "$declared" -gt $(( found * 3 )) ] && echo "$f: $declared entries, $found headings"
|
|||
|
|
done
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Options
|
|||
|
|
|
|||
|
|
| Option | Effect |
|
|||
|
|
|---|---|
|
|||
|
|
| `--outline` | Print an indented list rather than records |
|
|||
|
|
| `--quiet`, `-q` | Say nothing; the exit code is the answer |
|
|||
|
|
| `--man` | Write this page as a manual page, in roff |
|
|||
|
|
|
|||
|
|
## Exit codes
|
|||
|
|
|
|||
|
|
| Code | Meaning |
|
|||
|
|
|---|---|
|
|||
|
|
| `0` | It had a table of contents |
|
|||
|
|
| `1` | It had none |
|
|||
|
|
| `64` | The command line was wrong |
|
|||
|
|
| `65` | Not a format that keeps one |
|
|||
|
|
| `74` | The file could not be read |
|
|||
|
|
|
|||
|
|
Having none is an answer; not being able to have one is not, which is why
|
|||
|
|
those are different codes.
|
|||
|
|
|
|||
|
|
## Where it reads from
|
|||
|
|
|
|||
|
|
| Format | Where |
|
|||
|
|
|---|---|
|
|||
|
|
| EPUB | The NCX, or an EPUB 3 `<nav epub:type="toc">` |
|
|||
|
|
| PDF | The catalogue's outline |
|
|||
|
|
|
|||
|
|
**The NCX first**, even for books that declare themselves EPUB 3. It is what
|
|||
|
|
books actually carry — every one of thirteen real books sampled had one and
|
|||
|
|
none had a nav document — and its labels are plain text where a nav
|
|||
|
|
document's are markup.
|
|||
|
|
|
|||
|
|
From a nav document only the contents are read, never the page list or the
|
|||
|
|
landmarks, and markup inside a title is not part of the title.
|
|||
|
|
|
|||
|
|
A PDF outline entry resolves to a page where its destination names one
|
|||
|
|
directly. A named destination is not chased: that is a second index, and an
|
|||
|
|
entry without a page is still an entry.
|
|||
|
|
|
|||
|
|
## As a library
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
match tocfind::read(std::path::Path::new("book.epub")) {
|
|||
|
|
Ok(entries) => for entry in entries {
|
|||
|
|
println!("{}{}", " ".repeat(entry.depth as usize), entry.title);
|
|||
|
|
},
|
|||
|
|
Err(why) => eprintln!("no contents: {why}"),
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`tocfind::epub::from_ncx` and `from_nav` read a navigation document already in
|
|||
|
|
hand.
|
|||
|
|
|
|||
|
|
## See also
|
|||
|
|
|
|||
|
|
- [`mdsplit`](../mdsplit/README.md) — splitting a collection by its seams.
|
|||
|
|
- [`chunkmd`](../chunkmd/README.md) — which infers a chapter when the book
|
|||
|
|
does not say.
|
|||
|
|
- [`bookmeta`](../bookmeta/README.md) — the rest of what a file carries.
|