예제 #1
0
        /// <summary>
        /// Get text immediately preceding cpLimit.
        /// </summary>
        internal TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(TextSource textSource, int cpLimit)
        {
            if (cpLimit > 0)
            {
                SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition);
                if (textRunSpanRider.At(cpLimit - 1))
                {
                    CharacterBufferRange charString = CharacterBufferRange.Empty;
                    CultureInfo          culture    = null;

                    TextRun run = textRunSpanRider.CurrentElement as TextRun;

                    if (run != null)
                    {
                        // Only TextRun containing text would have non-empty Character buffer range.
                        if (TextRunInfo.GetRunType(run) == Plsrun.Text &&
                            run.CharacterBufferReference.CharacterBuffer != null)
                        {
                            charString = new CharacterBufferRange(
                                run.CharacterBufferReference,
                                cpLimit - textRunSpanRider.CurrentSpanStart);

                            culture = CultureMapper.GetSpecificCulture(run.Properties.CultureInfo);
                        }

                        return(new TextSpan <CultureSpecificCharacterBufferRange>(
                                   cpLimit - textRunSpanRider.CurrentSpanStart, // cp length
                                   new CultureSpecificCharacterBufferRange(culture, charString)
                                   ));
                    }
                }
            }

            // not in cache so call back to client
            return(textSource.GetPrecedingText(cpLimit));
        }
예제 #2
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);
        }
예제 #3
0
        /// <summary>
        /// Fetch text run and character string associated with it
        /// </summary>
        internal CharacterBufferRange FetchTextRun(
            int cpFetch,
            int cpFirst,
            out TextRun textRun,
            out int runLength
            )
        {
            int offsetToFirstCp;

            textRun = _runCache.FetchTextRun(this, cpFetch, cpFirst, out offsetToFirstCp, out runLength);

            CharacterBufferRange charString;

            switch (TextRunInfo.GetRunType(textRun))
            {
            case Plsrun.Text:
            {
                CharacterBufferReference charBufferRef = textRun.CharacterBufferReference;

                charString = new CharacterBufferRange(
                    charBufferRef.CharacterBuffer,
                    charBufferRef.OffsetToFirstChar + offsetToFirstCp,
                    runLength
                    );

                break;
            }

            case Plsrun.InlineObject:
                Debug.Assert(offsetToFirstCp == 0);
                unsafe
                {
                    charString = new CharacterBufferRange(TextStore.StrObjectReplacement, 0, 1);
                }
                break;

            case Plsrun.LineBreak:
                Debug.Assert(offsetToFirstCp == 0);
                unsafe
                {
                    //
                    // Line break is to be represented as "Line Separator" such that
                    // it doesn't terminate the optimal paragraph formatting session, but still breaks the line
                    // unambiguously.
                    //
                    charString = new CharacterBufferRange(TextStore.StrLineSeparator, 0, 1);
                }
                break;

            case Plsrun.ParaBreak:
                Debug.Assert(offsetToFirstCp == 0);
                unsafe
                {
                    //
                    // Paragraph break is special as it also terminates the paragraph.
                    // It should be represented as "Paragraph Separator" such that it will be correctly handled
                    // in Bidi and Optimal paragraph formatting.
                    //
                    charString = new CharacterBufferRange(TextStore.StrParaSeparator, 0, 1);
                }
                break;

            case Plsrun.Hidden:
                unsafe
                {
                    charString = new CharacterBufferRange(TextStore.StrHidden, 0, 1);
                }
                break;

            default:
                charString = CharacterBufferRange.Empty;
                break;
            }

            return(charString);
        }