M text/shaper.go => text/shaper.go +7 -5
@@ 223,11 223,13 @@ func (l *Shaper) layoutText(params Parameters, minWidth, maxWidth int, lc system
}
done = endByte == len(str)
}
- l.txt.append(l.layoutParagraph(params, minWidth, maxWidth, lc, str[startByte:endByte], l.paragraph))
- if truncating {
- params.MaxLines = maxLines - len(l.txt.lines)
- if params.MaxLines == 0 {
- done = true
+ if startByte != endByte || len(l.paragraph) > 0 {
+ l.txt.append(l.layoutParagraph(params, minWidth, maxWidth, lc, str[startByte:endByte], l.paragraph))
+ if truncating {
+ params.MaxLines = maxLines - len(l.txt.lines)
+ if params.MaxLines == 0 {
+ done = true
+ }
}
}
if done {
M text/shaper_test.go => text/shaper_test.go +27 -0
@@ 1,6 1,7 @@
package text
import (
+ "strings"
"testing"
nsareg "eliasnaur.com/font/noto/sans/arabic/regular"
@@ 40,6 41,32 @@ func TestWrappingTruncation(t *testing.T) {
}
}
+// TestShapingNewlineHandling checks that the shaper's newline splitting behaves
+// consistently and does not create spurious lines of text.
+func TestShapingNewlineHandling(t *testing.T) {
+ // Use a test string containing multiple newlines to ensure that they are shaped
+ // as separate paragraphs.
+ textInput := "\n"
+ ltrFace, _ := opentype.Parse(goregular.TTF)
+ collection := []FontFace{{Face: ltrFace}}
+ cache := NewShaper(collection)
+ cache.LayoutString(Parameters{
+ Alignment: Middle,
+ PxPerEm: fixed.I(10),
+ }, 200, 200, english, textInput)
+ if lineCount := len(cache.txt.lines); lineCount > 1 {
+ t.Errorf("shaping string %q created %d lines", textInput, lineCount)
+ }
+
+ cache.Layout(Parameters{
+ Alignment: Middle,
+ PxPerEm: fixed.I(10),
+ }, 200, 200, english, strings.NewReader(textInput))
+ if lineCount := len(cache.txt.lines); lineCount > 1 {
+ t.Errorf("shaping reader %q created %d lines", textInput, lineCount)
+ }
+}
+
// TestCacheEmptyString ensures that shaping the empty string returns a
// single synthetic glyph with ascent/descent info.
func TestCacheEmptyString(t *testing.T) {