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);
            }
        }
示例#2
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));
            }
        }
示例#3
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;
                    }
                }
            }
        }
示例#4
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)));
            }
        }
示例#5
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);
            }
        }
示例#6
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)));
            }
        }