diff options
| author | Simen A. W. Olsen <hello@simenandre.no> | 2026-05-28 23:54:24 +0200 |
|---|---|---|
| committer | Simen A. W. Olsen <hello@simenandre.no> | 2026-05-28 23:54:24 +0200 |
| commit | 330a0c896970d99320529a47f186c831a6ecc4a0 (patch) | |
| tree | 3dc8b3ac700a6e2715f0f81b96e7957320f6352e /src/epub.zig | |
| parent | b2d7d90add48e5cd3b644949b9b401fafc6eafbc (diff) | |
| download | ziglang-docs-epub-330a0c896970d99320529a47f186c831a6ecc4a0.tar.gz ziglang-docs-epub-330a0c896970d99320529a47f186c831a6ecc4a0.zip | |
feat: add table of content instead of sidebar
Diffstat (limited to 'src/epub.zig')
| -rw-r--r-- | src/epub.zig | 27 |
1 files changed, 21 insertions, 6 deletions
diff --git a/src/epub.zig b/src/epub.zig index 0bdf563..7e35089 100644 --- a/src/epub.zig +++ b/src/epub.zig @@ -13,8 +13,11 @@ pub const Options = struct { /// Version name, used in the title and as the unique identifier basis. version: []const u8, lang: []const u8 = "en", - /// Well-formed XHTML5 body (output of html.toXhtml). + /// Well-formed XHTML5 body (output of html.transform). xhtml: []const u8, + /// `<ol>...</ol>` of TOC entries extracted from the page, or null. When + /// present it becomes the navigation document and the book's opening page. + toc: ?[]const u8 = null, }; /// Build an EPUB3 archive from `opts`. Caller owns the returned bytes. @@ -74,6 +77,7 @@ fn buildOpf(allocator: std.mem.Allocator, opts: Options) ![]u8 { \\ <item id="index" href="index.xhtml" media-type="application/xhtml+xml"/> \\ </manifest> \\ <spine> + \\ <itemref idref="nav"/> \\ <itemref idref="index"/> \\ </spine> \\</package> @@ -82,21 +86,23 @@ fn buildOpf(allocator: std.mem.Allocator, opts: Options) ![]u8 { } fn buildNav(allocator: std.mem.Allocator, opts: Options) ![]u8 { + // Use the page's own table of contents when we extracted one; otherwise fall + // back to a single link to the whole document. + const list = opts.toc orelse + "<ol>\n <li><a href=\"index.xhtml\">Contents</a></li>\n </ol>"; return std.fmt.allocPrint(allocator, \\<?xml version="1.0" encoding="utf-8"?> \\<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="{s}"> \\ <head><title>{s} ({s})</title></head> \\ <body> \\ <nav epub:type="toc" id="toc"> - \\ <h1>Contents</h1> - \\ <ol> - \\ <li><a href="index.xhtml">{s} ({s})</a></li> - \\ </ol> + \\ <h1>{s} ({s})</h1> + \\ {s} \\ </nav> \\ </body> \\</html> \\ - , .{ opts.lang, opts.title, opts.version, opts.title, opts.version }); + , .{ opts.lang, opts.title, opts.version, opts.title, opts.version, list }); } test "epub starts with PK and mimetype, and round-trips" { @@ -105,6 +111,7 @@ test "epub starts with PK and mimetype, and round-trips" { .title = "Zig Language Reference", .version = "0.15.2", .xhtml = "<?xml version=\"1.0\"?>\n<html xmlns=\"http://www.w3.org/1999/xhtml\"><body>hi</body></html>", + .toc = "<ol><li><a href=\"index.xhtml#Intro\">Intro</a></li></ol>", }); defer gpa.free(bytes); @@ -126,4 +133,12 @@ test "epub starts with PK and mimetype, and round-trips" { const opf = try tmp.dir.readFileAlloc(gpa, "OEBPS/content.opf", 1 << 16); defer gpa.free(opf); try std.testing.expect(std.mem.indexOf(u8, opf, "0.15.2") != null); + // nav must come before index in the spine (TOC is the opening page). + const nav_ref = std.mem.indexOf(u8, opf, "idref=\"nav\"").?; + const idx_ref = std.mem.indexOf(u8, opf, "idref=\"index\"").?; + try std.testing.expect(nav_ref < idx_ref); + + const nav = try tmp.dir.readFileAlloc(gpa, "OEBPS/nav.xhtml", 1 << 16); + defer gpa.free(nav); + try std.testing.expect(std.mem.indexOf(u8, nav, "href=\"index.xhtml#Intro\"") != null); } |
