Browse Source

Improve text wrapping when multiple characters are combined into one glyph.

#1923
Sasha Szpakowski 1 year ago
parent
commit
48e23a048d

+ 27 - 17
src/modules/font/GenericShaper.cpp

@@ -151,8 +151,7 @@ int GenericShaper::computeWordWrapIndex(const ColoredCodepoints &codepoints, Ran
 	float w = 0.0f;
 	float w = 0.0f;
 	float outwidth = 0.0f;
 	float outwidth = 0.0f;
 	float widthbeforelastspace = 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++)
 	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);
 		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.
 		// Don't count trailing spaces in the output width.
 		if (isWhitespace(g))
 		if (isWhitespace(g))
 		{
 		{
-			lastspaceindex = i;
 			if (!isWhitespace(prevglyph))
 			if (!isWhitespace(prevglyph))
 				widthbeforelastspace = w;
 				widthbeforelastspace = w;
 		}
 		}
 		else
 		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;
 			outwidth = newwidth;
+		}
 
 
 		w = newwidth;
 		w = newwidth;
 		prevglyph = g;
 		prevglyph = g;
-		wrapindex = i;
 	}
 	}
 
 
 	if (width)
 	if (width)
 		*width = outwidth;
 		*width = outwidth;
 
 
-	return wrapindex;
+	// There wasn't any wrap in the middle of the range.
+	return range.last + 1;
 }
 }
 
 
 } // font
 } // font

+ 3 - 3
src/modules/font/TextShaper.cpp

@@ -303,10 +303,10 @@ void TextShaper::getWrap(const ColoredCodepoints &codepoints, float wraplimit, s
 			float width = 0.0f;
 			float width = 0.0f;
 			int wrapindex = computeWordWrapIndex(codepoints, r, wraplimit, &width);
 			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
 			else
 			{
 			{

+ 27 - 17
src/modules/font/freetype/HarfbuzzShaper.cpp

@@ -335,8 +335,7 @@ int HarfbuzzShaper::computeWordWrapIndex(const ColoredCodepoints &codepoints, Ra
 	float w = 0.0f;
 	float w = 0.0f;
 	float outwidth = 0.0f;
 	float outwidth = 0.0f;
 	float widthbeforelastspace = 0.0f;
 	float widthbeforelastspace = 0.0f;
-	int wrapindex = -1;
-	int lastspaceindex = -1;
+	int firstindexafterspace = -1;
 
 
 	uint32 prevcodepoint = 0;
 	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);
 			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.
 			// Don't count trailing spaces in the output width.
 			if (isWhitespace(clustercodepoint))
 			if (isWhitespace(clustercodepoint))
 			{
 			{
-				lastspaceindex = info.cluster;
 				if (!isWhitespace(prevcodepoint))
 				if (!isWhitespace(prevcodepoint))
 					widthbeforelastspace = w;
 					widthbeforelastspace = w;
 			}
 			}
 			else
 			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;
 				outwidth = newwidth;
+			}
 
 
 			w = newwidth;
 			w = newwidth;
 			prevcodepoint = clustercodepoint;
 			prevcodepoint = clustercodepoint;
-			wrapindex = info.cluster;
 		}
 		}
 	}
 	}
 
 
 	if (width)
 	if (width)
 		*width = outwidth;
 		*width = outwidth;
 
 
-	return wrapindex;
+	// There wasn't any wrap in the middle of the range.
+	return (int) range.last + 1;
 }
 }
 
 
 } // freetype
 } // freetype