Beispiel #1
0
        /// <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);
        }
Beispiel #3
0
            /// <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));
            }
Beispiel #4
0
            /// <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));
            }
Beispiel #5
0
        /// <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));
        }