/// <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)); }
/// <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); }
/// <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); }
/// <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)); }
/// <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); }
/// <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)); }
public SplitTextCharactersResult(ShapedTextRun first, ShapedTextRun second) { First = first; Second = second; }