# Epub Creation

> Create EPUB ebooks from markdown/text for e-readers.

- Skill: `wcpaka-lgtm/epub-creation` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add wcpaka-lgtm/epub-creation`
- Raw SKILL.md: https://api.skillmd.com/api/skills/wcpaka-lgtm/epub-creation/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: wcpaka-lgtm (https://skillmd.com/u/wcpaka-lgtm)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/wcpaka-lgtm/epub-creation

---


# EPUB Creation

Create EPUB ebook files from markdown or plain text content. EPUB is the universal ebook format — opens natively in Apple Books (iPhone/iPad/Mac), Google Play Books, Kobo, and most readers. Kindle uses MOBI/AZW3 but can accept EPUB via Send-to-Kindle.

## When to Use

- User asks to "make a book", "save as ebook", "EPUB", "iPhone Books", "도서 앱"
- User has long-form content (novel, guide, collection) and wants it readable on a phone/tablet
- User asks how to read something on their e-reader
- User asks for an **HTML site / converter page** ("html사이트", "변환 사이트", "텍스트를 epub으로") that turns TXT into EPUB → build it client-side, see below
- User has an **existing EPUB missing a cover** and wants to add/replace the cover photo ("표지없는 epub에 표지 추가", "표지 교체") → patch the zip in place, see "Replacing/adding a cover in an EXISTING EPUB" in `references/browser-side-epub.md` and `replaceEpubCover()` in the template

## Two routes

1. **Python/ebooklib script** (this SKILL.md) — batch conversion, agent-driven, delivers a .epub file.
2. **Browser-side single-HTML converter** — when the user wants a *site/tool* rather than one converted file: everything runs in the browser (no upload, works offline, WebView-embeddable). Full structure, zip rules, and Korean TXT intake in `references/browser-side-epub.md`; ready-to-adapt generator core (EPUB 3.0 + NCX, cover image, offline ZIP fallback, EUC-KR sniffing, chapter detection) in `templates/browser-epub-builder.js`. Known-good full implementation: `C:/Users/okya1/txt2epub/index.html` ("제본소").

## Prerequisites

```bash
# Use uv on Windows (pip may not be available in Hermes venv)
uv pip install ebooklib --python "C:/Users/<user>/AppData/Local/Programs/Python/Python312/python.exe"
# Or on Linux/macOS:
pip install ebooklib
```

ebooklib depends on lxml. If lxml is missing, install it first.

## Workflow

1. **Prepare source text** — markdown with `#`/`##`/`###` headers as chapter boundaries
2. **Run the conversion script** — see `scripts/make_epub.py` in this skill
3. **Deliver the .epub file** — via MEDIA: path, AirDrop, email, or cloud drive

## Chapter Splitting Logic

Split on markdown headers:
- `# Title` → book title (skip as chapter)
- `## Part/Section` → major division
- `### Chapter N` → individual chapter

Each chapter becomes a separate XHTML file inside the EPUB. The TOC (table of contents) is auto-generated from chapter titles.

## Markdown → HTML Conversion

Minimal conversion needed:
- `**bold**` → `<strong>`
- `*italic*` → `<em>`
- `---` → `<hr/>`
- Double newlines → paragraph breaks (`<p>`)
- Single newlines within paragraph → `<br/>`

## CSS for Korean/Japanese text

```css
body { font-family: "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", sans-serif; line-height: 1.8; }
p { text-indent: 1em; margin: 0.5em 0; }
```

## Pitfalls

- **Empty chapters crash ebooklib.** If a header is followed by another header with no content between them, the resulting EpubHtml has an empty body. lxml's `document_fromstring` raises `ParserError: Document is empty`. FIX: filter out chapters where `content.strip()` is empty or < 10 chars before adding to the book.
- **XML declaration in chapter content.** Do NOT include `<?xml version="1.0"?>` in `chapter.content` — ebooklib adds its own. Including it can cause parse errors.
- **Pass `content` as a str wrapped in `<html><body>`, not as bytes.** Setting `chap.content = html_string.encode("utf-8")` (bytes) or a bare HTML fragment without `<html><body>...</body></html>` can trigger `lxml.etree.ParserError: Document is empty` during `write_epub`. The reliable pattern (verified on Windows/lxml): `chap.content = '<html><body>' + body_html + '</body></html>'` as a plain `str`. ebooklib handles encoding itself.
- **Special characters in titles.** Escape `&`, `<`, `>` in chapter titles before embedding in HTML.
- **ebooklib not in Hermes venv.** The Hermes agent venv often lacks pip. Use `uv pip install --python <system-python-path>` to install into the system Python, then run the script with that Python explicitly.
- **Spine order matters.** `book.spine = ["nav"] + epub_chapters` — nav must come first or the TOC won't render on some readers.

## Delivery to iPhone Books

Tell the user (pick whichever fits their setup):
1. **AirDrop** (easiest): send .epub → tap → opens in Books
2. **KakaoTalk/email to self**: open attachment → share sheet → "Books"
3. **iCloud Drive**: upload → Files app → tap → share → Books
4. **USB (Windows)**: connect iPhone → iTunes/Finder → Books → drag .epub

## Verification

After generating, confirm:
- File exists and is > 0 bytes
- Chapter count matches expected
- Open with `epub.EpubBook()` reader to validate structure (optional)

## Related skills

`pdf` (PDF creation/manipulation), `docx` (Word documents).

