/// <summary> /// Creates a shapeable text run with unique properties. /// </summary> /// <param name="text">The text to create text runs from.</param> /// <param name="defaultProperties">The default text run properties.</param> /// <returns>A list of shapeable text runs.</returns> private ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice <char> text, TextRunProperties defaultProperties) { var defaultTypeface = defaultProperties.Typeface; var currentTypeface = defaultTypeface; if (TryGetRunProperties(text, currentTypeface, defaultTypeface, out var count)) { return(new ShapeableTextCharacters(text.Take(count), new GenericTextRunProperties(currentTypeface, defaultProperties.FontRenderingEmSize, defaultProperties.TextDecorations, defaultProperties.ForegroundBrush))); } var codepoint = Codepoint.ReadAt(text, count, out _); //ToDo: Fix FontFamily fallback var matchFound = FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight, defaultTypeface.FontFamily, defaultProperties.CultureInfo, out currentTypeface); if (matchFound && TryGetRunProperties(text, currentTypeface, defaultTypeface, out count)) { //Fallback found return(new ShapeableTextCharacters(text.Take(count), new GenericTextRunProperties(currentTypeface, defaultProperties.FontRenderingEmSize, defaultProperties.TextDecorations, defaultProperties.ForegroundBrush))); } // no fallback found currentTypeface = defaultTypeface; var glyphTypeface = currentTypeface.GlyphTypeface; var enumerator = new GraphemeEnumerator(text); while (enumerator.MoveNext()) { var grapheme = enumerator.Current; if (!grapheme.FirstCodepoint.IsWhiteSpace && glyphTypeface.TryGetGlyph(grapheme.FirstCodepoint, out _)) { break; } count += grapheme.Text.Length; } return(new ShapeableTextCharacters(text.Take(count), new GenericTextRunProperties(currentTypeface, defaultProperties.FontRenderingEmSize, defaultProperties.TextDecorations, defaultProperties.ForegroundBrush))); }
public void Should_Take() { var buffer = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; var slice = new ReadOnlySlice <int>(buffer); var taken = slice.Take(8); var expected = buffer.Take(8); Assert.Equal(expected, taken); }
/// <summary> /// Creates a span of text run properties that has modifier applied. /// </summary> /// <param name="text">The text to create the properties for.</param> /// <param name="defaultProperties">The default text properties.</param> /// <param name="textModifier">The text properties modifier.</param> /// <returns> /// The created text style run. /// </returns> private static ValueSpan <TextRunProperties> CreateTextStyleRun(ReadOnlySlice <char> text, TextRunProperties defaultProperties, IReadOnlyList <ValueSpan <TextRunProperties> > textModifier) { if (textModifier == null || textModifier.Count == 0) { return(new ValueSpan <TextRunProperties>(text.Start, text.Length, defaultProperties)); } var currentProperties = defaultProperties; var hasOverride = false; var i = 0; var length = 0; for (; i < textModifier.Count; i++) { var propertiesOverride = textModifier[i]; var textRange = new TextRange(propertiesOverride.Start, propertiesOverride.Length); if (textRange.End < text.Start) { continue; } if (textRange.Start > text.End) { length = text.Length; break; } if (textRange.Start > text.Start) { if (propertiesOverride.Value != currentProperties) { length = Math.Min(Math.Abs(textRange.Start - text.Start), text.Length); break; } } length += Math.Min(text.Length - length, textRange.Length); if (hasOverride) { continue; } hasOverride = true; currentProperties = propertiesOverride.Value; } if (length < text.Length && i == textModifier.Count) { if (currentProperties == defaultProperties) { length = text.Length; } } if (length != text.Length) { text = text.Take(length); } return(new ValueSpan <TextRunProperties>(text.Start, length, currentProperties)); }
/// <summary> /// Creates a text style run that has overrides applied. Only overrides with equal TextStyle. /// If optimizeForShaping is <c>true</c> Foreground is ignored. /// </summary> /// <param name="text">The text to create the run for.</param> /// <param name="defaultTextStyle">The default text style for segments that don't have an override.</param> /// <param name="textStyleOverrides">The text style overrides.</param> /// <returns> /// The created text style run. /// </returns> private static TextStyleRun CreateTextStyleRunWithOverride(ReadOnlySlice <char> text, TextStyle defaultTextStyle, IReadOnlyList <TextStyleRun> textStyleOverrides) { if (textStyleOverrides == null || textStyleOverrides.Count == 0) { return(new TextStyleRun(new TextPointer(text.Start, text.Length), defaultTextStyle)); } var currentTextStyle = defaultTextStyle; var hasOverride = false; var i = 0; var length = 0; for (; i < textStyleOverrides.Count; i++) { var styleOverride = textStyleOverrides[i]; var textPointer = styleOverride.TextPointer; if (textPointer.End < text.Start) { continue; } if (textPointer.Start > text.End) { length = text.Length; break; } if (textPointer.Start > text.Start) { if (styleOverride.Style.TextFormat != currentTextStyle.TextFormat || !currentTextStyle.Foreground.Equals(styleOverride.Style.Foreground)) { length = Math.Min(Math.Abs(textPointer.Start - text.Start), text.Length); break; } } length += Math.Min(text.Length - length, textPointer.Length); if (hasOverride) { continue; } hasOverride = true; currentTextStyle = styleOverride.Style; } if (length < text.Length && i == textStyleOverrides.Count) { if (currentTextStyle.Foreground.Equals(defaultTextStyle.Foreground) && currentTextStyle.TextFormat == defaultTextStyle.TextFormat) { length = text.Length; } } if (length != text.Length) { text = text.Take(length); } return(new TextStyleRun(new TextPointer(text.Start, length), currentTextStyle)); }
/// <summary> /// Creates a shapeable text run with unique properties. /// </summary> /// <param name="text">The text to create text runs from.</param> /// <param name="defaultProperties">The default text run properties.</param> /// <param name="biDiLevel">The bidi level of the run.</param> /// <param name="previousProperties"></param> /// <returns>A list of shapeable text runs.</returns> private static ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice <char> text, TextRunProperties defaultProperties, sbyte biDiLevel, ref TextRunProperties?previousProperties) { var defaultTypeface = defaultProperties.Typeface; var currentTypeface = defaultTypeface; var previousTypeface = previousProperties?.Typeface; if (TryGetShapeableLength(text, currentTypeface, null, out var count, out var script)) { if (script == Script.Common && previousTypeface is not null) { if (TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out var fallbackCount, out _)) { return(new ShapeableTextCharacters(text.Take(fallbackCount), defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel)); } } return(new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface), biDiLevel)); } if (previousTypeface is not null) { if (TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out count, out _)) { return(new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel)); } } var codepoint = Codepoint.ReplacementCodepoint; var codepointEnumerator = new CodepointEnumerator(text.Skip(count)); while (codepointEnumerator.MoveNext()) { if (codepointEnumerator.Current.IsWhiteSpace) { continue; } codepoint = codepointEnumerator.Current; break; } //ToDo: Fix FontFamily fallback var matchFound = FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight, defaultTypeface.Stretch, defaultTypeface.FontFamily, defaultProperties.CultureInfo, out currentTypeface); if (matchFound && TryGetShapeableLength(text, currentTypeface, defaultTypeface, out count, out _)) { //Fallback found return(new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface), biDiLevel)); } // no fallback found currentTypeface = defaultTypeface; var glyphTypeface = currentTypeface.GlyphTypeface; var enumerator = new GraphemeEnumerator(text); while (enumerator.MoveNext()) { var grapheme = enumerator.Current; if (!grapheme.FirstCodepoint.IsWhiteSpace && glyphTypeface.TryGetGlyph(grapheme.FirstCodepoint, out _)) { break; } count += grapheme.Text.Length; } return(new ShapeableTextCharacters(text.Take(count), defaultProperties, biDiLevel)); }