| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 | 
							- using System;
 
- using System.Buffers;
 
- using System.Globalization;
 
- using System.Runtime.InteropServices;
 
- using Avalonia.Media.TextFormatting;
 
- using Avalonia.Media.TextFormatting.Unicode;
 
- using Avalonia.Platform;
 
- using HarfBuzzSharp;
 
- using Buffer = HarfBuzzSharp.Buffer;
 
- using GlyphInfo = HarfBuzzSharp.GlyphInfo;
 
- namespace Avalonia.UnitTests
 
- {
 
-     internal class HarfBuzzTextShaperImpl : ITextShaperImpl
 
-     {
 
-         public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions options)
 
-         {
 
-             var textSpan = text.Span;
 
-             var typeface = options.Typeface;
 
-             var fontRenderingEmSize = options.FontRenderingEmSize;
 
-             var bidiLevel = options.BidiLevel;
 
-             var culture = options.Culture;
 
-             using (var buffer = new Buffer())
 
-             {
 
-                 // HarfBuzz needs the surrounding characters to correctly shape the text
 
-                 var containingText = GetContainingMemory(text, out var start, out var length);
 
-                 buffer.AddUtf16(containingText.Span, start, 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).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 targetInfos = shapedBuffer.GlyphInfos;
 
-                 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) + options.LetterSpacing;
 
-                     var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale);
 
-                     if (textSpan[i] == '\t')
 
-                     {
 
-                         glyphIndex = typeface.GetGlyph(' ');
 
-                         glyphAdvance = options.IncrementalTabWidth > 0 ?
 
-                             options.IncrementalTabWidth :
 
-                             4 * typeface.GetGlyphAdvance(glyphIndex) * textScale;
 
-                     }
 
-                     targetInfos[i] = new Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset);
 
-                 }
 
-                 return shapedBuffer;
 
-             }
 
-         }
 
-         private static void MergeBreakPair(Buffer buffer)
 
-         {
 
-             var length = buffer.Length;
 
-             var glyphInfos = buffer.GetGlyphInfoSpan();
 
-             var second = glyphInfos[length - 1];
 
-             if (!new Codepoint(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;
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-         private static Vector GetGlyphOffset(ReadOnlySpan<GlyphPosition> glyphPositions, int index, double textScale)
 
-         {
 
-             var position = glyphPositions[index];
 
-             var offsetX = position.XOffset * textScale;
 
-             var offsetY = position.YOffset * textScale;
 
-             return new Vector(offsetX, offsetY);
 
-         }
 
-         private static double GetGlyphAdvance(ReadOnlySpan<GlyphPosition> glyphPositions, int index, double textScale)
 
-         {
 
-             // Depends on direction of layout
 
-             // glyphPositions[index].YAdvance * textScale;
 
-             return glyphPositions[index].XAdvance * textScale;
 
-         }
 
-         private static ReadOnlyMemory<char> GetContainingMemory(ReadOnlyMemory<char> memory, out int start, out int length)
 
-         {
 
-             if (MemoryMarshal.TryGetString(memory, out var containingString, out start, out length))
 
-             {
 
-                 return containingString.AsMemory();
 
-             }
 
-             if (MemoryMarshal.TryGetArray(memory, out var segment))
 
-             {
 
-                 start = segment.Offset;
 
-                 length = segment.Count;
 
-                 return segment.Array.AsMemory();
 
-             }
 
-             if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<char> memoryManager, out start, out length))
 
-             {
 
-                 return memoryManager.Memory;
 
-             }
 
-             // should never happen
 
-             throw new InvalidOperationException("Memory not backed by string, array or manager");
 
-         }
 
-     }
 
- }
 
 
  |