feat(epub): add book navigation to markdown export - #3993
Conversation
|
✅ DCO Check Passed Thanks @salmonumbrella, all your commits are properly signed off. 🎉 |
Signed-off-by: salmonumbrella <182032677+salmonumbrella@users.noreply.github.com>
6f97bc5 to
f002b40
Compare
Merge Protections🟢 Merge protection satisfied — ready to merge. Show 1 satisfied protection🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
dolfim-ibm
left a comment
There was a problem hiding this comment.
@salmonumbrella thanks for the contribution. The serializer should be in docling-core, and I would propose to add Epub as a standalone output format
| @@ -0,0 +1,257 @@ | |||
| """Markdown serialization helpers for book-shaped EPUB documents.""" | |||
There was a problem hiding this comment.
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>
|
@salmonumbrella @dolfim-ibm with the new PR in core, can we close this? |
What changed
The
epuboutput 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.mdAlso 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(), sopyproject.tomlcurrently pointsdocling-coreat gitmain. 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
docling/backend/epub_serializer.pyis deleted. TheEpubDocumentsubclass ofDoclingDocumentwent with it: OPF metadata is transient backend state, so it now rides onConversionResult._epub_metadata, the same private-attr plumbing this repo already uses for_pdf_outline.--md-book-frontmatterand--md-chapter-indexare gone, replaced byOutputFormat.EPUB.One wart worth your call: the format is named
epubbut emits Markdown, so writing<name>.mdwould collide with--to md. I used<name>.epub.md, following the existing.dclg.xmlcompound-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 areTitleItem— and special-casing the first one would dropChapter Onein<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 passpytest tests/test_backend_epub.py tests/test_cli.py— 56 passedpytest 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 skippedResolves #3992
Checklist: