@storyteller-platform/epub
A Node.js library for inspecting, modifying, and creating EPUB 3 publications.
Installation
npm:
npm install @storyteller-platform/epub
yarn:
yarn add @storyteller-platform/epub
deno:
deno install npm:@storyteller-platform/epub
About
Throughout this library's documentation, there will be many references to the EPUB 3 specification. The lower level APIs exposed by this library require some knowledge of this specification. Here we will cover the very basics necessary to work with the library, but we recommend that users read through the linked specification to gain a deeper understanding of the format.
EPUB Basics
An EPUB file is a ZIP archive with a partially specified directory and file structure. Most of the metadata and content is specified as XML documents, with additional resources referenced from those XML documents.
The most important of these documents is the package document.
The package document is an XML document that consists of a set of elements that each encapsulate information about a particular aspect of an EPUB publication. These elements serve to centralize metadata, detail the individual resources, and provide the reading order and other information necessary for its rendering.
This library is primarily concerned with providing access to the metadata, manifest, and spine of the EPUB publication. Metadata refers to information about the publication, such as its title or authors. The manifest refers to the complete set of resources that are used to render the publication, such as XHTML documents and image files. And the spine refers to the ordered list of manifest items that represent the default reading order — the order that readers will encounter the manifest items by simply turning pages one at a time.
What this library does
@storyteller-platform/epub provides an API to interact with the metadata,
manifest, and spine of the EPUB publication. There are higher level APIs that
mostly abstract away the implementation details of the EPUB specification, like
epub.setTitle(title: string) and epub.getCreators(), as well as lower level
APIs like epub.writeItemContents(path: string, contents: Uint8Array) and
epub.addMetadata(entry: MetadataEntry), which require some understanding of
the EPUB structure to utilize effectively.
Because EPUB publications rely heavily on the XML document format, this library also provides utility methods for parsing, manipulating, and building XML documents. The underlying XML operations are based on fast-xml-parser.
Usage
The entrypoint to the library is through the Epub class. An Epub
can either be read from an existing EPUB publication file, or created from
scratch.
Reading from a file
// If you want to read or write to disk, import from the `/node`
// export
import { Epub } from "@storyteller-platform/epub/node"
const epub = await Epub.from("path/to/book.epub")
console.log(await epub.getTitle())
Creating from scratch
When creating an Epub from scratch, the title, language, and identifier
must be provided, as these are required for all publications by the EPUB 3
specification.
Other Dublin Core and non-core metadata may also be provided at creation time, or may be added incrementally after creation.
import { randomUUID } from "node:crypto"
import { Epub } from "@storyteller-platform/epub"
const epub = await Epub.create({
title: "S'mores For Everyone",
// This should be the primary language of the publication.
// Individual content resources may specify their own languages.
language: new Intl.Locale("en-US"),
// This can be any unique identifier, including UUIDs, ISBNs, etc
identifier: randomUUID(),
})
Adding a chapter
import { Epub, ManifestItem } from "@storyteller-platform/epub"
const epub = await Epub.from("path/to/book.epub")
// Construct a manifest item describing the chapter
const manifestItem: ManifestItem = {
id: "chapter-one",
// This is the filepath for the chapter contents within the
// EPUB archive.
href: "XHTML/chapter-one.xhtml",
mediaType: "application/xhtml+xml",
}
// You can specify the contents as a string
const contents = `<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:epub="http://www.idpf.org/2007/ops"
xml:lang="en-US"
lang="en-US">
<head></head>
<body>
<h1>Chapter 1</h1>
<p>At first, there were s'mores.</p>
</body>
</html>`
// Or you can specify the contents as an XML structure
const xmlContents = epub.createXhtmlDocument([
Epub.createXmlElement("h1", {}, [Epub.createXmlTextNode("Chapter 1")]),
Epub.createXmlElement("p", {}, [
Epub.createXmlTextNode("At first, there were s'mores."),
]),
])
// First, add the new item to the manifest, and add
// its contents to the publication
await epub.addManifestItem(manifestItem, contents, "utf-8")
// OR, using the XMl:
await epub.addManifestItem(manifestItem, xmlContents, "xml")
// Then add the item to the spine
await epub.addSpineItem(manifestItem.id)
Writing to disk
import { Epub } from "@storyteller-platform/epub/node"
const epub = await Epub.from("path/to/book.epub")
await epub.setTitle("S'mores for Everyone")
await epub.writeToFile("path/to/updated.epub")
Writing to a byte array
import { randomUUID } from "node:crypto"
import { Epub } from "@storyteller-platform/epub"
const epub = await Epub.create({
title: "S'mores For Everyone",
language: new Intl.Locale("en-US"),
identifier: randomUUID(),
})
const data: Uint8Array = await epub.writeToArray()
For more details about using the API, see the API documentation.
Development
This package lives in the Storyteller monorepo, and is developed alongside the Storyteller platform.
To get started with developing in the Storyteller monorepo, check out the development guides in the docs.