Пример #1
0
        /// <inheritdoc cref="TextFormatter.FormatLine"/>
        public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth,
                                            TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak = null)
        {
            var textWrapping = paragraphProperties.TextWrapping;

            var textRuns = FetchTextRuns(textSource, firstTextSourceIndex, previousLineBreak, out var nextLineBreak);

            var textRange = GetTextRange(textRuns);

            TextLine textLine;

            switch (textWrapping)
            {
            case TextWrapping.NoWrap:
            {
                var textLineMetrics =
                    TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties);

                textLine = new TextLineImpl(textRuns, textLineMetrics, nextLineBreak);
                break;
            }

            case TextWrapping.WrapWithOverflow:
            case TextWrapping.Wrap:
            {
                textLine = PerformTextWrapping(textRuns, textRange, paragraphWidth, paragraphProperties);
                break;
            }

            default:
                throw new ArgumentOutOfRangeException();
            }

            return(textLine);
        }
Пример #2
0
        /// <inheritdoc cref="TextFormatter.FormatLine"/>
        public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth,
                                            TextParagraphProperties paragraphProperties)
        {
            var      textTrimming = paragraphProperties.TextTrimming;
            var      textWrapping = paragraphProperties.TextWrapping;
            TextLine textLine;

            var textRuns = FormatTextRuns(textSource, firstTextSourceIndex, out var textPointer);

            if (textTrimming != TextTrimming.None)
            {
                textLine = PerformTextTrimming(textPointer, textRuns, paragraphWidth, paragraphProperties);
            }
            else
            {
                if (textWrapping == TextWrapping.Wrap)
                {
                    textLine = PerformTextWrapping(textPointer, textRuns, paragraphWidth, paragraphProperties);
                }
                else
                {
                    var textLineMetrics =
                        TextLineMetrics.Create(textRuns, paragraphWidth, paragraphProperties.TextAlignment);

                    textLine = new SimpleTextLine(textPointer, textRuns, textLineMetrics);
                }
            }

            return(textLine);
        }
Пример #3
0
 public TextLineImpl(IReadOnlyList <ShapedTextCharacters> textRuns, TextLineMetrics lineMetrics,
                     TextLineBreak lineBreak = null)
 {
     _textRuns   = textRuns;
     LineMetrics = lineMetrics;
     LineBreak   = lineBreak;
 }
Пример #4
0
 public TextLineImpl(List <ShapedTextCharacters> textRuns, TextLineMetrics lineMetrics,
                     TextLineBreak lineBreak = null, bool hasCollapsed = false)
 {
     _textRuns     = textRuns;
     LineMetrics   = lineMetrics;
     TextLineBreak = lineBreak;
     HasCollapsed  = hasCollapsed;
 }
Пример #5
0
        public TextLineImpl FinalizeLine()
        {
            BidiReorder();

            _textLineMetrics = CreateLineMetrics();

            return(this);
        }
Пример #6
0
        /// <summary>
        /// Creates an empty text line.
        /// </summary>
        /// <returns>The empty text line.</returns>
        private TextLine CreateEmptyTextLine(int startingIndex)
        {
            var textFormat = _paragraphProperties.DefaultTextStyle.TextFormat;

            var glyphRun = TextShaper.Current.ShapeText(s_empty, textFormat);

            var textRuns = new[] { new ShapedTextRun(glyphRun, _paragraphProperties.DefaultTextStyle) };

            return(new SimpleTextLine(new TextPointer(startingIndex, 0), textRuns,
                                      TextLineMetrics.Create(textRuns, MaxWidth, _paragraphProperties.TextAlignment)));
        }
Пример #7
0
        /// <summary>
        /// Creates an empty text line.
        /// </summary>
        /// <returns>The empty text line.</returns>
        private TextLine CreateEmptyTextLine(int startingIndex)
        {
            var properties = _paragraphProperties.DefaultTextRunProperties;

            var glyphRun = TextShaper.Current.ShapeText(new ReadOnlySlice <char>(s_empty, startingIndex, 1),
                                                        properties.Typeface, properties.FontRenderingEmSize, properties.CultureInfo);

            var textRuns = new[] { new ShapedTextCharacters(glyphRun, _paragraphProperties.DefaultTextRunProperties) };

            return(new TextLineImpl(textRuns,
                                    TextLineMetrics.Create(textRuns, new TextRange(startingIndex, 1), MaxWidth, _paragraphProperties)));
        }
Пример #8
0
        public TextLineImpl(List <ShapedTextCharacters> textRuns, TextRange textRange, double paragraphWidth,
                            TextParagraphProperties paragraphProperties, TextLineBreak?lineBreak = null, bool hasCollapsed = false)
        {
            TextRange     = textRange;
            TextLineBreak = lineBreak;
            HasCollapsed  = hasCollapsed;

            _shapedTextRuns      = textRuns;
            _paragraphWidth      = paragraphWidth;
            _paragraphProperties = paragraphProperties;

            _textLineMetrics = CreateLineMetrics();
        }
Пример #9
0
        /// <inheritdoc cref="TextFormatter.FormatLine"/>
        public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth,
                                            TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak = null)
        {
            var      textTrimming = paragraphProperties.TextTrimming;
            var      textWrapping = paragraphProperties.TextWrapping;
            TextLine textLine     = null;

            var textRuns = FetchTextRuns(textSource, firstTextSourceIndex, previousLineBreak, out var nextLineBreak);

            var textRange = GetTextRange(textRuns);

            if (textTrimming != TextTrimming.None)
            {
                textLine = PerformTextTrimming(textRuns, textRange, paragraphWidth, paragraphProperties);
            }
            else
            {
                switch (textWrapping)
                {
                case TextWrapping.NoWrap:
                {
                    var textLineMetrics =
                        TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties);

                    textLine = new TextLineImpl(textRuns, textLineMetrics, nextLineBreak);
                    break;
                }

                case TextWrapping.WrapWithOverflow:
                case TextWrapping.Wrap:
                {
                    textLine = PerformTextWrapping(textRuns, textRange, paragraphWidth, paragraphProperties);
                    break;
                }
                }
            }

            return(textLine);
        }
Пример #10
0
        /// <summary>
        /// Performs text wrapping returns a list of text lines.
        /// </summary>
        /// <param name="textRuns">The text run's.</param>
        /// <param name="textRange">The text range that is covered by the text runs.</param>
        /// <param name="paragraphWidth">The paragraph width.</param>
        /// <param name="paragraphProperties">The text paragraph properties.</param>
        /// <returns>The wrapped text line.</returns>
        private static TextLine PerformTextWrapping(IReadOnlyList <ShapedTextCharacters> textRuns, TextRange textRange,
                                                    double paragraphWidth, TextParagraphProperties paragraphProperties)
        {
            var availableWidth = paragraphWidth;
            var currentWidth   = 0.0;
            var runIndex       = 0;
            var currentLength  = 0;

            while (runIndex < textRuns.Count)
            {
                var currentRun = textRuns[runIndex];

                if (currentWidth + currentRun.GlyphRun.Bounds.Width > availableWidth)
                {
                    var measuredLength = MeasureCharacters(currentRun, paragraphWidth - currentWidth);

                    var breakFound = false;

                    var currentBreakPosition = 0;

                    if (measuredLength < currentRun.Text.Length)
                    {
                        var lineBreaker = new LineBreakEnumerator(currentRun.Text);

                        while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
                        {
                            var nextBreakPosition = lineBreaker.Current.PositionWrap;

                            if (nextBreakPosition == 0 || nextBreakPosition > measuredLength)
                            {
                                break;
                            }

                            breakFound = lineBreaker.Current.Required ||
                                         lineBreaker.Current.PositionWrap != currentRun.Text.Length;

                            currentBreakPosition = nextBreakPosition;
                        }
                    }

                    if (breakFound)
                    {
                        measuredLength = currentBreakPosition;
                    }
                    else
                    {
                        if (paragraphProperties.TextWrapping == TextWrapping.WrapWithOverflow)
                        {
                            var lineBreaker = new LineBreakEnumerator(currentRun.Text.Skip(currentBreakPosition));

                            if (lineBreaker.MoveNext())
                            {
                                measuredLength = currentBreakPosition + lineBreaker.Current.PositionWrap;
                            }
                        }
                    }

                    currentLength += measuredLength;

                    var splitResult = SplitTextRuns(textRuns, currentLength);

                    var textLineMetrics = TextLineMetrics.Create(splitResult.First,
                                                                 new TextRange(textRange.Start, currentLength), paragraphWidth, paragraphProperties);

                    var lineBreak = splitResult.Second != null && splitResult.Second.Count > 0 ?
                                    new TextLineBreak(splitResult.Second) :
                                    null;

                    return(new TextLineImpl(splitResult.First, textLineMetrics, lineBreak));
                }

                currentWidth += currentRun.GlyphRun.Bounds.Width;

                currentLength += currentRun.GlyphRun.Characters.Length;

                runIndex++;
            }

            return(new TextLineImpl(textRuns,
                                    TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties)));
        }
Пример #11
0
        /// <inheritdoc/>
        public override TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList)
        {
            if (collapsingPropertiesList == null || collapsingPropertiesList.Length == 0)
            {
                return(this);
            }

            var             collapsingProperties = collapsingPropertiesList[0];
            var             runIndex             = 0;
            var             currentWidth         = 0.0;
            var             textRange            = TextRange;
            var             collapsedLength      = 0;
            TextLineMetrics textLineMetrics;

            var shapedSymbol = CreateShapedSymbol(collapsingProperties.Symbol);

            var availableWidth = collapsingProperties.Width - shapedSymbol.Size.Width;

            while (runIndex < _textRuns.Count)
            {
                var currentRun = _textRuns[runIndex];

                currentWidth += currentRun.Size.Width;

                if (currentWidth > availableWidth)
                {
                    if (TextFormatterImpl.TryMeasureCharacters(currentRun, availableWidth, out var measuredLength))
                    {
                        if (collapsingProperties.Style == TextCollapsingStyle.TrailingWord && measuredLength < textRange.End)
                        {
                            var currentBreakPosition = 0;

                            var lineBreaker = new LineBreakEnumerator(currentRun.Text);

                            while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
                            {
                                var nextBreakPosition = lineBreaker.Current.PositionWrap;

                                if (nextBreakPosition == 0)
                                {
                                    break;
                                }

                                if (nextBreakPosition > measuredLength)
                                {
                                    break;
                                }

                                currentBreakPosition = nextBreakPosition;
                            }

                            measuredLength = currentBreakPosition;
                        }
                    }

                    collapsedLength += measuredLength;

                    var splitResult = TextFormatterImpl.SplitTextRuns(_textRuns, collapsedLength);

                    var shapedTextCharacters = new List <ShapedTextCharacters>(splitResult.First.Count + 1);

                    shapedTextCharacters.AddRange(splitResult.First);

                    shapedTextCharacters.Add(shapedSymbol);

                    textRange = new TextRange(textRange.Start, collapsedLength);

                    var shapedWidth = GetShapedWidth(shapedTextCharacters);

                    textLineMetrics = new TextLineMetrics(new Size(shapedWidth, LineMetrics.Size.Height),
                                                          LineMetrics.TextBaseline, textRange, false);

                    return(new TextLineImpl(shapedTextCharacters, textLineMetrics, TextLineBreak, true));
                }

                availableWidth -= currentRun.Size.Width;

                collapsedLength += currentRun.GlyphRun.Characters.Length;

                runIndex++;
            }

            textLineMetrics =
                new TextLineMetrics(LineMetrics.Size.WithWidth(LineMetrics.Size.Width + shapedSymbol.Size.Width),
                                    LineMetrics.TextBaseline, TextRange, LineMetrics.HasOverflowed);

            return(new TextLineImpl(new List <ShapedTextCharacters>(_textRuns)
            {
                shapedSymbol
            }, textLineMetrics, null,
                                    true));
        }
Пример #12
0
 public SimpleTextLine(TextPointer textPointer, IReadOnlyList <ShapedTextRun> textRuns, TextLineMetrics lineMetrics)
 {
     Text        = textPointer;
     _textRuns   = textRuns;
     LineMetrics = lineMetrics;
 }
Пример #13
0
        /// <summary>
        /// Performs text trimming and returns a trimmed line.
        /// </summary>
        /// <param name="textRuns">The text runs to perform the trimming on.</param>
        /// <param name="textRange">The text range that is covered by the text runs.</param>
        /// <param name="paragraphWidth">A <see cref="double"/> value that specifies the width of the paragraph that the line fills.</param>
        /// <param name="paragraphProperties">A <see cref="TextParagraphProperties"/> value that represents paragraph properties,
        /// such as TextWrapping, TextAlignment, or TextStyle.</param>
        /// <returns></returns>
        private static TextLine PerformTextTrimming(IReadOnlyList <ShapedTextCharacters> textRuns, TextRange textRange,
                                                    double paragraphWidth, TextParagraphProperties paragraphProperties)
        {
            var textTrimming   = paragraphProperties.TextTrimming;
            var availableWidth = paragraphWidth;
            var currentWidth   = 0.0;
            var runIndex       = 0;

            while (runIndex < textRuns.Count)
            {
                var currentRun = textRuns[runIndex];

                currentWidth += currentRun.GlyphRun.Bounds.Width;

                if (currentWidth > availableWidth)
                {
                    var ellipsisRun = CreateEllipsisRun(currentRun.Properties);

                    var measuredLength = MeasureText(currentRun, availableWidth - ellipsisRun.GlyphRun.Bounds.Width);

                    if (textTrimming == TextTrimming.WordEllipsis)
                    {
                        if (measuredLength < textRange.End)
                        {
                            var currentBreakPosition = 0;

                            var lineBreaker = new LineBreakEnumerator(currentRun.Text);

                            while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
                            {
                                var nextBreakPosition = lineBreaker.Current.PositionWrap;

                                if (nextBreakPosition == 0)
                                {
                                    break;
                                }

                                if (nextBreakPosition > measuredLength)
                                {
                                    break;
                                }

                                currentBreakPosition = nextBreakPosition;
                            }

                            measuredLength = currentBreakPosition;
                        }
                    }

                    var splitResult = SplitTextRuns(textRuns, measuredLength);

                    var trimmedRuns = new List <ShapedTextCharacters>(splitResult.First.Count + 1);

                    trimmedRuns.AddRange(splitResult.First);

                    trimmedRuns.Add(ellipsisRun);

                    var textLineMetrics =
                        TextLineMetrics.Create(trimmedRuns, textRange, paragraphWidth, paragraphProperties);

                    return(new TextLineImpl(trimmedRuns, textLineMetrics));
                }

                availableWidth -= currentRun.GlyphRun.Bounds.Width;

                runIndex++;
            }

            return(new TextLineImpl(textRuns,
                                    TextLineMetrics.Create(textRuns, textRange, paragraphWidth, paragraphProperties)));
        }
Пример #14
0
        /// <summary>
        /// Performs text wrapping returns a list of text lines.
        /// </summary>
        /// <param name="paragraphProperties">The text paragraph properties.</param>
        /// <param name="textRuns">The text run'S.</param>
        /// <param name="text">The text to analyze for break opportunities.</param>
        /// <param name="paragraphWidth"></param>
        /// <returns></returns>
        private static TextLine PerformTextWrapping(TextPointer text, IReadOnlyList <ShapedTextRun> textRuns,
                                                    double paragraphWidth, TextParagraphProperties paragraphProperties)
        {
            var availableWidth = paragraphWidth;
            var currentWidth   = 0.0;
            var runIndex       = 0;
            var length         = 0;

            while (runIndex < textRuns.Count)
            {
                var currentRun = textRuns[runIndex];

                if (currentWidth + currentRun.GlyphRun.Bounds.Width > availableWidth)
                {
                    var measuredLength = MeasureText(currentRun, paragraphWidth - currentWidth);

                    if (measuredLength < currentRun.Text.Length)
                    {
                        var currentBreakPosition = -1;

                        var lineBreaker = new LineBreakEnumerator(currentRun.Text);

                        while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
                        {
                            var nextBreakPosition = lineBreaker.Current.PositionWrap;

                            if (nextBreakPosition == 0)
                            {
                                break;
                            }

                            if (nextBreakPosition > measuredLength)
                            {
                                break;
                            }

                            currentBreakPosition = nextBreakPosition;
                        }

                        if (currentBreakPosition != -1)
                        {
                            measuredLength = currentBreakPosition;
                        }
                    }

                    length += measuredLength;

                    var splitResult = SplitTextRuns(textRuns, length);

                    var textLineMetrics =
                        TextLineMetrics.Create(splitResult.First, paragraphWidth, paragraphProperties.TextAlignment);

                    return(new SimpleTextLine(text.Take(length), splitResult.First, textLineMetrics));
                }

                currentWidth += currentRun.GlyphRun.Bounds.Width;

                length += currentRun.GlyphRun.Characters.Length;

                runIndex++;
            }

            return(new SimpleTextLine(text, textRuns,
                                      TextLineMetrics.Create(textRuns, paragraphWidth, paragraphProperties.TextAlignment)));
        }
Пример #15
0
        /// <inheritdoc/>
        public override void Justify(JustificationProperties justificationProperties)
        {
            justificationProperties.Justify(this);

            _textLineMetrics = CreateLineMetrics();
        }