Ejemplo n.º 1
0
        /// <summary>
        /// Splits the <see cref="TextRun"/> at specified length.
        /// </summary>
        /// <param name="length">The length.</param>
        /// <returns>The split result.</returns>
        public SplitTextCharactersResult Split(int length)
        {
            var glyphCount = 0;

            var firstCharacters = GlyphRun.Characters.Take(length);

            var codepointEnumerator = new CodepointEnumerator(firstCharacters);

            while (codepointEnumerator.MoveNext())
            {
                glyphCount++;
            }

            if (GlyphRun.Characters.Length == length)
            {
                return(new SplitTextCharactersResult(this, null));
            }

            if (GlyphRun.GlyphIndices.Length == glyphCount)
            {
                return(new SplitTextCharactersResult(this, null));
            }

            var firstGlyphRun = new GlyphRun(
                Properties.Typeface.GlyphTypeface,
                Properties.FontRenderingEmSize,
                GlyphRun.GlyphIndices.Take(glyphCount),
                GlyphRun.GlyphAdvances.Take(glyphCount),
                GlyphRun.GlyphOffsets.Take(glyphCount),
                GlyphRun.Characters.Take(length),
                GlyphRun.GlyphClusters.Take(glyphCount));

            var firstTextRun = new ShapedTextCharacters(firstGlyphRun, Properties);

            var secondGlyphRun = new GlyphRun(
                Properties.Typeface.GlyphTypeface,
                Properties.FontRenderingEmSize,
                GlyphRun.GlyphIndices.Skip(glyphCount),
                GlyphRun.GlyphAdvances.Skip(glyphCount),
                GlyphRun.GlyphOffsets.Skip(glyphCount),
                GlyphRun.Characters.Skip(length),
                GlyphRun.GlyphClusters.Skip(glyphCount));

            var secondTextRun = new ShapedTextCharacters(secondGlyphRun, Properties);

            return(new SplitTextCharactersResult(firstTextRun, secondTextRun));
        }
Ejemplo n.º 2
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));
        }