Skip to content

feat(epub): add book navigation to markdown export - #3993

Open
salmonumbrella wants to merge 2 commits into
docling-project:mainfrom
salmonumbrella:feat/epub-chapter-index
Open

feat(epub): add book navigation to markdown export#3993
salmonumbrella wants to merge 2 commits into
docling-project:mainfrom
salmonumbrella:feat/epub-chapter-index

Conversation

@salmonumbrella

@salmonumbrella salmonumbrella commented Aug 13, 2026

Copy link
Copy Markdown

What changed

The epub output format renders an EPUB as Markdown preceded by a YAML frontmatter block holding the book metadata and a chapter index. Every chapter entry carries the absolute UTF-8 byte offset and the 1-based line number of its heading, so an agent can seek straight to a chapter without unpacking the EPUB or scanning the full book.

docling book.epub --to epub    # writes book.epub.md
---
title: "Alice's Adventures in Wonderland"
authors: ["Lewis Carroll"]
language: "en"
source_file: "book.epub"
chapters:
  - title: "CHAPTER I. Down the Rabbit-Hole"
    line:         12
    byte:        287
---

Also preserves OPF title, creators, publication date, language, and source filename as transient conversion metadata, and rewrites links between EPUB spine documents to the rendered heading slugs.

Blocked on docling-core#720

Per @dolfim-ibm's review, the serializer moved out of this repo into docling-core: docling-project/docling-core#720. This PR consumes it via DoclingDocument.export_to_epub() / save_as_epub(), so pyproject.toml currently points docling-core at git main. That pin should be dropped once a docling-core release carries the serializer; until #720 lands, CI here will fail to resolve the symbol.

Responses to review

  • "The serializer should be in docling-core" — done, as docling-core#720. docling/backend/epub_serializer.py is deleted. The EpubDocument subclass of DoclingDocument went with it: OPF metadata is transient backend state, so it now rides on ConversionResult._epub_metadata, the same private-attr plumbing this repo already uses for _pdf_outline.
  • "add Epub as a standalone output format" — done. --md-book-frontmatter and --md-chapter-index are gone, replaced by OutputFormat.EPUB.

One wart worth your call: the format is named epub but emits Markdown, so writing <name>.md would collide with --to md. I used <name>.epub.md, following the existing .dclg.xml compound-extension precedent. Happy to rename the format or the extension if you'd prefer something else.

Bug found while porting

The chapter detector matched only SectionHeaderItem(level=1). The HTML backend maps <h1> to a title item, not a section header, so any EPUB that titles its chapters with <h1> — common outside Standard Ebooks — produced a silently empty chapter index. The test data here uses <h2> per section, which is why it went unnoticed. The core serializer now indexes both, and docling-core#720 has a regression test that fails against the old rule.

Consequence: the book's own # title is now indexed too. A serializer cannot tell a book title from a chapter title — both are TitleItem — and special-casing the first one would drop Chapter One in <h1>-per-chapter books. Indexing every top-level heading is the honest rule; the frontmatter still carries the book title separately.

Verification

  • make validate — all hooks pass
  • pytest tests/test_backend_epub.py tests/test_cli.py — 56 passed
  • pytest tests/test_cli_remote.py tests/test_invalid_input.py tests/test_service_client_sdk_unit.py tests/test_backend_pptx.py — 190 passed, 1 skipped
  • docling-core#720: 606 passed, 6 skipped; ruff + mypy clean

Resolves #3992

Checklist:

  • Documentation has been updated, if necessary.
  • Examples have been added, if necessary.
  • Tests have been added, if necessary.

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

DCO Check Passed

Thanks @salmonumbrella, all your commits are properly signed off. 🎉

Signed-off-by: salmonumbrella <182032677+salmonumbrella@users.noreply.github.com>
@salmonumbrella
salmonumbrella force-pushed the feat/epub-chapter-index branch from 6f97bc5 to f002b40 Compare August 13, 2026 19:59
@mergify

mergify Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 26.06635% with 156 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
docling/backend/epub_backend.py 12.37% 85 Missing ⚠️
docling/backend/epub_serializer.py 38.53% 67 Missing ⚠️
docling/cli/main.py 0.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@dolfim-ibm dolfim-ibm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@salmonumbrella thanks for the contribution. The serializer should be in docling-core, and I would propose to add Epub as a standalone output format

Comment thread docling/backend/epub_serializer.py Outdated
@@ -0,0 +1,257 @@
"""Markdown serialization helpers for book-shaped EPUB documents."""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should move to a serializer in docling-core

@salmonumbrella salmonumbrella Aug 14, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to docling-core as EpubDocSerializer: docling-project/docling-core#720. This file is deleted; docling now reaches it through DoclingDocument.export_to_epub() / save_as_epub().

The EpubDocument subclass of DoclingDocument went with it.

… format

Address review feedback on docling-project#3993:

- Drop docling/backend/epub_serializer.py. The serializer now lives in
  docling-core as EpubDocSerializer, reached through the new
  DoclingDocument.export_to_epub() / save_as_epub() shorthands
  (docling-project/docling-core#720).
- Replace the --md-book-frontmatter and --md-chapter-index flags with a
  standalone `epub` output format, written as <name>.epub.md. The CLI turns
  on rewrite_internal_links automatically when it is requested.
- Drop the EpubDocument subclass of DoclingDocument. The OPF package
  metadata is transient backend state, so it rides on
  ConversionResult._epub_metadata, the same private-attr plumbing already
  used for _pdf_outline.

The serializer tests moved to docling-core alongside the code; what remains
here covers the backend, the plumbing, and the CLI format.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: salmonumbrella <182032677+salmonumbrella@users.noreply.github.com>
@PeterStaar-IBM

Copy link
Copy Markdown
Member

@salmonumbrella @dolfim-ibm with the new PR in core, can we close this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add EPUB book navigation frontmatter to Markdown export

3 participants