From bfb47538aa0db22d965be5f07fbf81c102b0050d Mon Sep 17 00:00:00 2001 From: Chris Waldon Date: Thu, 15 Dec 2022 08:50:02 -0500 Subject: [PATCH] text: ensure runereader behaves same as string This commit fixes a subtle discrepancy in the handling of text input within the shaper. Text provided as an io.RuneReader with a trailing newline would generate an extra (empty) line of text, whereas the same input provided as a string would not. Signed-off-by: Chris Waldon --- text/shaper.go | 12 +++++++----- text/shaper_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/text/shaper.go b/text/shaper.go index cbf1bce8..34ad8512 100644 --- a/text/shaper.go +++ b/text/shaper.go @@ -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 { diff --git a/text/shaper_test.go b/text/shaper_test.go index 4ff47fa8..d14e92a4 100644 --- a/text/shaper_test.go +++ b/text/shaper_test.go @@ -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) {