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 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)); } }
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; } } } }
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))); } }
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); } }
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))); } }