fix: Restore first-line paragraph indentation (#2320)
This commit is contained in:
@@ -261,10 +261,18 @@ void ParsedText::addWord(std::string word, const EpdFontFamily::Style fontStyle,
|
||||
}
|
||||
}
|
||||
|
||||
int ParsedText::resolveFirstLineIndent(const bool isFirstLine) const {
|
||||
if (isFirstLine && blockStyle.textIndentDefined && (blockStyle.textIndent < 0 || !extraParagraphSpacing) &&
|
||||
isNaturalAlign) {
|
||||
return blockStyle.textIndent;
|
||||
int ParsedText::resolveFirstLineIndent(const bool isFirstLine, const GfxRenderer& renderer, const int fontId) const {
|
||||
if (!isFirstLine || !isNaturalAlign) {
|
||||
return 0;
|
||||
}
|
||||
if (blockStyle.textIndentDefined) {
|
||||
if (blockStyle.textIndent < 0 || !extraParagraphSpacing) {
|
||||
return blockStyle.textIndent;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (!extraParagraphSpacing) {
|
||||
return renderer.getSpaceWidth(fontId, EpdFontFamily::REGULAR) * 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -293,9 +301,6 @@ void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fo
|
||||
blockStyle.alignment == CssTextAlign::Justify ||
|
||||
(blockStyle.isRtl ? blockStyle.alignment == CssTextAlign::Right : blockStyle.alignment == CssTextAlign::Left);
|
||||
|
||||
// Apply fixed transforms before any per-line layout work.
|
||||
applyParagraphIndent();
|
||||
|
||||
// Ensure SD card font glyph metrics are loaded before measuring word widths.
|
||||
// For flash-based fonts isSdCardFont() returns false and this block is skipped
|
||||
// entirely — no heap allocation. For SD card fonts this reads glyph metadata
|
||||
@@ -356,7 +361,7 @@ std::vector<size_t> ParsedText::computeLineBreaks(const GfxRenderer& renderer, c
|
||||
return {};
|
||||
}
|
||||
|
||||
const int firstLineIndent = resolveFirstLineIndent(true);
|
||||
const int firstLineIndent = resolveFirstLineIndent(true, renderer, fontId);
|
||||
|
||||
// Ensure any word that would overflow even as the first entry on a line is split using fallback hyphenation.
|
||||
for (size_t i = 0; i < wordWidths.size(); ++i) {
|
||||
@@ -462,25 +467,11 @@ std::vector<size_t> ParsedText::computeLineBreaks(const GfxRenderer& renderer, c
|
||||
return lineBreakIndices;
|
||||
}
|
||||
|
||||
void ParsedText::applyParagraphIndent() {
|
||||
if (extraParagraphSpacing || words.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (blockStyle.textIndentDefined) {
|
||||
// CSS text-indent is explicitly set (even if 0) - don't use fallback EmSpace
|
||||
// The actual indent positioning is handled in extractLine()
|
||||
} else if (isNaturalAlign) {
|
||||
// No CSS text-indent defined - use EmSpace fallback for visual indent
|
||||
words.front().insert(0, "\xe2\x80\x83");
|
||||
}
|
||||
}
|
||||
|
||||
// Builds break indices while opportunistically splitting the word that would overflow the current line.
|
||||
std::vector<size_t> ParsedText::computeHyphenatedLineBreaks(const GfxRenderer& renderer, const int fontId,
|
||||
const int pageWidth, std::vector<uint16_t>& wordWidths,
|
||||
std::vector<bool>& continuesVec) {
|
||||
const int firstLineIndent = resolveFirstLineIndent(true);
|
||||
const int firstLineIndent = resolveFirstLineIndent(true, renderer, fontId);
|
||||
|
||||
std::vector<size_t> lineBreakIndices;
|
||||
size_t currentIndex = 0;
|
||||
@@ -643,7 +634,7 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const
|
||||
const size_t lastBreakAt = breakIndex > 0 ? lineBreakIndices[breakIndex - 1] : 0;
|
||||
const size_t lineWordCount = lineBreak - lastBreakAt;
|
||||
|
||||
const int firstLineIndent = resolveFirstLineIndent(breakIndex == 0);
|
||||
const int firstLineIndent = resolveFirstLineIndent(breakIndex == 0, renderer, fontId);
|
||||
|
||||
// Build line data by moving from the original vectors using index range
|
||||
std::vector<std::string> lineWords;
|
||||
|
||||
@@ -30,8 +30,7 @@ class ParsedText {
|
||||
std::vector<bool> reorderedFocusSuffixScratch;
|
||||
std::vector<uint16_t> visualOrderScratch;
|
||||
|
||||
void applyParagraphIndent();
|
||||
int resolveFirstLineIndent(bool isFirstLine) const;
|
||||
int resolveFirstLineIndent(bool isFirstLine, const GfxRenderer& renderer, int fontId) const;
|
||||
std::vector<size_t> computeLineBreaks(const GfxRenderer& renderer, int fontId, int pageWidth,
|
||||
std::vector<uint16_t>& wordWidths, std::vector<bool>& continuesVec);
|
||||
std::vector<size_t> computeHyphenatedLineBreaks(const GfxRenderer& renderer, int fontId, int pageWidth,
|
||||
|
||||
@@ -115,7 +115,7 @@ struct BlockStyle {
|
||||
blockStyle.paddingRight = std::min(cssStyle.paddingRight.toPixelsInt16(emSize, vw), maxHorizontalInsetPx);
|
||||
|
||||
// For textIndent: if it's a percentage we can't resolve (no viewport width),
|
||||
// leave textIndentDefined=false so the EmSpace fallback in applyParagraphIndent() is used
|
||||
// leave textIndentDefined=false so the space-width fallback in resolveFirstLineIndent() is used
|
||||
if (cssStyle.hasTextIndent() && cssStyle.textIndent.isResolvable(vw)) {
|
||||
blockStyle.textIndent = cssStyle.textIndent.toPixelsInt16(emSize, vw);
|
||||
blockStyle.textIndentDefined = true;
|
||||
|
||||
@@ -61,31 +61,14 @@ void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int
|
||||
|
||||
if (!scanning && (currentStyle & EpdFontFamily::UNDERLINE) != 0) {
|
||||
const std::string& w = words[i];
|
||||
const int fullWordWidth = renderer.getTextWidth(fontId, w.c_str(), currentStyle, baseDir);
|
||||
// y is the top of the text line; add ascender to reach baseline, then offset 2px below
|
||||
int underlineWidth = renderer.getTextWidth(fontId, w.c_str(), currentStyle, baseDir);
|
||||
const int underlineY = wordY + ascender + 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<uint8_t>(w[0]) == 0xE2 && static_cast<uint8_t>(w[1]) == 0x80 &&
|
||||
static_cast<uint8_t>(w[2]) == 0x83) {
|
||||
const char* visiblePtr = w.c_str() + 3;
|
||||
const int prefixWidth = renderer.getTextAdvanceX(fontId, "\xe2\x80\x83", currentStyle);
|
||||
const int visibleWidth = renderer.getTextWidth(fontId, visiblePtr, currentStyle, baseDir);
|
||||
startX = wordX + prefixWidth;
|
||||
underlineWidth = visibleWidth;
|
||||
}
|
||||
|
||||
// SUP/SUB words are rendered at 50% glyph scale (see the baseline comment
|
||||
// above and drawText), but getTextWidth reports the full-size width, so the
|
||||
// underline would be drawn ~2x too long. Halve it to match the scaled glyphs.
|
||||
if ((currentStyle & (EpdFontFamily::SUP | EpdFontFamily::SUB)) != 0) {
|
||||
underlineWidth = (underlineWidth + 1) / 2;
|
||||
}
|
||||
|
||||
renderer.drawLine(startX, underlineY, startX + underlineWidth, underlineY, true);
|
||||
renderer.drawLine(wordX, underlineY, wordX + underlineWidth, underlineY, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user