From 65d86895b8964f6644b725f5cf4472a1e56d61d9 Mon Sep 17 00:00:00 2001 From: Lucas Rodrigues Date: Mon, 9 Mar 2026 23:54:00 +0000 Subject: [PATCH] text: render SVG font as black-white outline Previously, using SVG fonts will cause Gio to render invisible "characters". Now, some fonts (like Noto Sans Emoji) will be rendered based on "Outline". Gio don't support SVG fonts, but now it will show the outline ("black-and-white") alternative. Signed-off-by: Lucas Rodrigues --- text/gotext.go | 95 +++++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/text/gotext.go b/text/gotext.go index 16605da7..298f2c62 100644 --- a/text/gotext.go +++ b/text/gotext.go @@ -656,53 +656,60 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec { } scaleFactor := fixedToFloat(ppem) / float32(face.Upem()) glyphData := face.GlyphData(gid) + + var outline font.GlyphOutline switch glyphData := glyphData.(type) { case font.GlyphOutline: - outline := glyphData - // Move to glyph position. - pos := f32.Point{ - X: fixedToFloat((g.X - x) + g.Offset.X), - Y: -fixedToFloat(g.Offset.Y), - } - builder.Move(pos.Sub(lastPos)) - lastPos = pos - var lastArg f32.Point - - // Convert fonts.Segments to relative segments. - for _, fseg := range outline.Segments { - nargs := 1 - switch fseg.Op { - case gotextot.SegmentOpQuadTo: - nargs = 2 - case gotextot.SegmentOpCubeTo: - nargs = 3 - } - var args [3]f32.Point - for i := range nargs { - a := f32.Point{ - X: fseg.Args[i].X * scaleFactor, - Y: -fseg.Args[i].Y * scaleFactor, - } - args[i] = a.Sub(lastArg) - if i == nargs-1 { - lastArg = a - } - } - switch fseg.Op { - case gotextot.SegmentOpMoveTo: - builder.Move(args[0]) - case gotextot.SegmentOpLineTo: - builder.Line(args[0]) - case gotextot.SegmentOpQuadTo: - builder.Quad(args[0], args[1]) - case gotextot.SegmentOpCubeTo: - builder.Cube(args[0], args[1], args[2]) - default: - panic("unsupported segment op") - } - } - lastPos = lastPos.Add(lastArg) + outline = glyphData + case font.GlyphSVG: + outline = glyphData.Outline + default: + continue } + + // Move to glyph position. + pos := f32.Point{ + X: fixedToFloat((g.X - x) - g.Offset.X), + Y: -fixedToFloat(g.Offset.Y), + } + builder.Move(pos.Sub(lastPos)) + lastPos = pos + var lastArg f32.Point + + // Convert fonts.Segments to relative segments. + for _, fseg := range outline.Segments { + nargs := 1 + switch fseg.Op { + case gotextot.SegmentOpQuadTo: + nargs = 2 + case gotextot.SegmentOpCubeTo: + nargs = 3 + } + var args [3]f32.Point + for i := range nargs { + a := f32.Point{ + X: fseg.Args[i].X * scaleFactor, + Y: -fseg.Args[i].Y * scaleFactor, + } + args[i] = a.Sub(lastArg) + if i == nargs-1 { + lastArg = a + } + } + switch fseg.Op { + case gotextot.SegmentOpMoveTo: + builder.Move(args[0]) + case gotextot.SegmentOpLineTo: + builder.Line(args[0]) + case gotextot.SegmentOpQuadTo: + builder.Quad(args[0], args[1]) + case gotextot.SegmentOpCubeTo: + builder.Cube(args[0], args[1], args[2]) + default: + panic("unsupported segment op") + } + } + lastPos = lastPos.Add(lastArg) } return builder.End() }