diff --git a/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/FontExtensions.cs b/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/FontExtensions.cs index b4d660e217..6571a373f9 100644 --- a/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/FontExtensions.cs +++ b/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/FontExtensions.cs @@ -26,5 +26,66 @@ public static void SetScale(this Font font, SKSizeI scale) font.SetScale(scale.Width, scale.Height); } + + public static SKPath GetShapedTextPath(this SKFont font, string text, SKPoint p, SKTextAlign textAlign) => + font.GetShapedTextPath(text, p.X, p.Y, textAlign); + + public static SKPath GetShapedTextPath(this SKFont font, string text, float x, float y, SKTextAlign textAlign) + { + if (string.IsNullOrEmpty(text)) + return new SKPath(); + + using var shaper = new SKShaper(font.Typeface); + return font.GetShapedTextPath(shaper, text, x, y, textAlign); + } + + public static SKPath GetShapedTextPath(this SKFont font, SKShaper shaper, string text, float x, float y, SKTextAlign textAlign) + { + var returnPath = new SKPath(); + + if (string.IsNullOrEmpty(text)) + return returnPath; + + if (shaper == null) + throw new ArgumentNullException(nameof(shaper)); + + font.Typeface = shaper.Typeface; + + // shape the text + var result = shaper.Shape(text, x, y, font); + + // adjust alignment + var xOffset = 0.0f; + if (textAlign != SKTextAlign.Left) + { + var width = result.Width; + if (textAlign == SKTextAlign.Center) + width *= 0.5f; + xOffset -= width; + } + + // generate a path for each glyph + for (var i = 0; i < result.Points.Length; i++) + { + // get the glyph path + using var glyphPath = font.GetGlyphPath((ushort)result.Codepoints[i]); + + if (glyphPath.IsEmpty) + continue; + + // translate the glyph path + var point = result.Points[i]; + glyphPath.Transform(new SKMatrix( + 1, 0, point.X + xOffset, + 0, 1, point.Y, + 0, 0, 1 + )); + + // append the glyph path + returnPath.AddPath(glyphPath); + } + + return returnPath; + } } } diff --git a/tests/Tests/SkiaSharp/SKShaperTest.cs b/tests/Tests/SkiaSharp/SKShaperTest.cs index 7f14de8a27..7a03e712f8 100644 --- a/tests/Tests/SkiaSharp/SKShaperTest.cs +++ b/tests/Tests/SkiaSharp/SKShaperTest.cs @@ -35,6 +35,33 @@ public void DrawShapedTextExtensionMethodDraws() } } + [SkippableFact] + public void GetShapedTextPathExtensionMethodDraws() + { + using (var bitmap = new SKBitmap(new SKImageInfo(512, 512))) + using (var canvas = new SKCanvas(bitmap)) + using (var tf = SKTypeface.FromFile(Path.Combine(PathToFonts, "content-font.ttf"))) + using (var shaper = new SKShaper(tf)) + using (var paint = new SKPaint { IsAntialias = true }) + using (var font = new SKFont { Size = 64, Typeface = tf}) + { + canvas.Clear(SKColors.White); + + using var shapedPath = font.GetShapedTextPath(shaper, "متن", 100, 200, SKTextAlign.Left); + canvas.DrawPath(shapedPath, paint); + + canvas.Flush(); + + Assert.Equal(SKColors.Black, bitmap.GetPixel(110, 210)); + Assert.Equal(SKColors.Black, bitmap.GetPixel(127, 196)); + Assert.Equal(SKColors.Black, bitmap.GetPixel(142, 197)); + Assert.Equal(SKColors.Black, bitmap.GetPixel(155, 195)); + Assert.Equal(SKColors.Black, bitmap.GetPixel(131, 181)); + Assert.Equal(SKColors.White, bitmap.GetPixel(155, 190)); + Assert.Equal(SKColors.White, bitmap.GetPixel(110, 200)); + } + } + [SkippableFact] public void CorrectlyShapesArabicScriptAtAnOffset() { @@ -112,7 +139,7 @@ public void TextAlignMovesTextPosition(SKTextAlign align, int offset) var fontFile = Path.Combine(PathToFonts, "segoeui.ttf"); using var tf = SKTypeface.FromFile(fontFile); - using var bitmap = new SKBitmap(600, 200); + using var bitmap = new SKBitmap(600, 300); using var canvas = new SKCanvas(bitmap); canvas.Clear(SKColors.White); @@ -127,28 +154,56 @@ public void TextAlignMovesTextPosition(SKTextAlign align, int offset) canvas.DrawShapedText("SkiaSharp", 300, 100, align, font, paint); - AssertTextAlign(bitmap, offset, 0); + AssertTextAlign(bitmap, offset, 100); + } + + [SkippableTheory] + [InlineData(SKTextAlign.Left, 300)] + [InlineData(SKTextAlign.Center, 162)] + [InlineData(SKTextAlign.Right, 23)] + public void TextAlignMovesTextPathPosition(SKTextAlign align, int offset) + { + var fontFile = Path.Combine(PathToFonts, "segoeui.ttf"); + using var tf = SKTypeface.FromFile(fontFile); + + using var bitmap = new SKBitmap(600, 300); + using var canvas = new SKCanvas(bitmap); + + canvas.Clear(SKColors.White); + + using var paint = new SKPaint(); + paint.IsAntialias = true; + paint.Color = SKColors.Black; + + using var font = new SKFont(); + font.Typeface = tf; + font.Size = 64; + + using var shapedPath = font.GetShapedTextPath("SkiaSharp", 300, 100, align); + canvas.DrawPath(shapedPath, paint); + + AssertTextAlign(bitmap, offset, 100); } private static void AssertTextAlign(SKBitmap bitmap, int x, int y) { // [S]kia[S]har[p] - Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 6, y + 66)); - Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 28, y + 87)); - Assert.Equal(SKColors.White, bitmap.GetPixel(x + 28, y + 66)); - Assert.Equal(SKColors.White, bitmap.GetPixel(x + 6, y + 87)); - - Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 120, y + 66)); - Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 142, y + 87)); - Assert.Equal(SKColors.White, bitmap.GetPixel(x + 142, y + 66)); - Assert.Equal(SKColors.White, bitmap.GetPixel(x + 120, y + 87)); - - Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 246, y + 70)); - Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 246, y + 113)); - Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 271, y + 83)); - Assert.Equal(SKColors.White, bitmap.GetPixel(x + 258, y + 83)); - Assert.Equal(SKColors.White, bitmap.GetPixel(x + 258, y + 113)); + Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 6, y - 34)); + Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 28, y - 13)); + Assert.Equal(SKColors.White, bitmap.GetPixel(x + 28, y - 34)); + Assert.Equal(SKColors.White, bitmap.GetPixel(x + 6, y - 13)); + + Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 120, y - 34)); + Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 142, y - 13)); + Assert.Equal(SKColors.White, bitmap.GetPixel(x + 142, y - 34)); + Assert.Equal(SKColors.White, bitmap.GetPixel(x + 120, y - 13)); + + Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 246, y - 30)); + Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 246, y + 13)); + Assert.Equal(SKColors.Black, bitmap.GetPixel(x + 271, y - 17)); + Assert.Equal(SKColors.White, bitmap.GetPixel(x + 258, y - 17)); + Assert.Equal(SKColors.White, bitmap.GetPixel(x + 258, y + 13)); } } }