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 {
|
int ParsedText::resolveFirstLineIndent(const bool isFirstLine, const GfxRenderer& renderer, const int fontId) const {
|
||||||
if (isFirstLine && blockStyle.textIndentDefined && (blockStyle.textIndent < 0 || !extraParagraphSpacing) &&
|
if (!isFirstLine || !isNaturalAlign) {
|
||||||
isNaturalAlign) {
|
return 0;
|
||||||
return blockStyle.textIndent;
|
}
|
||||||
|
if (blockStyle.textIndentDefined) {
|
||||||
|
if (blockStyle.textIndent < 0 || !extraParagraphSpacing) {
|
||||||
|
return blockStyle.textIndent;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!extraParagraphSpacing) {
|
||||||
|
return renderer.getSpaceWidth(fontId, EpdFontFamily::REGULAR) * 3;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -293,9 +301,6 @@ void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fo
|
|||||||
blockStyle.alignment == CssTextAlign::Justify ||
|
blockStyle.alignment == CssTextAlign::Justify ||
|
||||||
(blockStyle.isRtl ? blockStyle.alignment == CssTextAlign::Right : blockStyle.alignment == CssTextAlign::Left);
|
(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.
|
// Ensure SD card font glyph metrics are loaded before measuring word widths.
|
||||||
// For flash-based fonts isSdCardFont() returns false and this block is skipped
|
// For flash-based fonts isSdCardFont() returns false and this block is skipped
|
||||||
// entirely — no heap allocation. For SD card fonts this reads glyph metadata
|
// 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 {};
|
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.
|
// 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) {
|
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;
|
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.
|
// 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,
|
std::vector<size_t> ParsedText::computeHyphenatedLineBreaks(const GfxRenderer& renderer, const int fontId,
|
||||||
const int pageWidth, std::vector<uint16_t>& wordWidths,
|
const int pageWidth, std::vector<uint16_t>& wordWidths,
|
||||||
std::vector<bool>& continuesVec) {
|
std::vector<bool>& continuesVec) {
|
||||||
const int firstLineIndent = resolveFirstLineIndent(true);
|
const int firstLineIndent = resolveFirstLineIndent(true, renderer, fontId);
|
||||||
|
|
||||||
std::vector<size_t> lineBreakIndices;
|
std::vector<size_t> lineBreakIndices;
|
||||||
size_t currentIndex = 0;
|
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 lastBreakAt = breakIndex > 0 ? lineBreakIndices[breakIndex - 1] : 0;
|
||||||
const size_t lineWordCount = lineBreak - lastBreakAt;
|
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
|
// Build line data by moving from the original vectors using index range
|
||||||
std::vector<std::string> lineWords;
|
std::vector<std::string> lineWords;
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ class ParsedText {
|
|||||||
std::vector<bool> reorderedFocusSuffixScratch;
|
std::vector<bool> reorderedFocusSuffixScratch;
|
||||||
std::vector<uint16_t> visualOrderScratch;
|
std::vector<uint16_t> visualOrderScratch;
|
||||||
|
|
||||||
void applyParagraphIndent();
|
int resolveFirstLineIndent(bool isFirstLine, const GfxRenderer& renderer, int fontId) const;
|
||||||
int resolveFirstLineIndent(bool isFirstLine) const;
|
|
||||||
std::vector<size_t> computeLineBreaks(const GfxRenderer& renderer, int fontId, int pageWidth,
|
std::vector<size_t> computeLineBreaks(const GfxRenderer& renderer, int fontId, int pageWidth,
|
||||||
std::vector<uint16_t>& wordWidths, std::vector<bool>& continuesVec);
|
std::vector<uint16_t>& wordWidths, std::vector<bool>& continuesVec);
|
||||||
std::vector<size_t> computeHyphenatedLineBreaks(const GfxRenderer& renderer, int fontId, int pageWidth,
|
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);
|
blockStyle.paddingRight = std::min(cssStyle.paddingRight.toPixelsInt16(emSize, vw), maxHorizontalInsetPx);
|
||||||
|
|
||||||
// For textIndent: if it's a percentage we can't resolve (no viewport width),
|
// 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)) {
|
if (cssStyle.hasTextIndent() && cssStyle.textIndent.isResolvable(vw)) {
|
||||||
blockStyle.textIndent = cssStyle.textIndent.toPixelsInt16(emSize, vw);
|
blockStyle.textIndent = cssStyle.textIndent.toPixelsInt16(emSize, vw);
|
||||||
blockStyle.textIndentDefined = true;
|
blockStyle.textIndentDefined = true;
|
||||||
|
|||||||
@@ -61,31 +61,14 @@ void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int
|
|||||||
|
|
||||||
if (!scanning && (currentStyle & EpdFontFamily::UNDERLINE) != 0) {
|
if (!scanning && (currentStyle & EpdFontFamily::UNDERLINE) != 0) {
|
||||||
const std::string& w = words[i];
|
const std::string& w = words[i];
|
||||||
const int fullWordWidth = renderer.getTextWidth(fontId, w.c_str(), currentStyle, baseDir);
|
int underlineWidth = 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
|
|
||||||
const int underlineY = wordY + ascender + 2;
|
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) {
|
if ((currentStyle & (EpdFontFamily::SUP | EpdFontFamily::SUB)) != 0) {
|
||||||
underlineWidth = (underlineWidth + 1) / 2;
|
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