Skip to content

voidberg/quarto-go

Repository files navigation

quarto

quarto-go

Turn HTML into EPUB3 and Kobo kepub files, entirely in memory, with no external binaries. This is a Go port of @voidberg/quarto (TypeScript). It is pure Go with no cgo, so it cross-compiles to a static ARM binary and runs natively on e-readers like Kobo and reMarkable, as well as in any other Go program.

CI Go Reference

Install

go get github.com/voidberg/quarto-go

The module path ends in quarto-go, but the package is named quarto, so you call quarto.Generate(...):

import "github.com/voidberg/quarto-go"

Quick start

package main

import (
	"context"
	"os"

	"github.com/voidberg/quarto-go"
)

func main() {
	in := quarto.Input{
		Title:   "On the Shortness of Life",
		Authors: []string{"Seneca"},
		Chapters: []quarto.Chapter{
			{Title: "I", HTML: "<p>It is not that we have a short time to live...</p>"},
		},
	}

	epub, err := quarto.Generate(context.Background(), in)
	if err != nil {
		panic(err)
	}
	os.WriteFile("seneca.epub", epub, 0o644)

	// A Kobo kepub in one call (Generate + ToKepub):
	kepub, err := quarto.GenerateKepub(context.Background(), in)
	if err != nil {
		panic(err)
	}
	os.WriteFile("seneca.kepub.epub", kepub, 0o644)
}

What it does

  • Valid EPUB3, checked against EPUBCheck.
  • Native kepub conversion (ToKepub): Kobo reading-location spans and the book-columns/book-inner wrappers, with no kepubify binary.
  • Optional table of contents (on by default, off with NoToc).
  • Parses messy HTML fragments and re-serializes them as well-formed XHTML.
  • Downloads and embeds remote images so the book is self-contained, or drops them with NoDownloadImages.
  • Deterministic: the same input always produces the same bytes (a stable FNV-1a id, no wall-clock or randomness).
  • One non-stdlib dependency, golang.org/x/net.

API

func Generate(ctx context.Context, in Input) ([]byte, error)       // HTML -> EPUB3
func ToKepub(data []byte) ([]byte, error)                          // EPUB3 -> kepub
func GenerateKepub(ctx context.Context, in Input) ([]byte, error)  // Generate then ToKepub
func ImageSize(data []byte, mime string) (width, height int, ok bool)

context.Context is the first argument so image downloads honor cancellation and deadlines. Functions return errors; they never panic (an empty Chapters is an error, for example).

Options

Build a book from an Input. The zero value picks quarto's defaults, so you only set what you need.

Field Default Notes
Title (required) Book title.
Authors none Zero or more dc:creator values.
Publisher, Description none Dublin Core metadata.
Language "en" BCP-47 tag.
ID deterministic urn:uuid Derived from title and authors when unset.
Date none ISO-8601 publication date.
Series, SeriesIndex none Emits both EPUB3 belongs-to-collection and Calibre series metadata. SeriesIndex is a *float64 because 0 is a valid index.
NoToc TOC on Set to suppress the visible table of contents.
TocTitle "Table of Contents" Heading for the TOC page.
CSS, NoCSS bundled stylesheet Override with your own CSS, or ship none.
NoDownloadImages downloads on Set to drop every non-data: image instead of fetching.
Client http.DefaultClient Inject your own *http.Client for timeouts, transport, or fixed bytes in tests.

Per chapter (Chapter):

Field Notes
Title Used in the nav/TOC and as the page <h1>.
HTML The body fragment; need not be well-formed.
Author Optional byline under the title.
ExcludeFromToc Keep the chapter in reading order but out of the nav and NCX (useful for front matter).
SkipTitle Don't generate the <h1> heading; render only your HTML.

Covers and images

  • Supply a cover by URL (CoverURL) or raw bytes (CoverData); bytes win when both are set. CoverBackground sets the letterbox color on the cover page.
  • CoverFromLeadImage promotes a chapter's leading image to the cover when no explicit cover is given.
  • TransformImage runs on each fetched image before it is embedded, so you can transcode, resize, or drop it. TransformCover builds the final cover and runs even when there is no source image, so you can compose one from metadata.

Running on e-readers

With no cgo and no external binaries, a CGO_ENABLED=0 cross-build produces a static binary you can copy straight onto an ARM Linux device and run:

# 32-bit ARMv7 — every Kobo (all models), reMarkable 1 / 2:
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o quarto .

# ARM64 — reMarkable Paper Pro, Paper Pro Move, Paper Pure:
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o quarto .

Every Kobo ships a 32-bit userspace, even newer models (Sage, Elipsa, Libra / Clara Colour) whose chips are 64-bit capable, so GOARCH=arm is right for any Kobo. When in doubt, run uname -m on the device over SSH: armv7l → the first build, aarch64 → the second.

Pick the output format per device:

  • Kobo: call GenerateKepub for the Kobo-optimised kepub.
  • reMarkable (and any standard EPUB reader): call Generate. reMarkable reads EPUB natively; the Kobo-specific kepub extensions aren't needed.

Requirements

Go 1.25 or newer.

Status and versioning

This is a v0.x release: the API may still change. It will stabilize at v1.0.0 once the first consumer (instakobo) has integrated it.

quarto-go and the TypeScript @voidberg/quarto are parallel packages. The Go port was validated byte-for-byte against the published TypeScript library across a fixture corpus, but the two might evolve independently in the future.

License

MIT. See LICENSE.

About

Turn HTML into EPUB3 and Kobo kepub files, entirely in memory, with no external binaries. Built to run natively on Kobo and Remarkable devices, and in any other Go program.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages