mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 14:47:37 +03:00
Fix footnote placement for paragraphs spanning multiple pages
This commit is contained in:
parent
0e46822a8b
commit
c51d617368
@ -49,16 +49,24 @@ uint16_t measureWordWidth(const GfxRenderer& renderer, const int fontId, const s
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ParsedText::addWord(std::string word, const EpdFontFamily::Style fontStyle) {
|
void ParsedText::addWord(std::string word, const EpdFontFamily::Style fontStyle,
|
||||||
|
std::unique_ptr<FootnoteEntry> footnote) {
|
||||||
if (word.empty()) return;
|
if (word.empty()) return;
|
||||||
|
|
||||||
words.push_back(std::move(word));
|
words.push_back(std::move(word));
|
||||||
wordStyles.push_back(fontStyle);
|
wordStyles.push_back(fontStyle);
|
||||||
|
if (footnote) {
|
||||||
|
wordHasFootnote.push_back(1);
|
||||||
|
footnoteQueue.push_back(*footnote);
|
||||||
|
} else {
|
||||||
|
wordHasFootnote.push_back(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consumes data to minimize memory usage
|
// Consumes data to minimize memory usage
|
||||||
void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fontId, const uint16_t viewportWidth,
|
void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fontId, const uint16_t viewportWidth,
|
||||||
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
|
const std::function<void(std::shared_ptr<TextBlock>,
|
||||||
|
const std::vector<FootnoteEntry>&)>& processLine,
|
||||||
const bool includeLastLine) {
|
const bool includeLastLine) {
|
||||||
if (words.empty()) {
|
if (words.empty()) {
|
||||||
return;
|
return;
|
||||||
@ -255,8 +263,8 @@ std::vector<size_t> ParsedText::computeHyphenatedLineBreaks(const GfxRenderer& r
|
|||||||
return lineBreakIndices;
|
return lineBreakIndices;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Splits words[wordIndex] into prefix (adding a hyphen only when needed) and remainder when a legal breakpoint fits the
|
// Splits words[wordIndex] into prefix (adding a hyphen only when needed)
|
||||||
// available width.
|
// and remainder when a legal breakpoint fits the available width.
|
||||||
bool ParsedText::hyphenateWordAtIndex(const size_t wordIndex, const int availableWidth, const GfxRenderer& renderer,
|
bool ParsedText::hyphenateWordAtIndex(const size_t wordIndex, const int availableWidth, const GfxRenderer& renderer,
|
||||||
const int fontId, std::vector<uint16_t>& wordWidths,
|
const int fontId, std::vector<uint16_t>& wordWidths,
|
||||||
const bool allowFallbackBreaks) {
|
const bool allowFallbackBreaks) {
|
||||||
@ -320,6 +328,13 @@ bool ParsedText::hyphenateWordAtIndex(const size_t wordIndex, const int availabl
|
|||||||
words.insert(insertWordIt, remainder);
|
words.insert(insertWordIt, remainder);
|
||||||
wordStyles.insert(insertStyleIt, style);
|
wordStyles.insert(insertStyleIt, style);
|
||||||
|
|
||||||
|
// Split wordHasFootnote as well. The footnote (if any) is associated with the remainder word.
|
||||||
|
auto wordHasFootnoteIt = wordHasFootnote.begin();
|
||||||
|
std::advance(wordHasFootnoteIt, wordIndex);
|
||||||
|
uint8_t hasFootnote = *wordHasFootnoteIt;
|
||||||
|
*wordHasFootnoteIt = 0; // First part doesn't have it anymore
|
||||||
|
wordHasFootnote.insert(std::next(wordHasFootnoteIt), hasFootnote);
|
||||||
|
|
||||||
// Update cached widths to reflect the new prefix/remainder pairing.
|
// Update cached widths to reflect the new prefix/remainder pairing.
|
||||||
wordWidths[wordIndex] = static_cast<uint16_t>(chosenWidth);
|
wordWidths[wordIndex] = static_cast<uint16_t>(chosenWidth);
|
||||||
const uint16_t remainderWidth = measureWordWidth(renderer, fontId, remainder, style);
|
const uint16_t remainderWidth = measureWordWidth(renderer, fontId, remainder, style);
|
||||||
@ -329,7 +344,8 @@ bool ParsedText::hyphenateWordAtIndex(const size_t wordIndex, const int availabl
|
|||||||
|
|
||||||
void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const int spaceWidth,
|
void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const int spaceWidth,
|
||||||
const std::vector<uint16_t>& wordWidths, const std::vector<size_t>& lineBreakIndices,
|
const std::vector<uint16_t>& wordWidths, const std::vector<size_t>& lineBreakIndices,
|
||||||
const std::function<void(std::shared_ptr<TextBlock>)>& processLine) {
|
const std::function<void(std::shared_ptr<TextBlock>, const std::vector<FootnoteEntry>&)>&
|
||||||
|
processLine) {
|
||||||
const size_t lineBreak = lineBreakIndices[breakIndex];
|
const size_t lineBreak = lineBreakIndices[breakIndex];
|
||||||
const size_t lastBreakAt = breakIndex > 0 ? lineBreakIndices[breakIndex - 1] : 0;
|
const size_t lastBreakAt = breakIndex > 0 ? lineBreakIndices[breakIndex - 1] : 0;
|
||||||
const size_t lineWordCount = lineBreak - lastBreakAt;
|
const size_t lineWordCount = lineBreak - lastBreakAt;
|
||||||
@ -372,17 +388,35 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const
|
|||||||
std::advance(wordEndIt, lineWordCount);
|
std::advance(wordEndIt, lineWordCount);
|
||||||
std::advance(wordStyleEndIt, lineWordCount);
|
std::advance(wordStyleEndIt, lineWordCount);
|
||||||
|
|
||||||
// *** CRITICAL STEP: CONSUME DATA USING SPLICE ***
|
|
||||||
std::list<std::string> lineWords;
|
std::list<std::string> lineWords;
|
||||||
lineWords.splice(lineWords.begin(), words, words.begin(), wordEndIt);
|
lineWords.splice(lineWords.begin(), words, words.begin(), wordEndIt);
|
||||||
std::list<EpdFontFamily::Style> lineWordStyles;
|
std::list<EpdFontFamily::Style> lineWordStyles;
|
||||||
lineWordStyles.splice(lineWordStyles.begin(), wordStyles, wordStyles.begin(), wordStyleEndIt);
|
lineWordStyles.splice(lineWordStyles.begin(), wordStyles, wordStyles.begin(), wordStyleEndIt);
|
||||||
|
|
||||||
|
// Extract footnote flags from deque
|
||||||
|
std::vector<FootnoteEntry> lineFootnotes;
|
||||||
|
for (size_t i = 0; i < lineWordCount; i++) {
|
||||||
|
if (!wordHasFootnote.empty()) {
|
||||||
|
uint8_t hasFn = wordHasFootnote.front();
|
||||||
|
wordHasFootnote.pop_front();
|
||||||
|
|
||||||
|
if (hasFn) {
|
||||||
|
if (footnoteQueue.empty()) {
|
||||||
|
Serial.printf("[%lu] [ERROR] Footnote flag set but queue empty! Flags/queue out of sync.\n", millis());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lineFootnotes.push_back(footnoteQueue.front());
|
||||||
|
footnoteQueue.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto& word : lineWords) {
|
for (auto& word : lineWords) {
|
||||||
if (containsSoftHyphen(word)) {
|
if (containsSoftHyphen(word)) {
|
||||||
stripSoftHyphensInPlace(word);
|
stripSoftHyphensInPlace(word);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processLine(std::make_shared<TextBlock>(std::move(lineWords), std::move(lineXPos), std::move(lineWordStyles), style));
|
processLine(std::make_shared<TextBlock>(std::move(lineWords), std::move(lineXPos), std::move(lineWordStyles), style),
|
||||||
|
lineFootnotes);
|
||||||
}
|
}
|
||||||
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
#include <EpdFontFamily.h>
|
#include <EpdFontFamily.h>
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "FootnoteEntry.h"
|
||||||
#include "blocks/TextBlock.h"
|
#include "blocks/TextBlock.h"
|
||||||
|
|
||||||
class GfxRenderer;
|
class GfxRenderer;
|
||||||
@ -15,6 +17,8 @@ class GfxRenderer;
|
|||||||
class ParsedText {
|
class ParsedText {
|
||||||
std::list<std::string> words;
|
std::list<std::string> words;
|
||||||
std::list<EpdFontFamily::Style> wordStyles;
|
std::list<EpdFontFamily::Style> wordStyles;
|
||||||
|
std::deque<uint8_t> wordHasFootnote;
|
||||||
|
std::deque<FootnoteEntry> footnoteQueue;
|
||||||
TextBlock::Style style;
|
TextBlock::Style style;
|
||||||
bool extraParagraphSpacing;
|
bool extraParagraphSpacing;
|
||||||
bool hyphenationEnabled;
|
bool hyphenationEnabled;
|
||||||
@ -28,7 +32,8 @@ class ParsedText {
|
|||||||
std::vector<uint16_t>& wordWidths, bool allowFallbackBreaks);
|
std::vector<uint16_t>& wordWidths, bool allowFallbackBreaks);
|
||||||
void extractLine(size_t breakIndex, int pageWidth, int spaceWidth, const std::vector<uint16_t>& wordWidths,
|
void extractLine(size_t breakIndex, int pageWidth, int spaceWidth, const std::vector<uint16_t>& wordWidths,
|
||||||
const std::vector<size_t>& lineBreakIndices,
|
const std::vector<size_t>& lineBreakIndices,
|
||||||
const std::function<void(std::shared_ptr<TextBlock>)>& processLine);
|
const std::function<void(std::shared_ptr<TextBlock>, const std::vector<FootnoteEntry>&)>&
|
||||||
|
processLine);
|
||||||
std::vector<uint16_t> calculateWordWidths(const GfxRenderer& renderer, int fontId);
|
std::vector<uint16_t> calculateWordWidths(const GfxRenderer& renderer, int fontId);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -37,12 +42,13 @@ class ParsedText {
|
|||||||
: style(style), extraParagraphSpacing(extraParagraphSpacing), hyphenationEnabled(hyphenationEnabled) {}
|
: style(style), extraParagraphSpacing(extraParagraphSpacing), hyphenationEnabled(hyphenationEnabled) {}
|
||||||
~ParsedText() = default;
|
~ParsedText() = default;
|
||||||
|
|
||||||
void addWord(std::string word, EpdFontFamily::Style fontStyle);
|
void addWord(std::string word, EpdFontFamily::Style fontStyle, std::unique_ptr<FootnoteEntry> footnote = nullptr);
|
||||||
void setStyle(const TextBlock::Style style) { this->style = style; }
|
void setStyle(const TextBlock::Style style) { this->style = style; }
|
||||||
TextBlock::Style getStyle() const { return style; }
|
TextBlock::Style getStyle() const { return style; }
|
||||||
size_t size() const { return words.size(); }
|
size_t size() const { return words.size(); }
|
||||||
bool isEmpty() const { return words.empty(); }
|
bool isEmpty() const { return words.empty(); }
|
||||||
void layoutAndExtractLines(const GfxRenderer& renderer, int fontId, uint16_t viewportWidth,
|
void layoutAndExtractLines(const GfxRenderer& renderer, int fontId, uint16_t viewportWidth,
|
||||||
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
|
const std::function<void(std::shared_ptr<TextBlock>, const std::vector<FootnoteEntry>&)>&
|
||||||
|
processLine,
|
||||||
bool includeLastLine = true);
|
bool includeLastLine = true);
|
||||||
};
|
};
|
||||||
@ -116,14 +116,14 @@ void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::Style style) {
|
|||||||
currentTextBlock.reset(new ParsedText(style, extraParagraphSpacing, hyphenationEnabled));
|
currentTextBlock.reset(new ParsedText(style, extraParagraphSpacing, hyphenationEnabled));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChapterHtmlSlimParser::addFootnoteToCurrentPage(const char* number, const char* href) {
|
std::unique_ptr<FootnoteEntry> ChapterHtmlSlimParser::createFootnoteEntry(const char* number, const char* href) {
|
||||||
if (currentPageFootnoteCount >= 16) return;
|
auto entry = std::unique_ptr<FootnoteEntry>(new FootnoteEntry());
|
||||||
|
|
||||||
Serial.printf("[%lu] [ADDFT] Adding footnote: num=%s, href=%s\n", millis(), number, href);
|
Serial.printf("[%lu] [ADDFT] Creating footnote: num=%s, href=%s\n", millis(), number, href);
|
||||||
|
|
||||||
// Copy number
|
// Copy number
|
||||||
strncpy(currentPageFootnotes[currentPageFootnoteCount].number, number, 2);
|
strncpy(entry->number, number, 2);
|
||||||
currentPageFootnotes[currentPageFootnoteCount].number[2] = '\0';
|
entry->number[2] = '\0';
|
||||||
|
|
||||||
// Check if this is an inline footnote reference
|
// Check if this is an inline footnote reference
|
||||||
const char* hashPos = strchr(href, '#');
|
const char* hashPos = strchr(href, '#');
|
||||||
@ -138,8 +138,8 @@ void ChapterHtmlSlimParser::addFootnoteToCurrentPage(const char* number, const c
|
|||||||
char rewrittenHref[64];
|
char rewrittenHref[64];
|
||||||
snprintf(rewrittenHref, sizeof(rewrittenHref), "inline_%s.html#%s", inlineId, inlineId);
|
snprintf(rewrittenHref, sizeof(rewrittenHref), "inline_%s.html#%s", inlineId, inlineId);
|
||||||
|
|
||||||
strncpy(currentPageFootnotes[currentPageFootnoteCount].href, rewrittenHref, 63);
|
strncpy(entry->href, rewrittenHref, 63);
|
||||||
currentPageFootnotes[currentPageFootnoteCount].href[63] = '\0';
|
entry->href[63] = '\0';
|
||||||
|
|
||||||
Serial.printf("[%lu] [ADDFT] Rewrote inline href to: %s\n", millis(), rewrittenHref);
|
Serial.printf("[%lu] [ADDFT] Rewrote inline href to: %s\n", millis(), rewrittenHref);
|
||||||
foundInline = true;
|
foundInline = true;
|
||||||
@ -154,8 +154,8 @@ void ChapterHtmlSlimParser::addFootnoteToCurrentPage(const char* number, const c
|
|||||||
char rewrittenHref[64];
|
char rewrittenHref[64];
|
||||||
snprintf(rewrittenHref, sizeof(rewrittenHref), "pnote_%s.html#%s", inlineId, inlineId);
|
snprintf(rewrittenHref, sizeof(rewrittenHref), "pnote_%s.html#%s", inlineId, inlineId);
|
||||||
|
|
||||||
strncpy(currentPageFootnotes[currentPageFootnoteCount].href, rewrittenHref, 63);
|
strncpy(entry->href, rewrittenHref, 63);
|
||||||
currentPageFootnotes[currentPageFootnoteCount].href[63] = '\0';
|
entry->href[63] = '\0';
|
||||||
|
|
||||||
Serial.printf("[%lu] [ADDFT] Rewrote paragraph note href to: %s\n", millis(), rewrittenHref);
|
Serial.printf("[%lu] [ADDFT] Rewrote paragraph note href to: %s\n", millis(), rewrittenHref);
|
||||||
foundInline = true;
|
foundInline = true;
|
||||||
@ -166,20 +166,17 @@ void ChapterHtmlSlimParser::addFootnoteToCurrentPage(const char* number, const c
|
|||||||
|
|
||||||
if (!foundInline) {
|
if (!foundInline) {
|
||||||
// Normal href, just copy it
|
// Normal href, just copy it
|
||||||
strncpy(currentPageFootnotes[currentPageFootnoteCount].href, href, 63);
|
strncpy(entry->href, href, 63);
|
||||||
currentPageFootnotes[currentPageFootnoteCount].href[63] = '\0';
|
entry->href[63] = '\0';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No anchor, just copy
|
// No anchor, just copy
|
||||||
strncpy(currentPageFootnotes[currentPageFootnoteCount].href, href, 63);
|
strncpy(entry->href, href, 63);
|
||||||
currentPageFootnotes[currentPageFootnoteCount].href[63] = '\0';
|
entry->href[63] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPageFootnoteCount++;
|
Serial.printf("[%lu] [ADDFT] Created as: num=%s, href=%s\n", millis(), entry->number, entry->href);
|
||||||
|
return entry;
|
||||||
Serial.printf("[%lu] [ADDFT] Stored as: num=%s, href=%s\n", millis(),
|
|
||||||
currentPageFootnotes[currentPageFootnoteCount - 1].number,
|
|
||||||
currentPageFootnotes[currentPageFootnoteCount - 1].href);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) {
|
void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) {
|
||||||
@ -593,7 +590,10 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
|
|||||||
Serial.printf("[%lu] [EHP] Text block too long, splitting into multiple pages\n", millis());
|
Serial.printf("[%lu] [EHP] Text block too long, splitting into multiple pages\n", millis());
|
||||||
self->currentTextBlock->layoutAndExtractLines(
|
self->currentTextBlock->layoutAndExtractLines(
|
||||||
self->renderer, self->fontId, self->viewportWidth,
|
self->renderer, self->fontId, self->viewportWidth,
|
||||||
[self](const std::shared_ptr<TextBlock>& textBlock) { self->addLineToPage(textBlock); }, false);
|
[self](const std::shared_ptr<TextBlock>& textBlock, const std::vector<FootnoteEntry>& footnotes) {
|
||||||
|
self->addLineToPage(textBlock, footnotes);
|
||||||
|
},
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -688,18 +688,17 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
|
|||||||
if (self->currentNoterefTextLen > 0) {
|
if (self->currentNoterefTextLen > 0) {
|
||||||
Serial.printf("[%lu] [NOTEREF] %s -> %s\n", millis(), self->currentNoterefText, self->currentNoterefHref);
|
Serial.printf("[%lu] [NOTEREF] %s -> %s\n", millis(), self->currentNoterefText, self->currentNoterefHref);
|
||||||
|
|
||||||
// Add footnote first (this does the rewriting)
|
// Create the footnote entry (this does the rewriting)
|
||||||
self->addFootnoteToCurrentPage(self->currentNoterefText, self->currentNoterefHref);
|
std::unique_ptr<FootnoteEntry> footnote =
|
||||||
|
self->createFootnoteEntry(self->currentNoterefText, self->currentNoterefHref);
|
||||||
|
|
||||||
// Then call callback with the REWRITTEN href from currentPageFootnotes
|
// Then call callback with the REWRITTEN href
|
||||||
if (self->noterefCallback && self->currentPageFootnoteCount > 0) {
|
if (self->noterefCallback && footnote) {
|
||||||
Noteref noteref;
|
Noteref noteref;
|
||||||
strncpy(noteref.number, self->currentNoterefText, 15);
|
strncpy(noteref.number, self->currentNoterefText, 15);
|
||||||
noteref.number[15] = '\0';
|
noteref.number[15] = '\0';
|
||||||
|
|
||||||
// Use the STORED href which has been rewritten
|
strncpy(noteref.href, footnote->href, 127);
|
||||||
FootnoteEntry* lastFootnote = &self->currentPageFootnotes[self->currentPageFootnoteCount - 1];
|
|
||||||
strncpy(noteref.href, lastFootnote->href, 127);
|
|
||||||
noteref.href[127] = '\0';
|
noteref.href[127] = '\0';
|
||||||
|
|
||||||
self->noterefCallback(noteref);
|
self->noterefCallback(noteref);
|
||||||
@ -712,9 +711,9 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
|
|||||||
char formattedNoteref[32];
|
char formattedNoteref[32];
|
||||||
snprintf(formattedNoteref, sizeof(formattedNoteref), "[%s]", self->currentNoterefText);
|
snprintf(formattedNoteref, sizeof(formattedNoteref), "[%s]", self->currentNoterefText);
|
||||||
|
|
||||||
// Add it as a word to the current text block
|
// Add it as a word to the current text block with the footnote attached
|
||||||
if (self->currentTextBlock) {
|
if (self->currentTextBlock) {
|
||||||
self->currentTextBlock->addWord(formattedNoteref, fontStyle);
|
self->currentTextBlock->addWord(formattedNoteref, fontStyle, std::move(footnote));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,7 +845,6 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
|||||||
partWordBufferIndex = 0;
|
partWordBufferIndex = 0;
|
||||||
insideNoteref = false;
|
insideNoteref = false;
|
||||||
insideAsideFootnote = false;
|
insideAsideFootnote = false;
|
||||||
currentPageFootnoteCount = 0;
|
|
||||||
isPass1CollectingAsides = false;
|
isPass1CollectingAsides = false;
|
||||||
|
|
||||||
supDepth = -1;
|
supDepth = -1;
|
||||||
@ -928,10 +926,6 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
|||||||
makePages();
|
makePages();
|
||||||
|
|
||||||
if (currentPage) {
|
if (currentPage) {
|
||||||
for (int i = 0; i < currentPageFootnoteCount; i++) {
|
|
||||||
currentPage->addFootnote(currentPageFootnotes[i].number, currentPageFootnotes[i].href);
|
|
||||||
}
|
|
||||||
currentPageFootnoteCount = 0;
|
|
||||||
completePageFn(std::move(currentPage));
|
completePageFn(std::move(currentPage));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -942,17 +936,10 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChapterHtmlSlimParser::addLineToPage(std::shared_ptr<TextBlock> line) {
|
void ChapterHtmlSlimParser::addLineToPage(std::shared_ptr<TextBlock> line, const std::vector<FootnoteEntry>& footnotes) {
|
||||||
const int lineHeight = renderer.getLineHeight(fontId) * lineCompression;
|
const int lineHeight = renderer.getLineHeight(fontId) * lineCompression;
|
||||||
|
|
||||||
if (currentPageNextY + lineHeight > viewportHeight) {
|
if (currentPageNextY + lineHeight > viewportHeight) {
|
||||||
if (currentPage) {
|
|
||||||
for (int i = 0; i < currentPageFootnoteCount; i++) {
|
|
||||||
currentPage->addFootnote(currentPageFootnotes[i].number, currentPageFootnotes[i].href);
|
|
||||||
}
|
|
||||||
currentPageFootnoteCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
completePageFn(std::move(currentPage));
|
completePageFn(std::move(currentPage));
|
||||||
currentPage.reset(new Page());
|
currentPage.reset(new Page());
|
||||||
currentPageNextY = 0;
|
currentPageNextY = 0;
|
||||||
@ -961,6 +948,11 @@ void ChapterHtmlSlimParser::addLineToPage(std::shared_ptr<TextBlock> line) {
|
|||||||
if (currentPage && currentPage->elements.size() < 24) { // Assuming generic capacity check or vector size
|
if (currentPage && currentPage->elements.size() < 24) { // Assuming generic capacity check or vector size
|
||||||
currentPage->elements.push_back(std::make_shared<PageLine>(line, 0, currentPageNextY));
|
currentPage->elements.push_back(std::make_shared<PageLine>(line, 0, currentPageNextY));
|
||||||
currentPageNextY += lineHeight;
|
currentPageNextY += lineHeight;
|
||||||
|
|
||||||
|
// Add footnotes for this line to the current page
|
||||||
|
for (const auto& fn : footnotes) {
|
||||||
|
currentPage->addFootnote(fn.number, fn.href);
|
||||||
|
}
|
||||||
} else if (currentPage) {
|
} else if (currentPage) {
|
||||||
Serial.printf("[%lu] [EHP] WARNING: Page element capacity reached, skipping element\n", millis());
|
Serial.printf("[%lu] [EHP] WARNING: Page element capacity reached, skipping element\n", millis());
|
||||||
}
|
}
|
||||||
@ -980,7 +972,9 @@ void ChapterHtmlSlimParser::makePages() {
|
|||||||
const int lineHeight = renderer.getLineHeight(fontId) * lineCompression;
|
const int lineHeight = renderer.getLineHeight(fontId) * lineCompression;
|
||||||
currentTextBlock->layoutAndExtractLines(
|
currentTextBlock->layoutAndExtractLines(
|
||||||
renderer, fontId, viewportWidth,
|
renderer, fontId, viewportWidth,
|
||||||
[this](const std::shared_ptr<TextBlock>& textBlock) { addLineToPage(textBlock); });
|
[this](const std::shared_ptr<TextBlock>& textBlock, const std::vector<FootnoteEntry>& footnotes) {
|
||||||
|
addLineToPage(textBlock, footnotes);
|
||||||
|
});
|
||||||
// Extra paragraph spacing if enabled
|
// Extra paragraph spacing if enabled
|
||||||
if (extraParagraphSpacing) {
|
if (extraParagraphSpacing) {
|
||||||
currentPageNextY += lineHeight / 2;
|
currentPageNextY += lineHeight / 2;
|
||||||
|
|||||||
@ -77,10 +77,6 @@ class ChapterHtmlSlimParser {
|
|||||||
int currentNoterefHrefLen = 0;
|
int currentNoterefHrefLen = 0;
|
||||||
std::function<void(Noteref&)> noterefCallback = nullptr;
|
std::function<void(Noteref&)> noterefCallback = nullptr;
|
||||||
|
|
||||||
// Footnote tracking for current page
|
|
||||||
FootnoteEntry currentPageFootnotes[16];
|
|
||||||
int currentPageFootnoteCount = 0;
|
|
||||||
|
|
||||||
// Inline footnotes (aside) tracking
|
// Inline footnotes (aside) tracking
|
||||||
bool insideAsideFootnote = false;
|
bool insideAsideFootnote = false;
|
||||||
int asideDepth = 0;
|
int asideDepth = 0;
|
||||||
@ -106,7 +102,7 @@ class ChapterHtmlSlimParser {
|
|||||||
int supDepth = -1;
|
int supDepth = -1;
|
||||||
int anchorDepth = -1;
|
int anchorDepth = -1;
|
||||||
|
|
||||||
void addFootnoteToCurrentPage(const char* number, const char* href);
|
std::unique_ptr<FootnoteEntry> createFootnoteEntry(const char* number, const char* href);
|
||||||
void startNewTextBlock(TextBlock::Style style);
|
void startNewTextBlock(TextBlock::Style style);
|
||||||
EpdFontFamily::Style getCurrentFontStyle() const;
|
EpdFontFamily::Style getCurrentFontStyle() const;
|
||||||
void flushPartWordBuffer();
|
void flushPartWordBuffer();
|
||||||
@ -161,7 +157,7 @@ class ChapterHtmlSlimParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool parseAndBuildPages();
|
bool parseAndBuildPages();
|
||||||
void addLineToPage(std::shared_ptr<TextBlock> line);
|
void addLineToPage(std::shared_ptr<TextBlock> line, const std::vector<FootnoteEntry>& footnotes);
|
||||||
|
|
||||||
void setNoterefCallback(const std::function<void(Noteref&)>& callback) { noterefCallback = callback; }
|
void setNoterefCallback(const std::function<void(Noteref&)>& callback) { noterefCallback = callback; }
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user