Skip to content

feat: add EPUB book Markdown serializer - #720

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

feat: add EPUB book Markdown serializer#720
salmonumbrella wants to merge 2 commits into
docling-project:mainfrom
salmonumbrella:feat/epub-book-serializer

Conversation

@salmonumbrella

Copy link
Copy Markdown

What changed

Adds EpubDocSerializer, a MarkdownDocSerializer variant that renders a book as Markdown preceded by a YAML frontmatter block holding the book metadata and a chapter index, plus DoclingDocument.export_to_epub() / save_as_epub().

Every chapter entry carries the absolute UTF-8 byte offset and the 1-based line number of its heading, so a reader can seek straight to a chapter instead of unpacking the source or scanning the whole book.

---
title: "Alice's Adventures in Wonderland"
authors: ["Lewis Carroll"]
language: "en"
chapters:
  - title: "CHAPTER I. Down the Rabbit-Hole"
    line:         12
    byte:        287
---

Why

Requested by @dolfim-ibm in review of docling-project/docling#3993, which had the same serializer living in docling/backend/epub_serializer.py:

The serializer should be in docling-core, and I would propose to add Epub as a standalone output format

The companion docling PR is being restructured to drop its local serializer and consume this one behind a new epub output format.

Notes for reviewers

Two details are load-bearing and have dedicated tests:

  • The offsets are advertised inside the block they measure. The frontmatter is rendered twice: once to measure how far it shifts the body, once with the shifted offsets. Padding every offset to a fixed width keeps the two renderings byte-identical; a guard raises if that ever stops holding.
  • A chapter is a TitleItem or a level-1 SectionHeaderItem. An EPUB spine document may title its chapters with either <h1> or <h2>, and the HTML backend maps <h1> to a title item. Matching only section headers leaves the index empty for every book that uses <h1> per chapter.

save_as_epub() writes raw UTF-8 bytes rather than going through text mode, since the advertised offsets index the saved file and Windows newline translation would invalidate them.

Checklist

  • Tests added (test/test_epub_serialization.py, 9 cases)
  • pytest test — 606 passed, 6 skipped
  • ruff check / ruff format / mypy clean
  • Generated docs regenerated: no schema drift

🤖 Generated with Claude Code

Serialize a DoclingDocument as Markdown preceded by a YAML frontmatter
block holding the book metadata and a chapter index. Each chapter entry
carries the absolute UTF-8 byte offset and the 1-based line number of its
heading, so a reader can seek straight to a chapter instead of scanning
the whole book.

The offsets are advertised inside the block they measure, so the
frontmatter is rendered twice: once to measure how far it shifts the
body, and once with the shifted offsets. Padding every offset to a fixed
width keeps the two renderings the same size.

A chapter is a top-level part opening on a TitleItem or a level-1
SectionHeaderItem, because an EPUB spine document may title its chapters
with either <h1> or <h2>.

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

Copy link
Copy Markdown
Contributor

DCO Check Passed

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

@mergify

mergify Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🔴 1 of 2 protections blocking · waiting on 👀 reviews

Protection Waiting on
🔴 Require two reviewer for test updates 👀 reviews
🟢 Enforce conventional commit

🔴 Require two reviewer for test updates

Waiting for

  • #approved-reviews-by >= 2
This rule is failing.

When test data is updated, we require two reviewers

  • #approved-reviews-by >= 2

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)(?:\(.+\))?(!)?:

salmonumbrella added a commit to salmonumbrella/docling that referenced this pull request Aug 14, 2026
… 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>
@codecov

codecov Bot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.50562% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
docling_core/transforms/serializer/epub.py 97.33% 2 Missing ⚠️
docling_core/types/doc/document.py 85.71% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Signed-off-by: salmonumbrella <182032677+salmonumbrella@users.noreply.github.com>
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.

1 participant