Beispiel #1
0
        /// <summary>
        /// Format and produce a text line either with or without previously known
        /// line break point.
        /// </summary>
        private TextLine FormatLineInternal(
            TextSource textSource,
            int firstCharIndex,
            int lineLength,
            double paragraphWidth,
            TextParagraphProperties paragraphProperties,
            TextLineBreak previousLineBreak,
            TextRunCache textRunCache
            )
        {
            // prepare formatting settings
            FormatSettings settings = PrepareFormatSettings(
                textSource,
                firstCharIndex,
                paragraphWidth,
                paragraphProperties,
                previousLineBreak,
                textRunCache,
                (lineLength != 0), // Do optimal break if break is given
                true,              // isSingleLineFormatting
                _textFormattingMode
                );

            TextLine textLine = null;

            if (!settings.Pap.AlwaysCollapsible &&
                previousLineBreak == null &&
                lineLength <= 0
                )
            {
                // simple text line.
                textLine = SimpleTextLine.Create(
                    settings,
                    firstCharIndex,
                    RealToIdealFloor(paragraphWidth),
                    textSource.PixelsPerDip
                    ) as TextLine;
            }

            if (textLine == null)
            {
                // content is complex, creating complex line
                textLine = new TextMetrics.FullTextLine(
                    settings,
                    firstCharIndex,
                    lineLength,
                    RealToIdealFloor(paragraphWidth),
                    LineFlags.None
                    ) as TextLine;
            }

            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordText, EventTrace.Level.Verbose, EventTrace.Event.WClientStringEnd, "TextFormatterImp.FormatLineInternal End");

            return(textLine);
        }
Beispiel #2
0
        /// <summary>
        /// Create a fulltext state object from formatting info for subsequent fulltext formatting
        /// e.g. fulltext line, mix/max computation, potential breakpoints for optimal paragraph formatting.
        /// </summary>
        internal static FullTextState Create(
            FormatSettings settings,
            int cpFirst,
            int finiteFormatWidth
            )
        {
            // prepare text stores
            TextStore store = new TextStore(
                settings,
                cpFirst,
                0,
                settings.GetFormatWidth(finiteFormatWidth)
                );

            ParaProp  pap         = settings.Pap;
            TextStore markerStore = null;

            if (pap.FirstLineInParagraph &&
                pap.TextMarkerProperties != null &&
                pap.TextMarkerProperties.TextSource != null)
            {
                // create text store specifically for marker
                markerStore = new TextStore(
                    // create specialized settings for marker store e.g. with marker text source
                    new FormatSettings(
                        settings.Formatter,
                        pap.TextMarkerProperties.TextSource,
                        new TextRunCacheImp(),   // no cross-call run cache available for marker store
                        pap,                     // marker by default use the same paragraph properties
                        null,                    // not consider previousLineBreak
                        true,                    // isSingleLineFormatting
                        settings.TextFormattingMode,
                        settings.IsSideways
                        ),
                    0,                           // marker store always started with cp == 0
                    TextStore.LscpFirstMarker,   // first lscp value for marker text
                    Constants.IdealInfiniteWidth // formatWidth
                    );
            }

            // construct a new fulltext state object
            return(new FullTextState(store, markerStore, settings.IsSideways));
        }
Beispiel #3
0
        /// <summary>
        /// Fetch cached textrun
        /// </summary>
        internal TextRun FetchTextRun(
            FormatSettings settings,
            int cpFetch,
            int cpFirst,
            out int offsetToFirstCp,
            out int runLength
            )
        {
            SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition, cpFetch);

            _latestPosition = textRunSpanRider.SpanPosition;
            TextRun textRun = (TextRun)textRunSpanRider.CurrentElement;

            if (textRun == null)
            {
                // run not already cached, fetch new run and cache it

                textRun = settings.TextSource.GetTextRun(cpFetch);

                if (textRun.Length < 1)
                {
                    throw new ArgumentOutOfRangeException("textRun.Length", SR.Get(SRID.ParameterMustBeGreaterThanZero));
                }

                Plsrun plsrun = TextRunInfo.GetRunType(textRun);

                if (plsrun == Plsrun.Text || plsrun == Plsrun.InlineObject)
                {
                    TextRunProperties properties = textRun.Properties;

                    if (properties == null)
                    {
                        throw new ArgumentException(SR.Get(SRID.TextRunPropertiesCannotBeNull));
                    }

                    if (properties.FontRenderingEmSize <= 0)
                    {
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassMustBeGreaterThanZero, "FontRenderingEmSize", "TextRunProperties"));
                    }

                    double realMaxFontRenderingEmSize = Constants.RealInfiniteWidth / Constants.GreatestMutiplierOfEm;

                    if (properties.FontRenderingEmSize > realMaxFontRenderingEmSize)
                    {
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeGreaterThan, "FontRenderingEmSize", "TextRunProperties", realMaxFontRenderingEmSize));
                    }

                    CultureInfo culture = CultureMapper.GetSpecificCulture(properties.CultureInfo);

                    if (culture == null)
                    {
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeNull, "CultureInfo", "TextRunProperties"));
                    }

                    if (properties.Typeface == null)
                    {
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeNull, "Typeface", "TextRunProperties"));
                    }
                }


                //
                // TextRun is specifial to SpanVector because TextRun also encodes position which needs to be
                // consistent with the positions encoded by SpanVector. In run cache, the begining of a span
                // should always correspond to the begining of a cached text run. If the end of the currently fetched
                // run overlaps with the begining of an already cached run, the begining of the cached run needs to be
                // adjusted as well as its span. Because we can't gurantee the correctness of the overlapped range
                // so we'll simply remove the overlapped runs here.
                //

                // Move the rider to the end of the current run
                textRunSpanRider.At(cpFetch + textRun.Length - 1);
                _latestPosition = textRunSpanRider.SpanPosition;

                if (textRunSpanRider.CurrentElement != _textRunVector.Default)
                {
                    // The end overlaps with one or more cached runs, clear the range from the
                    // begining of the current fetched run to the end of the last overlapped cached run.
                    _latestPosition = _textRunVector.SetReference(
                        cpFetch,
                        textRunSpanRider.CurrentPosition + textRunSpanRider.Length - cpFetch,
                        _textRunVector.Default,
                        _latestPosition
                        );
                }

                _latestPosition = _textRunVector.SetReference(cpFetch, textRun.Length, textRun, _latestPosition);

                // Refresh the rider's SpanPosition following previous SpanVector.SetReference calls
                textRunSpanRider.At(_latestPosition, cpFetch);
            }

            // If the TextRun was obtained from the cache, make sure it has the right PixelsPerDip set on its properties.

            if (textRun.Properties != null)
            {
                textRun.Properties.PixelsPerDip = settings.TextSource.PixelsPerDip;
            }

            offsetToFirstCp = textRunSpanRider.CurrentPosition - textRunSpanRider.CurrentSpanStart;
            runLength       = textRunSpanRider.Length;
            Debug.Assert(textRun != null && runLength > 0, "Invalid run!");

            bool isText = textRun is ITextSymbols;

            if (isText)
            {
                // Chop text run to optimal length so we dont spend forever analysing
                // them all at once.

                int looseCharLength = TextStore.TypicalCharactersPerLine - cpFetch + cpFirst;

                if (looseCharLength <= 0)
                {
                    // this line already exceeds typical line length, incremental fetch goes
                    // about a quarter of the typical length.

                    looseCharLength = (int)Math.Round(TextStore.TypicalCharactersPerLine * 0.25);
                }

                if (runLength > looseCharLength)
                {
                    if (TextRunInfo.GetRunType(textRun) == Plsrun.Text)
                    {
                        //
                        // When chopping the run at the typical line length,
                        // - don't chop in between of higher & lower surrogate
                        // - don't chop combining mark away from its base character
                        // - don't chop joiner from surrounding characters
                        //
                        // Starting from the initial chopping point, we look ahead to find a safe position. We stop at
                        // a limit in case the run consists of many combining mark & joiner. That is rare and doesn't make
                        // much sense in shaping already.
                        //

                        CharacterBufferReference charBufferRef = textRun.CharacterBufferReference;

                        // We look ahead by one more line at most. It is not normal to have
                        // so many combining mark or joiner characters in a row. It doesn't make sense to
                        // look further if so.
                        int lookAheadLimit = Math.Min(runLength, looseCharLength + TextStore.TypicalCharactersPerLine);

                        int  sizeOfChar = 0;
                        int  endOffset  = 0;
                        bool canBreakAfterPrecedingChar = false;

                        for (endOffset = looseCharLength - 1; endOffset < lookAheadLimit; endOffset += sizeOfChar)
                        {
                            CharacterBufferRange charString = new CharacterBufferRange(
                                charBufferRef.CharacterBuffer,
                                charBufferRef.OffsetToFirstChar + offsetToFirstCp + endOffset,
                                runLength - endOffset
                                );

                            int ch = Classification.UnicodeScalar(charString, out sizeOfChar);

                            // We can only safely break if the preceding char is not a joiner character (i.e. can-break-after),
                            // and the current char is not combining or joiner (i.e. can-break-before).
                            if (canBreakAfterPrecedingChar && !Classification.IsCombining(ch) && !Classification.IsJoiner(ch))
                            {
                                break;
                            }

                            canBreakAfterPrecedingChar = !Classification.IsJoiner(ch);
                        }

                        looseCharLength = Math.Min(runLength, endOffset);
                    }

                    runLength = looseCharLength;
                }
            }


            Debug.Assert(

                // valid run found
                runLength > 0

                // non-text run always fetched at run start
                && (isText ||
                    textRunSpanRider.CurrentSpanStart - textRunSpanRider.CurrentPosition == 0)

                // span rider of both text and format point to valid position
                && (textRunSpanRider.Length > 0 && textRunSpanRider.CurrentElement != null),

                "Text run fetching error!"
                );

            return(textRun);
        }
Beispiel #4
0
            /// <summary>
            /// format text line using LS
            /// </summary>
            /// <param name="fullText">state of the full text backing store</param>
            /// <param name="cpFirst">first cp to format</param>
            /// <param name="lineLength">character length of the line</param>
            /// <param name="formatWidth">width used to format</param>
            /// <param name="finiteFormatWidth">width used to detect overflowing of format result</param>
            /// <param name="paragraphWidth">paragraph width</param>
            /// <param name="lineFlags">line formatting control flags</param>
            /// <param name="collapsingSymbol">line end collapsing symbol</param>
            private void FormatLine(
                FullTextState fullText,
                int cpFirst,
                int lineLength,
                int formatWidth,
                int finiteFormatWidth,
                int paragraphWidth,
                LineFlags lineFlags,
                FormattedTextSymbols collapsingSymbol
                )
            {
                _fullText       = fullText;
                _paragraphWidth = paragraphWidth;

                TextStore      store    = fullText.TextStore;
                FormatSettings settings = store.Settings;
                ParaProp       pap      = settings.Pap;

                _paragraphTextDecorations = pap.TextDecorations;
                if (_paragraphTextDecorations != null)
                {
                    if (_paragraphTextDecorations.Count != 0)
                    {
                        _defaultTextDecorationsBrush = pap.DefaultTextDecorationsBrush;
                    }
                    else
                    {
                        _paragraphTextDecorations = null;
                    }
                }

                _metrics = GetLineMetrics(fullText, cpFirst, lineLength, formatWidth, finiteFormatWidth, paragraphWidth, lineFlags, collapsingSymbol);

                lineLength = _metrics._cchLength;
                if (lineLength > _metrics._cchNewline)
                {
                    var lineBreakpoints = store.FindLineBreakpoints(cpFirst, lineLength);

                    // check for line wrap
                    if (pap.Wrap && _metrics._textStart + _metrics._textWidthAtTrailing > finiteFormatWidth)
                    {
                        for (int i = lineLength - 1; i > 0; i--)
                        {
                            if (lineBreakpoints.GetBreakConditionBefore(i + cpFirst) == DWriteBreakCondition.CanBreak)
                            {
                                var trailingWhitespace = lineBreakpoints.WhitespaceLengthBefore(i + cpFirst);
                                var trimmedMetrics     = GetLineMetrics(fullText, cpFirst, i - trailingWhitespace, formatWidth, finiteFormatWidth, paragraphWidth, lineFlags, collapsingSymbol);
                                if (trimmedMetrics._textStart + trimmedMetrics._textWidthAtTrailing <= finiteFormatWidth)
                                {
                                    _metrics = GetLineMetrics(fullText, cpFirst, i, formatWidth, finiteFormatWidth, paragraphWidth, lineFlags, collapsingSymbol);
                                    _metrics._cchTrailing         = trailingWhitespace;
                                    _metrics._textWidthAtTrailing = trimmedMetrics._textWidth;
                                    break;
                                }
                            }
                        }
                    }
                }

                if (collapsingSymbol == null)
                {
                    // overflow detection for potential collapsible line
                    if (_metrics._textStart + _metrics._textWidthAtTrailing > finiteFormatWidth)
                    {
                        bool hasOverflowed = true;
                        if (_textFormattingMode == TextFormattingMode.Display)
                        {
                            // apply display-mode rounding before checking for overflow
                            double realWidth       = Width;
                            double realFormatWidth = _metrics._formatter.IdealToReal(finiteFormatWidth, PixelsPerDip);
                            hasOverflowed = (TextFormatterImp.CompareReal(realWidth, realFormatWidth, PixelsPerDip, _textFormattingMode) > 0);
                        }

                        if (hasOverflowed)
                        {
                            // line has overflowed
                            _statusFlags |= StatusFlags.HasOverflowed;
                        }
                    }
                }
            }
Beispiel #5
0
            // For a given line length, return TextMetrics for as manu characters fit in the width
            private TextMetrics GetLineMetrics(
                FullTextState fullText,
                int cpFirst,
                int lineLength,
                int formatWidth,
                int finiteFormatWidth,
                int paragraphWidth,
                LineFlags lineFlags,
                FormattedTextSymbols collapsingSymbol
                )
            {
                TextMetrics result = new TextMetrics();

                result._formatter = fullText.Formatter;
                Debug.Assert(result._formatter != null);

                TextStore      store        = fullText.TextStore;
                FormatSettings settings     = store.Settings;
                ParaProp       pap          = settings.Pap;
                var            pixelsPerDip = settings.TextSource.PixelsPerDip;

                int pos             = cpFirst;
                int content_ascent  = 0;
                int content_descent = 0;

                while (lineLength <= 0 || cpFirst + lineLength > pos)
                {
                    TextRun textRun;
                    int     runLength;
                    CharacterBufferRange chars = settings.FetchTextRun(pos, cpFirst, out textRun, out runLength);

                    if (lineLength > 0 && pos + runLength > cpFirst + lineLength)
                    {
                        runLength = cpFirst + lineLength - pos;
                        chars     = new CharacterBufferRange(chars, 0, runLength);
                    }

                    if (textRun is TextEndOfParagraph || textRun is TextEndOfLine)
                    {
                        pos += runLength;
                        result._cchNewline = runLength;
                        break;
                    }

                    TextMetrics runMetrics = GetRunMetrics(fullText, textRun, chars);

                    if (content_ascent < runMetrics._textAscent)
                    {
                        content_ascent = runMetrics._textAscent;
                    }

                    if (content_descent < runMetrics._textHeight - runMetrics._textAscent)
                    {
                        content_descent = runMetrics._textHeight - runMetrics._textAscent;
                    }

                    result._textWidth += runMetrics._textWidth;

                    pos += runLength;
                }

                result._pixelsPerDip        = pixelsPerDip;
                result._cchLength           = pos - cpFirst;
                result._textWidthAtTrailing = result._textWidth;                 // will be set later by FormatLine

                if (pap.LineHeight > 0)
                {
                    // Host specifies line height, honor it.
                    result._height         = pap.LineHeight;
                    result._baselineOffset = (int)Math.Round(
                        result._height
                        * pap.DefaultTypeface.Baseline(pap.EmSize, Constants.DefaultIdealToReal, pixelsPerDip, _textFormattingMode)
                        / pap.DefaultTypeface.LineSpacing(pap.EmSize, Constants.DefaultIdealToReal, pixelsPerDip, _textFormattingMode)
                        );
                }

                if (content_ascent > 0)
                {
                    result._textAscent = content_ascent;
                    result._textHeight = content_ascent + content_descent;

                    // TODO: VerticalAdjust
                }
                else
                {
                    // Line is empty so text height and text baseline are based on the default typeface;
                    // it doesn't make sense even for an emtpy line to have zero text height
                    result._textAscent = (int)Math.Round(pap.DefaultTypeface.Baseline(pap.EmSize, Constants.DefaultIdealToReal, pixelsPerDip, _textFormattingMode));
                    result._textHeight = (int)Math.Round(pap.DefaultTypeface.LineSpacing(pap.EmSize, Constants.DefaultIdealToReal, pixelsPerDip, _textFormattingMode));
                }

                if (result._height <= 0)
                {
                    result._height         = result._textHeight;
                    result._baselineOffset = result._textAscent;
                }

                return(result);
            }