glepub/cfi

EPUB Canonical Fragment Identifiers — epubcfi(/6/4!/4/10/2:3) — the standard way to address a point inside a publication.

A CFI is a path of child steps. Even indices address element children (/2 is the first element child, /4 the second, …), odd indices the text between them, and ! steps out of the package document into the content document the itemref references. The part before the first ! identifies the spine item — SpineItem.cfi holds exactly that path — and the rest addresses a node inside the chapter, optionally ending in a :n character offset into a text node. A step may carry an [assertion], usually the id the addressed element is expected to have.

A range CFI — epubcfi(/6/4!/4/10/2,:1,:5) — addresses a stretch of content between two such points: a shared parent path followed by two local paths, one per endpoint. Range models one as its two absolute endpoints, which is the shape every consumer wants — resolve either endpoint like any point, compare points to sort ranges by document position — and the shared-parent split is recomputed on printing.

This module parses and prints point and range CFIs and locates them in a book’s spine. The temporal/spatial offsets used for audio and images are not supported.

Types

pub type Cfi {
  Cfi(parts: List(List(Step)), offset: option.Option(Int))
}

Constructors

  • Cfi(parts: List(List(Step)), offset: option.Option(Int))

    Arguments

    parts

    One list of steps per document: the first walks the package document to an itemref, and each subsequent list follows a ! indirection into the referenced content document.

    offset

    The :n character offset into the text node the last step lands on.

A range CFI: two points in the same document, in document order. Build one with range or parse_range; both uphold the invariants printing relies on, so construction is the only place a range can fail.

pub opaque type Range
pub type Step {
  Step(index: Int, assertion: option.Option(String))
}

Constructors

  • Step(index: Int, assertion: option.Option(String))

    Arguments

    index

    Even for element children, odd for the text between them.

    assertion

    The id the addressed element is asserted to have, from [...].

Values

pub fn compare(a: Cfi, b: Cfi) -> order.Order

Document order over points: step by step down the tree, with a node sorting before its own contents, and character offsets breaking ties between points on the same node. Assertions do not participate.

pub fn locate(
  cfi: Cfi,
) -> Result(#(Int, option.Option(Cfi)), Nil)

Split a publication-level CFI into the spine position it addresses and the remainder pointing within that chapter’s document, if any.

pub fn parse(text: String) -> Result(Cfi, Nil)

Parse an epubcfi(...) string addressing a single point. Range CFIs are rejected here; use parse_range for them.

pub fn parse_range(text: String) -> Result(Range, Nil)

Parse an epubcfi(parent,start,end) range string. The two local paths are joined onto the parent to make absolute endpoints; each may be a run of steps, a bare :n offset, or empty (the endpoint is the parent itself). Locals may not cross a ! indirection of their own.

pub fn path_to_string(cfi: Cfi) -> String

The CFI path without the epubcfi(...) wrapper — the form used for the intra-document part of a fragment, and for joining onto a spine item’s base path with !.

pub fn range(from from: Cfi, to to: Cfi) -> Result(Range, Nil)

Build a range from two points, normalising them into document order. The points must lie in the same document: every part but the last must match, and the final parts must share their first step — endpoints in different chapters do not form a range.

pub fn range_end(range: Range) -> Cfi

The point a range ends at, as an ordinary absolute CFI.

pub fn range_path_to_string(range: Range) -> String

The range’s path without the epubcfi(...) wrapper, for fragment use and for joining onto a spine item’s base path with !.

pub fn range_start(range: Range) -> Cfi

The point a range starts at, as an ordinary absolute CFI.

pub fn range_to_string(range: Range) -> String

Print a range back out as an epubcfi(parent,start,end) string. The printed form is canonical: the parent takes the maximal shared prefix of the two endpoints, whatever split the range was parsed from.

pub fn spine_item(
  book: glepub.Book,
  cfi: Cfi,
) -> Result(#(glepub.SpineItem, option.Option(Cfi)), Nil)

The spine item a CFI addresses, with the remainder of the CFI pointing within that chapter’s document, if any.

pub fn to_string(cfi: Cfi) -> String

Print a CFI back out as an epubcfi(...) string.

✨ Search Document