diff --git a/text/gotext.go b/text/gotext.go index 8a6ecfdb..f9ff8c6d 100644 --- a/text/gotext.go +++ b/text/gotext.go @@ -523,22 +523,27 @@ func calculateYOffsets(lines []line) { // LayoutRunes shapes and wraps the text, and returns the result in Gio's shaped text format. func (s *shaperImpl) LayoutRunes(params Parameters, txt []rune) document { hasNewline := len(txt) > 0 && txt[len(txt)-1] == '\n' - justNewline := false + var ls []shaping.Line + var truncated int if hasNewline { txt = txt[:len(txt)-1] - if len(txt) == 0 { - // If we only have a newline, shape a space to get line metrics. - txt = []rune{' '} - justNewline = true - } } - ls, truncated := s.shapeAndWrapText(params, replaceControlCharacters(txt)) - if justNewline { - // We shaped a space to get proper line metrics, but we need to drop - // the rune/glyph info since it isn't actually part of the text. - ls[0][0].Glyphs = ls[0][0].Glyphs[:0] - ls[0][0].Runes.Count = 0 - ls[0][0].Advance = 0 + if hasNewline && len(txt) == 0 { + // If we only have a newline, shape a space to get line metrics. + ls, truncated = s.shapeAndWrapText(params, []rune{' '}) + if truncated > 0 { + // Our space was truncated. Since our space didn't exist in any meaningful + // capacity, ensure the truncated count is zeroed out. + truncated = 0 + } else { + // We shaped a space to get proper line metrics, but we need to drop + // the rune/glyph info since it isn't actually part of the text. + ls[0][0].Glyphs = ls[0][0].Glyphs[:0] + ls[0][0].Advance = 0 + ls[0][0].Runes.Count = 0 + } + } else { + ls, truncated = s.shapeAndWrapText(params, replaceControlCharacters(txt)) } didTruncate := truncated > 0 || (params.forceTruncate && params.MaxLines == len(ls)) diff --git a/text/shaper_test.go b/text/shaper_test.go index 6d3d69a3..87f7dd71 100644 --- a/text/shaper_test.go +++ b/text/shaper_test.go @@ -484,6 +484,19 @@ func TestShapeStringRuneAccounting(t *testing.T) { MaxWidth: 999929, }, }, + { + name: "newline zero-width regression", + input: "\n", + params: Parameters{ + Font: font.Font{Typeface: "Go", Style: font.Regular, Weight: font.Normal}, + Alignment: Start, + PxPerEm: 768, + MaxLines: 1, + Truncator: "\u200b", + WrapPolicy: WrapHeuristically, + MaxWidth: 0, + }, + }, } { t.Run(tc.name, func(t *testing.T) { for _, setup := range []setup{