#include "TextBlock.h" #include #include void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int x, const int y) const { // Validate iterator bounds before rendering if (words.size() != wordXpos.size() || words.size() != wordStyles.size()) { Serial.printf("[%lu] [TXB] Render skipped: size mismatch (words=%u, xpos=%u, styles=%u)\n", millis(), (uint32_t)words.size(), (uint32_t)wordXpos.size(), (uint32_t)wordStyles.size()); return; } auto wordIt = words.begin(); auto wordStylesIt = wordStyles.begin(); auto wordXposIt = wordXpos.begin(); for (size_t i = 0; i < words.size(); i++) { const int wordX = *wordXposIt + x; const EpdFontFamily::Style currentStyle = *wordStylesIt; renderer.drawText(fontId, wordX, y, wordIt->c_str(), true, currentStyle); if ((currentStyle & EpdFontFamily::UNDERLINE) != 0) { const std::string& w = *wordIt; const int fullWordWidth = renderer.getTextWidth(fontId, w.c_str(), currentStyle); // y is the top of the text line; add ascender to reach baseline, then offset 2px below const int underlineY = y + renderer.getFontAscenderSize(fontId) + 2; int startX = wordX; int underlineWidth = fullWordWidth; // if word starts with em-space ("\xe2\x80\x83"), account for the additional indent before drawing the line if (w.size() >= 3 && static_cast(w[0]) == 0xE2 && static_cast(w[1]) == 0x80 && static_cast(w[2]) == 0x83) { const char* visiblePtr = w.c_str() + 3; const int prefixWidth = renderer.getTextAdvanceX(fontId, std::string("\xe2\x80\x83").c_str()); const int visibleWidth = renderer.getTextWidth(fontId, visiblePtr, currentStyle); startX = wordX + prefixWidth; underlineWidth = visibleWidth; } renderer.drawLine(startX, underlineY, startX + underlineWidth, underlineY, true); } std::advance(wordIt, 1); std::advance(wordStylesIt, 1); std::advance(wordXposIt, 1); } } bool TextBlock::serialize(FsFile& file) const { if (words.size() != wordXpos.size() || words.size() != wordStyles.size()) { Serial.printf("[%lu] [TXB] Serialization failed: size mismatch (words=%u, xpos=%u, styles=%u)\n", millis(), words.size(), wordXpos.size(), wordStyles.size()); return false; } // Word data serialization::writePod(file, static_cast(words.size())); for (const auto& w : words) serialization::writeString(file, w); for (auto x : wordXpos) serialization::writePod(file, x); for (auto s : wordStyles) serialization::writePod(file, s); // Style (alignment + margins/padding/indent) serialization::writePod(file, blockStyle.alignment); serialization::writePod(file, blockStyle.textAlignDefined); serialization::writePod(file, blockStyle.marginTop); serialization::writePod(file, blockStyle.marginBottom); serialization::writePod(file, blockStyle.marginLeft); serialization::writePod(file, blockStyle.marginRight); serialization::writePod(file, blockStyle.paddingTop); serialization::writePod(file, blockStyle.paddingBottom); serialization::writePod(file, blockStyle.paddingLeft); serialization::writePod(file, blockStyle.paddingRight); serialization::writePod(file, blockStyle.textIndent); serialization::writePod(file, blockStyle.textIndentDefined); return true; } std::unique_ptr TextBlock::deserialize(FsFile& file) { uint16_t wc; std::list words; std::list wordXpos; std::list wordStyles; BlockStyle blockStyle; // Word count serialization::readPod(file, wc); // Sanity check: prevent allocation of unreasonably large lists (max 10000 words per block) if (wc > 10000) { Serial.printf("[%lu] [TXB] Deserialization failed: word count %u exceeds maximum\n", millis(), wc); return nullptr; } // Word data words.resize(wc); wordXpos.resize(wc); wordStyles.resize(wc); for (auto& w : words) serialization::readString(file, w); for (auto& x : wordXpos) serialization::readPod(file, x); for (auto& s : wordStyles) serialization::readPod(file, s); // Style (alignment + margins/padding/indent) serialization::readPod(file, blockStyle.alignment); serialization::readPod(file, blockStyle.textAlignDefined); serialization::readPod(file, blockStyle.marginTop); serialization::readPod(file, blockStyle.marginBottom); serialization::readPod(file, blockStyle.marginLeft); serialization::readPod(file, blockStyle.marginRight); serialization::readPod(file, blockStyle.paddingTop); serialization::readPod(file, blockStyle.paddingBottom); serialization::readPod(file, blockStyle.paddingLeft); serialization::readPod(file, blockStyle.paddingRight); serialization::readPod(file, blockStyle.textIndent); serialization::readPod(file, blockStyle.textIndentDefined); return std::unique_ptr( new TextBlock(std::move(words), std::move(wordXpos), std::move(wordStyles), blockStyle)); }