The org.nisoku.anno EXTRA namespace stores highlights, bookmarks, and notes directly in the Honzo file. Annotations travel with the ebook and survive re-conversion.

Data format

Annotations are stored as a MessagePack map:

{
  "version": 1,
  "annotations": [
    {
      "id": "uuid-annotation-1",
      "type": "highlight",
      "chunk": 0,
      "start": 1024,
      "end": 1080,
      "text": "The highlighted text excerpt",
      "note": "Reader's note about this passage",
      "color": "#FFEB3B",
      "created": "2025-01-15T10:30:00Z",
      "modified": "2025-01-15T10:30:00Z"
    }
  ]
}

Annotation types

Type Description
highlight Text highlight with start/end byte offsets
bookmark Position bookmark (page or location marker)
note A note attached to a specific position
underline Underlined text passage
drawing Freeform drawing on a page

Fields

Field Type Description
id string UUID
type string Annotation type
chunk int TOC index of the annotated chunk
start int Byte offset within decompressed chunk
end int End byte offset
text string The annotated text excerpt
note string? Reader’s note
color string? Display color (hex)
created string ISO 8601 timestamp
modified string? Last modification timestamp

Storing annotations

use honzo_io::{HonzoBuilder, ExtraEntry};

// Add annotation data as an EXTRA entry
let anno_data = serde_json::to_vec(&annotation_map).unwrap();
let hzo = HonzoBuilder::new()
    .add_extra("org.nisoku.anno", &anno_data)
    .add_chapter("Chapter 1", ...)
    .finalize()
    .unwrap();

Reading annotations

use honzo_core::HonzoParser;

let p = HonzoParser::new(&data, 1).unwrap();
for entry in p.extra_entries() {
    if entry.namespace() == "org.nisoku.anno" {
        let anno_bytes = p.read_extra(&entry).unwrap();
        // Parse as MessagePack or JSON
    }
}

Design notes

Annotations reference chunks by TOC index. They do not reference chapter titles. This choice survives title changes.

Byte offsets are relative to the decompressed chunk data. If compression changes, offsets stay valid.

Multiple annotation entries are merged at read time by the consuming application.

Applications should preserve unknown annotation types rather than dropping them.