diff --git a/samples/svgc/ImageSharpAssetLoader.cs b/samples/svgc/ImageSharpAssetLoader.cs index 598eab9aa0..746a335bcc 100644 --- a/samples/svgc/ImageSharpAssetLoader.cs +++ b/samples/svgc/ImageSharpAssetLoader.cs @@ -56,4 +56,9 @@ public float MeasureText(string? text, ShimSkiaSharp.SKPaint paint, ref ShimSkia bounds = new ShimSkiaSharp.SKRect(0, -size * 0.8f, width, size * 0.2f); return width; } + + public ShimSkiaSharp.SKPath? GetTextPath(string? text, ShimSkiaSharp.SKPaint paint, float x, float y) + { + return null; + } } diff --git a/src/Svg.Controls.Avalonia/AvaloniaSvgAssetLoader.cs b/src/Svg.Controls.Avalonia/AvaloniaSvgAssetLoader.cs index 9fac52c405..a3f67796f8 100644 --- a/src/Svg.Controls.Avalonia/AvaloniaSvgAssetLoader.cs +++ b/src/Svg.Controls.Avalonia/AvaloniaSvgAssetLoader.cs @@ -173,4 +173,10 @@ public float MeasureText(string? text, SKPaint paint, ref SKRect bounds) bounds = new SKRect(0, -ascent, width, descent); return width; } + + /// + public SKPath? GetTextPath(string? text, SKPaint paint, float x, float y) + { + return null; + } } diff --git a/src/Svg.Model/Drawables/Elements/TextDrawable.cs b/src/Svg.Model/Drawables/Elements/TextDrawable.cs index c98ecf7b02..fa20c3b914 100644 --- a/src/Svg.Model/Drawables/Elements/TextDrawable.cs +++ b/src/Svg.Model/Drawables/Elements/TextDrawable.cs @@ -18,6 +18,8 @@ public sealed class TextDrawable : DrawableBase public SKRect OwnerBounds { get; set; } + public SKPath? Path { get; private set; } + private TextDrawable(ISvgAssetLoader assetLoader, HashSet? references) : base(assetLoader, references) { @@ -87,6 +89,7 @@ private void Initialize() var width = AssetLoader.MeasureText(text, paint, ref bounds); GeometryBounds = new SKRect(x, y + metricsAscent, x + width, y + metricsDescent); + Path = AssetLoader.GetTextPath(text, paint, x, y); Transform = TransformsService.ToMatrix(Text.Transforms); } diff --git a/src/Svg.Model/ISvgAssetLoader.cs b/src/Svg.Model/ISvgAssetLoader.cs index ddbd41be7c..e57634279a 100644 --- a/src/Svg.Model/ISvgAssetLoader.cs +++ b/src/Svg.Model/ISvgAssetLoader.cs @@ -14,4 +14,5 @@ public interface ISvgAssetLoader List FindTypefaces(string? text, SKPaint paintPreferredTypeface); SKFontMetrics GetFontMetrics(SKPaint paint); float MeasureText(string? text, SKPaint paint, ref SKRect bounds); + SKPath? GetTextPath(string? text, SKPaint paint, float x, float y); } diff --git a/src/Svg.Skia/SkiaModel.cs b/src/Svg.Skia/SkiaModel.cs index 906b40e8ae..bb1c704ef5 100644 --- a/src/Svg.Skia/SkiaModel.cs +++ b/src/Svg.Skia/SkiaModel.cs @@ -994,6 +994,15 @@ public SkiaSharp.SKPathFillType ToSKPathFillType(SKPathFillType pathFillType) }; } + public SKPathFillType FromSKPathFillType(SkiaSharp.SKPathFillType pathFillType) + { + return pathFillType switch + { + SkiaSharp.SKPathFillType.EvenOdd => SKPathFillType.EvenOdd, + _ => SKPathFillType.Winding + }; + } + public SkiaSharp.SKPathArcSize ToSKPathArcSize(SKPathArcSize pathArcSize) { return pathArcSize switch @@ -1130,6 +1139,42 @@ public SkiaSharp.SKPath ToSKPath(SKPath path) return skPath; } + public SKPath FromSKPath(SkiaSharp.SKPath skPath) + { + var path = new SKPath + { + FillType = FromSKPathFillType(skPath.FillType) + }; + + using var iter = skPath.CreateRawIterator(); + var pts = new SkiaSharp.SKPoint[4]; + while (true) + { + var verb = iter.Next(pts); + switch (verb) + { + case SkiaSharp.SKPathVerb.Move: + path.Commands?.Add(new MoveToPathCommand(pts[0].X, pts[0].Y)); + break; + case SkiaSharp.SKPathVerb.Line: + path.Commands?.Add(new LineToPathCommand(pts[1].X, pts[1].Y)); + break; + case SkiaSharp.SKPathVerb.Quad: + case SkiaSharp.SKPathVerb.Conic: + path.Commands?.Add(new QuadToPathCommand(pts[1].X, pts[1].Y, pts[2].X, pts[2].Y)); + break; + case SkiaSharp.SKPathVerb.Cubic: + path.Commands?.Add(new CubicToPathCommand(pts[1].X, pts[1].Y, pts[2].X, pts[2].Y, pts[3].X, pts[3].Y)); + break; + case SkiaSharp.SKPathVerb.Close: + path.Commands?.Add(new ClosePathCommand()); + break; + case SkiaSharp.SKPathVerb.Done: + return path; + } + } + } + public SkiaSharp.SKPath? ToSKPath(ClipPath? clipPath) { if (clipPath?.Clips is null) diff --git a/src/Svg.Skia/SkiaSvgAssetLoader.cs b/src/Svg.Skia/SkiaSvgAssetLoader.cs index 5ecc33e95d..abbe8a646f 100644 --- a/src/Svg.Skia/SkiaSvgAssetLoader.cs +++ b/src/Svg.Skia/SkiaSvgAssetLoader.cs @@ -151,4 +151,17 @@ public float MeasureText(string? text, ShimSkiaSharp.SKPaint paint, ref ShimSkia bounds = new ShimSkiaSharp.SKRect(skBounds.Left, skBounds.Top, skBounds.Right, skBounds.Bottom); return width; } + + /// + public ShimSkiaSharp.SKPath? GetTextPath(string? text, ShimSkiaSharp.SKPaint paint, float x, float y) + { + using var skPaint = _skiaModel.ToSKPaint(paint); + if (skPaint is null || text is null) + { + return null; + } + + using var skPath = skPaint.GetTextPath(text, x, y); + return _skiaModel.FromSKPath(skPath); + } } diff --git a/src/Svg.SourceGenerator.Skia/SkiaGeneratorSvgAssetLoader.cs b/src/Svg.SourceGenerator.Skia/SkiaGeneratorSvgAssetLoader.cs index 7f971a0d3d..5f1725df71 100644 --- a/src/Svg.SourceGenerator.Skia/SkiaGeneratorSvgAssetLoader.cs +++ b/src/Svg.SourceGenerator.Skia/SkiaGeneratorSvgAssetLoader.cs @@ -57,4 +57,9 @@ public float MeasureText(string? text, ShimSkiaSharp.SKPaint paint, ref ShimSkia bounds = new ShimSkiaSharp.SKRect(0, -size * 0.8f, width, size * 0.2f); return width; } + + public ShimSkiaSharp.SKPath? GetTextPath(string? text, ShimSkiaSharp.SKPaint paint, float x, float y) + { + return null; + } }