Devices & export targets

Reading doesn’t stop at the app: works end up on e-readers, tablets, and phones whose native format isn’t a ScriptPub syntax at all. This page defines the export layer the Overview reserves — three concepts that let a library say “these works go to my Kindle, as EPUB, rendered through this view”:

  • a device profile describes one piece of reading hardware;
  • a package format is a serialize-only output encoding (EPUB);
  • an export target is the per-library policy tying them together.

The act of exporting or syncing remains tooling over the store (list + read + write, Storage); what this page adds to the format is the policy — portable intent that travels with the library, the way organize and defaults already do. A target never carries a location; a reader that ignores library.targets entirely loses nothing.

Device profiles

A device profile is a named description of a reading device:

{
  "id": "kindle-paperwhite",   // class-safe token
  "label": "Kindle Paperwhite",
  "class": "ereader",          // phone · tablet · desktop · ereader
  "screen": { "width": 396, "height": 528, "ppi": 300 },
  "color": false
}
Field Type Description
id string Class-safe token. Also a data-spub-device guard token (Conditions).
label string Display name.
class "phone" | "tablet" | "desktop" | "ereader" The form-factor class — the same closed vocabulary data-spub-device guards test. A monochrome device is ereader.
screen { width, height, ppi? } CSS pixels, not hardware pixels (ppi is informative). A Paperwhite is ~396 CSS px wide, not 1236 — this keeps every existing use.width clause meaningful.
color boolean Colour vs grayscale/e-ink. Feeds the color selection clause.

Built-in profiles (informative — an engine-shipped registry, not spec-frozen; screen values are approximate CSS px):

id class screen color default package
kindle-paperwhite ereader 396 × 528 no epub
kindle-scribe ereader 595 × 794 no epub
kobo-clara ereader 343 × 463 no epub
remarkable-2 ereader 596 × 795 no epub
generic-tablet tablet 768 × 1024 yes epub
generic-phone phone 390 × 844 yes epub

The default package column is a UI prefill hint, not a device property. A device not covered by the registry is declared inline on the target (the device field takes a profile object as well as an id).

Only the four classes are normative selection vocabulary. A format SHOULD prefer classes in its use.device clauses — ["ereader"] means something to every engine; ["kobo-clara"] only to one that knows the profile.

An ereader profile implies no media playback: renderers and encoders targeting one degrade a video to its poster image (else its alt text), and audio to its alt text — the ordinary graceful-degradation rule applied to a device class. A grayscale profile flattens alpha over the view’s paper colour.

Package formats

A package format is a serialize-only output encoding. Unlike the six syntax adapters — which round-trip text ↔ model — a package only writes, may produce bytes rather than text, and may span multiple files. Package ids and syntax ids are separate registries: epub is a package, never a syntax, and never appears in a view’s use.syntax clause (that is what the data-spub-package guard axis is for).

A package encoder is a renderer in the sense of graceful degradation: a view describing more than the package can carry degrades — a paged flow becomes reflowable, page furniture drops — and the rest renders.

Package Output Notes
folder a directory The library export — every work re-pathed per organize + a fresh index; the output is itself a complete, servable library.
epub one .epub per work EPUB 3, below.
pdf one .pdf per work Reserved; not yet specified.

The epub package

One EPUB 3 (reflowable) file per work. The mapping from the canonical model:

  • Metadata — from meta:

    EPUB Source
    dc:identifier meta.uri; when absent, a stable identifier derived from the work’s library-relative path — stable across re-exports, so a re-sync never mints a new book identity.
    dcterms:modified the work’s own modified time — never the encode time, so an unchanged work encodes byte-identically.
    dc:title meta.title
    dc:creator meta.author (one element per author)
    dc:language meta.language, else the library’s defaults.language, else "en"
  • Navigation — the nav document is generated from the divisions outline: rank hierarchy → nested <ol>. A work with no divisions gets a single-entry nav (its title).

  • Spine — one XHTML content document per top-rank division; a work with no divisions is a single document.

  • Covermeta.cover, when present, becomes the manifest’s cover-image item (meta.coverAlt its description). Absent means no cover — the encoder never invents one (the generated-card fallback is a catalog-surface behaviour, not a packaging one).

  • Assets — every media reference the document makes (block and inline src, poster, narration sources, the cover) is collected by collectAssets and resolved to bytes by the tooling — the encoder stays store-blind, receiving PackagedAssets. Packaging rules:

    • Assets land under EPUB/media/; each gets one manifest <item> with its media type. Names derive deterministically from the source references (content-hash deduplication; entries sorted) — an unchanged work with unchanged assets re-encodes byte-identically, preserving the determinism contract above.
    • Rendered XHTML references rewrite from document-relative to chunk-relative (../media/… from text/part-N.xhtml). data: URIs pass through untouched.
    • A reference the tooling could not resolve keeps its element with the alt text, drops the source, and reports a warning — never a broken manifest reference.
    • A media type outside the EPUB core media types (JPEG / PNG / GIF / SVG / WebP images; MP3 / AAC-MP4 audio; H.264-MP4 recommended video) packages with a warning.
    • Remote (http(s)) audio/video pass through with properties="remote-resources"; remote images warn and drop their source (many reading systems require local images).
  • Content — each document is the work rendered under the target’s materialized conditions, serialized as XHTML (EPUB content documents are XML). Block/mark markup follows the html adapter’s contract.

  • Styling — the document sheet, materialized + flattened, with CSS custom properties flattened to literal values and font tokens resolved to concrete family stacks — legacy e-reader engines don’t support var(). The curated CSS subset otherwise maps onto e-reader engines directly.

  • Container — per OCF: mimetype first in the zip, stored uncompressed.

Media overlays

A work carrying narration alignmentmeta.narration plus block-level narration clips — holds everything an EPUB 3 Media Overlay needs. The mapping is normative; emitting it is an encoder capability (an encoder without it packages the same work minus the overlay, warning once):

  • One SMIL document per spine chunk (part-N.smil, application/smil+xml, wired to its XHTML via the manifest’s media-overlay attribute).
  • One <par> per narrated block: <text src="part-N.xhtml#bI"/> — the encoder’s own stable per-block ids, no persisted ids needed — plus <audio src="…" clipBegin="12.25s" clipEnd="17.5s"/> from the clip (its src, else meta.narration).
  • Package metadata: media:duration per overlay and total (computed from the clips), media:narrator from meta.narrator, and media:active-class: -epub-media-overlay-active.

Export targets

An export target lives in the library config (kind: "library" only — a collection owns no store to export from):

"library": {
  "targets": [
    {
      "id": "my-kindle",
      "label": "Kindle Paperwhite",
      "device": "kindle-paperwhite",   // profile id, or an inline profile object
      "package": "epub",               // default "epub"
      "view": "read",                  // optional view-id hint
      "works": {
        "collection": "collections/kindle-picks.spub.html",
        "include": ["**"],
        "exclude": ["**/drafts/**"],
        "languages": ["en"]
      }
    }
  ]
}
Field Type Description
id string Class-safe token, unique among targets.
label string Display name (“Mom’s Kobo”).
device string | profile A built-in profile id, or an inline device profile.
package string The package format; default "epub".
view string Optional view-id hint — see view selection below.
works object Which works go to this device — see below. Absent = every scanned work.

Which works go

Selection composes three optional clauses, in order:

  1. collection — a library-relative reference to a collection; its entries are the base set. Entries resolving outside the library are skipped (with a warning). Absent: the base set is every scanned work.
  2. include — store-path globs (the same mini-glob as scan) that intersect the base set.
  3. exclude — globs that subtract.

languages filters translations: absent, a selected work brings its translations along, each as its own sibling package output (<slug>.<lang>.epub); stated, only the listed languages are emitted.

Which view renders

The target derives the fixed environment + guard state the encoder materializes each work’s sheet against: its device profile contributes the screen size and (monochrome); the state carries intent: read, the package token, and the device class + profile id tokens. syntax stays unset in a package-export context; package is the axis guards test.

{ "medium": "export", "intent": "read",
  "device": "ereader", "deviceId": "kindle-paperwhite",
  "width": 396, "scheme": "light", "color": false,
  "package": "epub", "viewId": "read" }

— and each work’s own views are scored against it (a library is heterogeneous; every work carries its own format). A grayscale profile contributes scheme: "light", color: false. The target’s view field feeds viewId: the usual rule applies — an explicit pick wins in any work that has that view, and yields to scoring where it doesn’t fit. syntax is unset in a package-export context; package is the axis views select on.

Policy, not binding

A target declares intent only. It must not carry a store handle, path, URL, or credential — where a target syncs to (which folder, which mounted drive) is app-local per machine, outside the format. This is the access-is-store-capability rule applied to output: the library file stays portable and safe to publish, and the same target works on every machine the library reaches.

Validation

  • Target id / profile id — class-safe, unique — error.
  • extends-style references: a works.collection that doesn’t parse as a reference — error.
  • A device string not matching a built-in profile id — warning (assumed to be a profile this engine doesn’t ship; tooling may ask the user to define it inline).
  • An unknown package id — warning (degrade: the target is listed but not runnable).
  • Out-of-vocabulary classwarning, treated as tablet.

See the Glossary for the one-line definitions.