mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
SimpleShapedText: Fix line break behaviour with text trailing whitespaces
This fixes an issue with the text wrapping logic for text chunks ending in a whitespace. When trying to fit a text chunk, the logic works with two values: width with trailing whitespace, and width without trailing whitespace. When the trailingWhitespacesShouldFit option is false, the logic checks if "withoutTrailingWhitespace" can still fit inside the remaining width. Prior to this fix, it then decremented the remaining width with "withoutTrailingWhitespace", but it should have used "withTrailingWhitespace" for the decrement operation, always, regardless of the value of the withTrailingWhitespacesShouldFit option. This mistake only caused an observable issue when multiple fonts were used for the shaping operation, and a different font would be used immediately after a whitespace falling at the end of a line.
This commit is contained in:
parent
aee65c147e
commit
d794fac44c
1 changed files with 25 additions and 12 deletions
|
|
@ -148,6 +148,9 @@ public:
|
|||
SimpleShapedText (const String* data,
|
||||
const ShapedTextOptions& options);
|
||||
|
||||
/* The returned container associates line numbers with the range of glyphs (not input codepoints)
|
||||
that make up the line.
|
||||
*/
|
||||
const auto& getLineNumbers() const { return lineNumbers; }
|
||||
|
||||
const auto& getResolvedFonts() const { return resolvedFonts; }
|
||||
|
|
@ -615,6 +618,12 @@ struct ShapingParams
|
|||
Font resolvedFont;
|
||||
};
|
||||
|
||||
struct LineAdvance
|
||||
{
|
||||
float includingTrailingWhitespace;
|
||||
float maybeIgnoringWhitespace;
|
||||
};
|
||||
|
||||
struct ConsumableGlyphs
|
||||
{
|
||||
public:
|
||||
|
|
@ -683,12 +692,9 @@ public:
|
|||
/* If this function returns a value that also means that it's safe to break before the provided
|
||||
codepoint. Otherwise, we couldn't meaningfully calculate the requested value.
|
||||
*/
|
||||
std::optional<float> getAdvanceXUpToBreakPointIfSafe (int64 breakBefore,
|
||||
bool whitespaceShouldFitInLine) const
|
||||
std::optional<LineAdvance> getAdvanceXUpToBreakPointIfSafe (int64 breakBefore,
|
||||
bool whitespaceShouldFitInLine) const
|
||||
{
|
||||
if (breakBefore == range.getEnd())
|
||||
return cumulativeAdvanceX.back();
|
||||
|
||||
const auto breakBeforeGlyphIndex = [&]() -> std::optional<size_t>
|
||||
{
|
||||
if (breakBefore == range.getEnd())
|
||||
|
|
@ -703,16 +709,18 @@ public:
|
|||
if (! breakBeforeGlyphIndex.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
const auto includingTrailingWhitespace = cumulativeAdvanceX [*breakBeforeGlyphIndex];
|
||||
|
||||
if (! whitespaceShouldFitInLine)
|
||||
{
|
||||
for (auto i = (int64) *breakBeforeGlyphIndex; --i >= 0;)
|
||||
{
|
||||
if (! glyphs[(size_t) i].whitespace)
|
||||
return cumulativeAdvanceX[(size_t) i + 1];
|
||||
return LineAdvance { includingTrailingWhitespace, cumulativeAdvanceX[(size_t) i + 1] };
|
||||
}
|
||||
}
|
||||
|
||||
return cumulativeAdvanceX [*breakBeforeGlyphIndex];
|
||||
return LineAdvance { includingTrailingWhitespace, includingTrailingWhitespace };
|
||||
}
|
||||
|
||||
auto isEmpty() const
|
||||
|
|
@ -996,7 +1004,12 @@ void SimpleShapedText::shape (const String& data,
|
|||
struct BestMatch
|
||||
{
|
||||
int64 breakBefore{};
|
||||
float advance{};
|
||||
|
||||
// We need to use maybeIgnoringWhitespace in comparisions, but
|
||||
// includingTrailingWhitespace when using subtraction to calculate the remaining
|
||||
// space.
|
||||
LineAdvance advance{};
|
||||
|
||||
bool unsafe{};
|
||||
std::vector<ShapedGlyph> unsafeGlyphs;
|
||||
};
|
||||
|
|
@ -1011,7 +1024,7 @@ void SimpleShapedText::shape (const String& data,
|
|||
if (auto safeAdvance = glyphsToConsume.getAdvanceXUpToBreakPointIfSafe (*breakBefore,
|
||||
options.getTrailingWhitespacesShouldFit()))
|
||||
{
|
||||
if (*safeAdvance < remainingWidth || ! bestMatch.has_value())
|
||||
if (safeAdvance->maybeIgnoringWhitespace < remainingWidth || ! bestMatch.has_value())
|
||||
bestMatch = BestMatch { *breakBefore, *safeAdvance, false, std::vector<ShapedGlyph> {} };
|
||||
else
|
||||
break; // We found a safe break that is too large to fit. Anything beyond
|
||||
|
|
@ -1048,7 +1061,7 @@ void SimpleShapedText::shape (const String& data,
|
|||
[] (auto acc, const auto& elem) { return acc + elem.advance.getX(); });
|
||||
|
||||
if (advance < remainingWidth || ! bestMatch.has_value())
|
||||
bestMatch = BestMatch { *breakBefore, advance, true, std::move (glyphs) };
|
||||
bestMatch = BestMatch { *breakBefore, { advance, advance }, true, std::move (glyphs) };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1086,12 +1099,12 @@ void SimpleShapedText::shape (const String& data,
|
|||
numGlyphsInLine += (int64) glyphs.size();
|
||||
|
||||
if (remainingWidth.has_value())
|
||||
remainingWidth = *remainingWidth - bestMatch->advance;
|
||||
remainingWidth = *remainingWidth - bestMatch->advance.includingTrailingWhitespace;
|
||||
|
||||
glyphsToConsume.breakBeforeAndConsume (bestMatch->breakBefore);
|
||||
};
|
||||
|
||||
if (bestMatch->advance >= remainingWidth)
|
||||
if (bestMatch->advance.maybeIgnoringWhitespace >= remainingWidth)
|
||||
{
|
||||
// Even an empty line is too short to fit any of the text
|
||||
if (numGlyphsInLine == 0 && exactlyEqual (remainingWidth, options.getMaxWidth()))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue