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:identifiermeta.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:modifiedthe work’s own modified time — never the encode time, so an unchanged work encodes byte-identically. dc:titlemeta.titledc:creatormeta.author(one element per author)dc:languagemeta.language, else the library’sdefaults.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.
-
Cover —
meta.cover, when present, becomes the manifest’scover-imageitem (meta.coverAltits 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 bycollectAssetsand resolved to bytes by the tooling — the encoder stays store-blind, receivingPackagedAssets. 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/…fromtext/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 withproperties="remote-resources"; remote images warn and drop their source (many reading systems require local images).
- Assets land under
-
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:
mimetypefirst in the zip, stored uncompressed.
Media overlays
A work carrying narration alignment —
meta.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’smedia-overlayattribute). - 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 (itssrc, elsemeta.narration). - Package metadata:
media:durationper overlay and total (computed from the clips),media:narratorfrommeta.narrator, andmedia: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:
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.include— store-path globs (the same mini-glob asscan) that intersect the base set.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/ profileid— class-safe, unique — error. extends-style references: aworks.collectionthat doesn’t parse as a reference — error.- A
devicestring 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
packageid — warning (degrade: the target is listed but not runnable). - Out-of-vocabulary
class— warning, treated astablet.
See the Glossary for the one-line definitions.