From 48e23a048deb16b563e3947187f9484482daa352 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Fri, 8 Sep 2023 22:38:17 -0300 Subject: [PATCH] Improve text wrapping when multiple characters are combined into one glyph. #1923 --- src/modules/font/GenericShaper.cpp | 44 ++++++++++++-------- src/modules/font/TextShaper.cpp | 6 +-- src/modules/font/freetype/HarfbuzzShaper.cpp | 44 ++++++++++++-------- 3 files changed, 57 insertions(+), 37 deletions(-) diff --git a/src/modules/font/GenericShaper.cpp b/src/modules/font/GenericShaper.cpp index ef5854602..d96e8f90f 100644 --- a/src/modules/font/GenericShaper.cpp +++ b/src/modules/font/GenericShaper.cpp @@ -151,8 +151,7 @@ int GenericShaper::computeWordWrapIndex(const ColoredCodepoints &codepoints, Ran float w = 0.0f; float outwidth = 0.0f; float widthbeforelastspace = 0.0f; - int wrapindex = -1; - int lastspaceindex = -1; + int firstindexafterspace = -1; for (int i = (int)range.getMin(); i <= (int)range.getMax(); i++) { @@ -166,37 +165,48 @@ int GenericShaper::computeWordWrapIndex(const ColoredCodepoints &codepoints, Ran float newwidth = w + getKerning(prevglyph, g) + getGlyphAdvance(g); - // Only wrap when there's a non-space character. - if (newwidth > wraplimit && !isWhitespace(g)) - { - // Rewind to the last seen space when wrapping. - if (lastspaceindex != -1) - { - wrapindex = lastspaceindex; - outwidth = widthbeforelastspace; - } - break; - } - // Don't count trailing spaces in the output width. if (isWhitespace(g)) { - lastspaceindex = i; if (!isWhitespace(prevglyph)) widthbeforelastspace = w; } else + { + if (isWhitespace(prevglyph)) + firstindexafterspace = i; + + // Only wrap when there's a non-space character. + if (newwidth > wraplimit) + { + // If this is the first character, wrap from the next one instead of this one. + int wrapindex = i > (int)range.first ? i : (int)range.first + 1; + + // Rewind to after the last seen space when wrapping. + if (firstindexafterspace != -1) + { + wrapindex = firstindexafterspace; + outwidth = widthbeforelastspace; + } + + if (width) + *width = outwidth; + + return wrapindex; + } + outwidth = newwidth; + } w = newwidth; prevglyph = g; - wrapindex = i; } if (width) *width = outwidth; - return wrapindex; + // There wasn't any wrap in the middle of the range. + return range.last + 1; } } // font diff --git a/src/modules/font/TextShaper.cpp b/src/modules/font/TextShaper.cpp index 43854e719..8abf2b19c 100644 --- a/src/modules/font/TextShaper.cpp +++ b/src/modules/font/TextShaper.cpp @@ -303,10 +303,10 @@ void TextShaper::getWrap(const ColoredCodepoints &codepoints, float wraplimit, s float width = 0.0f; int wrapindex = computeWordWrapIndex(codepoints, r, wraplimit, &width); - if (wrapindex >= (int) i) + if (wrapindex > (int) i) { - r = Range(i, (size_t) wrapindex + 1 - i); - i = (size_t)wrapindex + 1; + r = Range(i, (size_t) wrapindex - i); + i = (size_t)wrapindex; } else { diff --git a/src/modules/font/freetype/HarfbuzzShaper.cpp b/src/modules/font/freetype/HarfbuzzShaper.cpp index b38859741..70767dada 100644 --- a/src/modules/font/freetype/HarfbuzzShaper.cpp +++ b/src/modules/font/freetype/HarfbuzzShaper.cpp @@ -335,8 +335,7 @@ int HarfbuzzShaper::computeWordWrapIndex(const ColoredCodepoints &codepoints, Ra float w = 0.0f; float outwidth = 0.0f; float widthbeforelastspace = 0.0f; - int wrapindex = -1; - int lastspaceindex = -1; + int firstindexafterspace = -1; uint32 prevcodepoint = 0; @@ -376,38 +375,49 @@ int HarfbuzzShaper::computeWordWrapIndex(const ColoredCodepoints &codepoints, Ra float newwidth = w + floorf((glyphpos.x_advance >> 6) / dpiScales[0] + 0.5f); - // Only wrap when there's a non-space character. - if (newwidth > wraplimit && !isWhitespace(clustercodepoint)) - { - // Rewind to the last seen space when wrapping. - if (lastspaceindex != -1) - { - wrapindex = lastspaceindex; - outwidth = widthbeforelastspace; - } - break; - } - // Don't count trailing spaces in the output width. if (isWhitespace(clustercodepoint)) { - lastspaceindex = info.cluster; if (!isWhitespace(prevcodepoint)) widthbeforelastspace = w; } else + { + if (isWhitespace(prevcodepoint)) + firstindexafterspace = info.cluster; + + // Only wrap when there's a non-space character. + if (newwidth > wraplimit) + { + // If this is the first character, wrap from the next one instead of this one. + int wrapindex = info.cluster > (int) range.first ? info.cluster : (int) range.first + 1; + + // Rewind to after the last seen space when wrapping. + if (firstindexafterspace != -1) + { + wrapindex = firstindexafterspace; + outwidth = widthbeforelastspace; + } + + if (width) + *width = outwidth; + + return wrapindex; + } + outwidth = newwidth; + } w = newwidth; prevcodepoint = clustercodepoint; - wrapindex = info.cluster; } } if (width) *width = outwidth; - return wrapindex; + // There wasn't any wrap in the middle of the range. + return (int) range.last + 1; } } // freetype