From d1e4650e19638fb74b745df9e09b90f3f29271c5 Mon Sep 17 00:00:00 2001 From: SurprisedDuck Date: Sun, 21 Jun 2026 15:12:14 +0200 Subject: [PATCH] fix: don't justify-stretch a leading no-break space (#2185) (#2298) --- lib/Epub/Epub/ParsedText.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/Epub/Epub/ParsedText.cpp b/lib/Epub/Epub/ParsedText.cpp index e57d1743..ebe758db 100644 --- a/lib/Epub/Epub/ParsedText.cpp +++ b/lib/Epub/Epub/ParsedText.cpp @@ -1006,7 +1006,10 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const int advance = renderer.getKerning(fontId, lastCodepoint(reorderedWordsScratch[wordIdx]), firstCodepoint(reorderedWordsScratch[wordIdx + 1]), reorderedStylesScratch[wordIdx]); - if (reorderedWordsScratch[wordIdx] == " " && reorderedContinuesScratch[wordIdx] && + // wordIdx > 0 mirrors the gap accounting above (which skips index 0): a leading + // no-break space must not receive justifyExtra, or the line over-stretches by one + // gap and the last word is pushed past the right margin (issue #2185). + if (wordIdx > 0 && reorderedWordsScratch[wordIdx] == " " && reorderedContinuesScratch[wordIdx] && effectiveAlignment == CssTextAlign::Justify && !isLastLine) { advance += reorderedJustifyExtra; } @@ -1048,7 +1051,8 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const // Cross-boundary kerning for continuation words int advance = renderer.getKerning(fontId, lastCodepoint(lineWords[wordIdx]), firstCodepoint(lineWords[wordIdx + 1]), lineWordStyles[wordIdx]); - if (lineWords[wordIdx] == " " && continuesVec[lastBreakAt + wordIdx] && + // wordIdx > 0: see the LTR branch — a leading no-break space is not a justifiable gap. + if (wordIdx > 0 && lineWords[wordIdx] == " " && continuesVec[lastBreakAt + wordIdx] && effectiveAlignment == CssTextAlign::Justify && !isLastLine) { advance += justifyExtra; } @@ -1086,7 +1090,10 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const int advance = wordWidths[lastBreakAt + wordIdx]; advance += renderer.getKerning(fontId, lastCodepoint(lineWords[wordIdx]), firstCodepoint(lineWords[wordIdx + 1]), lineWordStyles[wordIdx]); - if (lineWords[wordIdx] == " " && continuesVec[lastBreakAt + wordIdx] && + // wordIdx > 0 mirrors the gap accounting above (which skips index 0): a leading + // no-break space must not receive justifyExtra, or the line over-stretches by one + // gap and the last word is pushed past the right margin (issue #2185). + if (wordIdx > 0 && lineWords[wordIdx] == " " && continuesVec[lastBreakAt + wordIdx] && effectiveAlignment == CssTextAlign::Justify && !isLastLine) { advance += justifyExtra; }