Skip to content

.pagination

The reference for reader.pagination. The pagination guide explains how to use it: when to read it rather than the reading position, how its stream behaves, and what a settled result is.

Whether a page can be turned or another spine item reached is not part of pagination: it is on reader.navigation.state$, under Observing navigation.

Observable<EnhancerPaginationInfo>

The current result as soon as you subscribe, then every new one. The stream is smoothed, so it can briefly lag behind the reader: the guide's Reading the stream explains how.

EnhancerPaginationInfo

The latest result of .state$, read synchronously. Before the book is first laid out it is an unsettled estimate, which can place both edges at the start of the first spine item whatever page the book opens on. Nothing else is known yet: chapterInfo, spineItemReadingDirection and absolutePageIndex are undefined, and numberOfTotalPages and percentageEstimateOfBook are 0.

A result is the core result, enriched by the pagination enhancer that every reader created with createReader includes. Every type below is exported from @prose-reader/core.

type VisibleRange<TEdge extends PaginationEdge> =
| {
isSettled: false
begin: TEdge
end: TEdge
}
| {
isSettled: true
begin: SettledPaginationEdge<TEdge>
end: SettledPaginationEdge<TEdge>
}
type SettledPaginationEdge<TEdge extends PaginationEdge> = TEdge & {
cfi: string
}

The shape of every result: the two edges of what is visible, and whether their positions have resolved.

Field Meaning
begin The first page visible, in reading order rather than on screen: in a right-to-left spread, the one on the right
end The last page visible, in reading order. The same page as begin when a single page is visible
isSettled Whether the result describes the visible pages of the current layout over content that is ready, or failed to load. The guide's Settlement says when it drops and when it returns

Settlement is the only thing the two variants differ in, and it is expressed on the edges: a settled result's edges have resolved positions, so their cfi is typed as present, and TypeScript makes you check isSettled before reading one as a string.

type PaginationEdge = {
cfi: string | undefined
spineItemIndex: number | undefined
pageIndexInSpineItem: number | undefined
numberOfPagesInSpineItem: number
}

One edge of the core result.

Field Meaning
cfi The first visible character of the edge's page, or the start of its spine item when the page has no text to point to. On end too it is where the page starts, not the last character visible. Until the result settles it may not describe the page shown: it can still be the previous result's, or the start of the spine item
spineItemIndex The index in the spine of the item the page belongs to
pageIndexInSpineItem The index of the page in that item, from 0
numberOfPagesInSpineItem How many pages that item is laid out on

cfi, spineItemIndex and pageIndexInSpineItem are undefined only while the edge is in no spine item.

The cfi is where the page starts on the current layout, so it moves when the book is laid out again. It is not the value to save to reopen the book there: that is reader.navigation.readingPosition$, under Reading position, and the guide's Pagination or reading position explains why.

type PaginationInfo = VisibleRange<PaginationEdge>

The core result alone, before the pagination enhancer enriches it. reader.pagination carries the enriched one, EnhancerPaginationInfo.

type EnhancerPaginationEdge = PaginationEdge & {
chapterInfo: ChapterInfo | undefined
spineItemReadingDirection: "rtl" | "ltr" | undefined
absolutePageIndex: number | undefined
}

An edge of the reader's result: a PaginationEdge, and what the pagination enhancer adds to it.

Field Meaning
chapterInfo The table of contents entry the page is in, nested down to the most specific one: see ChapterInfo below. undefined when no entry matches the page's spine item
spineItemReadingDirection The direction the spine item's document is written in, read from its styles: "rtl" for right-to-left and for vertical-rl writing. undefined while the document is not loaded, or when its renderer does not report one
absolutePageIndex The index of the page counted from the start of the book, from 0: the index reader.navigation.goToAbsolutePageIndex() takes. It counts the pages laid out now, so it has the same limits as numberOfTotalPages. undefined when the page is not laid out
type ExtraPaginationInfo = {
percentageEstimateOfBook: number
numberOfTotalPages: number
isUsingSpread: boolean
}

What the pagination enhancer adds about the book as a whole.

Field Meaning
percentageEstimateOfBook How far into the book the end edge is, from 0 to 1. It is based on the progressionWeight of each spine item in the manifest and on the pages of the current one: not exact, but a good general idea of the progress. When a spine item has no progressionWeight, or the weights add up to nothing, every item counts as an even share. The progress to save is the reading position's own, under Reading position
numberOfTotalPages How many pages the book is laid out on. Only exact for pre-paginated books, or for reflowable ones when every spine item is loaded: spine items load and unload as the reader moves through the book, and a reflowable item's pages can only be measured while it is loaded
isUsingSpread Whether the reader shows two pages side by side
type EnhancerPaginationInfo = ExtraPaginationInfo &
VisibleRange<EnhancerPaginationEdge>

What .state and .state$ carry: a VisibleRange over EnhancerPaginationEdge, which discriminates on isSettled exactly like PaginationInfo, and the fields of ExtraPaginationInfo.

type ChapterInfo = {
title: string
path: string
subChapter?: ChapterInfo
}

A table of contents entry, with the entry inside it that the page is in.

Field Meaning
title The entry's title, from the manifest's nav.toc
path The entry's path, from the manifest's nav.toc
subChapter The entry nested in this one that the page is in. Absent on the most specific entry