#pragma once #include #include #include #include #include #include #include "Epub/FootnoteEntry.h" #include "Epub/ParsedText.h" #include "Epub/blocks/ImageBlock.h" #include "Epub/blocks/TextBlock.h" #include "Epub/css/CssParser.h" #include "Epub/css/CssStyle.h" class Page; class GfxRenderer; class Epub; #define MAX_WORD_SIZE 200 class ChapterHtmlSlimParser { std::shared_ptr epub; const std::string& filepath; GfxRenderer& renderer; std::function, uint16_t, uint16_t)> completePageFn; std::function popupFn; // Popup callback int depth = 0; int skipUntilDepth = INT_MAX; int boldUntilDepth = INT_MAX; int italicUntilDepth = INT_MAX; int underlineUntilDepth = INT_MAX; // buffer for building up words from characters, will auto break if longer than this // leave one char at end for null pointer char partWordBuffer[MAX_WORD_SIZE + 1] = {}; int partWordBufferIndex = 0; bool nextWordContinues = false; // true when next flushed word attaches to previous (inline element boundary) std::unique_ptr currentTextBlock = nullptr; std::unique_ptr currentPage = nullptr; int16_t currentPageNextY = 0; int fontId; float lineCompression; bool extraParagraphSpacing; uint8_t paragraphAlignment; uint16_t viewportWidth; uint16_t viewportHeight; bool hyphenationEnabled; bool focusReadingEnabled; const CssParser* cssParser; bool embeddedStyle; uint8_t imageRendering; std::string contentBase; std::string imageBasePath; int imageCounter = 0; // Style tracking (replaces depth-based approach) struct StyleStackEntry { int depth = 0; bool hasBold = false, bold = false; bool hasItalic = false, italic = false; bool hasUnderline = false, underline = false; bool hasDirection = false; CssTextDirection direction = CssTextDirection::Ltr; bool hasSup = false, sup = false; bool hasSub = false, sub = false; }; std::vector inlineStyleStack; std::vector blockStyleStack; // accumulated block styles from open ancestor elements CssStyle currentCssStyle; bool effectiveBold = false; bool effectiveItalic = false; bool effectiveUnderline = false; bool effectiveDirectionDefined = false; CssTextDirection effectiveDirection = CssTextDirection::Ltr; bool effectiveSup = false; bool effectiveSub = false; int tableDepth = 0; int tableRowIndex = 0; int tableColIndex = 0; // Anchor-to-page mapping: tracks which page each HTML id attribute lands on int completedPageCount = 0; std::vector> anchorData; std::string pendingAnchorId; // deferred until after previous text block is flushed std::vector tocAnchors; // the list of anchors that are TOC chapter boundaries uint16_t xpathParagraphIndex = 0; uint16_t xpathListItemIndex = 0; // Footnote link tracking bool insideFootnoteLink = false; int footnoteLinkDepth = -1; FootnoteEntry currentFootnote = {}; int currentFootnoteLinkTextLen = 0; std::vector> pendingFootnotes; // int wordsExtractedInBlock = 0; void updateEffectiveInlineStyle(); void startNewTextBlock(const BlockStyle& blockStyle); void flushPendingAnchor(); void flushPartWordBuffer(); void makePages(); static void applyDirectionToEntry(StyleStackEntry& entry, const CssStyle& css); void emitHorizontalRule(const BlockStyle& blockStyle); // XML callbacks static void XMLCALL startElement(void* userData, const XML_Char* name, const XML_Char** atts); static void XMLCALL characterData(void* userData, const XML_Char* s, int len); static void XMLCALL defaultHandlerExpand(void* userData, const XML_Char* s, int len); static void XMLCALL endElement(void* userData, const XML_Char* name); public: explicit ChapterHtmlSlimParser(std::shared_ptr epub, const std::string& filepath, GfxRenderer& renderer, const int fontId, const float lineCompression, const bool extraParagraphSpacing, const uint8_t paragraphAlignment, const uint16_t viewportWidth, const uint16_t viewportHeight, const bool hyphenationEnabled, const bool focusReadingEnabled, const std::function, uint16_t, uint16_t)>& completePageFn, const bool embeddedStyle, const std::string& contentBase, const std::string& imageBasePath, const uint8_t imageRendering = 0, std::vector tocAnchors = {}, const std::function& popupFn = nullptr, const CssParser* cssParser = nullptr) : epub(epub), filepath(filepath), renderer(renderer), fontId(fontId), lineCompression(lineCompression), extraParagraphSpacing(extraParagraphSpacing), paragraphAlignment(paragraphAlignment), viewportWidth(viewportWidth), viewportHeight(viewportHeight), hyphenationEnabled(hyphenationEnabled), focusReadingEnabled(focusReadingEnabled), completePageFn(completePageFn), popupFn(popupFn), cssParser(cssParser), embeddedStyle(embeddedStyle), imageRendering(imageRendering), contentBase(contentBase), imageBasePath(imageBasePath), tocAnchors(std::move(tocAnchors)) {} ~ChapterHtmlSlimParser() = default; bool parseAndBuildPages(); void addLineToPage(std::shared_ptr line); const std::vector>& getAnchors() const { return anchorData; } };