Exemplo n.º 1
0
        public MyShaper(SKTypeface typeface)
        {
            if (typeface == null)
            {
                throw new ArgumentNullException(nameof(typeface));
            }
            ;
            Typeface = typeface;
            int index;

            using (var blob = Typeface.OpenStream(out index).ToHarfBuzzBlob())
                using (var face = new Face(blob, (uint)index))
                {
                    face.Index      = (uint)index;
                    face.UnitsPerEm = (uint)Typeface.UnitsPerEm;

                    font = new Font(face);
                    font.SetScale(FONT_SIZE_SCALE, FONT_SIZE_SCALE);
#if __MAC__
                    font.SetFunctionsOpenType();
#endif
                }

            buffer = new HarfBuzzSharp.Buffer();
        }
Exemplo n.º 2
0
 public void ShouldThrowInvalidOperationExceptionOnSerializeGlyphsWhenBufferIsEmpty()
 {
     using (var buffer = new Buffer())
     {
         Assert.Throws <InvalidOperationException>(() => { buffer.SerializeGlyphs(); });
     }
 }
Exemplo n.º 3
0
        public ShapedBuffer ShapeText(ReadOnlySlice <char> text, TextShaperOptions options)
        {
            var typeface            = options.Typeface;
            var fontRenderingEmSize = options.FontRenderingEmSize;
            var bidiLevel           = options.BidLevel;
            var culture             = options.Culture;

            using (var buffer = new Buffer())
            {
                buffer.AddUtf16(text.Buffer.Span, text.Start, text.Length);

                MergeBreakPair(buffer);

                buffer.GuessSegmentProperties();

                buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft;

                buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);

                var font = ((HarfBuzzGlyphTypefaceImpl)typeface.PlatformImpl).Font;

                font.Shape(buffer);

                if (buffer.Direction == Direction.RightToLeft)
                {
                    buffer.Reverse();
                }

                font.GetScale(out var scaleX, out _);

                var textScale = fontRenderingEmSize / scaleX;

                var bufferLength = buffer.Length;

                var shapedBuffer = new ShapedBuffer(text, bufferLength, typeface, fontRenderingEmSize, bidiLevel);

                var glyphInfos = buffer.GetGlyphInfoSpan();

                var glyphPositions = buffer.GetGlyphPositionSpan();

                for (var i = 0; i < bufferLength; i++)
                {
                    var sourceInfo = glyphInfos[i];

                    var glyphIndex = (ushort)sourceInfo.Codepoint;

                    var glyphCluster = (int)sourceInfo.Cluster;

                    var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale);

                    var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale);

                    var targetInfo = new Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset);

                    shapedBuffer[i] = targetInfo;
                }

                return(shapedBuffer);
            }
        }
Exemplo n.º 4
0
        public new Result Shape(string text, float xOffset, float yOffset, SKPaint paint)
        {
            if (string.IsNullOrEmpty(text))
            {
                return(new Result());
            }

            using var buffer = new Buffer();
            switch (paint.TextEncoding)
            {
            case SKTextEncoding.Utf8:
                buffer.AddUtf8(text);
                break;

            case SKTextEncoding.Utf16:
                buffer.AddUtf16(text);
                break;

            case SKTextEncoding.Utf32:
                buffer.AddUtf32(text);
                break;

            default:
                throw new NotSupportedException("TextEncoding of type GlyphId is not supported.");
            }

            buffer.GuessSegmentProperties();
            return(Shape(buffer, xOffset, yOffset, paint));
        }
Exemplo n.º 5
0
        public void CanCreateFaceShaperFromTypeface()
        {
            var skiaTypeface = SKTypeface.FromFile(Path.Combine(PathToFonts, "content-font.ttf"));

            var clusters   = new uint[] { 4, 2, 0 };
            var codepoints = new uint[] { 629, 668, 891 };

            using (var face = new Face(GetFaceBlob, () => skiaTypeface.Dispose()))
                using (var font = new Font(face))
                    using (var buffer = new HarfBuzzSharp.Buffer())
                    {
                        buffer.AddUtf8("متن");
                        buffer.GuessSegmentProperties();

                        font.Shape(buffer);

                        Assert.Equal(clusters, buffer.GlyphInfos.Select(i => i.Cluster));
                        Assert.Equal(codepoints, buffer.GlyphInfos.Select(i => i.Codepoint));
                    }

            Blob GetFaceBlob(Face face, Tag tag)
            {
                var size = skiaTypeface.GetTableSize(tag);
                var data = Marshal.AllocCoTaskMem(size);

                skiaTypeface.TryGetTableData(tag, 0, size, data);
                return(new Blob(data, size, MemoryMode.Writeable, () => Marshal.FreeCoTaskMem(data)));
            }
        }
Exemplo n.º 6
0
            static List <TextFormatting.GlyphInfo> GetGlyphs(HarfBuzzSharp.Buffer buffer, int clusterStart, float textSizeX, float textSizeY)
            {
                int length      = buffer.Length;
                var hbGlyphs    = buffer.GlyphInfos;
                var hbPositions = buffer.GlyphPositions;

                List <TextFormatting.GlyphInfo> glyphs = new(length);

                for (int i = 0; i < length; i++)
                {
                    var hbGlyph = hbGlyphs[i];
                    var hbPos   = hbPositions[i];

                    TextFormatting.GlyphInfo glyph = new(
                        (ushort)hbGlyph.Codepoint,
                        clusterStart + (int)hbGlyph.Cluster,
                        hbPos.XAdvance *textSizeX,
                        hbPos.XOffset *textSizeX,
                        hbPos.YOffset *textSizeY
                        );

                    glyphs.Add(glyph);
                }

                return(glyphs);
            }
Exemplo n.º 7
0
        public GlyphRun ShapeText(ReadOnlySlice <char> text, Typeface typeface, double fontRenderingEmSize,
                                  CultureInfo culture)
        {
            using (var buffer = new Buffer())
            {
                FillBuffer(buffer, text);

                buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);

                buffer.GuessSegmentProperties();

                var glyphTypeface = typeface.GlyphTypeface;

                var font = ((HarfBuzzGlyphTypefaceImpl)glyphTypeface.PlatformImpl).Font;

                font.Shape(buffer);

                font.GetScale(out var scaleX, out _);

                var textScale = fontRenderingEmSize / scaleX;

                var bufferLength = buffer.Length;

                var glyphInfos = buffer.GetGlyphInfoSpan();

                var glyphPositions = buffer.GetGlyphPositionSpan();

                var glyphIndices = new ushort[bufferLength];

                var clusters = new ushort[bufferLength];

                double[] glyphAdvances = null;

                Vector[] glyphOffsets = null;

                for (var i = 0; i < bufferLength; i++)
                {
                    glyphIndices[i] = (ushort)glyphInfos[i].Codepoint;

                    clusters[i] = (ushort)glyphInfos[i].Cluster;

                    if (!glyphTypeface.IsFixedPitch)
                    {
                        SetAdvance(glyphPositions, i, textScale, ref glyphAdvances);
                    }

                    SetOffset(glyphPositions, i, textScale, ref glyphOffsets);
                }

                return(new GlyphRun(glyphTypeface, fontRenderingEmSize,
                                    new ReadOnlySlice <ushort>(glyphIndices),
                                    new ReadOnlySlice <double>(glyphAdvances),
                                    new ReadOnlySlice <Vector>(glyphOffsets),
                                    text,
                                    new ReadOnlySlice <ushort>(clusters),
                                    buffer.Direction == Direction.LeftToRight ? 0 : 1));
            }
        }
Exemplo n.º 8
0
        public void ShouldThrowInvalidOperationExceptionOnDeserializeGlyphsWhenBufferIsNonEmpty()
        {
            using (var buffer = new Buffer())
            {
                buffer.AddUtf8("A");

                Assert.Throws <InvalidOperationException>(() => { buffer.DeserializeGlyphs(SerializedSimpleText); });
            }
        }
Exemplo n.º 9
0
        public void ShouldThrowInvalidOperationExceptionOnSerializeGlyphsWhenBufferIsUnShaped()
        {
            using (var buffer = new Buffer())
            {
                buffer.AddUtf8("A");

                Assert.Throws <InvalidOperationException>(() => { buffer.SerializeGlyphs(); });
            }
        }
Exemplo n.º 10
0
        public void ShouldAppendBuffer()
        {
            using (var buffer = new Buffer())
                using (var source = new Buffer())
                {
                    source.AddUtf8("123");

                    buffer.Append(source, 0, source.Length);
                }
        }
Exemplo n.º 11
0
        public void ShouldHaveDefaultStateAfterReset()
        {
            using (var buffer = new Buffer())
            {
                buffer.AddUtf8(SimpleText);

                buffer.Reset();

                Assert.Equal(ContentType.Invalid, buffer.ContentType);

                Assert.Equal(0, buffer.Length);
            }
        }
Exemplo n.º 12
0
        public void ShouldClearContents()
        {
            using (var buffer = new Buffer())
            {
                buffer.AddUtf8(SimpleText);

                Assert.Equal(SimpleText.Length, buffer.GlyphInfos.Length);

                buffer.ClearContents();

                Assert.Empty(buffer.GlyphInfos);
            }
        }
Exemplo n.º 13
0
        public void ShouldThrowInvalidOperationExceptionOnAddUtfWhenBufferIsShaped()
        {
            using (var typeface = SKTypeface.FromFile(Path.Combine(PathToFonts, "content-font.ttf")))
                using (var blob = typeface.OpenStream(out var index).ToHarfBuzzBlob())
                    using (var face = new Face(blob, index))
                        using (var font = new Font(face))
                            using (var buffer = new Buffer())
                            {
                                buffer.AddUtf8(SimpleText);

                                font.Shape(buffer);

                                Assert.Throws <InvalidOperationException>(() => { buffer.AddUtf8("A"); });
                            }
        }
Exemplo n.º 14
0
        public void ShouldReverseClusters()
        {
            using (var buffer = new Buffer())
            {
                buffer.AddUtf8("12");

                buffer.ReverseClusters();

                Assert.Equal(50u, buffer.GlyphInfos[0].Codepoint);
                Assert.Equal(1u, buffer.GlyphInfos[0].Cluster);

                Assert.Equal(49u, buffer.GlyphInfos[1].Codepoint);
                Assert.Equal(0u, buffer.GlyphInfos[1].Cluster);
            }
        }
Exemplo n.º 15
0
        public CustomSKShaper(SKTypeface typeface) : base(typeface)
        {
            using (var blob = Typeface.OpenStream(out var index).ToHarfBuzzBlob())
                using (var face = new Face(blob, index))
                {
                    face.Index      = index;
                    face.UnitsPerEm = Typeface.UnitsPerEm;

                    _font = new Font(face);
                    _font.SetScale(_FONT_SIZE_SCALE, _FONT_SIZE_SCALE);
                    _font.SetFunctionsOpenType();
                }

            _buffer = new Buffer();
        }
Exemplo n.º 16
0
        private static void MergeBreakPair(Buffer buffer)
        {
            var length = buffer.Length;

            var glyphInfos = buffer.GetGlyphInfoSpan();

            var second = glyphInfos[length - 1];

            if (!new Codepoint((int)second.Codepoint).IsBreakChar)
            {
                return;
            }

            if (length > 1 && glyphInfos[length - 2].Codepoint == '\r' && second.Codepoint == '\n')
            {
                var first = glyphInfos[length - 2];

                first.Codepoint  = '\u200C';
                second.Codepoint = '\u200C';
                second.Cluster   = first.Cluster;

                unsafe
                {
                    fixed(GlyphInfo *p = &glyphInfos[length - 2])
                    {
                        *p = first;
                    }

                    fixed(GlyphInfo *p = &glyphInfos[length - 1])
                    {
                        *p = second;
                    }
                }
            }
            else
            {
                second.Codepoint = '\u200C';

                unsafe
                {
                    fixed(GlyphInfo *p = &glyphInfos[length - 1])
                    {
                        *p = second;
                    }
                }
            }
        }
Exemplo n.º 17
0
        public void ShouldNormalize()
        {
            using (var typeface = SKTypeface.FromFile(Path.Combine(PathToFonts, "content-font.ttf")))
                using (var blob = typeface.OpenStream(out var index).ToHarfBuzzBlob())
                    using (var face = new Face(blob, index))
                        using (var font = new Font(face))
                            using (var buffer = new Buffer())
                            {
                                buffer.AddUtf16("Â̶");

                                font.Shape(buffer);

                                buffer.NormalizeGlyphs();

                                Assert.Equal(1027, buffer.GlyphPositions[1].YOffset);
                            }
        }
Exemplo n.º 18
0
        public void ShouldAddUtfByString()
        {
            using (var buffer = new Buffer())
            {
                buffer.AddUtf8("A");

                Assert.Equal(1, buffer.Length);

                buffer.AddUtf8("B");

                Assert.Equal(2, buffer.Length);

                buffer.AddUtf8("C");

                Assert.Equal(3, buffer.Length);
            }
        }
Exemplo n.º 19
0
        public Result Shape(Buffer buffer, float xOffset, float yOffset, SKPaint paint)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (paint == null)
            {
                throw new ArgumentNullException(nameof(paint));
            }

            // do the shaping
            font.Shape(buffer);

            // get the shaping results
            var len  = buffer.Length;
            var info = buffer.GlyphInfos;
            var pos  = buffer.GlyphPositions;

            // get the sizes
            float textSizeY = paint.TextSize / FONT_SIZE_SCALE;
            float textSizeX = textSizeY * paint.TextScaleX;

            var points     = new SKPoint[len];
            var clusters   = new uint[len];
            var codepoints = new uint[len];

            for (var i = 0; i < len; i++)
            {
                codepoints[i] = info[i].Codepoint;

                clusters[i] = info[i].Cluster;

                points[i] = new SKPoint(
                    xOffset + pos[i].XOffset * textSizeX,
                    yOffset - pos[i].YOffset * textSizeY);

                // move the cursor
                xOffset += pos[i].XAdvance * textSizeX;
                yOffset += pos[i].YAdvance * textSizeY;
            }

            return(new Result(codepoints, clusters, points));
        }
Exemplo n.º 20
0
        private static void FillBuffer(Buffer buffer, ReadOnlySlice <char> text)
        {
            buffer.ContentType = ContentType.Unicode;

            var i = 0;

            while (i < text.Length)
            {
                var codepoint = Codepoint.ReadAt(text, i, out var count);

                var cluster = (uint)(text.Start + i);

                if (codepoint.IsBreakChar)
                {
                    if (i + 1 < text.Length)
                    {
                        var nextCodepoint = Codepoint.ReadAt(text, i + 1, out _);

                        if (nextCodepoint == '\r' && codepoint == '\n' || nextCodepoint == '\n' && codepoint == '\r')
                        {
                            count++;

                            buffer.Add('\u200C', cluster);

                            buffer.Add('\u200D', cluster);
                        }
                        else
                        {
                            buffer.Add('\u200C', cluster);
                        }
                    }
                    else
                    {
                        buffer.Add('\u200C', cluster);
                    }
                }
                else
                {
                    buffer.Add(codepoint, cluster);
                }

                i += count;
            }
        }
Exemplo n.º 21
0
        public void ShouldSerializeGlyphs()
        {
            using (var typeface = SKTypeface.FromFile(Path.Combine(PathToFonts, "content-font.ttf")))
                using (var blob = typeface.OpenStream(out var index).ToHarfBuzzBlob())
                    using (var face = new Face(blob, index))
                        using (var font = new Font(face))
                            using (var buffer = new Buffer())
                            {
                                buffer.AddUtf16(SimpleText);

                                buffer.GuessSegmentProperties();

                                font.Shape(buffer);

                                var serializedGlyphs = buffer.SerializeGlyphs();

                                Assert.Equal(SerializedSimpleText, serializedGlyphs);
                            }
        }
Exemplo n.º 22
0
        public void ShouldHaveCorrectContentType()
        {
            using (var typeface = SKTypeface.FromFile(Path.Combine(PathToFonts, "content-font.ttf")))
                using (var blob = typeface.OpenStream(out var index).ToHarfBuzzBlob())
                    using (var face = new Face(blob, index))
                        using (var font = new Font(face))
                            using (var buffer = new Buffer())
                            {
                                Assert.Equal(ContentType.Invalid, buffer.ContentType);

                                buffer.AddUtf8(SimpleText);

                                Assert.Equal(ContentType.Unicode, buffer.ContentType);

                                font.Shape(buffer);

                                Assert.Equal(ContentType.Glyphs, buffer.ContentType);
                            }
        }
Exemplo n.º 23
0
        public SKShaper(SKTypeface typeface)
        {
            Typeface = typeface ?? throw new ArgumentNullException(nameof(typeface));

            int index;

            using (var blob = Typeface.OpenStream(out index).ToHarfBuzzBlob())
                using (var face = new Face(blob, index))
                {
                    face.Index      = index;
                    face.UnitsPerEm = Typeface.UnitsPerEm;

                    font = new Font(face);
                    font.SetScale(FONT_SIZE_SCALE, FONT_SIZE_SCALE);

                    font.SetFunctionsOpenType();
                }

            buffer = new Buffer();
        }
Exemplo n.º 24
0
        public void ShouldDeserializeGlyphs()
        {
            using (var buffer = new Buffer())
            {
                buffer.DeserializeGlyphs(SerializedSimpleText);

                Assert.Equal(SimpleText.Length, buffer.Length);

                Assert.Equal(0u, buffer.GlyphInfos[0].Cluster);
                Assert.Equal(25u, buffer.GlyphInfos[0].Codepoint);

                Assert.Equal(1u, buffer.GlyphInfos[1].Cluster);
                Assert.Equal(26u, buffer.GlyphInfos[1].Codepoint);

                Assert.Equal(2u, buffer.GlyphInfos[2].Cluster);
                Assert.Equal(27u, buffer.GlyphInfos[2].Codepoint);

                Assert.Equal(3u, buffer.GlyphInfos[3].Cluster);
                Assert.Equal(28u, buffer.GlyphInfos[3].Codepoint);
            }
        }
Exemplo n.º 25
0
        public void ShouldAddUtfBySpan()
        {
            using (var buffer = new Buffer())
            {
                var utf8 = Encoding.UTF8.GetBytes("A").AsSpan();

                buffer.AddUtf8(utf8);

                Assert.Equal(1, buffer.Length);

                var utf16 = "B".AsSpan();

                buffer.AddUtf16(utf16);

                Assert.Equal(2, buffer.Length);

                var utf32 = new[] { char.ConvertToUtf32("C", 0) };

                buffer.AddUtf32(utf32);

                Assert.Equal(3, buffer.Length);
            }
        }
Exemplo n.º 26
0
        public ShapedBuffer ShapeText(ReadOnlySlice <char> text, TextShaperOptions options)
        {
            var typeface            = options.Typeface;
            var fontRenderingEmSize = options.FontRenderingEmSize;
            var bidiLevel           = options.BidLevel;
            var culture             = options.Culture;

            using (var buffer = new Buffer())
            {
                buffer.AddUtf16(text.Buffer.Span, text.BufferOffset, text.Length);

                MergeBreakPair(buffer);

                buffer.GuessSegmentProperties();

                buffer.Direction = Direction.LeftToRight; //Always shape LeftToRight

                buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);

                var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font;

                font.Shape(buffer);

                font.GetScale(out var scaleX, out _);

                var textScale = fontRenderingEmSize / scaleX;

                var bufferLength = buffer.Length;

                var shapedBuffer = new ShapedBuffer(text, bufferLength, typeface, fontRenderingEmSize, bidiLevel);

                var glyphInfos = buffer.GetGlyphInfoSpan();

                var glyphPositions = buffer.GetGlyphPositionSpan();

                for (var i = 0; i < bufferLength; i++)
                {
                    var sourceInfo = glyphInfos[i];

                    var glyphIndex = (ushort)sourceInfo.Codepoint;

                    var glyphCluster = (int)(sourceInfo.Cluster);

                    var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale);

                    var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale);

                    if (glyphIndex == 0 && text.Buffer.Span[glyphCluster] == '\t')
                    {
                        glyphIndex = typeface.GetGlyph(' ');

                        glyphAdvance = options.IncrementalTabWidth > 0 ?
                                       options.IncrementalTabWidth :
                                       4 * typeface.GetGlyphAdvance(glyphIndex) * textScale;
                    }

                    var targetInfo = new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset);

                    shapedBuffer[i] = targetInfo;
                }

                return(shapedBuffer);
            }
        }
Exemplo n.º 27
0
 public void Shape(Buffer buffer)
 {
     font.Shape(buffer);
 }
Exemplo n.º 28
0
        public GlyphRun ShapeText(ReadOnlySlice <char> text, TextFormat textFormat)
        {
            using (var buffer = new Buffer())
            {
                buffer.ContentType = ContentType.Unicode;

                var breakCharPosition = text.Length - 1;

                var codepoint = Codepoint.ReadAt(text, breakCharPosition, out var count);

                if (codepoint.IsBreakChar)
                {
                    var breakCharCount = 1;

                    if (text.Length > 1)
                    {
                        var previousCodepoint = Codepoint.ReadAt(text, breakCharPosition - count, out _);

                        if (codepoint == '\r' && previousCodepoint == '\n' ||
                            codepoint == '\n' && previousCodepoint == '\r')
                        {
                            breakCharCount = 2;
                        }
                    }

                    if (breakCharPosition != text.Start)
                    {
                        buffer.AddUtf16(text.Buffer.Span.Slice(0, text.Length - breakCharCount));
                    }

                    var cluster = buffer.GlyphInfos.Length > 0 ?
                                  buffer.GlyphInfos[buffer.Length - 1].Cluster + 1 :
                                  (uint)text.Start;

                    switch (breakCharCount)
                    {
                    case 1:
                        buffer.Add('\u200C', cluster);
                        break;

                    case 2:
                        buffer.Add('\u200C', cluster);
                        buffer.Add('\u200D', cluster);
                        break;
                    }
                }
                else
                {
                    buffer.AddUtf16(text.Buffer.Span);
                }

                buffer.GuessSegmentProperties();

                var glyphTypeface = textFormat.Typeface.GlyphTypeface;

                var font = ((GlyphTypefaceImpl)glyphTypeface.PlatformImpl).Font;

                font.Shape(buffer);

                font.GetScale(out var scaleX, out _);

                var textScale = textFormat.FontRenderingEmSize / scaleX;

                var len = buffer.Length;

                var info = buffer.GetGlyphInfoSpan();

                var pos = buffer.GetGlyphPositionSpan();

                var glyphIndices = new ushort[len];

                var clusters = new ushort[len];

                var glyphAdvances = new double[len];

                var glyphOffsets = new Vector[len];

                for (var i = 0; i < len; i++)
                {
                    glyphIndices[i] = (ushort)info[i].Codepoint;

                    clusters[i] = (ushort)(text.Start + info[i].Cluster);

                    var advanceX = pos[i].XAdvance * textScale;
                    // Depends on direction of layout
                    //var advanceY = pos[i].YAdvance * textScale;

                    glyphAdvances[i] = advanceX;

                    var offsetX = pos[i].XOffset * textScale;
                    var offsetY = pos[i].YOffset * textScale;

                    glyphOffsets[i] = new Vector(offsetX, offsetY);
                }

                return(new GlyphRun(glyphTypeface, textFormat.FontRenderingEmSize,
                                    new ReadOnlySlice <ushort>(glyphIndices),
                                    new ReadOnlySlice <double>(glyphAdvances),
                                    new ReadOnlySlice <Vector>(glyphOffsets),
                                    text,
                                    new ReadOnlySlice <ushort>(clusters)));
            }
        }
Exemplo n.º 29
0
        public GlyphRun ShapeText(ReadOnlySlice <char> text, Typeface typeface, double fontRenderingEmSize, CultureInfo culture)
        {
            using (var buffer = new Buffer())
            {
                buffer.ContentType = ContentType.Unicode;

                var breakCharPosition = text.Length - 1;

                var codepoint = Codepoint.ReadAt(text, breakCharPosition, out var count);

                if (codepoint.IsBreakChar)
                {
                    var breakCharCount = 1;

                    if (text.Length > 1)
                    {
                        var previousCodepoint = Codepoint.ReadAt(text, breakCharPosition - count, out _);

                        if (codepoint == '\r' && previousCodepoint == '\n' ||
                            codepoint == '\n' && previousCodepoint == '\r')
                        {
                            breakCharCount = 2;
                        }
                    }

                    if (breakCharPosition != text.Start)
                    {
                        buffer.AddUtf16(text.Buffer.Span.Slice(0, text.Length - breakCharCount));
                    }

                    var cluster = buffer.GlyphInfos.Length > 0 ?
                                  buffer.GlyphInfos[buffer.Length - 1].Cluster + 1 :
                                  (uint)text.Start;

                    switch (breakCharCount)
                    {
                    case 1:
                        buffer.Add('\u200C', cluster);
                        break;

                    case 2:
                        buffer.Add('\u200C', cluster);
                        buffer.Add('\u200D', cluster);
                        break;
                    }
                }
                else
                {
                    buffer.AddUtf16(text.Buffer.Span);
                }

                buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);

                buffer.GuessSegmentProperties();

                var glyphTypeface = typeface.GlyphTypeface;

                var font = ((GlyphTypefaceImpl)glyphTypeface.PlatformImpl).Font;

                font.Shape(buffer);

                font.GetScale(out var scaleX, out _);

                var textScale = fontRenderingEmSize / scaleX;

                var bufferLength = buffer.Length;

                var glyphInfos = buffer.GetGlyphInfoSpan();

                var glyphPositions = buffer.GetGlyphPositionSpan();

                var glyphIndices = new ushort[bufferLength];

                var clusters = new ushort[bufferLength];

                double[] glyphAdvances = null;

                Vector[] glyphOffsets = null;

                for (var i = 0; i < bufferLength; i++)
                {
                    glyphIndices[i] = (ushort)glyphInfos[i].Codepoint;

                    clusters[i] = (ushort)(text.Start + glyphInfos[i].Cluster);

                    if (!glyphTypeface.IsFixedPitch)
                    {
                        SetAdvance(glyphPositions, i, textScale, ref glyphAdvances);
                    }

                    SetOffset(glyphPositions, i, textScale, ref glyphOffsets);
                }

                return(new GlyphRun(glyphTypeface, fontRenderingEmSize,
                                    new ReadOnlySlice <ushort>(glyphIndices),
                                    new ReadOnlySlice <double>(glyphAdvances),
                                    new ReadOnlySlice <Vector>(glyphOffsets),
                                    text,
                                    new ReadOnlySlice <ushort>(clusters)));
            }
        }
Exemplo n.º 30
0
 public Result Shape(Buffer buffer, SKPaint paint) =>
 Shape(buffer, 0, 0, paint);