goreader/epub
| Language | Go |
|---|---|
| License | MIT |
| Repository | github.com/taylorskalyo/goreader (package epub) |
| Documentation | pkg.go.dev/github.com/taylorskalyo/goreader/epub |
| Depends on | Go standard library only (archive/zip, encoding/xml) |
| Read / write | yes / no |
The epub package of goreader (a terminal EPUB reader) parses an EPUB in a few small structs: the container’s rootfiles, the OPF metadata (title, creator, language, identifier, publisher, description, dates), the manifest items and the spine. Each manifest item can be opened as an io.ReadCloser to read its XHTML or image data. It also reads chapter labels from the EPUB 3 navigation document or the EPUB 2 NCX.
It’s intentionally small: metadata fields are single strings (only the first dc:creator is kept), and it doesn’t convert XHTML to text. Use it when you need to list or extract the contents of an EPUB without pulling in a large dependency.
Installation#
go get github.com/taylorskalyo/goreader/epub
Basic usage: read metadata and chapters#
package main
import (
"fmt"
"io"
"log"
"github.com/taylorskalyo/goreader/epub"
)
func main() {
rc, err := epub.OpenReader("book.epub")
if err != nil {
log.Fatal(err)
}
defer rc.Close()
// the package (content.opf); books almost always have exactly one
book := rc.Rootfiles[0]
fmt.Println("Title: ", book.Title)
fmt.Println("Author: ", book.Creator)
fmt.Println("Language:", book.Language)
// chapters in reading order
for _, itemref := range book.Spine.Itemrefs {
r, err := itemref.Open()
if err != nil {
log.Fatal(err)
}
data, err := io.ReadAll(r)
r.Close()
if err != nil {
log.Fatal(err)
}
label := book.ItemName(itemref.HREF) // title from the table of contents, if any
fmt.Printf("%s (%s): %d bytes\n", itemref.HREF, label, len(data))
}
}
How to list all files in an EPUB manifest#
for _, item := range rc.Rootfiles[0].Manifest.Items {
fmt.Printf("%-20s %-30s %s\n", item.ID, item.HREF, item.MediaType)
}
How to extract images from an EPUB#
for i := range book.Manifest.Items {
item := &book.Manifest.Items[i]
if !strings.HasPrefix(item.MediaType, "image/") {
continue
}
r, err := item.Open()
if err != nil {
log.Fatal(err)
}
out, err := os.Create(path.Base(item.HREF))
if err != nil {
log.Fatal(err)
}
io.Copy(out, r)
out.Close()
r.Close()
}
(imports: io, os, path, strings)
How to read an EPUB from memory#
NewReader takes an io.ReaderAt and size, e.g. a file you downloaded into a byte slice:
data, err := os.ReadFile("book.epub")
if err != nil {
log.Fatal(err)
}
r, err := epub.NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
log.Fatal(err)
}
fmt.Println(r.Rootfiles[0].Title)
Tips#
OpenReaderreturns errors such asepub.ErrNoRootfileorepub.ErrBadManifestfor broken books; check them witherrors.Is.- To get plain text from a chapter, parse the XHTML with
golang.org/x/net/htmland collect text nodes.