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); } }
public void Should_Get_CharacterHit_From_Distance(string text, sbyte direction) { using (Start()) { var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, direction, CultureInfo.CurrentCulture); var shapedBuffer = TextShaper.Current.ShapeText(text.AsMemory(), options); var glyphRun = CreateGlyphRun(shapedBuffer); if (glyphRun.IsLeftToRight) { var characterHit = glyphRun.GetCharacterHitFromDistance(glyphRun.Metrics.WidthIncludingTrailingWhitespace, out _); Assert.Equal(glyphRun.Characters.Length, characterHit.FirstCharacterIndex + characterHit.TrailingLength); } else { shapedBuffer.GlyphInfos.Span.Reverse(); var characterHit = glyphRun.GetCharacterHitFromDistance(0, out _); Assert.Equal(glyphRun.Characters.Length, characterHit.FirstCharacterIndex + characterHit.TrailingLength); } var rects = BuildRects(glyphRun); var lastCluster = -1; var index = 0; if (!glyphRun.IsLeftToRight) { rects.Reverse(); } foreach (var rect in rects) { var currentCluster = glyphRun.GlyphClusters[index]; while (currentCluster == lastCluster && index + 1 < glyphRun.GlyphClusters.Count) { currentCluster = glyphRun.GlyphClusters[++index]; } //Non trailing edge var distance = glyphRun.IsLeftToRight ? rect.Left : rect.Right; var characterHit = glyphRun.GetCharacterHitFromDistance(distance, out _); Assert.Equal(currentCluster, characterHit.FirstCharacterIndex + characterHit.TrailingLength); lastCluster = currentCluster; index++; } } }
public ShapedBuffer ShapeText(ReadOnlySlice <char> text, TextShaperOptions options) { var typeface = options.Typeface; var fontRenderingEmSize = options.FontRenderingEmSize; var bidiLevel = options.BidiLevel; return(new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel)); }
public void Should_Apply_IncrementalTabWidth() { using (Start()) { var text = "\t".AsMemory(); var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 12, 0, CultureInfo.CurrentCulture, 100); var shapedBuffer = TextShaper.Current.ShapeText(text, options); Assert.Equal(shapedBuffer.Length, text.Length); Assert.Equal(100, shapedBuffer.GlyphAdvances[0]); } }
public void Should_Form_Clusters_For_BreakPairs() { using (Start()) { var text = "\n\r\n".AsMemory(); var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 12, 0, CultureInfo.CurrentCulture); var shapedBuffer = TextShaper.Current.ShapeText(text, options); Assert.Equal(shapedBuffer.Text.Length, text.Length); Assert.Equal(shapedBuffer.GlyphClusters.Count, text.Length); Assert.Equal(0, shapedBuffer.GlyphClusters[0]); Assert.Equal(1, shapedBuffer.GlyphClusters[1]); Assert.Equal(1, shapedBuffer.GlyphClusters[2]); } }
public void Should_Get_Previous_CharacterHit(string text, sbyte direction) { using (Start()) { var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, direction, CultureInfo.CurrentCulture); var shapedBuffer = TextShaper.Current.ShapeText(text.AsMemory(), options); var glyphRun = CreateGlyphRun(shapedBuffer); var characterHit = new CharacterHit(text.Length); var rects = BuildRects(glyphRun); rects.Reverse(); if (glyphRun.IsLeftToRight) { foreach (var rect in rects) { characterHit = glyphRun.GetPreviousCaretCharacterHit(characterHit); var distance = glyphRun.GetDistanceFromCharacterHit(characterHit); Assert.Equal(rect.Left, distance); } } else { shapedBuffer.GlyphInfos.Span.Reverse(); foreach (var rect in rects) { characterHit = glyphRun.GetPreviousCaretCharacterHit(characterHit); var distance = glyphRun.GetDistanceFromCharacterHit(characterHit); Assert.Equal(rect.Right, distance); } } } }
public ShapedBuffer ShapeText(ReadOnlySlice <char> text, TextShaperOptions options) { var typeface = options.Typeface; var fontRenderingEmSize = options.FontRenderingEmSize; var bidiLevel = options.BidLevel; var shapedBuffer = new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel); for (var i = 0; i < shapedBuffer.Length;) { var glyphCluster = i + text.Start; var codepoint = Codepoint.ReadAt(text, i, out var count); var glyphIndex = typeface.GetGlyph(codepoint); shapedBuffer[i] = new GlyphInfo(glyphIndex, glyphCluster, 10); i += count; } return(shapedBuffer); }
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); } }