From 0ad2ac71c7ed96919120263b641da4efed1e4698 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Tue, 31 Mar 2026 21:10:22 +0200 Subject: [PATCH] Introduce ordered list support --- .../Epub/parsers/ChapterHtmlSlimParser.cpp | 20 ++++++++++++++++++- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 8 ++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index 368a4c60..50ceb86f 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -554,6 +554,12 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* } } + // Track ul/ol nesting so li markers can be numbered or bulleted accordingly + if (strcmp(name, "ul") == 0 || strcmp(name, "ol") == 0) { + self->listStack.push_back({self->depth, strcmp(name, "ol") == 0, 0}); + // fall through to depth increment + } + const float emSize = static_cast(self->renderer.getFontAscenderSize(self->fontId)); const auto userAlignmentBlockStyle = BlockStyle::fromCssStyle( cssStyle, emSize, static_cast(self->paragraphAlignment), self->viewportWidth); @@ -581,7 +587,14 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* self->updateEffectiveInlineStyle(); if (strcmp(name, "li") == 0) { - self->currentTextBlock->addWord("\xe2\x80\xa2", EpdFontFamily::REGULAR); + char marker[12]; + if (!self->listStack.empty() && self->listStack.back().isOrdered) { + self->listStack.back().counter += 1; + snprintf(marker, sizeof(marker), "%d.", self->listStack.back().counter); + } else { + strncpy(marker, "\xe2\x80\xa2", sizeof(marker)); + } + self->currentTextBlock->addWord(marker, EpdFontFamily::REGULAR); } } } else if (matches(name, UNDERLINE_TAGS, NUM_UNDERLINE_TAGS)) { @@ -893,6 +906,11 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n self->depth -= 1; + // Pop list entries whose ul/ol is now out of scope + while (!self->listStack.empty() && self->listStack.back().depth >= self->depth) { + self->listStack.pop_back(); + } + // Closing a footnote link — create entry from collected text and href if (self->insideFootnoteLink && self->depth == self->footnoteLinkDepth) { if (self->currentFootnoteLinkText[0] != '\0' && self->currentFootnoteLinkHref[0] != '\0') { diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index 1cc0ea39..dec13ca0 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -70,6 +70,14 @@ class ChapterHtmlSlimParser { int tableRowIndex = 0; int tableColIndex = 0; + // List nesting tracking for ul/ol markers + struct ListEntry { + int depth; + bool isOrdered; + int counter; + }; + std::vector listStack; + // Anchor-to-page mapping: tracks which page each HTML id attribute lands on int completedPageCount = 0; std::vector> anchorData;