From e27b2aae1cfe07e09ce60a8534928eedbe59333e Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 9 Nov 2026 12:43:57 +0100 Subject: [PATCH] docs: the printed table of contents, in the library and on the ribbon ops v0.34.0 writes the other kind of table of contents -- the one that gets printed, a page with a line for each entry and its page number at the right -- and app v0.43.0 puts it on the Contents tab. Both pages said only how the /Outlines tree is written. With the rule that makes the numbers right: a contents page in front of a document pushes every page of it down, so ContentsPage returns how many pages it added and ShiftBookmarks is the same rule for the tree, edge and all. Covers go-pdfkit/ops#78 and go-pdfkit/app#98. Co-Authored-By: Claude Opus 5 --- content/working-with-a-file/operations.md | 34 ++++++++++++++++++++++- content/working-with-a-file/workbench.md | 9 ++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/content/working-with-a-file/operations.md b/content/working-with-a-file/operations.md index 7404057..2d395f8 100644 --- a/content/working-with-a-file/operations.md +++ b/content/working-with-a-file/operations.md @@ -150,7 +150,39 @@ has been walked. A row more than one level under the row before it is a `*ops.DepthError` naming the row. The browser workbench reads a document's own bookmarks that way before adding one to them, and `pdfops outline -from` reads the file; both go through the same code, so there is one grammar rather than -two that have to be kept in step. +two that have to be kept in step. `ops.Flatten` is `Nest` the other way round. + +### The printed kind + +The `/Outlines` tree is the table of contents a reader *navigates* by. +`Doc.ContentsPage` writes the one that gets **printed** — a page with a line for +each entry, its page number at the right and dots leading to it — from the same +entries: + +```go +n, err := doc.ContentsPage(marks, ops.ContentsOptions{}) +if err != nil { + return err +} +doc.SetOutline(ops.ShiftBookmarks(marks, 1, n)) // if you want both +``` + +⛔ **The numbers it prints are moved by the pages it adds.** A contents page put +in front of a document pushes every page of it down, so an entry that pointed at +page 7 is on page 8 afterwards, and printing the 7 would send a reader one page +short of every heading in the book. `ContentsPage` returns how many pages it +added, and `ShiftBookmarks(marks, from, by)` is the same rule for the tree — +a function rather than a line in each caller because of its edge: an entry +pointing at a page *before* the insertion has not moved, and shifting that too +goes wrong in exactly the documents that have a preface. + +It runs onto as many pages as it needs, and refuses rather than printing a line +whose title has no room at all: a page number with nothing to read beside it +cannot be told from a document whose headings have no names. The refusal comes +before a page is inserted, so a document that was refused is untouched. + +`pdfops contents -from toc.txt -bookmarks book.pdf out.pdf` does both from one +file, and `-title`, `-before`, `-size` and `-margin` set the rest. ## On the command line diff --git a/content/working-with-a-file/workbench.md b/content/working-with-a-file/workbench.md index 5456815..0227f88 100644 --- a/content/working-with-a-file/workbench.md +++ b/content/working-with-a-file/workbench.md @@ -66,6 +66,15 @@ a page number a line, two spaces of indent per level, which is the file `pdfops outline -from` takes, read by the library's own `ops.ParseOutline` rather than by a second parser written here. +*Write a contents page* is the other kind of table of contents: the **printed** +one, a page at the front with a line for each bookmark, its page number at the +right and dots leading to it, built from the entries the rail is already +showing. The numbers account for the page it adds. It does not rewrite the +bookmarks — `ops` carries a document's own outline over and remaps each entry to +the page object it still points at, so the tree follows its pages across the +insertion, where writing it back from the workbench's own walk would lose the +entries that point nowhere and anything past the thousand it reads. + The last three are the ones worth telling apart. **Read** is where you look at what a page says — read it, search it, copy it — and nothing on it changes the document. **Mark up** is where you act on the words you selected: mark them,