Example #1
0
        /// <summary>
        /// Measures the number of characters that fits into available width.
        /// </summary>
        /// <param name="textRun">The text run.</param>
        /// <param name="availableWidth">The available width.</param>
        /// <returns></returns>
        private int MeasureText(ShapedTextRun textRun, double availableWidth)
        {
            if (textRun.GlyphRun.Bounds.Width < availableWidth)
            {
                return(textRun.Text.Length);
            }

            var measuredWidth = 0.0;

            var index = 0;

            for (; index < textRun.GlyphRun.GlyphAdvances.Length; index++)
            {
                var advance = textRun.GlyphRun.GlyphAdvances[index];

                if (measuredWidth + advance > availableWidth)
                {
                    break;
                }

                measuredWidth += advance;
            }

            var cluster = textRun.GlyphRun.GlyphClusters[index];

            var characterHit = textRun.GlyphRun.FindNearestCharacterHit(cluster, out _);

            return(characterHit.FirstCharacterIndex - textRun.GlyphRun.Characters.Start +
                   (textRun.GlyphRun.IsLeftToRight ? characterHit.TrailingLength : 0));
        }
Example #2
0
        /// <summary>
        /// Measures the number of characters that fits into available width.
        /// </summary>
        /// <param name="textRun">The text run.</param>
        /// <param name="availableWidth">The available width.</param>
        /// <returns></returns>
        private static int MeasureText(ShapedTextRun textRun, double availableWidth)
        {
            var glyphRun = textRun.GlyphRun;

            var characterHit = glyphRun.GetCharacterHitFromDistance(availableWidth, out _);

            return(characterHit.FirstCharacterIndex + characterHit.TrailingLength - textRun.Text.Start);
        }
Example #3
0
        /// <summary>
        /// Formats text runs with optional text style overrides.
        /// </summary>
        /// <param name="textSource">The text source.</param>
        /// <param name="firstTextSourceIndex">The first text source index.</param>
        /// <param name="textPointer">The text pointer that covers the formatted text runs.</param>
        /// <returns>
        /// The formatted text runs.
        /// </returns>
        private List <ShapedTextRun> FormatTextRuns(ITextSource textSource, int firstTextSourceIndex, out TextPointer textPointer)
        {
            var start  = -1;
            var length = 0;

            var textRuns = new List <ShapedTextRun>();

            while (true)
            {
                var textRun = textSource.GetTextRun(firstTextSourceIndex + length);

                if (start == -1)
                {
                    start = textRun.Text.Start;
                }

                if (textRun is TextEndOfLine)
                {
                    break;
                }

                switch (textRun)
                {
                case TextCharacters textCharacters:

                    var runText = textCharacters.Text;

                    while (!runText.IsEmpty)
                    {
                        var shapableTextStyleRun = CreateShapableTextStyleRun(runText, textRun.Style);

                        var shapedRun = new ShapedTextRun(runText.Take(shapableTextStyleRun.TextPointer.Length),
                                                          shapableTextStyleRun.Style);

                        textRuns.Add(shapedRun);

                        runText = runText.Skip(shapedRun.Text.Length);
                    }

                    break;

                default:
                    throw new NotSupportedException("Run type not supported by the formatter.");
                }

                length += textRun.Text.Length;
            }

            textPointer = new TextPointer(start, length);

            return(textRuns);
        }
Example #4
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(
                Style.TextFormat.Typeface.GlyphTypeface,
                Style.TextFormat.FontRenderingEmSize,
                GlyphRun.GlyphIndices.Take(glyphCount),
                GlyphRun.GlyphAdvances.Take(glyphCount),
                GlyphRun.GlyphOffsets.Take(glyphCount),
                GlyphRun.Characters.Take(length),
                GlyphRun.GlyphClusters.Take(length));

            var firstTextRun = new ShapedTextRun(firstGlyphRun, Style);

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

            var secondTextRun = new ShapedTextRun(secondGlyphRun, Style);

            return(new SplitTextCharactersResult(firstTextRun, secondTextRun));
        }
Example #5
0
        /// <summary>
        /// Formats text runs with optional text style overrides.
        /// </summary>
        /// <param name="textSource">The text source.</param>
        /// <param name="firstTextSourceIndex">The first text source index.</param>
        /// <param name="textPointer">The text pointer that covers the formatted text runs.</param>
        /// <returns>
        /// The formatted text runs.
        /// </returns>
        private List <ShapedTextRun> FormatTextRuns(ITextSource textSource, int firstTextSourceIndex, out TextPointer textPointer)
        {
            var start = firstTextSourceIndex;

            var textRuns = new List <ShapedTextRun>();

            while (true)
            {
                var textRun = textSource.GetTextRun(firstTextSourceIndex);

                if (textRun.Text.IsEmpty)
                {
                    break;
                }

                if (textRun is TextEndOfLine)
                {
                    break;
                }

                if (!(textRun is TextCharacters))
                {
                    throw new NotSupportedException("Run type not supported by the formatter.");
                }

                var runText = textRun.Text;

                while (!runText.IsEmpty)
                {
                    var shapableTextStyleRun = CreateShapableTextStyleRun(runText, textRun.Style);

                    var shapedRun = new ShapedTextRun(runText.Take(shapableTextStyleRun.TextPointer.Length),
                                                      shapableTextStyleRun.Style);

                    textRuns.Add(shapedRun);

                    runText = runText.Skip(shapedRun.Text.Length);
                }

                firstTextSourceIndex += textRun.Text.Length;
            }

            textPointer = new TextPointer(start, firstTextSourceIndex - start);

            return(textRuns);
        }
Example #6
0
        /// <summary>
        /// Split a sequence of runs into two segments at specified length.
        /// </summary>
        /// <param name="textRuns">The text run's.</param>
        /// <param name="length">The length to split at.</param>
        /// <returns></returns>
        private static SplitTextRunsResult SplitTextRuns(IReadOnlyList <ShapedTextRun> textRuns, int length)
        {
            var currentLength = 0;

            for (var i = 0; i < textRuns.Count; i++)
            {
                var currentRun = textRuns[i];

                if (currentLength + currentRun.GlyphRun.Characters.Length < length)
                {
                    currentLength += currentRun.GlyphRun.Characters.Length;
                    continue;
                }

                var firstCount = currentRun.GlyphRun.Characters.Length >= 1 ? i + 1 : i;

                var first = new ShapedTextRun[firstCount];

                if (firstCount > 1)
                {
                    for (var j = 0; j < i; j++)
                    {
                        first[j] = textRuns[j];
                    }
                }

                var secondCount = textRuns.Count - firstCount;

                if (currentLength + currentRun.GlyphRun.Characters.Length == length)
                {
                    var second = new ShapedTextRun[secondCount];

                    var offset = currentRun.GlyphRun.Characters.Length > 1 ? 1 : 0;

                    if (secondCount > 0)
                    {
                        for (var j = 0; j < secondCount; j++)
                        {
                            second[j] = textRuns[i + j + offset];
                        }
                    }

                    first[i] = currentRun;

                    return(new SplitTextRunsResult(first, second));
                }
                else
                {
                    secondCount++;

                    var second = new ShapedTextRun[secondCount];

                    if (secondCount > 0)
                    {
                        for (var j = 1; j < secondCount; j++)
                        {
                            second[j] = textRuns[i + j];
                        }
                    }

                    var split = currentRun.Split(length - currentLength);

                    first[i] = split.First;

                    second[0] = split.Second;

                    return(new SplitTextRunsResult(first, second));
                }
            }

            return(new SplitTextRunsResult(textRuns, null));
        }
Example #7
0
            public SplitTextCharactersResult(ShapedTextRun first, ShapedTextRun second)
            {
                First = first;

                Second = second;
            }