From 1d3405318c795e16b48a8863fbdf46546194ce0b Mon Sep 17 00:00:00 2001 From: jpirnay Date: Thu, 26 Mar 2026 11:16:53 +0100 Subject: [PATCH] Integrate PT 1455 --- docs/epub-toc-navigation.md | 159 ++ lib/Epub/Epub/Section.cpp | 177 +- lib/Epub/Epub/Section.h | 22 + .../Epub/parsers/ChapterHtmlSlimParser.cpp | 27 +- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 5 +- open-x4-sdk | 2 +- scripts/generate_spine_toc_edges_epub.py | 2096 +++++++++++++++++ src/activities/ActivityResult.h | 2 + src/activities/reader/EpubReaderActivity.cpp | 92 +- src/activities/reader/EpubReaderActivity.h | 5 + .../EpubReaderChapterSelectionActivity.cpp | 6 +- .../EpubReaderChapterSelectionActivity.h | 6 +- test/epubs/test_spine_toc_edges.epub | Bin 0 -> 66978 bytes 13 files changed, 2572 insertions(+), 27 deletions(-) create mode 100644 docs/epub-toc-navigation.md create mode 100644 scripts/generate_spine_toc_edges_epub.py create mode 100644 test/epubs/test_spine_toc_edges.epub diff --git a/docs/epub-toc-navigation.md b/docs/epub-toc-navigation.md new file mode 100644 index 00000000..2e623c65 --- /dev/null +++ b/docs/epub-toc-navigation.md @@ -0,0 +1,159 @@ +# EPUB TOC Anchor Navigation + +This document describes how the reader handles EPUB Table of Contents (TOC) entries that use fragment anchors to point into spine files, enabling navigation to sub-chapters within a single XHTML file. + +## Background: EPUB spine and TOC structure + +An EPUB's **spine** is an ordered list of XHTML files that define reading order. The **TOC** (table of contents) maps chapter names to positions in the spine, optionally with fragment anchors (e.g. `chapter1.xhtml#section-5`). + +Two layouts are relevant here: + +- **1:1** -- one TOC entry per spine item (most common, no anchors needed) +- **Multi-TOC-per-spine** -- multiple TOC entries point into a single spine file using fragment anchors (e.g. Moby Dick from Project Gutenberg packs 3-9 chapters per file) + +Spine items before the first TOC entry (cover pages) and after the last (appendices, copyright) have no TOC entry of their own. + +## BookMetadataCache and TOC-to-spine mapping + +`BookMetadataCache` builds the mapping between spine items and TOC entries at epub open time. Key details: + +- Each `SpineEntry` has a `tocIndex` field set during cache building. For spines with no matching TOC entry, `tocIndex` inherits the previous spine's value (`lastSpineTocIndex`). This means orphan spines (cover pages, appendices) are treated as continuations of the nearest preceding chapter. +- `getTocIndexForSpineIndex(i)` returns the stored `tocIndex` for spine `i` -- a file seek into BookMetadataCache, not computed on the fly. +- `getTocItem(i)` returns the TOC entry (title, spineIndex, anchor) for TOC index `i` -- also a file seek per call, not cached in memory. Code that queries TOC metadata in a loop should cache the results locally first. +- `getSpineIndexForTocIndex(i)` does the reverse lookup (TOC index to spine index). + +## Section cache file format + +The section cache (`.bin`) stores pre-rendered page data for a spine item. The file layout: + +``` +[header: version, render parameters, pageCount, lutOffset, anchorMapOffset] +[serialized pages...] +[page LUT: array of uint32_t file offsets, one per page] +[anchor map: uint16_t count, then (string, uint16_t) pairs] +``` + +The header size is defined by `HEADER_SIZE` (a constexpr computed via `sizeof` sum) and validated with a `static_assert`. Three functions read this header independently and must stay in sync: + +- `loadSectionFile` -- full section load, reads header + builds TOC boundaries from anchor map +- `getPageForAnchor` -- seeks directly to anchor map offset from header +- `writeSectionFileHeader` -- writes the header during cache creation + +When modifying the header layout, bump `SECTION_FILE_VERSION` to invalidate stale caches and update all read paths. + +## Anchor-to-page mapping + +### Recording anchors during parsing + +`ChapterHtmlSlimParser` records every HTML `id` attribute and its corresponding page number into `anchorData` (a flat `std::vector>`). Recording is deferred via `pendingAnchorId` until `startNewTextBlock()`, after the previous text block is flushed to pages via `makePages()`. This ensures `completedPageCount` reflects the correct page. + +For TOC anchors specifically, `startNewTextBlock` also forces a page break before recording, so chapters start on fresh pages rather than mid-page. The parser receives the set of TOC anchor strings via `tocAnchors` (a `std::vector`) from `Section::createSectionFile`. + +### On-disk format + +The anchor data is serialized at the end of the section cache file (`.bin`), after the page LUT. The header stores the anchor map offset. Format: + +``` +[uint16_t count] +[string anchor_1][uint16_t page_1] +[string anchor_2][uint16_t page_2] +... +``` + +This data serves two purposes: +- **Footnote navigation** (`getPageForAnchor`): on-demand linear scan for a single anchor +- **TOC boundary resolution** (`buildTocBoundariesFromFile`): scan matching only TOC anchors + +### Data structure choices + +All anchor storage uses flat vectors, not `std::map` or `std::set`. On the ESP32-C3, each `std::map`/`std::set` node requires its own heap allocation, causing fragmentation. Vectors use a single contiguous allocation. The entry counts are small enough (typically 1-10 TOC anchors per spine, dozens to hundreds of total anchors) that linear scans are faster than tree lookups at these sizes. + +## TOC boundaries in Section + +When a section is loaded or created, `Section` builds an in-memory `tocBoundaries` vector mapping each TOC entry in that spine to its start page. This is a small vector (1-3 entries typically) that enables O(1) lookups without file I/O. + +### Two build paths + +**From in-memory anchors** (`buildTocBoundaries`): Called after `createSectionFile` when the parser's anchor vector is still in memory. Iterates TOC entries and does linear scans against the anchor vector. + +**From disk** (`buildTocBoundariesFromFile`): Called from `loadSectionFile` when loading a cached section. Caches the small set of TOC anchor strings first (since `getTocItem()` does file I/O to `BookMetadataCache`), then streams through on-disk anchors matching only those, stopping early once all are resolved. Uses a reusable `std::string` buffer to avoid per-entry heap allocation. + +The two functions are kept separate because their iteration patterns differ fundamentally: in-memory iterates TOC entries with inner scans of anchors, while the disk path iterates disk entries with inner scans of the small TOC anchor set. + +### Early exit optimization + +If no TOC entries in the spine have anchors (`unresolvedCount == 0`), both functions return immediately without storing any boundaries. `getTocIndexForPage` falls back to `epub->getTocIndexForSpineIndex`, which gives the correct answer for the common 1:1 case. + +### Query methods + +- `getTocIndexForPage(page)` -- binary search on sorted `tocBoundaries` to find which chapter a page belongs to +- `getPageForTocIndex(tocIndex)` -- linear scan to find a chapter's start page +- `getPageRangeForTocIndex(tocIndex)` -- returns `[startPage, endPage)` range for a chapter within this spine + +All are in-memory, no file I/O. + +## Chapter navigation in EpubReaderActivity + +### Chapter skip (long-press) + +Navigates by TOC index, not spine index. Uses `getTocIndexForPage` to determine the current chapter, then increments or decrements. + +- **Same-spine skip**: Resolves the target page via `getPageForTocIndex` entirely in memory +- **Cross-spine skip**: Sets `pendingTocIndex` (a `std::optional`) which is resolved after the target section loads in `render()` +- **Forward past last TOC entry**: Jumps to end-of-book (spine index clamped in `render()`) +- **Backward before first TOC entry**: Jumps to the spine before the current chapter's first spine (clamped to 0 in `render()`) +- **No TOC entry for spine** (`curTocIndex < 0`): Falls back to spine-level skip + +### Chapter selector + +The chapter selection activity receives `currentTocIndex` (per-page, not per-spine) so it highlights the correct sub-chapter. Returns `ChapterResult` with both `spineIndex` and `std::optional tocIndex`. The reader resolves the page via `getPageForTocIndex` for same-spine navigation or defers via `pendingTocIndex` for cross-spine. + +### Footnote navigation + +Uses the existing `pendingAnchor` mechanism from the footnote anchor navigation commit (4d222567). `getPageForAnchor` does an on-demand linear scan of the on-disk anchor data. This is separate from TOC boundaries -- it reads all anchors (not just TOC ones) and is only called for footnote jumps. + +### Status bar + +Uses `getTocIndexForPage()` for the chapter title, so the status bar shows the correct sub-chapter name when reading a multi-TOC-per-spine file. + +## Orphan spine handling + +Spine items without a TOC entry inherit the previous spine's `tocIndex` in `BookMetadataCache`. This means: + +- Pre-TOC spines (cover pages) may have `tocIndex == -1` if they're before any chapter +- Post-TOC spines (appendices, copyright) inherit the last chapter's `tocIndex` + +The chapter skip logic guards against `curTocIndex < 0` and falls back to spine-level navigation. + +## Implementation pitfalls and edge cases + +### Anchor recording timing + +The `pendingAnchorId` deferred recording pattern is critical for correctness. Anchors must be recorded *after* `makePages()` flushes the previous text block (so `completedPageCount` reflects the right page) but the TOC page break must happen *before* recording (so the anchor lands on the new page). Both of these happen inside `startNewTextBlock()`. An earlier design used a `recordAnchor` lambda called at various points in `startElement()`, but this had wrong timing for headings and block elements -- `startNewTextBlock` would consume `pendingAnchorId` before `recordAnchor` could force the page break. Moving all page-break logic into `startNewTextBlock` fixed this. + +### pendingAnchorId overwrite on consecutive elements + +If two elements with `id` attributes appear before any `startNewTextBlock` call (e.g. nested divs), the second `id` overwrites `pendingAnchorId` and the first anchor is never recorded. This is a known limitation inherited from the footnote anchor navigation commit (4d222567) on master. In practice, TOC anchors are on chapter headings which trigger `startNewTextBlock`, so this doesn't affect TOC navigation. + +### wordsExtractedInBlock reset on empty block reuse + +When `startNewTextBlock` reuses an empty text block (the early-return path), `wordsExtractedInBlock` must be reset to 0. Without this, footnotes in the reused block could be assigned to wrong pages based on stale word counts from a prior block. + +### getTocItem() does file I/O + +`epub->getTocItem()` reads from `BookMetadataCache` via file seek on every call. This is why `buildTocBoundariesFromFile` caches the TOC anchor strings into a small vector before entering the disk scan loop -- otherwise the inner loop would do file I/O (BookMetadataCache) for every on-disk anchor entry. + +### Defensive sort on tocBoundaries + +`tocBoundaries` is sorted by `startPage` after building. In well-formed EPUBs, entries are already in order (TOC follows document order). The sort is a safety net for malformed EPUBs where TOC entries might be out of document order. With 1-3 entries it has no measurable cost. + +## Test epub + +`scripts/generate_spine_toc_edges_epub.py` generates `test/epubs/test_spine_toc_edges.epub`, a purpose-built epub that exercises spine/TOC relationship patterns. See the script header for the full list of edge cases covered. + +## Performance characteristics + +- **Per page turn**: All in-memory. `getTocIndexForPage` (binary search on 1-3 entries), `getTocItem` for title (one file seek to BookMetadataCache -- noted as a future optimization opportunity). +- **Section load**: One file open for the section cache. `buildTocBoundariesFromFile` scans the anchor map for a few TOC entries with early exit. +- **Footnote navigation**: One additional file open to scan the anchor map for a single anchor. +- **1:1 TOC-to-spine (common case)**: No overhead. `unresolvedCount == 0`, `tocBoundaries` stays empty, all queries fall back to spine-level methods. diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index a752bcb6..499e9437 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "Epub/css/CssParser.h" #include "Page.h" #include "hyphenation/Hyphenator.h" @@ -147,6 +149,10 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con return false; } } + // Build TOC boundaries by scanning anchor data from the still-open file, + // matching only the TOC anchors we need (avoids loading all anchors into memory). + buildTocBoundariesFromFile(file); + // File is intentionally left open; subsequent loadPageFromSectionFile() calls // seek within this handle instead of re-opening the file each time. LOG_DBG("SCT", "Deserialization succeeded: %d pages, LUT cached", pageCount); @@ -244,11 +250,24 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c } } + // Collect TOC anchors for this spine so the parser can insert page breaks at chapter boundaries + std::vector tocAnchors; + const int startTocIndex = epub->getTocIndexForSpineIndex(spineIndex); + if (startTocIndex >= 0) { + for (int i = startTocIndex; i < epub->getTocItemsCount(); i++) { + auto entry = epub->getTocItem(i); + if (entry.spineIndex != spineIndex) break; + if (!entry.anchor.empty()) { + tocAnchors.push_back(std::move(entry.anchor)); + } + } + } + ChapterHtmlSlimParser visitor( epub, tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth, viewportHeight, hyphenationEnabled, [this, &lut](std::unique_ptr page) { lut.emplace_back(this->onPageComplete(std::move(page))); }, - embeddedStyle, contentBase, imageBasePath, imageRendering, progressFn, cssParser); + embeddedStyle, contentBase, imageBasePath, imageRendering, std::move(tocAnchors), progressFn, cssParser); Hyphenator::setPreferredLanguage(epub->getLanguage()); success = visitor.parseAndBuildPages(); @@ -281,7 +300,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c return false; } - // Write anchor-to-page map for fragment navigation (e.g. footnote targets) + // Write anchor-to-page map for fragment navigation (TOC + footnote targets) const uint32_t anchorMapOffset = file.position(); const auto& anchors = visitor.getAnchors(); serialization::writePod(file, static_cast(anchors.size())); @@ -309,6 +328,8 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c cssParser->clear(); } + buildTocBoundaries(anchors); + // Cache the LUT in memory and open the file for reading so that // subsequent loadPageFromSectionFile() calls can seek directly without re-opening. if (!Storage.openFileForRead("SCT", filePath, file)) { @@ -342,6 +363,158 @@ std::unique_ptr Section::loadPageFromSectionFile() { // File is intentionally NOT closed; stays open for the next page load } +// Resolve TOC anchor-to-page mappings from the parser's in-memory anchor vector. +// Called after createSectionFile when anchors are already in memory. +// See buildTocBoundariesFromFile for the on-disk variant; the two are kept separate +// because the anchor resolution has fundamentally different iteration patterns +// (scan in-memory vector vs. stream from file with early exit). +void Section::buildTocBoundaries(const std::vector>& anchors) { + const int startTocIndex = epub->getTocIndexForSpineIndex(spineIndex); + if (startTocIndex < 0) return; + + // Count TOC entries for this spine and how many have anchors to resolve + const int tocCount = epub->getTocItemsCount(); + uint16_t totalEntries = 0; + uint16_t unresolvedCount = 0; + for (int i = startTocIndex; i < tocCount; i++) { + const auto entry = epub->getTocItem(i); + if (entry.spineIndex != spineIndex) break; + totalEntries++; + if (!entry.anchor.empty()) unresolvedCount++; + } + + // If no TOC entries have anchors, all chapters start at page 0 and + // getTocIndexForPage falls back to epub->getTocIndexForSpineIndex, + // so there's nothing to resolve and no value in storing boundaries. + if (totalEntries == 0 || unresolvedCount == 0) return; + + tocBoundaries.reserve(totalEntries); + for (int i = startTocIndex; i < startTocIndex + totalEntries; i++) { + const auto entry = epub->getTocItem(i); + uint16_t page = 0; + if (!entry.anchor.empty()) { + for (const auto& [key, val] : anchors) { + if (key == entry.anchor) { + page = val; + break; + } + } + } + tocBoundaries.push_back({i, page}); + } + + // Defensive sort in case TOC entries are out of document order in a malformed epub + std::sort(tocBoundaries.begin(), tocBoundaries.end(), + [](const TocBoundary& a, const TocBoundary& b) { return a.startPage < b.startPage; }); +} + +// Resolve TOC anchor-to-page mappings by scanning the section cache's on-disk anchor data. +// Called from loadSectionFile when anchors are not in memory. Caches the small set of +// TOC anchor strings first (since getTocItem does file I/O to BookMetadataCache), then +// streams through on-disk anchors matching only those, stopping as soon as all are found. +// See buildTocBoundaries for the in-memory variant. +void Section::buildTocBoundariesFromFile(FsFile& f) { + const int startTocIndex = epub->getTocIndexForSpineIndex(spineIndex); + if (startTocIndex < 0) return; + + // Count TOC entries for this spine, then reserve and populate + const int tocCount = epub->getTocItemsCount(); + uint16_t totalEntries = 0; + uint16_t unresolvedCount = 0; + for (int i = startTocIndex; i < tocCount; i++) { + const auto entry = epub->getTocItem(i); + if (entry.spineIndex != spineIndex) break; + totalEntries++; + if (!entry.anchor.empty()) unresolvedCount++; + } + + // If no TOC entries have anchors, all chapters start at page 0 and + // getTocIndexForPage falls back to epub->getTocIndexForSpineIndex, + // so there's nothing to resolve and no value in storing boundaries. + if (totalEntries == 0 || unresolvedCount == 0) return; + + // Cache TOC anchor strings before scanning disk, since getTocItem() does file I/O + struct TocAnchorEntry { + int tocIndex; + std::string anchor; + }; + std::vector tocAnchorsToResolve; + tocAnchorsToResolve.reserve(unresolvedCount); + tocBoundaries.reserve(totalEntries); + for (int i = startTocIndex; i < startTocIndex + totalEntries; i++) { + const auto entry = epub->getTocItem(i); + tocBoundaries.push_back({i, 0}); + if (!entry.anchor.empty()) { + tocAnchorsToResolve.push_back({i, std::move(entry.anchor)}); + } + } + + // Single pass through on-disk anchors, matching against cached TOC anchors. + // Stop early once all TOC anchors are resolved. + // Header layout: ... | lutOffset (u32) | anchorMapOffset (u32) | paragraphLutOffset (u32) | + f.seek(HEADER_SIZE - sizeof(uint32_t) * 2); + uint32_t anchorMapOffset; + serialization::readPod(f, anchorMapOffset); + + if (anchorMapOffset != 0) { + f.seek(anchorMapOffset); + uint16_t count; + serialization::readPod(f, count); + std::string key; + for (uint16_t i = 0; i < count && unresolvedCount > 0; i++) { + uint16_t page; + serialization::readString(f, key); + serialization::readPod(f, page); + for (auto& tocAnchor : tocAnchorsToResolve) { + if (!tocAnchor.anchor.empty() && key == tocAnchor.anchor) { + tocBoundaries[tocAnchor.tocIndex - startTocIndex].startPage = page; + tocAnchor.anchor.clear(); // mark resolved + unresolvedCount--; + break; + } + } + } + } + + // Defensive sort in case TOC entries are out of document order in a malformed epub + std::sort(tocBoundaries.begin(), tocBoundaries.end(), + [](const TocBoundary& a, const TocBoundary& b) { return a.startPage < b.startPage; }); +} + +int Section::getTocIndexForPage(const int page) const { + if (tocBoundaries.empty()) { + return epub->getTocIndexForSpineIndex(spineIndex); + } + + // Find the first boundary AFTER page, then step back one + auto it = std::upper_bound(tocBoundaries.begin(), tocBoundaries.end(), static_cast(page), + [](uint16_t page, const TocBoundary& boundary) { return page < boundary.startPage; }); + if (it == tocBoundaries.begin()) { + return tocBoundaries[0].tocIndex; + } + return std::prev(it)->tocIndex; +} + +std::optional Section::getPageForTocIndex(const int tocIndex) const { + for (const auto& boundary : tocBoundaries) { + if (boundary.tocIndex == tocIndex) { + return boundary.startPage; + } + } + return std::nullopt; +} + +std::optional Section::getPageRangeForTocIndex(const int tocIndex) const { + for (size_t i = 0; i < tocBoundaries.size(); i++) { + if (tocBoundaries[i].tocIndex == tocIndex) { + const int startPage = tocBoundaries[i].startPage; + const int endPage = (i + 1 < tocBoundaries.size()) ? static_cast(tocBoundaries[i + 1].startPage) : pageCount; + return TocPageRange{startPage, endPage}; + } + } + return std::nullopt; +} + std::optional Section::getPageForAnchor(const std::string& anchor) const { FsFile f; if (!Storage.openFileForRead("SCT", filePath, f)) { diff --git a/lib/Epub/Epub/Section.h b/lib/Epub/Epub/Section.h index b04ea5f7..9cfba2e7 100644 --- a/lib/Epub/Epub/Section.h +++ b/lib/Epub/Epub/Section.h @@ -23,6 +23,15 @@ class Section { bool embeddedStyle, uint8_t imageRendering); uint32_t onPageComplete(std::unique_ptr page); + struct TocBoundary { + int tocIndex = 0; + uint16_t startPage = 0; + }; + std::vector tocBoundaries; + + void buildTocBoundaries(const std::vector>& anchors); + void buildTocBoundariesFromFile(FsFile& f); + public: uint16_t pageCount = 0; int currentPage = 0; @@ -42,6 +51,19 @@ class Section { uint8_t imageRendering, const std::function& progressFn = nullptr); std::unique_ptr loadPageFromSectionFile(); + // Given a page in this section, return the TOC index for that page. + int getTocIndexForPage(int page) const; + // Given a TOC index, return the start page in this section. + // Returns nullopt if the TOC index doesn't map to a boundary in this spine (e.g. belongs to a different spine). + std::optional getPageForTocIndex(int tocIndex) const; + + struct TocPageRange { + int startPage; // inclusive + int endPage; // exclusive + }; + // Returns the page range [start, end) within this spine that belongs to the given TOC index. + std::optional getPageRangeForTocIndex(int tocIndex) const; + // Look up the page number for an anchor id from the section cache file. std::optional getPageForAnchor(const std::string& anchor) const; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index e9e46fc4..8aeaa405 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include "../../Epub.h" #include "../Page.h" #include "../converters/ImageDecoderFactory.h" @@ -146,15 +148,35 @@ void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) { currentTextBlock->setBlockStyle(currentTextBlock->getBlockStyle().getCombinedBlockStyle(incoming)); if (!pendingAnchorId.empty()) { + if (std::find(tocAnchors.begin(), tocAnchors.end(), pendingAnchorId) != tocAnchors.end()) { + if (currentPage && !currentPage->elements.empty()) { + completePageFn(std::move(currentPage)); + completedPageCount++; + currentPage.reset(new Page()); + currentPageNextY = 0; + } + } anchorData.push_back({std::move(pendingAnchorId), static_cast(completedPageCount)}); pendingAnchorId.clear(); } + wordsExtractedInBlock = 0; return; } makePages(); } - // Record deferred anchor after previous block is flushed + // If the pending anchor is a TOC chapter boundary, force a page break after the previous + // block is flushed so the chapter starts on a fresh page. + if (!pendingAnchorId.empty() && + std::find(tocAnchors.begin(), tocAnchors.end(), pendingAnchorId) != tocAnchors.end()) { + if (currentPage && !currentPage->elements.empty()) { + completePageFn(std::move(currentPage)); + completedPageCount++; + currentPage.reset(new Page()); + currentPageNextY = 0; + } + } + // Record deferred anchor after previous block is flushed (and any TOC page break) if (!pendingAnchorId.empty()) { anchorData.push_back({std::move(pendingAnchorId), static_cast(completedPageCount)}); pendingAnchorId.clear(); @@ -182,7 +204,8 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* } else if (strcmp(atts[i], "style") == 0) { styleAttr = atts[i + 1]; } else if (strcmp(atts[i], "id") == 0) { - // Defer recording until startNewTextBlock, after previous block is flushed to pages + // Defer both anchor recording and TOC page breaks until startNewTextBlock, + // after the previous block is flushed to pages via makePages(). self->pendingAnchorId = atts[i + 1]; } } diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index e1202c9f..1802ac76 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -74,6 +74,7 @@ class ChapterHtmlSlimParser { int completedPageCount = 0; std::vector> anchorData; std::string pendingAnchorId; // deferred until after previous text block is flushed + std::vector tocAnchors; // Paragraph index tracking for XPath-to-page lookup table. // Counts

sibling indices (1-based, matching XPath convention) during page building. @@ -110,6 +111,7 @@ class ChapterHtmlSlimParser { const std::function)>& completePageFn, const bool embeddedStyle, const std::string& contentBase, const std::string& imageBasePath, const uint8_t imageRendering = 0, + std::vector tocAnchors = {}, const std::function& progressFn = nullptr, const CssParser* cssParser = nullptr) @@ -129,7 +131,8 @@ class ChapterHtmlSlimParser { embeddedStyle(embeddedStyle), imageRendering(imageRendering), contentBase(contentBase), - imageBasePath(imageBasePath) {} + imageBasePath(imageBasePath), + tocAnchors(std::move(tocAnchors)) {} ~ChapterHtmlSlimParser() = default; bool parseAndBuildPages(); diff --git a/open-x4-sdk b/open-x4-sdk index 9f76376a..f14be998 160000 --- a/open-x4-sdk +++ b/open-x4-sdk @@ -1 +1 @@ -Subproject commit 9f76376a5cc7894cff9ca87bbdd34dab715d8a59 +Subproject commit f14be998ab510616ac2779153251630b7b203f51 diff --git a/scripts/generate_spine_toc_edges_epub.py b/scripts/generate_spine_toc_edges_epub.py new file mode 100644 index 00000000..7b05e53c --- /dev/null +++ b/scripts/generate_spine_toc_edges_epub.py @@ -0,0 +1,2096 @@ +#!/usr/bin/env python3 +""" +Generate a test EPUB that exercises edge cases in spine/TOC relationships. + +Edge cases covered: + + 1. Multiple TOC entries → single spine item (via fragment anchors) + - frontmatter.xhtml: Dedication (#dedication), Epigraph (#epigraph), + Foreword (#foreword) — three TOC entries, one spine item + - chapter3.xhtml: Chapter 3 heading + three sub-sections + (#the-reef, #the-current, #the-depths) — four TOC entries, one spine item + - chapter5.xhtml: Chapter 5 heading + three sub-sections + (#isle-of-echoes, #compass-rose-atoll, #whirlpool-narrows) — four TOC entries + - appendix.xhtml: Appendix A (#appendix-a), B (#appendix-b), + C (#appendix-c), D (#appendix-d), E (#appendix-e) + — five TOC entries, one spine item (D and E are deliberately tiny) + + 2. Single TOC entry → multiple spine items (chapter spans files) + - Chapter 2: chapter2_part1.xhtml + chapter2_part2.xhtml — one TOC entry, + two spine items + - Chapter 4: chapter4_part1.xhtml + chapter4_part2.xhtml + + chapter4_part3.xhtml — one TOC entry, three spine items + + 3. Spine item with no TOC entry + - interlude.xhtml: present in spine order, absent from TOC nav + + 4. TOC entry pointing to mid-file anchor (not file start) + - backmatter.xhtml#colophon: the file starts with an Author's Note, + but only the mid-file Colophon anchor appears in the TOC + + 5. Nested TOC hierarchy + - Chapter 3 and Chapter 5 use nested

    sub-entries in the nav + + 6. Normal 1:1 spine-to-TOC mapping (baseline) + - chapter1.xhtml: one spine item, one TOC entry +""" + +import io +import os +import zipfile +import uuid +from datetime import datetime + +try: + from PIL import Image, ImageDraw, ImageFont +except ImportError: + print("Please install Pillow: pip install Pillow") + exit(1) + + +_PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +_BOOKERLY_FONT = os.path.join( + _PROJECT_ROOT, "lib", "EpdFont", "builtinFonts", "source", + "Bookerly", "Bookerly-Regular.ttf", +) + + +def _get_font(size=20): + """Get the Bookerly font at the requested size, with system fallbacks.""" + for path in [_BOOKERLY_FONT]: + try: + return ImageFont.truetype(path, size) + except (OSError, IOError): + continue + return ImageFont.load_default(size) + + +def _draw_text_centered(draw, y, text, font, fill, width): + bbox = draw.textbbox((0, 0), text, font=font) + text_width = bbox[2] - bbox[0] + x = (width - text_width) // 2 + draw.text((x, y), text, font=font, fill=fill) + + +def create_cover_image(): + """Generate a cover image and return JPEG bytes.""" + width, height = 536, 800 + bg_color = (15, 55, 65) + text_color = (225, 220, 205) + + img = Image.new("RGB", (width, height), bg_color) + draw = ImageDraw.Draw(img) + + font_title = _get_font(72) + font_subtitle = _get_font(26) + font_author = _get_font(14) + font_ornament = _get_font(64) + + title_lines = ["Spine", "& Anchor"] + title_y = 140 + for line in title_lines: + _draw_text_centered(draw, title_y, line, font_title, text_color, width) + title_y += 90 + + ornament_y = title_y + 10 + _draw_text_centered(draw, ornament_y, "*", font_ornament, text_color, width) + + subtitle_y = ornament_y + 72 + _draw_text_centered(draw, subtitle_y, "A Nautical Misadventure", + font_subtitle, text_color, width) + + _draw_text_centered(draw, height - 70, "CROSSPOINT TEST FIXTURES", + font_author, text_color, width) + + buf = io.BytesIO() + img.save(buf, "JPEG", quality=90) + return buf.getvalue() + + +# --------------------------------------------------------------------------- +# Book metadata +# --------------------------------------------------------------------------- + +BOOK_UUID = str(uuid.uuid5(uuid.NAMESPACE_URL, "crosspoint:test:spine-anchor")) +TITLE = "Spine & Anchor: A Nautical Misadventure" +AUTHOR = "Crosspoint Test Fixtures" +DATE = datetime.now().strftime("%Y-%m-%d") + +# --------------------------------------------------------------------------- +# FRONTMATTER — three TOC anchors in one spine item +# --------------------------------------------------------------------------- + +FRONTMATTER = """\ + + + +Front Matter + + + +

    Dedication

    + +

    For every reader whose bookmark landed on the wrong page, and every +navigator whose chart placed the lighthouse on the wrong shore.

    + +

    Epigraph

    + +
    +

    “A captain who trusts a single table of contents has never +sailed past the edge of a spine.”

    +

    — Admiral Fragmenta, On the Perils of Pagination

    +
    + +

    Foreword

    + +

    The document you hold in your hands — or rather, the document +your e-reader is attempting to reassemble from its constituent +parts — is deliberately, almost aggressively, tangled.

    + +

    Some chapters sprawl across multiple files. Others cram several +table-of-contents entries into a single page. At least one section +appears in the reading order yet refuses to show up in the table of +contents at all. And if you look carefully, you will find a table of +contents entry that points not to the beginning of its file, but to a +spot squarely in the middle.

    + +

    This is all by design. If your reader survives, it can survive +anything.

    + + + +""" + +# --------------------------------------------------------------------------- +# CHAPTER 1 — normal 1:1 spine-to-TOC (baseline) +# --------------------------------------------------------------------------- + +CHAPTER_1 = """\ + + + +Chapter 1 + + + +

    Chapter 1
    Setting Sail

    + +

    Anchora had packed three things for the voyage: a sextant with a +cracked lens, a notebook whose pages were already curling from salt air, +and an unshakeable conviction that every archipelago deserved a proper +atlas.

    + +

    The sextant had belonged to her grandmother, a woman who had charted +the entire Leeward Passage in an open dinghy using nothing but dead +reckoning and a vocabulary of profanity that could strip barnacles at +forty paces. The crack in the lens was the result of an encounter with +a boom during an unexpected jibe — not Anchora’s +grandmother’s boom, but rather the boom of a racing yacht that +had cut across her bow in the harbor approaches at a speed the +grandmother had described, with characteristic understatement, as +“imprudent.”

    + +

    The notebook was new, purchased from a stationer in the port town +who specialized in paper for maritime use. It was bound in oilskin and +stitched with waxed thread, and the pages were a heavy cream stock that +the stationer claimed would resist salt spray, coffee spills, and the +tears of frustrated navigators in roughly equal measure. Anchora had +tested the first two claims. She hoped not to test the third.

    + +

    “The trouble with charts,” she told the harbour-master +as he untied her bowline, “is that they assume the world sits +still long enough to be drawn.”

    + +

    The harbour-master, whose name was Gideon and whose experience of +the sea was limited to watching it from the end of the municipal pier, +nodded politely and threw her the rope. He had heard variations of this +sentiment from every cartographer, surveyor, and marine geologist who +had passed through his harbour in thirty-one years of service, and he +had learned that the most efficient response was agreement.

    + +

    “Tides shift, sandbars migrate, and new rocks appear where +no rocks have any business being,” Anchora continued, warming to +her theme. She was coiling the bowline with the automatic precision of +someone who has coiled ten thousand ropes. “By the time you +publish an atlas, half the coastline has wandered off. You might as well +try to draw a portrait of a cloud.”

    + +

    “And yet you keep drawing them,” Gideon observed.

    + +

    “And yet I keep drawing them,” Anchora agreed. She +stowed the coiled rope in the lazarette and checked the halyards. The +main was neatly furled on the boom; the jib was hanked on and ready. +Everything was in order. Everything was always in order aboard the +Pagination. Anchora tolerated chaos in the natural world because +she had no choice, but she would not tolerate it aboard her own boat.

    + +

    The Pagination was a twenty-eight-foot sloop of a design that +had been fashionable forty years ago and was now merely practical, which +suited Anchora considerably better. She had a long waterline, a shallow +keel for poking into the kind of anchorages that deeper boats could only +look at wistfully, and a rig that one person could handle in all but +the most theatrical weather. Her hull was white, her boot-stripe was +navy, and her name was painted across the transom in gold leaf that was +beginning, after twelve seasons, to develop the kind of distinguished +patina that a less generous observer might have called peeling.

    + +

    Anchora had bought her for a sum that the previous owner had +described as “a bargain” and that Anchora’s bank +manager had described as “concerning.” That had been eight +years ago. In the intervening time, the Pagination had carried +Anchora to thirty-seven islands, through four named storms, across two +time zones, and into one regrettable encounter with a shipping container +that had fallen off a freighter in heavy weather and was drifting, +unmarked, at exactly the height of a sloop’s waterline. The +encounter had cost Anchora a starboard stanchion and three weeks in a +boatyard. The container, so far as she knew, was still drifting.

    + +

    She cast off the stern line and let the Pagination drift clear +of the dock. The wind was from the northwest at eight knots, which was +enough to move but not enough to excite. She unfurled the jib, sheeted +it in, and felt the boat come alive beneath her feet — that small, +particular pleasure of a hull finding its groove in the water.

    + +

    The harbour fell away behind her: the stone breakwater, the red and +green channel markers, the row of chandleries and fish restaurants that +constituted the town’s entire economy. Gideon was still standing +on the dock, growing smaller. He raised a hand. Anchora raised hers in +return.

    + +

    Beyond the breakwater, the sea opened up. It was mid-morning and the +light was the flat, even grey of an overcast day — not dramatic, +not beautiful, but excellent for navigation. Dramatic light made for +good paintings but unreliable bearings. Anchora preferred her horizons +unambiguous.

    + +

    She unrolled the chart she had prepared the night before and pinned +it to the table in the companionway. It was mostly blank. A few +coastlines sketched from Admiralty data. A scattering of depth soundings +copied from a survey that was either thirty or sixty years old, +depending on which edition you believed. A compass rose in the corner, +drawn with the care of someone who understood that a compass rose is +both a tool and a promise.

    + +

    The rest was empty space. White paper waiting for ink. Terra +incognita, or rather aqua incognita, which sounded less impressive in +Latin but was considerably more relevant to someone in a boat.

    + +

    She spread a fresh sheet of vellum on the chart table, uncapped her +pen, and wrote at the top in careful block letters: ATLAS OF THE +UNCHARTED REACH.

    + +

    Below this she added, in smaller letters: Compiled from original +observations by A. Vellum, Master Cartographer, aboard the sloop +Pagination. This was perhaps optimistic — the atlas currently +contained no observations at all — but Anchora believed in +stating one’s intentions clearly. A blank atlas was not an +admission of ignorance; it was a declaration of ambition.

    + +

    She set the pen down, took a bearing on the harbour entrance — +now two miles astern and shrinking — and plotted her first +position fix on the chart. A small cross, precisely drawn, with the time +noted beside it: 09:17, departure.

    + +

    The first mark on a new chart. It never got old.

    + +

    She adjusted course to the southeast, trimmed the jib, and settled +into the cockpit with her notebook on her knee. The Pagination +heeled gently to port and began to make way. The wake streamed out +behind in a narrow V, the only mark she would leave on the water. By +evening it would be gone. By tomorrow the sea would have no memory of +her passage at all.

    + +

    That was the fundamental asymmetry of cartography. The cartographer +remembers the sea; the sea does not remember the cartographer. You +could spend a lifetime recording every depth, every current, every +contour of a coastline, and the ocean would regard you with precisely +the same indifference it had shown the first person who ever put to +sea on a log.

    + +

    Anchora found this oddly comforting. It meant the work was never +finished. It meant there was always another chart to draw.

    + +

    She uncapped her pen again and began to sketch the headland that was +passing to starboard: a blunt promontory of dark rock, topped with +scrub grass and a navigation light that blinked every four seconds. She +noted the light’s characteristics in the margin — +Fl(1) 4s, 12m, 8M — and added a small annotation about +the rocks that extended from the headland’s base in a series of +jagged shelves. These rocks did not appear on the Admiralty chart. They +were, she suspected, a relatively recent arrival, deposited by a +landslip that no one had bothered to report to the hydrographic +office.

    + +

    This was exactly the sort of thing that justified the voyage. The +official charts were not wrong, exactly. They were merely incomplete. +And in navigation, incomplete and wrong amounted to the same thing +when your keel was the instrument of discovery.

    + +

    By noon, the headland was well astern and the coast had receded to +a thin grey line on the port beam. Anchora made a sandwich from the +provisions she had stowed in the ice-box — hard cheese, cured +ham, and bread that would be stale by tomorrow — and ate it in +the cockpit with one hand on the tiller. The wind was backing toward +the west and strengthening. She would need to reef before evening.

    + +

    She washed the sandwich down with coffee from the thermos, plotted +another position fix, and wrote in her notebook: Day 1, 12:30. +Twenty miles offshore. Wind NW 12 kn, backing. Seas moderate. One +headland charted, with previously unreported rocks. Atlas begun.

    + +

    She had no idea how uncharted things were about to become.

    + + + +""" + +# --------------------------------------------------------------------------- +# CHAPTER 2 — one TOC entry, TWO spine items +# --------------------------------------------------------------------------- + +CHAPTER_2_PART1 = """\ + + + +Chapter 2 (Part 1) + + + +

    Chapter 2
    The Twin Harbors

    + +

    The first landmark on Anchora’s route was a pair of harbors so +close together that early cartographers had drawn them as one. Later +cartographers, attempting to correct the error, had split the entry in +two — but neglected to update the name on the second sheet. The +result was a pair of harbors that existed on some charts as a single +entity and on others as two separate places with the same name, +separated by a narrow cut of water that was either an inlet, a channel, +or “the bit in the middle,” depending on whom you +asked.

    + +

    Anchora first spotted the harbors from three miles out. They +presented themselves as a low, dark line of wharves and warehouses, +punctuated by the vertical strokes of mast-heads and the occasional +plume of woodsmoke from a chandler’s stove. Behind the +waterfront, the town rose in terraces: whitewashed houses with terracotta +roofs, a church tower, and what appeared to be either a lighthouse or a +very ambitious chimney.

    + +

    She consulted her chart. According to the Admiralty edition, this was +Port Gemini — a single harbor with a single entrance. According +to the pilot book, it was East Harbor and West Harbor, two separate +harbors sharing a common breakwater. According to the local fishing +cooperative’s newsletter, which Anchora had found pinned to a +notice board during her last provisioning stop, it was “the +Twins,” and had been called that for longer than anyone could +remember, which is to say at least forty years.

    + +

    A pilot boat emerged from behind the breakwater as Anchora rounded +the outer buoy. It was a stubby craft, painted orange, with a cabin +that looked like it had been designed by someone who believed windows +were a sign of weakness. A man stood on the foredeck, holding a coiled +line and wearing an expression of professional neutrality.

    + +

    “So which is East Harbor and which is West?” Anchora +called across the narrowing gap.

    + +

    “Depends which chart you’re reading,” the pilot +replied cheerfully. “On mine, you’re entering East. On +the harbourmaster’s, this is still West. We gave up arguing about +it years ago.”

    + +

    “And the Admiralty?”

    + +

    “The Admiralty says there’s only one harbor, which will +come as a surprise to the three hundred boats moored in the other one. +We sent them a letter about it. Twice. They sent back a form asking us +to confirm the coordinates of the harbor we were claiming didn’t +exist, which rather missed the point.”

    + +

    The pilot threw his line to Anchora, who caught it, made it fast to +the midship cleat, and allowed herself to be towed through the entrance +at a stately two knots. The breakwater slid past: massive blocks of +granite, green with weed below the tide line, topped with a rusting +iron railing and a row of bollards that had seen better centuries.

    + +

    Inside the breakwater, the harbor opened up. It was larger than +Anchora had expected — a broad basin ringed with pontoons, +moorings, and the kind of stone quay walls that speak of an era when +civic infrastructure was built to impress rather than merely to +function. Fishing boats occupied most of the moorings: trawlers with +rust-streaked hulls, lobster boats bristling with pots, and the +occasional sleek yacht that looked profoundly uncomfortable in such +working company.

    + +

    “This is East Harbor, then?” Anchora asked as the +pilot pointed her toward an empty berth on the visitors’ +pontoon.

    + +

    “If you like,” the pilot said agreeably. “The +post office calls it East. The tax office calls it West. The pub calls +it ‘here.’ I find that last one the most accurate.” +

    + +

    Anchora secured the Pagination to the pontoon with a bowline +fore and aft, adjusted the fenders, and stepped ashore. The pontoon +swayed under her feet in a way that she found pleasantly familiar. Solid +ground, by contrast, always felt slightly unreliable.

    + +

    She spent the morning exploring the first harbor on foot, making +notes and sketches. The quay wall was three hundred and twelve paces +long — she measured it, because that was the kind of person she +was. The depth alongside ranged from two to four metres, depending on +the state of the tide, which was semi-diurnal with a range of about two +metres. There were fourteen pontoons, each capable of berthing eight to +ten boats. The harbor entrance was fifty metres wide, oriented to the +southwest, and partially sheltered by the breakwater from the prevailing +swell.

    + +

    All of this she recorded in her notebook, then transferred to her +chart in the careful, precise hand that was her professional signature. +She drew the quay walls in solid black lines, the pontoons in dashed +lines, the depth contours in fine blue, and added a small compass rose +in the corner of the inset. It was satisfying work. By lunchtime she +had a complete survey of what she was, for the sake of her sanity, +calling East Harbor.

    + +

    In the afternoon she walked through the narrow cut that connected the +two basins. It was barely thirty metres wide — a slot in the +rock through which the tide funneled with surprising force. The walls +of the cut were sheer, rising six or seven metres above the waterline, +and covered with the layered evidence of centuries of marine growth: +barnacles, mussels, a fur of green algae, and the occasional optimistic +sea anemone.

    + +

    Anchora recorded the discrepancy in her notebook — one harbor +or two? — and sailed the Pagination through the narrow +cut under engine. The water changed from grey-green to a deep cobalt as +the bottom dropped away. The echo of the engine bounced off the cut +walls and returned with a slight delay, as though the passage were +thinking about what it had heard before deciding to repeat it.

    + + + +""" + +CHAPTER_2_PART2 = """\ + + + +Chapter 2 (Part 2) + + + +

    On the far side of the cut, the second harbor opened like a cupped +hand. Fishing boats bobbed in neat rows, their hulls painted in fading +primaries: cadmium red, cerulean blue, chrome yellow. This harbor was +slightly smaller than the first but considerably busier: a fish market +was in full cry along the north quay, and the air smelled of brine, +diesel, and the particular sharp tang of fresh-caught mackerel.

    + +

    Anchora nosed the Pagination into a gap between two lobster +boats whose skippers were engaged in a conversation conducted entirely +in hand gestures and appeared to concern the ownership of a crate of +bait. She made fast, fore and aft, and went ashore with her notebook.

    + +

    The second harbor was, if anything, more confusing than the first. +It had its own harbourmaster — a different one from the East +Harbor harbourmaster, which suggested a degree of administrative +independence that was hard to reconcile with the Admiralty’s +insistence that this was all one place. It also had its own set of +charts, posted on a notice board outside the harbourmaster’s +office, and these charts were not merely different from the Admiralty +charts; they were different from the charts posted in the East +Harbor office.

    + +

    “We draw our own,” the West Harbor harbourmaster +explained when Anchora asked about this. Her name was Constance and she +had the weathered complexion and steady gaze of someone who had spent +forty years watching boats do foolish things. “The Admiralty +charts are… aspirational. We prefer something grounded in what +we can actually see.”

    + +

    “And East Harbor?”

    + +

    “They draw their own as well. Different from ours, naturally. +We disagree about the depth in the cut — they say three metres at +low water, we say two and a half. We also disagree about the location +of the inner reef — they put it forty metres south of where we +put it. We have been disagreeing about this for, I believe, about +seventy years.”

    + +

    “Has anyone thought of measuring?” Anchora +ventured.

    + +

    Constance gave her a look that suggested this question had been asked +before and that the answer was both yes and complicated.

    + +

    “Three times,” Constance said. “The first +survey found three metres. The second found two and a half. The third +found two point seven but was conducted by the East Harbor +harbourmaster’s nephew and is therefore inadmissible.”

    + +

    Anchora spent two days surveying the second harbor with the same +methodical care she had applied to the first. She measured the quay +walls, sounded the depths, timed the tides, and charted the moorings. +She also, because she was thorough, measured the cut from both ends and +found it to be two point six metres at low water springs, which +satisfied nobody but at least had the virtue of being her own +measurement.

    + +

    On the second evening, as the sun dropped below the breakwater and +turned the water in the harbor to molten copper, the pilot boat came +alongside the Pagination. The pilot — whose name, Anchora +had learned, was Rufus — was sitting on the gunwale eating an +apple.

    + +

    “You’ll want to note,” he called across, +“that neither harbor appears on the Admiralty chart. Officially, +this is all open water.”

    + +

    “I had noticed,” Anchora said.

    + +

    “The chart shows a straight coastline where we’re +sitting. No indentation, no breakwater, no harbors. According to the +Royal Hydrographic Office, we are currently bobbing about in the open +sea. I sometimes think about this when I’m tying up to the +pontoon.”

    + +

    Anchora drew both harbors on a single sheet — one chart, two +basins, no ambiguity — and labeled it with coordinates she +trusted rather than names she did not. She drew the cut at its correct +width, marked the depth as 2.6 m LAT, and added the inner reef in the +position she had determined by triangulation from three fixed points on +shore. She used blue for the water, black for the structures, and a +particularly assertive shade of red for the reef, because reefs that +are subject to a seventy-year argument deserve to be drawn in a colour +that commands attention.

    + +

    She pinned the completed chart to the wall above her berth, where +the varnished wood held brass tacks with the grip of long practice. +The chart looked good. Clean lines, clear labels, unambiguous depth +figures. For a moment — a brief, perfect moment — the +world felt like a place that could be made orderly.

    + +

    She would not feel that way again for some time.

    + + + +""" + +# --------------------------------------------------------------------------- +# CHAPTER 3 — one spine item, FOUR TOC entries (chapter + 3 sub-sections) +# --------------------------------------------------------------------------- + +CHAPTER_3 = """\ + + + +Chapter 3 + + + +

    Chapter 3
    Charting the Depths

    + +

    Beyond the Twin Harbors, the continental shelf dropped away in three +distinct terraces. Anchora’s fathom-line, weighted with lead and +tallow, told the story in numbers: ten, forty, two hundred. Each number +represented a different world — a different temperature, a +different colour, a different set of hazards — and she intended +to chart them all.

    + +

    She had sailed out of the second harbor at dawn, motoring through +the cut in the grey half-light while the fishing boats were still +sorting their nets. Constance had waved from the harbourmaster’s +office window. Rufus had been asleep on his pilot boat and had not +waved at all.

    + +

    Each terrace had its own character, its own light, its own hazards. +She gave each a name and — because she was, above all, a +cartographer — a sub-heading.

    + +

    The Reef

    + +

    The first terrace was a broad shelf of coral, alive with colour and +peril in roughly equal measure. It extended for perhaps two miles from +the outer edge of the harbor approaches, a gradually shoaling platform +of limestone and living coral that the chart described as “foul +ground” and the local fishermen described with a word that +Anchora did not record in her notebook but committed to memory for +future use.

    + +

    Staghorn formations reached toward the hull like bony fingers. Fan +corals swayed in the current with the slow grace of a metronome. Brain +corals squatted on the seabed like boulders, their surfaces etched with +grooves that looked, from above, like the contour lines on a +particularly complicated chart. Anchora appreciated the irony. Even +the ocean floor was trying to be a map.

    + +

    The water over the reef was extraordinarily clear. She could see the +bottom in seven metres of water as though it were behind glass: every +coral head, every sandy patch, every dark crevice where a moray eel +might be contemplating its options. Fish moved through this landscape in +schools and squadrons, their colours too vivid for the grey day +— electric blue, acid yellow, a hot pink that no chart +convention had a symbol for.

    + +

    Anchora worked her way across the reef under reduced sail, taking +soundings every hundred metres. The depth varied unpredictably: three +metres here, seven metres there, and in one alarming spot, barely one +and a half metres over a coral head that lurked just below the surface +like a geological ambush. She marked each sounding on her chart with +the compulsive precision of someone who knows that a single missed +hazard can ruin a hull and a reputation in the same instant.

    + +

    “Beautiful,” she murmured, leaning over the rail to +watch a school of parrotfish graze on the coral below. The fish were +vivid turquoise, the size of dinner plates, and they ate the coral with +an audible crunching sound that was both fascinating and faintly +disturbing. Then her keel scraped limestone — a sound like a +giant clearing its throat — and she revised her opinion to +“beautiful but inconvenient.”

    + +

    She started the engine and motored off the coral head, checking the +bilge for leaks (none, thank goodness; the Pagination’s +hull was tougher than it looked) and the keel for damage (a scrape, +nothing more, but a scrape that would need antifouling paint before +the season was out). The parrotfish watched her departure with the +sublime indifference of creatures that do not have keels.

    + +

    By late morning she had completed her survey of the reef. It covered +an area of roughly four square miles and contained, by her count, +twenty-three significant coral heads within two metres of the surface +at low water. She inked the reef in hatched lines on her chart, shading +the shallowest areas in a warning blue, and adding a marginal note: +Draft exceeding 1.5 m: proceed with caution and strong +language.

    + +

    She added, after a moment’s thought: Draft exceeding 2.0 m: +do not proceed at all. Go around.

    + +

    The reef chart was one of the most detailed pieces of work she had +produced. She allowed herself a moment of satisfaction, then turned her +attention to the second terrace.

    + +

    The Current

    + +

    Below the reef shelf, the seabed fell away sharply — not +gradually, not gently, but with the decisiveness of a cliff edge. One +moment the depth sounder was reading eight metres; the next it was +reading forty, and still dropping. The colour of the water changed too, +from the pale turquoise of the reef to a deep, serious blue that +suggested the ocean was no longer in a playful mood.

    + +

    And the water began to move. Not the slow, companionable drift of +tidal flow, not the rhythmic surge of swell, but a purposeful lateral +current that pushed the Pagination sideways at a rate Anchora +found personally offensive. She had been sailing a straight course +toward a landmark on the far shore; within ten minutes, the landmark +had migrated from dead ahead to thirty degrees off the starboard bow, +and the Pagination was crabbing sideways like a startled dog on +a polished floor.

    + +

    Anchora dropped the jib, started the engine, and pointed the bow +directly into the current. The Pagination held position, but +only just. The engine was working at three-quarters throttle to make +zero progress over the ground, which was the nautical equivalent of +running on a treadmill.

    + +

    She set a kedge anchor — a small, light anchor attached to a +long line that she rowed out in the dinghy and dropped on the seabed +upstream of the current. With the boat held in place by the anchor, she +could take bearings without the distraction of being pushed steadily +toward a different postcode.

    + +

    The boat swung in lazy arcs at the end of her anchor line, like a +weathervane that could not quite decide where the wind was coming from. +Anchora took bearings on three fixed points on shore every fifteen +minutes, plotted them on her chart, and calculated the current’s +speed and direction from the resulting drift vectors.

    + +

    The current, she calculated, ran at two and a quarter knots on the +ebb tide, swinging to three and a half on the flood. It flowed roughly +parallel to the coast, with a slight onshore component during the flood +that would push an unwary vessel toward the reef. At springs — +when the tidal range was at its greatest — she estimated the +current would exceed four knots, which was faster than many boats could +motor against and would certainly be faster than any sailboat could +point into.

    + +

    It was the kind of data that looked innocent in a table and lethal on +a lee shore. Anchora had a healthy respect for currents. They were the +ocean’s way of reminding you that it was bigger than you and had +somewhere to be.

    + +

    She spent the rest of the day anchored in the current zone, taking +readings at half-hour intervals. By evening she had enough data to draw +a current chart: a pattern of arrows showing direction and speed at +various states of the tide, annotated with times referenced to high +water at the Twin Harbors. She added a caution box in the margin: +Spring rates may exceed 4 kn. Passage against the flood inadvisable +for vessels under power of 15 hp. Passage under sail alone is a matter +for the individual conscience.

    + +

    She noted it all down, adding small arrows to indicate direction and +strength. The chart was filling up nicely. She had coral, she had +current, and she had the beginnings of a bathymetric profile that would +make a hydrographer weep with something that might have been either +admiration or envy.

    + +

    The Depths

    + +

    Past the current, the water turned from blue to black. It was a +gradual transition — not a line, not a boundary, but a slow +deepening of colour that spoke of depths where sunlight was a rumour +and pressure was a fact of life. The Pagination sailed into +this darker water on a light breeze, and Anchora felt the temperature +drop a degree as the deep water exhaled its chill.

    + +

    Anchora’s lead-line ran out at two hundred fathoms and found +nothing. She tied on an extension — fifty fathoms of spare line +she kept coiled in the lazarette for exactly this purpose. At three +hundred fathoms: nothing. The line hung straight down into the black +water, trembling slightly with the current, offering no information +except that the bottom was not yet here.

    + +

    She tied on a second extension. At four hundred fathoms — +nearly half a mile of line, its weight now enough to make her arms ache +from the hauling — she felt a distant, uncertain bump. The lead +had found something. Whether it was bedrock, sediment, a drowned +mountain, or the roof of something she preferred not to think about, +the line could not tell her. It only knew that it had stopped going +down.

    + +

    “Well,” she said to the empty cockpit, “there +is apparently a bottom.”

    + +

    She hauled the line in, arm over arm, for what felt like an hour but +was probably fifteen minutes. The lead came up coated in a fine grey +clay that told her the bottom was soft sediment — the +accumulated drift of millennia, particles of sand and silt and the +ground-down remains of creatures that had lived and died in the water +column above and settled, with infinite slowness, to the floor.

    + +

    She examined the clay sample with the professional interest of +someone who understands that even mud has a story to tell. It was +fine-grained, slightly sticky, with no visible shell fragments or +organic material. Deep-water sediment. The kind of bottom that offers +poor holding for an anchor but excellent preservation for anything that +sinks to it.

    + +

    She took three more soundings over the course of the afternoon, +sailing a line perpendicular to the coast. The depths were remarkably +consistent: three hundred and eighty fathoms, four hundred and ten, +three hundred and ninety-five. The seabed here was a flat plain, +featureless and unchanging, stretching into the darkness in every +direction.

    + +

    She drew a single contour line on her chart — the four-hundred +fathom line, traced with a steady hand despite the gentle rolling of +the boat — and wrote 400+ fm beside it. Below the line +she left blank space: the cartographer’s admission that some +things are simply too deep to record, too far from light and air and +the concerns of surface-dwellers to justify the ink.

    + +

    But she kept the clay sample, sealed in a small glass jar and +labeled with the date, position, and depth. A cartographer records +what can be recorded. The blank space on the chart was not ignorance. +It was honesty.

    + + + +""" + +# --------------------------------------------------------------------------- +# INTERLUDE — in spine, NOT in TOC +# --------------------------------------------------------------------------- + +INTERLUDE = """\ + + + +Interlude + + + +

    The Phantom Island

    + +

    Between Chapter Three and Chapter Four, Anchora sailed past an island +that did not exist.

    + +

    She spotted it on the morning of her third day out from the Twin +Harbors, in a stretch of sea where her charts showed nothing but open +water and a scattering of depth soundings that ranged from +“deep” to “very deep.” It appeared first +as a dark smudge on the horizon, which she initially took for a low +cloud or a particularly ambitious wave. But clouds do not stay in one +place, and waves do not have palm trees, and this thing had both.

    + +

    She altered course to investigate. A cartographer who sails past an +uncharted island without investigating is not, in Anchora’s +professional opinion, a cartographer at all, but merely someone with a +boat and a collection of pens.

    + +

    The island resolved itself as she approached. It was small — +perhaps a quarter of a mile across, roughly circular, fringed with a +beach of white sand that was so bright in the morning sun it made her +squint. Behind the beach, a stand of coconut palms rose to a modest +height, their fronds rustling in a breeze that Anchora could not feel +from the water. At the island’s centre, a low hill rose perhaps +fifty feet above sea level, covered in scrub grass and what appeared to +be a single, determined frangipani tree in full bloom.

    + +

    It had, in short, all the attributes of an island. It had mass, it +had volume, it had geographic coordinates that Anchora carefully noted +from her sextant readings. It had a beach of white sand that squeaked +underfoot when she rowed the dinghy ashore and stepped out, which is a +very specific and tactile quality that imaginary places tend not to +possess.

    + +

    It had a small but enthusiastic colony of penguins, which was +geographically improbable but zoologically undeniable. They were small +penguins — about eighteen inches tall, with blue-grey backs and +white fronts and an air of having been expecting her. They stood in a +loose group near the tree line, regarding her with the intensity of +creatures who have opinions about visitors but lack the vocal apparatus +to express them clearly.

    + +

    “You’re not supposed to be here,” Anchora told +them. Penguins, she was fairly certain, belonged in the southern +hemisphere, on rocky coasts and ice shelves, in places where the water +was cold and the fish were plentiful and the nearest palm tree was +several thousand miles away. These penguins appeared not to have read +the relevant literature.

    + +

    She walked the perimeter of the island, which took twenty-three +minutes at a brisk pace. The beach was continuous — fine white +sand, no rocks, gently shelving into water that was a shade of turquoise +that postcards aspire to but rarely achieve. The interior was equally +unremarkable: the palm grove, the scrub hill, the frangipani, and an +absence of anything that might explain why this island was here. No +volcanic cone. No coral atoll. No geological justification +whatsoever.

    + +

    She took careful measurements. She recorded the latitude and +longitude from three separate sextant shots, all of which agreed. She +sounded the water around the island’s shore and found a uniform +depth of four fathoms, dropping sharply to twenty fathoms fifty metres +out. She noted the compass bearing to the nearest known landmark +— the headland beyond the Twin Harbors, barely visible on the +horizon — and calculated the distance.

    + +

    All the data said the island was real. The numbers were precise and +consistent. The sand was tangible. The penguins were audible (they had +begun a low, conversational muttering among themselves that suggested +they were discussing her survey technique).

    + +

    She reached for her pen to add it to the atlas, then paused.

    + +

    The island was not in the table of contents. She checked — +carefully, methodically, the way she checked everything. She ran her +finger down the list of chapters, sections, and appendices. The +Foreword was there. The five chapters were there. The appendices were +there. But between Chapter Three and Chapter Four, where this island +chronologically belonged, there was no entry. No heading, no +sub-heading, no footnote.

    + +

    It was not listed in the index. It appeared in the reading order +— she was, after all, reading about it right now, and so were +you — but no navigation entry pointed to it. If you were using +the table of contents to move through this book, you would skip from +Chapter Three directly to Chapter Four and never know this island was +here. It existed in the spine but not in the navigation. It was +structurally present but navigationally invisible.

    + +

    “If a place has no entry,” she asked one of the +penguins, “does it really exist?”

    + +

    The penguin regarded her with the weary patience of a creature that +has heard this question before — perhaps from other +cartographers who had stumbled upon the island and grappled with the +same ontological difficulty. It tilted its head to one side, blinked +once with each eye in sequence, and then waddled back to the surf with +the unhurried dignity of a creature whose existence does not depend on +being listed in a table of contents.

    + +

    Anchora sat on the beach for a while, thinking about this. The +penguins went about their business around her: waddling, swimming, +standing in small groups and staring at the horizon with the focused +attention of creatures who are waiting for something but have forgotten +what. The waves lapped at the sand. The palm fronds rustled. The +frangipani released its scent into the warm air.

    + +

    It was, she had to admit, a very pleasant island. Peaceful. +Unhurried. The kind of place where you could sit for an afternoon and +forget that you had charts to draw and depths to sound and harbors to +argue about. Perhaps that was the point. Perhaps some places are better +off uncharted — hidden in the reading order, reachable only by +those patient enough to read every page rather than jumping from heading +to heading.

    + +

    She considered this philosophical position for approximately four +minutes, then rejected it. She was a cartographer. Uncharted places +were not romantic; they were errors.

    + +

    And yet.

    + +

    She closed her notebook. She rowed back to the Pagination. +She hauled up the anchor, set the jib, and sailed away from the island +on a reaching course that would bring her to the coordinates she had +been given for the beginning of the Long Voyage.

    + +

    She did not add the island to her chart. There was no heading for it +in the atlas, no entry in the navigation, no place for it in the +structure she had so carefully planned. An island without a table of +contents entry was, from a cartographic perspective, an orphan — +and Anchora’s atlas did not have room for orphans.

    + +

    She did, however, add penguins to her growing list of things that +were not supposed to be there but were. The list, she reflected, was +getting longer with every day of the voyage. She was not sure whether +this said something about the world or about her list.

    + +

    Behind her, the island shimmered in the heat haze and slowly sank +below the horizon, taking its penguins and its palm trees and its +existential questions with it. By evening it was gone. By morning she +would not be entirely sure she had seen it at all.

    + +

    But the sand between her toes — fine, white, faintly +squeaky — suggested otherwise.

    + + + +""" + +# --------------------------------------------------------------------------- +# CHAPTER 4 — one TOC entry, THREE spine items +# --------------------------------------------------------------------------- + +CHAPTER_4_PART1 = """\ + + + +Chapter 4 (Day 1) + + + +

    Chapter 4
    The Long Voyage

    + +

    Day One — Departure

    + +

    The passage to the outer archipelago was, by all accounts, a +three-day sail. This estimate came from Rufus the pilot, who had never +made the passage himself but had spoken to several people who had, and +from Constance the harbourmaster, who had made it once, thirty years +ago, in a boat considerably faster than the Pagination and in +weather considerably better than the forecast was promising.

    + +

    Anchora provisioned accordingly: three days of hard-tack, three days +of tinned sardines, and three days of the sort of instant coffee that +dissolves under protest. She also packed a fourth day’s supplies, +because she had learned through experience that three-day passages have +a way of becoming four-day passages, and a sailor without coffee on +day four is a sailor without hope.

    + +

    She reviewed her charts one more time before casting off. The outer +archipelago was marked on the Admiralty chart as a cluster of small +islands approximately seventy miles to the southeast — close +enough to reach in three days of moderate sailing, far enough to +require planning, provisioning, and the acceptance that the nearest +help, should help be needed, was a day’s sail behind her. The +islands themselves were drawn as rough outlines, their coastlines +sketched from satellite imagery and the reports of passing ships. No +one had surveyed them properly. That was why Anchora was going.

    + +

    She cast off at first light, motoring through the harbor and out +past the breakwater into open water. The dawn was a thin line of amber +on the eastern horizon, and the sea was a flat expanse of grey that +merged, at the edges, with a sky of precisely the same shade. The wind +was light from the southeast — a soldier’s wind, steady +and reliable, the kind of wind that does not require constant attention +to the sheets.

    + +

    Day one was, in the main, uneventful. The wind held steady, the seas +were moderate — a long, low swell from the south that the +Pagination rode with an easy, rocking motion — and the +boat made six knots on a beam reach without complaint. This was her +best point of sail: the wind on the beam, the sails drawing well, the +hull slicing through the water with the minimum of fuss and the maximum +of speed.

    + +

    Anchora spent the daylight hours sketching coastline profiles from +the deck. The coast was still visible to the northwest, a low grey line +that rose occasionally into headlands and fell back into bays. She +drew each headland as she passed it, noting its shape, its height, its +distinguishing features. A lighthouse here. A radio mast there. A cliff +face that had collapsed into a jumble of rocks that would, she +suspected, not appear on any chart for another decade.

    + +

    By mid-morning the coast had begun to recede, and by early +afternoon it was little more than a suggestion — a slight +thickening of the horizon, a shade of grey that was fractionally darker +than the grey of the sea. Anchora took a last bearing on the highest +headland, plotted it on her chart, and acknowledged that she was, for +practical purposes, out of sight of land.

    + +

    This was a moment that some sailors dreaded and others relished. +Anchora was in the second category. Out of sight of land, the world +simplified itself. There was the boat, there was the sea, and there was +the compass. Everything else — the harbors and their arguments, +the charts and their disagreements, the penguins and their inexplicable +geography — fell away. What remained was navigation in its purest +form: a vessel, a heading, and the conviction that the maths would +work out.

    + +

    She spent the evening hours transferring her coastline sketches to +the master chart, working by the light of the oil lamp that swung from +a hook above the chart table. The lamp threw a warm, yellow circle of +light that moved gently with the boat’s motion, and the shadows +it cast gave the chart a depth and texture that the flat light of day +did not. She added her soundings from the reef and current zones, drew +the four-hundred-fathom contour line she had found over the deep water, +and penciled in a tentative route to the archipelago.

    + +

    By sunset, the Twin Harbors had sunk below the horizon and there was +nothing in any direction but water and the thin, bright line where it +met the sky. The sky turned from grey to amber to a deep blue-violet +that held the first stars in its upper reaches. The wind eased slightly +as the land breeze died and the sea breeze had not yet arrived. The +Pagination slowed to four knots, then three, and the water +around her hull changed from a hiss to a whisper.

    + +

    Anchora set the self-steering gear — a wind-vane device of +her own construction that held the boat on course without human +intervention, provided the wind did not shift by more than fifteen +degrees, which it did roughly once an hour — and went below to +make dinner. Tinned sardines on hard-tack, with a cup of instant coffee +that tasted, as always, of ambition unfulfilled.

    + +

    She marked her position on the chart with a small cross and the +time: Day 1, 18:47, all well. 42 miles made good. Barometer steady. +Wind SE 8 kn. Coffee: adequate.

    + + + +""" + +CHAPTER_4_PART2 = """\ + + + +Chapter 4 (Day 2) + + + +

    Day Two — Becalmed

    + +

    The wind died at dawn and did not return. It did not die gradually, +the way winds usually die — fading through a series of +diminishing puffs until the last breath of air gives up and the sails +go slack. It died all at once, as though someone had closed a door. +One moment the Pagination was sailing; the next she was not.

    + +

    The Pagination sat on water so flat it reflected the clouds +like a tray of mercury. Every ripple, every swell, every hint of motion +had been smoothed away. The sea was a mirror. The sky was reflected in +it so perfectly that the horizon vanished, and Anchora had the +unsettling sensation of floating in the centre of a sphere, with +identical views in every direction, up and down included.

    + +

    The sails hung in dispirited loops. The main sagged against the +shrouds. The jib, unfurled and hopeful an hour ago, now hung from the +forestay like a bedsheet on a clothesline. Even the tell-tales — +small strips of yarn attached to the shrouds to indicate wind direction +— drooped vertically, pointing at the deck with the resigned +finality of arrows that have given up looking for a target.

    + +

    Anchora tried whistling for wind (maritime tradition). She stood in +the cockpit and whistled a jaunty tune toward each of the four cardinal +points in turn, which was supposed to summon a breeze from the +direction you whistled at. The tradition did not specify what to do when +all four directions were equally windless. Anchora whistled at all of +them anyway, on the principle that comprehensive failure was preferable +to targeted failure.

    + +

    She tried tapping the barometer (maritime superstition). The +barometer was a fine brass instrument mounted on the bulkhead in the +companionway, and it showed 1024 millibars, which was high and stable +and thoroughly uninterested in change. Tapping it — a gentle, +respectful tap with the knuckle of the index finger, as tradition +demanded — produced no change. She tapped it again, harder. +Still nothing. The barometer regarded her with the mechanical smugness +of a device that knows it is right.

    + +

    She tried simply sitting in the cockpit and staring at the horizon +(maritime realism). This was, in many ways, the most honest response to +a calm: the acceptance that the wind would return when it chose and not +before, and that no amount of whistling, tapping, or staring would +hasten its arrival. Sailors who raged against calms accomplished nothing +except to make themselves hot and irritable. Sailors who accepted them +at least stayed cool.

    + +

    The morning passed in a haze of heat and stillness. Anchora read a +chapter of the pilot book, made a cup of coffee (the second-to-last +packet of instant — she was rationing now), and wrote a long +entry in her notebook about the geology of the continental shelf, +drawing on the soundings she had taken over the past few days. The +writing helped. It gave her hands something to do and her mind something +to chew on that was not the infuriating absence of wind.

    + +

    By noon the heat was ferocious. The sun stood directly overhead, +pouring its energy into the flat sea and the flat boat and the flat +cartographer who was beginning to feel like a piece of hard-tack left +on a windowsill. The deck was too hot to touch barefoot. The cabin was +an oven. The cockpit, shaded by the boom, was merely oppressive rather +than unbearable, which was the best that could be said for it.

    + +

    She rigged a sun-shade from the spare jib, draping the heavy +sailcloth over the boom and tying it to the shrouds on either side. +This created a rectangle of shade over the cockpit that was eight feet +by six feet and reduced the temperature from “punitive” +to “merely unpleasant.” She sat in this shade and spent +the afternoon measuring the depth with her lead-line, which was at +least productive even if it was not comfortable.

    + +

    The bottom here was sixty fathoms of soft mud — the same +fine grey clay she had found in the deep water, but closer to the +surface. The mud yielded to the lead with a reluctant sucking sound +that Anchora could feel through the line, a hundred and twenty metres +away. She noted this on the chart, adding a small sounding figure at +her current position, though she suspected no one would ever care. +Sixty fathoms of mud in the middle of nowhere was not the kind of +information that made it into the Admiralty Notices to Mariners.

    + +

    She took several more soundings as the afternoon wore on, drifting +imperceptibly — the current was weak here, barely a quarter of +a knot — and recording each depth. Fifty-eight fathoms. +Sixty-two. Fifty-five. The seabed was gently undulating, rising and +falling by a few fathoms over a distance of perhaps half a mile. She +drew these contours on her chart, and the result looked like the gentle +hills of a drowned landscape, which is precisely what it was.

    + +

    At sunset, a catspaw rippled the surface to the north — a +small, dark patch of ruffled water that raced across the mirror-flat +sea like a whispered secret. Anchora leaped to her feet, freed the +jib sheet, and hauled in the main, her hands moving with the desperate +speed of someone who has been waiting all day for this exact moment. +By the time she had the sails trimmed, the catspaw had arrived, +touched the Pagination’s sails with the lightest of +fingers, and moved on. The sails filled for a moment — a +single, heartbreaking moment — and then fell slack again.

    + +

    The catspaw raced on to the south and disappeared. The sea +returned to its mirror state. The tell-tales drooped.

    + +

    Anchora sat down in the cockpit and regarded the empty horizon with +an expression that, in a less disciplined cartographer, might have been +called despair. In Anchora it was merely a very thorough species of +disappointment.

    + +

    She marked her position: Day 2, 19:02, becalmed. 3 miles made +good by drift. Barometer 1024, steady. Wind: none. Morale stable but +coffee supply critical. Lead-line soundings taken; seabed contour added +to chart. If the wind does not return by morning I shall motor, which +is an admission of defeat but at least it is defeat with forward +progress.

    + + + +""" + +CHAPTER_4_PART3 = """\ + + + +Chapter 4 (Day 3) + + + +

    Day Three — Arrival

    + +

    The wind returned overnight with the subtlety of a cannon shot. One +moment the Pagination was drifting, her hull barely creasing the +water, her sails hanging like theatre curtains after the final act. The +next moment she was heeled over at twenty degrees, spray flying from the +bow in white sheets, the rigging singing a chord in B-flat minor that +rose in pitch as the gusts built and fell again as they passed.

    + +

    Anchora, who had been asleep in the quarterberth with her head on a +folded chart and her feet on a coil of rope, arrived on deck wearing +one boot and an expression of startled competence. The wind was from +the northwest — exactly the direction she needed — and +blowing at what she estimated was twenty knots, gusting twenty-five. +This was more wind than the Pagination wanted with full sail +set, and considerably more wind than Anchora wanted at three o’clock +in the morning with one boot on.

    + +

    She reefed the main in the dark, working by feel and muscle memory. +The reef points were where her fingers expected them to be; the lines +ran through the blocks without snagging; the sail came down to its +first reef in under two minutes. She trimmed the jib to match, easing +the sheet until the sail stopped flogging and settled into the shape it +wanted, and pointed the bow toward the coordinates she had been given +for the outer archipelago.

    + +

    The Pagination responded immediately. With the reef in, she +was balanced, manageable, and making seven knots through the water +— her best speed of the voyage. The bow wave hissed along the +hull. The wake stretched out behind in a long, white tail. The self- +steering gear held the course without complaint, and Anchora, who was +now fully awake and beginning to enjoy herself, went below to find the +other boot.

    + +

    Dawn came slowly, the way it does at sea: a gradual lightening of +the eastern sky, from black to charcoal to a pale grey that spread +upward like water rising in a glass. The horizon emerged from the +darkness, sharp and clean, and Anchora scanned it with the practiced +eye of someone who has been looking for land from the deck of a small +boat for most of her adult life.

    + +

    Nothing. Not yet. But the chart said the archipelago should be +visible by mid-morning at this speed, and Anchora trusted her chart +because she had drawn it herself and she knew exactly how much trust +it deserved, which was a moderate amount tempered by professional +humility.

    + +

    She made coffee — the last packet, which she regarded as both +a sacrifice and an incentive — and drank it in the cockpit while +the Pagination sailed herself. The wind was settling down to a +steady eighteen knots, the gusts becoming less frequent, the sea +developing a regular pattern of waves that the boat cut through with +a satisfying, rhythmic motion. This was good sailing. The kind of +sailing you remember long after the voyage is over.

    + +

    At ten o’clock she saw something on the horizon that was not +cloud and not sea. It was a faint, dark irregularity in the line +where sky met water — a slight bump, barely perceptible, that +could have been a distant ship or a trick of the light but wasn’t. +She knew what it was. She had been looking for it for three days.

    + +

    Land appeared at noon: a low, dark smudge that resolved slowly into +individual islands, then individual trees, then individual birds +perched in the individual trees. She counted seven major islands and +an uncountable number of rocks, shoals, and ambiguous features that +might have been either. The islands were spread across perhaps ten miles +of sea, arranged in a rough arc that opened toward the northwest +— toward her, as it happened, which felt like a welcome even +though she knew it was merely geography.

    + +

    She sailed closer, reducing speed as the water shallowed. The +depth sounder, which had been reading “deep” for two +days, suddenly began producing numbers: 80 metres, 60, 40, 30. She +could see the bottom now — a sandy seabed dappled with shadows +from the islands above. Fish scattered as the Pagination’s +shadow passed over them.

    + +

    She rounded the northern tip of the largest island, staying well +clear of a line of rocks that extended from the shore like a broken +jetty, and found herself in a sheltered anchorage on the island’s +lee side. The water was calm here, protected from the northwest wind +by the island’s bulk. She dropped anchor in four fathoms of +sand, felt it bite, and paid out enough chain to hold in a gale.

    + +

    Then she sat in the cockpit and looked at the islands spread before +her. Seven islands. Uncharted, unsurveyed, waiting. This was why she +had come. Not for the harbors with their naming disputes, not for the +reefs with their lurking coral heads, not for the currents with their +treacherous pull. She had come for this: blank space on the chart, +waiting to be filled.

    + +

    She uncapped her pen. This, at last, was what she had come for.

    + +

    She marked her position one final time: Day 3, 12:15, landfall. +Anchored in the lee of the largest island, 4 fm sand. Seven islands +visible. Wind NW 18 kn. Coffee: exhausted. The atlas begins in +earnest.

    + + + +""" + +# --------------------------------------------------------------------------- +# CHAPTER 5 — one spine item, nested TOC (chapter + 3 sub-entries) +# --------------------------------------------------------------------------- + +CHAPTER_5 = """\ + + + +Chapter 5 + + + +

    Chapter 5
    Archipelago of Wonders

    + +

    The outer archipelago comprised seven islands, but Anchora quickly +learned that three of them demanded most of her attention. The other +four were low, sandy, and profoundly uninteresting — the kind of +islands that exist primarily to give seabirds somewhere to argue. She +surveyed them anyway, because a cartographer who skips the boring bits +produces an atlas with holes in it, and an atlas with holes is just a +collection of maps pretending to be complete.

    + +

    The four minor islands took two days to chart. They were, as expected, +unremarkable: flat coral platforms, none more than ten feet above sea +level, covered in coarse grass and nesting terns. Each island had a +fringing reef, a scattering of rocks, and a beach that was pleasant +enough in theory but occupied in practice by large numbers of +territorial seabirds that regarded Anchora’s survey equipment +with undisguised hostility.

    + +

    She drew them quickly and moved on to the three islands that +mattered.

    + +

    Isle of Echoes

    + +

    The first notable island was ringed by basalt cliffs so sheer they +formed a natural amphitheatre. The cliffs rose two hundred feet from +the waterline, sheer and dark, their faces carved into columns by +millennia of cooling and cracking. At the base, where the waves struck, +the rock had been hollowed into caves and arches that amplified every +sound into a symphony of percussion.

    + +

    Every sound bounced from wall to wall in diminishing repetitions: +the crash of a wave became a series of claps; a shout became a +conversation with oneself; the rattle of an anchor chain became a +cascading diminuendo that lasted for eight or nine seconds before +finally fading into the ambient hiss of the sea.

    + +

    Anchora discovered the echoes accidentally. She was approaching the +island from the northeast, looking for an anchorage, when she called +out “Hello!” to see if anyone was ashore. The island +replied: Hello … hello … hello … ello +… lo … It was, she reflected, the most polite +island she had ever visited. Most islands ignored you entirely.

    + +

    She tested the acoustics systematically, because that was her nature. +She stood in the cockpit, positioned the Pagination at measured +distances from the cliff face, and shouted a series of test words: +single syllables first, then multi-syllable words, then complete +sentences. She timed the echoes with her watch and recorded the results +in her notebook.

    + +

    She tested the acoustics further by reading her coordinates aloud. +“Fourteen degrees, thirty-seven minutes south,” she +announced in a clear, carrying voice. The cliffs repeated it back, +each echo slightly garbled by the complex geometry of the rock face, +until by the seventh repetition the island appeared to be declaring +itself at forty-seven degrees north. She noted this as a navigational +hazard of a kind not typically covered in the pilot books.

    + +

    The anchorage, when she found it, was on the island’s +sheltered western side, where the cliffs gave way to a small bay with +a sandy bottom in three fathoms. She anchored here and spent two days +surveying the island from the dinghy, rowing along the cliff face with +a notebook in one hand and an oar in the other, measuring the height +of the cliffs by trigonometry and the depth of the water by lead-line. +The caves at the base of the cliffs were deep enough to row into +— dark, dripping spaces where the sound of the oars echoed in +unsettling ways and the water was so still it reflected the cave roof +like polished obsidian.

    + +

    She mapped each cave entrance, noting its position, width, height, +and the depth of water at its threshold. There were eleven caves in +total, ranging from barely large enough for the dinghy to a cathedral- +sized chamber that could have sheltered a small fishing fleet. In the +largest cave she found the remains of an old mooring ring, bolted into +the rock at waterline level and rusted to a deep orange-brown. Someone +had been here before. Someone had anchored in this cave, in this +darkness, and had thought it worthwhile to drill a hole in the basalt +and hammer in a ring.

    + +

    She added this to the chart with a small symbol and the annotation: +Old mooring ring, condition poor. Cave depth 14 m, width 8 m, +height 5 m at entrance. Adequate shelter in westerly weather.

    + +

    The chart grew a detailed inset of the Isle of Echoes, complete with +soundings, anchorage notes, cave positions, and a warning about acoustic +anomalies that she phrased with care: Echoes from basalt cliffs may +distort voice communications. Coordinates heard from echo may not +correspond to coordinates spoken. Fog signals unreliable within 0.5 nm +of cliff face.

    + +

    Compass Rose Atoll

    + +

    The second island was not, strictly speaking, an island at all. It +was an atoll: a ring of coral enclosing a shallow lagoon, with four +narrow passes at the cardinal points. From the air — or from a +sufficiently tall mast — it looked exactly like a compass rose, +its four passes aligned so precisely with north, south, east, and west +that Anchora suspected the coincidence was too perfect to be entirely +natural and too natural to be entirely coincidence.

    + +

    The atoll was roughly circular, perhaps a mile across, its rim +barely six feet above the water at its highest point. The coral was +old and dense, covered with a thin layer of sand and scrub vegetation +that had taken hold wherever the salt spray was not too fierce. On the +wider sections of the rim, coconut palms grew in sparse lines, their +trunks curved by years of prevailing wind into identical +question-mark shapes.

    + +

    Anchora sailed through the north pass and anchored in the lagoon. +The pass was narrow — barely thirty metres wide, with coral +walls on either side that rose steeply from a bottom of clean white +sand — and the current through it was brisk on the flood tide, +perhaps two knots, which required attention but not alarm. She entered +on the slack, when the current was negligible, and dropped anchor in +the centre of the lagoon.

    + +

    The water was so clear she could see her anchor chain lying on the +bottom in gentle curves, like cursive script. The bottom was four +fathoms of white sand, undisturbed by current, and the anchor had dug +in with the easy confidence of a hook in soft ground. Fish moved +through the water column above the sand: small, bright, purposeful +fish that paid no attention to the boat or its occupant.

    + +

    She spent a full day surveying the atoll, measuring each pass and +sounding the lagoon. The symmetry was remarkable: each pass was within +a boat-length of the same width, and the lagoon was uniformly four +fathoms deep. The north and south passes were slightly wider than the +east and west — thirty-two metres versus twenty-eight — +but the difference was so small it might have been within the margin of +error of her measuring method, which involved rowing the dinghy across +the pass and counting oar-strokes.

    + +

    She sounded the lagoon on a grid pattern, rowing the dinghy in +north-south lines spaced fifty metres apart and dropping the lead +every twenty metres. The result was a bathymetric chart of extraordinary +regularity: a flat, featureless bowl of sand, uniformly four fathoms +deep, with no coral heads, no rocks, no obstacles of any kind. It was, +she reflected, the most boring piece of underwater terrain she had ever +surveyed, and also one of the most useful: a lagoon with a flat bottom +and predictable depth was a sailor’s dream, a place where you +could anchor anywhere with equal confidence.

    + +

    Nature, it seemed, had a taste for geometry. Anchora, who also had +a taste for geometry, appreciated this more than most.

    + +

    She drew the atoll with particular care, using a compass to lay out +the circular rim and placing each pass at its precisely measured bearing. +The chart of Compass Rose Atoll was, she thought, the most beautiful +piece of cartography she had produced on the voyage: clean lines, +perfect symmetry, four passes opening like the petals of a flower. +She added a small compass rose in the corner of the inset chart, which +created the pleasing recursion of a compass rose drawn inside a +compass rose.

    + +

    The Whirlpool Narrows

    + +

    Between the second and third islands, the tidal flow compressed +through a gap barely a cable’s length wide. The two islands were +close here — so close that their reefs almost touched, leaving +only a narrow channel of deep water between them. Through this channel, +twice a day, the entire volume of water that filled and emptied the +lagoons and bays of the archipelago had to pass, and it did so with +considerable energy and very little patience.

    + +

    The result was a whirlpool that spun with metronomic regularity: +clockwise on the ebb, counterclockwise on the flood, and in a state +of churning indecision at slack water. The whirlpool was not large +— perhaps twenty metres across at its widest — but it +was vigorous, and the water within it moved with a purposeful circular +motion that was both mesmerizing and slightly alarming.

    + +

    Anchora observed the whirlpool from a safe distance, anchored in +the lee of the eastern island, and made detailed notes. The clockwise +rotation during the ebb was faster than the counterclockwise rotation +during the flood, which she attributed to the shape of the channel: +slightly wider at the southern end, which gave the ebb current — +flowing from north to south — more room to accelerate before +hitting the narrows. She estimated the rotational speed at three +revolutions per minute during the peak ebb and two during the +peak flood.

    + +

    The whirlpool was accompanied by an array of secondary effects that +were equally interesting and equally dangerous. Standing waves formed +at the edges of the narrows, where the moving water met the still +water beyond, creating a line of breaking crests that would have been +at home on a surfing beach. Eddies spun off the main whirlpool like +sparks from a wheel, racing downstream in tight spirals before +dissipating in the calmer water beyond. And the sound — a low, +continuous roar, punctuated by the slap of breaking waves — +carried for half a mile in every direction.

    + +

    Anchora timed the cycles, measured the diameter, and estimated the +rotational speed. She also measured the current through the narrows at +various states of the tide, using a timing float — a sealed +bottle with a small flag attached — that she dropped into the +current and tracked with bearings. The maximum current was four and a +half knots during the spring ebb, which was faster than the +Pagination could motor against and considerably faster than +anyone would want to sail through.

    + +

    The slack water between the ebb and flood was brief — eight +minutes by her measurement, during which the whirlpool lost its +coherence, the standing waves subsided, and the channel was briefly, +deceptively calm. This was the transit window: eight minutes in which +a careful boat could pass through the narrows without being spun, +swamped, or swept sideways into the reef.

    + +

    Eight minutes was not a generous margin. It was, however, sufficient +for a boat that was ready and a skipper who was decisive. Anchora filed +this information under “useful” and drew the whirlpool +on her chart as a neat spiral with arrows, then added the note: +Transit at slack water only. Allow margin for error. Do not bring +the good sextant.

    + +

    She had now been in the archipelago for four days, and her chart was +beginning to look like something a real navigator might trust. The +blank spaces were filling in. The coastlines were taking shape. The +soundings and current data were building a picture of an archipelago +that was complex, challenging, and deeply satisfying to chart.

    + +

    This pleased her more than she would have admitted to the +penguins.

    + + + +""" + +# --------------------------------------------------------------------------- +# APPENDIX — one spine item, THREE TOC entries via anchors +# --------------------------------------------------------------------------- + +APPENDIX = """\ + + + +Appendices + + + +

    Appendix A
    Knot Types Employed

    + +

    The following knots were tied at various points during the voyage +and are reproduced here for the edification of the reader. Each knot +is described in terms of its construction, its application aboard the +Pagination, and Anchora’s personal assessment of its +character, because all knots have character, and some have more +character than their tyers would prefer.

    + +

    Bowline. The king of knots. Used to secure the +Pagination to docks, bollards, and on one occasion a +surprisingly cooperative palm tree on the Isle of Echoes. The bowline +forms a fixed loop at the end of a line that will not slip under load +and will not jam when you want to untie it, which is a combination of +virtues that very few knots can claim. Anchora tied bowlines +instinctively; her fingers would form the loop, pass the tail around +the standing part, and thread it back through the loop in a single +fluid motion that took approximately one and a half seconds. She had +once won a bowline-tying competition at a sailing club in the Leeward +Islands, beating a boatswain with forty years’ experience and +very large hands. The boatswain had been gracious in defeat and had +bought her a rum, which is the traditional currency of nautical +respect. Advantages: does not slip, does not jam, can be tied under +load if necessary. Disadvantages: requires two hands, which is one +more than a sailor holding a coffee cup has available.

    + +

    Cleat hitch. Used at every marina, every pontoon, and every +dock that the Pagination visited. The cleat hitch is not a +glamorous knot — it is the workhorse of the marina, the knot +equivalent of a reliable sedan — but it is satisfyingly quick +to tie and untie, and it holds with absolute reliability in any +conditions. Anchora could do it in four seconds. She timed herself, +because she was the sort of person who timed herself tying knots, and +she regarded this as a perfectly normal hobby. The cleat hitch involves +one full turn around the base of the cleat, followed by two +figure-eight turns over the horns, finished with a locking hitch. The +locking hitch is optional but recommended by Anchora, who had once seen +an unlocked cleat hitch work loose in a gale and release a thirty-foot +yacht into the fairway, where it drifted gently into a row of dinghies +and caused what the insurance industry would later describe as a +“multi-vessel incident.”

    + +

    Figure-eight. Used as a stopper knot on every sheet and +halyard aboard the Pagination. The figure-eight prevents the +line from running out through its block or fairlead, which is the kind +of thing that happens at the worst possible moment — during a +tack, in heavy weather, when both hands are already occupied with +something else that is going wrong. The figure-eight is the kind of +knot that, once learned, the fingers tie without consulting the brain. +Anchora tied figure-eights the way other people blink: automatically, +unconsciously, and with a frequency that suggested a deep-seated +neurological commitment to the prevention of runaway lines.

    + +

    Round turn and two half-hitches. Used for securing the dinghy +painter to rocks, trees, and the occasional bollard that was too large +or too oddly shaped for a bowline. This knot is not elegant, but it is +secure, adjustable, and can be tied around objects of any shape, which +makes it the knot of last resort — the one you reach for when +nothing else will work. Anchora used it approximately once per week and +thought about it approximately never, which is the highest compliment +a sailor can pay a knot.

    + +

    Reef knot. Used for tying reef points when shortening sail. +The reef knot is perhaps the most misunderstood knot in the nautical +lexicon: it is often taught as a general-purpose binding knot, which it +is not. It will slip if loaded unevenly, it will jam if loaded +heavily, and it will capsize if looked at sternly. For tying reef +points, however, it is perfect: quick to tie, quick to untie, and +adequate for the modest loads involved. Anchora tied reef knots in the +dark, in the rain, on a heaving deck, with numb fingers, and the reef +knot never once let her down, because she never once asked it to do +anything it was not designed to do.

    + +

    Sheet bend. Used for joining two lines of different diameter. +Anchora used this knot exactly once during the voyage, when she needed +to extend her lead-line with a lighter-weight extension over the deep +water. The sheet bend held, the sounding was taken, and the knot was +untied. It was, she reflected, the knot equivalent of a specialist +consultant: expensive to learn, rarely needed, but invaluable on the +one occasion you called upon it.

    + +

    Appendix B
    Signal Flags Observed

    + +

    During the voyage, the following International Code of Signals flags +were observed flying from other vessels. The International Code is a +system of flag signals that allows ships of different nationalities to +communicate without a common language, which is an admirable ambition +that works considerably better in theory than in practice, because the +system assumes that all parties can identify forty flags at a distance +and under conditions that typically include spray, glare, and a +rolling deck.

    + +

    Alpha. A white and blue swallowtail flag. “I have a +diver down; keep well clear at slow speed.” Observed near the +reef, flying from a battered workboat whose diver was, presumably, +somewhere below, doing whatever it is that divers do in two metres of +water over a coral reef. Anchora kept well clear, as instructed, and +made a note of the workboat’s position so that she could add a +“diving operations” annotation to her chart of the reef. +She later removed the annotation on the grounds that the diving was +presumably temporary, whereas the chart was intended to be permanent, or +at least as permanent as a chart of a reef can be when the reef is +still growing.

    + +

    Bravo. A red swallowtail flag. “I am taking in, or +discharging, or carrying dangerous goods.” Observed at the Twin +Harbors, flying from a rusty coaster that was moored alongside the +north quay in East Harbor (or possibly West Harbor; the question, as +always, remained unresolved). The dangerous goods turned out to be a +crate of live chickens, which the harbourmaster’s regulations +classified as “livestock” but which the chickens’ +behaviour — aggressive, noisy, and apparently attempting to +escape — suggested might more accurately have been classified as +“hazardous materials.”

    + +

    Hotel. A white flag with a red vertical stripe. “I +have a pilot on board.” Observed on Rufus’s pilot boat, +naturally, and on several vessels entering and leaving the Twin Harbors +under his guidance. The Hotel flag is one of the more useful signals in +the code, as it tells other vessels that the flagged vessel is being +navigated by someone who knows the local waters, and that any unusual +manoeuvres it performs are probably intentional rather than accidental. +Anchora flew the Hotel flag exactly once, when Rufus came aboard to +guide her through the cut between the two harbors, and she removed it +the moment he left, because flying a pilot flag without a pilot aboard +is both improper and illegal, and Anchora was punctilious about such +things.

    + +

    November. A chequered blue and white flag. “No” +or “negative.” Observed on a yacht that was being hailed +by the coast guard and appeared to be declining to stop. Anchora watched +this exchange with professional interest and noted in her log that the +yacht was making nine knots downwind, which was probably insufficient +to outrun the coast guard cutter but was certainly a spirited attempt. +The outcome of the pursuit was not recorded, as the Pagination +had by then sailed out of visual range.

    + +

    Quebec. A solid yellow flag. “My vessel is healthy and +I request free pratique.” Flown by the Pagination herself +upon arrival at each port, mostly out of optimism. Free pratique is +permission to make contact with the shore, granted by the port health +authority after they have satisfied themselves that your vessel is not +carrying plague, cholera, or any other communicable disease. In modern +times, the flag is largely ceremonial — most ports grant +pratique automatically — but Anchora flew it anyway, because +traditions are traditions, and because a yellow flag on a small sloop +entering a foreign harbor has a certain jaunty charm that she privately +enjoyed.

    + +

    Appendix C
    Tidal Observations

    + +

    Tidal data collected during the voyage is summarized below. All +times are approximate, all heights are measured from chart datum, and +all predictions should be treated with the same confidence one extends +to a weather forecast — which is to say, they are probably +correct, but you should not bet your keel on it.

    + +

    Tides in the region are predominantly semi-diurnal, with two high +waters and two low waters in each lunar day. The tidal range varies +with the phase of the moon: greatest at springs (when the sun and moon +are aligned) and smallest at neaps (when they are at right angles). +Anchora recorded tidal observations at every anchorage, using a +graduated staff driven into the seabed at the waterline, which she +read at hourly intervals whenever she was aboard and awake, and at +less regular intervals when she was not.

    + +

    Twin Harbors. Semi-diurnal, range 1.8–2.4 m at +springs, 0.8–1.2 m at neaps. High water approximately coincides +with lunar transit, with a lag of roughly forty minutes that Anchora +attributed to the constriction of the harbor entrance and the friction +of the water flowing over the shallow bar at the approach channel. The +two basins exhibit a 12-minute phase lag — high water in East +Harbor occurs twelve minutes before high water in West Harbor — +which the locals blame on a submerged rock formation in the cut between +the two basins and Anchora blames on insufficient data. She measured +the phase lag on three consecutive tidal cycles and got three different +results: 10 minutes, 14 minutes, and 12 minutes. The average was 12 +minutes. She reported 12 minutes. The truth, she suspected, was +considerably more complicated than a single number could express, but +a single number was what the chart demanded, and the chart was, for +better or worse, what people would rely on.

    + +

    The Reef. Tidal range similar to the Twin Harbors. The +critical observation for the reef is not the range but the height of +low water at springs, which determines how much of the coral is +exposed. At LAT (Lowest Astronomical Tide), several of the larger +coral heads break the surface, creating visible markers that are useful +for daylight navigation but invisible — and therefore lethal +— at night. Anchora marked these drying heights on her chart +with the standard symbol: a depth figure with a line underneath, +indicating that the number represents elevation above chart datum rather +than depth below it.

    + +

    Whirlpool Narrows. Tidal streams reach 4.5 kn at springs, +2.8 kn at neaps. The flood stream sets northward; the ebb sets +southward. Slack water occurs approximately 15 minutes before local +high and low water and lasts approximately 8 minutes at springs, 12 +minutes at neaps. This is not a generous margin in either case, but +it is sufficient for a transit if the vessel is prepared and the skipper +decisive. Anchora recommended approaching the narrows under power +rather than sail, on the grounds that an engine provides consistent +thrust regardless of wind angle, whereas a sail provides consistent +anxiety regardless of everything.

    + +

    Compass Rose Atoll. Negligible tidal range inside the lagoon +(0.3 m at springs, barely measurable at neaps). This is because the +four passes, while wide enough for navigation, are narrow enough to +attenuate the tidal wave as it enters the lagoon, spreading the rise +and fall over a much longer period and reducing its amplitude to +almost nothing. The practical consequence is that the lagoon maintains +a nearly constant depth, which makes it an ideal anchorage in any +conditions — a rare and valuable quality in an archipelago where +most of the water is trying to go somewhere in a hurry. Currents +through the passes reach 2 kn on springs but are predictable and +well-behaved. Anchora described them in her notes as “the only +polite water in the archipelago.”

    + +

    Isle of Echoes. Semi-diurnal, range 1.6–2.0 m. The +tidal stream along the cliff face sets to the north on the flood and +to the south on the ebb, reaching 1.5 kn at springs. The stream is +strongest at the headlands and weakest in the embayments, which is the +normal pattern for tidal flow around a rocky island and was entirely +predictable from the chart. What was not predictable was the acoustic +effect of the tide on the cave systems at the base of the cliffs: +at certain states of the tide, the incoming water compressed the air +inside the caves and produced a low, resonant boom that could be heard +from the anchorage, half a mile away. Anchora noted this phenomenon +in her log but did not add it to the chart, on the grounds that +“makes spooky noises at half tide” was not a recognised +chart annotation.

    + +

    Appendix D
    Errata

    + +

    No errors have been found. This is, in itself, suspicious.

    + +

    Appendix E
    Acknowledgments

    + +

    The author wishes to thank the penguins.

    + + + +""" + +# --------------------------------------------------------------------------- +# BACKMATTER — TOC points to #colophon (mid-file), not file start +# --------------------------------------------------------------------------- + +BACKMATTER = """\ + + + +Back Matter + + + +

    Author’s Note

    + +

    This section has no entry in the table of contents. If you have +arrived here by paging forward from the appendices, congratulations: +you are reading in spine order, which is the only reliable way to find +content that the navigation has chosen to ignore.

    + +

    The Author’s Note exists to test a specific edge case: a +spine item whose table-of-contents entry points not to the beginning +of the file, but to an anchor partway through it. Everything above the +anchor is “dark matter” — present in the document, +reachable by paging, but invisible to the TOC.

    + +

    Consider, for a moment, what this means for a reading application. +When a user taps “Colophon” in the table of contents, +the application must navigate not to the start of this file, but to +the colophon anchor partway through it. The content above the +anchor — the text you are reading right now — exists in +the spine, occupies space on the page, and can be reached by turning +pages forward from the appendices. But it cannot be reached by any +navigation element. It is, in the language of the EPUB specification, +part of the spine order but outside the navigation document.

    + +

    This creates an interesting problem for any reader that attempts to +map TOC entries to page ranges. If the TOC says the Colophon begins +at anchor #colophon, what is the page range of the content before +that anchor? Does it belong to the previous TOC entry (Appendix C)? +Does it belong to the Colophon? Does it belong to no entry at all? +These are the kinds of questions that this test fixture is designed +to provoke.

    + +

    Anchora would have had opinions about this. Cartographers do not +approve of places that exist but cannot be navigated to. An island +without a name on the chart is an island that will, sooner or later, +catch a keel.

    + +

    But perhaps some content is meant to be found only by those who +read sequentially, page by page, without jumping ahead. Perhaps the +Author’s Note is a reward for patience. Or perhaps it is +simply a test fixture dressed up in a narrative voice.

    + +

    Either way, you have found it. Well done.

    + +

    Colophon

    + +

    This EPUB was generated by a Python script as a test fixture for the +Crosspoint Reader project. It is not a real book, though it contains +real sentences arranged in a real order, which is more than can be said +for some publications that aspire to the title.

    + +

    The spine of this EPUB contains fourteen items. Its table of contents +contains eighteen entries. The relationship between the two is, by +design, entertainingly non-trivial. Specifically:

    + +

    Three TOC entries point to anchors within frontmatter.xhtml, +making it a single spine item with multiple navigation targets. Four +TOC entries (one parent and three children) point to anchors within +chapter3.xhtml, and four more point into chapter5.xhtml, +testing nested TOC hierarchies within a single spine item. Three TOC +entries point to anchors within appendix.xhtml, the same +pattern without nesting. Two of these appendix entries (D and E) +are deliberately tiny — a single sentence each — to +test how the reader handles TOC sections too small to fill a +screen.

    + +

    Chapter 2 occupies two spine items (chapter2_part1.xhtml and +chapter2_part2.xhtml) but appears as a single entry in the TOC. +Chapter 4 occupies three spine items and also appears as a single TOC +entry. These test the case where a reader must determine that consecutive +spine items belong to the same logical chapter.

    + +

    The interlude (interlude.xhtml) appears in the spine between +Chapter 3 and Chapter 4 but has no TOC entry whatsoever, testing the +case where spine items exist outside the navigation hierarchy.

    + +

    And this very file (backmatter.xhtml) has a TOC entry that +points to the #colophon anchor, which is not at the beginning of the +file. The Author’s Note above occupies the first portion of the +file but is invisible to the TOC, testing the case where a navigation +target lands in the middle of a document.

    + +

    The cover image was generated programmatically using the Bookerly +typeface at 536×800 pixels, rendered on a deep teal background +(RGB 15, 55, 65) with light beige text (RGB 225, 220, 205).

    + +

    No penguins were harmed in the making of this book. Several were +mildly inconvenienced, but they bore it with their customary dignity.

    + + + +""" + +# --------------------------------------------------------------------------- +# EPUB boilerplate +# --------------------------------------------------------------------------- + +COVER_XHTML = """\ + + + +Cover + + + +Spine & Anchor: A Nautical Misadventure + + +""" + +STYLESHEET = """\ +body { + font-family: serif; + margin: 2em; + line-height: 1.6; +} +h1 { + font-size: 1.5em; + text-align: center; + margin-bottom: 1.5em; + line-height: 1.3; +} +h2 { + font-size: 1.15em; + margin-top: 1.5em; + margin-bottom: 0.5em; +} +p { + text-indent: 1.5em; + margin: 0.25em 0; + text-align: justify; +} +blockquote p { + text-indent: 0; + margin: 0.5em 1.5em; + font-style: italic; +} +""" + +CONTAINER_XML = """\ + + + + + + +""" + +CONTENT_OPF = f"""\ + + + + urn:uuid:{BOOK_UUID} + {TITLE} + {AUTHOR} + en + {DATE} + {DATE}T00:00:00Z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +TOC_XHTML = """\ + + + +Table of Contents + + +

    Spine & Anchor

    + + + +""" + + +# --------------------------------------------------------------------------- +# Build +# --------------------------------------------------------------------------- + +def build_epub(output_path: str): + cover_data = create_cover_image() + + with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf: + zf.writestr("mimetype", "application/epub+zip", compress_type=zipfile.ZIP_STORED) + zf.writestr("META-INF/container.xml", CONTAINER_XML) + zf.writestr("OEBPS/content.opf", CONTENT_OPF) + zf.writestr("OEBPS/toc.xhtml", TOC_XHTML) + zf.writestr("OEBPS/style.css", STYLESHEET) + zf.writestr("OEBPS/cover.jpg", cover_data) + zf.writestr("OEBPS/cover.xhtml", COVER_XHTML) + zf.writestr("OEBPS/frontmatter.xhtml", FRONTMATTER) + zf.writestr("OEBPS/chapter1.xhtml", CHAPTER_1) + zf.writestr("OEBPS/chapter2_part1.xhtml", CHAPTER_2_PART1) + zf.writestr("OEBPS/chapter2_part2.xhtml", CHAPTER_2_PART2) + zf.writestr("OEBPS/chapter3.xhtml", CHAPTER_3) + zf.writestr("OEBPS/interlude.xhtml", INTERLUDE) + zf.writestr("OEBPS/chapter4_part1.xhtml", CHAPTER_4_PART1) + zf.writestr("OEBPS/chapter4_part2.xhtml", CHAPTER_4_PART2) + zf.writestr("OEBPS/chapter4_part3.xhtml", CHAPTER_4_PART3) + zf.writestr("OEBPS/chapter5.xhtml", CHAPTER_5) + zf.writestr("OEBPS/appendix.xhtml", APPENDIX) + zf.writestr("OEBPS/backmatter.xhtml", BACKMATTER) + print(f"EPUB written to {output_path}") + + +if __name__ == "__main__": + project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + out = os.path.join(project_root, "test", "epubs", "test_spine_toc_edges.epub") + os.makedirs(os.path.dirname(out), exist_ok=True) + build_epub(out) diff --git a/src/activities/ActivityResult.h b/src/activities/ActivityResult.h index 92be0fdd..c6ddcf86 100644 --- a/src/activities/ActivityResult.h +++ b/src/activities/ActivityResult.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,7 @@ struct MenuResult { struct ChapterResult { int spineIndex = 0; + std::optional tocIndex; }; struct PercentResult { diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 5c884e5a..41e58456 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -219,14 +219,58 @@ void EpubReaderActivity::loop() { const bool skipChapter = SETTINGS.longPressChapterSkip && mappedInput.getHeldTime() > skipChapterMs; + // Chapter skip navigates by TOC entries, not spine boundaries. + // Spine items without their own TOC entry inherit the previous spine's tocIndex + // (see BookMetadataCache), so they're treated as continuations of the last chapter. + // At the boundaries: skipping forward past the last TOC entry jumps to end-of-book + // (clamped in render()); skipping backward before the first TOC entry jumps to the + // spine before the current chapter's first spine (clamped to 0 in render()). if (skipChapter) { lastPageTurnTime = millis(); - // We don't want to delete the section mid-render, so grab the semaphore { RenderLock lock(*this); - nextPageNumber = 0; - currentSpineIndex = nextTriggered ? currentSpineIndex + 1 : currentSpineIndex - 1; - section.reset(); + + if (section && section->pageCount > 0) { + const int curTocIndex = section->getTocIndexForPage(section->currentPage); + const int nextTocIndex = nextTriggered ? curTocIndex + 1 : curTocIndex - 1; + + if (curTocIndex < 0) { + // No TOC entry for this spine, fall back to spine-level skip + nextPageNumber = 0; + currentSpineIndex = nextTriggered ? currentSpineIndex + 1 : currentSpineIndex - 1; + section.reset(); + } else if (nextTocIndex >= 0 && nextTocIndex < epub->getTocItemsCount()) { + const int newSpineIndex = epub->getSpineIndexForTocIndex(nextTocIndex); + + if (newSpineIndex == currentSpineIndex) { + if (const auto resolvedPage = section->getPageForTocIndex(nextTocIndex)) { + section->currentPage = *resolvedPage; + } else { + LOG_DBG("ERS", "No page boundary for TOC %d in spine %d, staying on current page", nextTocIndex, + currentSpineIndex); + } + } else { + pendingTocIndex = nextTocIndex; + nextPageNumber = 0; + currentSpineIndex = newSpineIndex; + section.reset(); + } + } else if (nextTriggered) { + // Beyond last TOC entry, go to end of book + nextPageNumber = 0; + currentSpineIndex = epub->getSpineItemsCount(); + section.reset(); + } else { + // Before first TOC entry, skip to spine before the current chapter + nextPageNumber = 0; + currentSpineIndex = epub->getTocItem(curTocIndex).spineIndex - 1; + section.reset(); + } + } else { + nextPageNumber = 0; + currentSpineIndex = nextTriggered ? currentSpineIndex + 1 : currentSpineIndex - 1; + section.reset(); + } } requestUpdate(); return; @@ -312,13 +356,23 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction switch (action) { case EpubReaderMenuActivity::MenuAction::SELECT_CHAPTER: { const int spineIdx = currentSpineIndex; + const int tocIdx = section ? section->getTocIndexForPage(section->currentPage) + : epub->getTocIndexForSpineIndex(currentSpineIndex); const std::string path = epub->getPath(); startActivityForResult( - std::make_unique(renderer, mappedInput, epub, path, spineIdx), + std::make_unique(renderer, mappedInput, epub, path, spineIdx, tocIdx), [this](const ActivityResult& result) { - if (!result.isCancelled && currentSpineIndex != std::get(result.data).spineIndex) { - RenderLock lock(*this); - currentSpineIndex = std::get(result.data).spineIndex; + if (result.isCancelled) return; + RenderLock lock(*this); + const auto& chapter = std::get(result.data); + auto resolvedPage = (chapter.tocIndex && chapter.spineIndex == currentSpineIndex && section) + ? section->getPageForTocIndex(*chapter.tocIndex) + : std::nullopt; + if (resolvedPage) { + section->currentPage = *resolvedPage; + } else { + pendingTocIndex = chapter.tocIndex; + currentSpineIndex = chapter.spineIndex; nextPageNumber = 0; section.reset(); } @@ -613,7 +667,7 @@ void EpubReaderActivity::render(RenderLock&& lock) { const uint8_t imageRendering = getEffectiveImageRendering(); const auto filepath = epub->getSpineItem(currentSpineIndex).href; LOG_DBG("ERS", "Loading file: %s, index: %d", filepath.c_str(), currentSpineIndex); - section = std::unique_ptr
    (new Section(epub, currentSpineIndex, renderer)); + section = std::make_unique
    (epub, currentSpineIndex, renderer); if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, @@ -646,6 +700,13 @@ void EpubReaderActivity::render(RenderLock&& lock) { section->currentPage = nextPageNumber; } + if (pendingTocIndex) { + if (const auto resolvedPage = section->getPageForTocIndex(*pendingTocIndex)) { + section->currentPage = *resolvedPage; + } + pendingTocIndex.reset(); + } + if (!pendingAnchor.empty()) { if (const auto page = section->getPageForAnchor(pendingAnchor)) { section->currentPage = *page; @@ -895,28 +956,25 @@ void EpubReaderActivity::renderStatusBar() const { const float bookProgress = epub->calculateProgress(currentSpineIndex, sectionChapterProg) * 100; std::string title; - int textYOffset = 0; if (automaticPageTurnActive) { title = tr(STR_AUTO_TURN_ENABLED) + std::to_string(60 * 1000 / pageTurnDuration); - // calculates textYOffset when rendering title in status bar const uint8_t statusBarHeight = UITheme::getInstance().getStatusBarHeight(); - - // offsets text if no status bar or progress bar only if (statusBarHeight == 0 || statusBarHeight == UITheme::getInstance().getProgressBarHeight()) { textYOffset += UITheme::getInstance().getMetrics().statusBarVerticalMargin; } } else if (SETTINGS.statusBarTitle == CrossPointSettings::STATUS_BAR_TITLE::CHAPTER_TITLE) { - title = tr(STR_UNNAMED); - const int tocIndex = epub->getTocIndexForSpineIndex(currentSpineIndex); - if (tocIndex != -1) { + const int tocIndex = + section ? section->getTocIndexForPage(section->currentPage) : epub->getTocIndexForSpineIndex(currentSpineIndex); + if (tocIndex == -1) { + title = tr(STR_UNNAMED); + } else { const auto tocItem = epub->getTocItem(tocIndex); title = tocItem.title; } - } else if (SETTINGS.statusBarTitle == CrossPointSettings::STATUS_BAR_TITLE::BOOK_TITLE) { title = epub->getTitle(); } diff --git a/src/activities/reader/EpubReaderActivity.h b/src/activities/reader/EpubReaderActivity.h index 0f1b3a8f..3467828b 100644 --- a/src/activities/reader/EpubReaderActivity.h +++ b/src/activities/reader/EpubReaderActivity.h @@ -3,6 +3,8 @@ #include #include +#include + #include "EpubReaderMenuActivity.h" #include "activities/Activity.h" @@ -11,6 +13,9 @@ class EpubReaderActivity final : public Activity { std::unique_ptr
    section = nullptr; int currentSpineIndex = 0; int nextPageNumber = 0; + // Set when navigating to a TOC entry in a different spine (chapter skip or chapter selector). + // Cleared on the next render after the new section loads and resolves it to a page. + std::optional pendingTocIndex; // Set when navigating to a footnote href with a fragment (e.g. #note1). // Cleared on the next render after the new section loads and resolves it to a page. std::string pendingAnchor; diff --git a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp index 821db372..39b31c4e 100644 --- a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp +++ b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp @@ -25,7 +25,9 @@ void EpubReaderChapterSelectionActivity::onEnter() { return; } - selectorIndex = epub->getTocIndexForSpineIndex(currentSpineIndex); + selectorIndex = (currentTocIndex >= 0 && currentTocIndex < epub->getTocItemsCount()) + ? currentTocIndex + : epub->getTocIndexForSpineIndex(currentSpineIndex); if (selectorIndex == -1) { selectorIndex = 0; } @@ -48,7 +50,7 @@ void EpubReaderChapterSelectionActivity::loop() { setResult(std::move(result)); finish(); } else { - setResult(ChapterResult{newSpineIndex}); + setResult(ChapterResult{newSpineIndex, selectorIndex}); finish(); } } else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { diff --git a/src/activities/reader/EpubReaderChapterSelectionActivity.h b/src/activities/reader/EpubReaderChapterSelectionActivity.h index 20b53aa4..6565a324 100644 --- a/src/activities/reader/EpubReaderChapterSelectionActivity.h +++ b/src/activities/reader/EpubReaderChapterSelectionActivity.h @@ -11,6 +11,7 @@ class EpubReaderChapterSelectionActivity final : public Activity { std::string epubPath; ButtonNavigator buttonNavigator; int currentSpineIndex = 0; + int currentTocIndex = 0; int selectorIndex = 0; // Number of items that fit on a page, derived from logical screen height. @@ -23,11 +24,12 @@ class EpubReaderChapterSelectionActivity final : public Activity { public: explicit EpubReaderChapterSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::shared_ptr& epub, const std::string& epubPath, - const int currentSpineIndex) + const int currentSpineIndex, const int currentTocIndex) : Activity("EpubReaderChapterSelection", renderer, mappedInput), epub(epub), epubPath(epubPath), - currentSpineIndex(currentSpineIndex) {} + currentSpineIndex(currentSpineIndex), + currentTocIndex(currentTocIndex) {} void onEnter() override; void onExit() override; void loop() override; diff --git a/test/epubs/test_spine_toc_edges.epub b/test/epubs/test_spine_toc_edges.epub new file mode 100644 index 0000000000000000000000000000000000000000..de19b981925af653eebb6c005301bd94a3b56a9f GIT binary patch literal 66978 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 O4gvs>5Ac1?0sMc>zjF-$ literal 0 HcmV?d00001