예제 #1
0
        private static CultureInfo GetNumberCulture(TextRunProperties properties, out NumberSubstitutionMethod method, out bool ignoreUserOverride)
        {
            ignoreUserOverride = true;
            NumberSubstitution sub = properties.NumberSubstitution;

            if (sub == null)
            {
                method = NumberSubstitutionMethod.AsCulture;
                return(CultureMapper.GetSpecificCulture(properties.CultureInfo));
            }

            method = sub.Substitution;

            switch (sub.CultureSource)
            {
            case NumberCultureSource.Text:
                return(CultureMapper.GetSpecificCulture(properties.CultureInfo));

            case NumberCultureSource.User:
                ignoreUserOverride = false;
                return(CultureInfo.CurrentCulture);

            case NumberCultureSource.Override:
                return(sub.CultureOverride);
            }

            return(null);
        }
예제 #2
0
        /// <summary>
        /// TextFormatter to get text immediately before specified text source position.
        /// </summary>
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(
            int textSourceCharacterIndexLimit
            )
        {
            CharacterBufferRange charString = CharacterBufferRange.Empty;

            if (textSourceCharacterIndexLimit > 0)
            {
                charString = new CharacterBufferRange(
                    new CharacterBufferReference(_characterArray, 0),
                    Math.Min(_characterArray.Length, textSourceCharacterIndexLimit)
                    );
            }

            return(new TextSpan <CultureSpecificCharacterBufferRange> (
                       textSourceCharacterIndexLimit,
                       new CultureSpecificCharacterBufferRange(CultureMapper.GetSpecificCulture(_textRunProperties.CultureInfo), charString)
                       ));
        }
예제 #3
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));
        }
예제 #4
0
        internal TextMarkerSource(
            TextParagraphProperties textParagraphProperties,
            TextMarkerStyle markerStyle,
            int autoNumberingIndex
            )
        {
            Debug.Assert(markerStyle != TextMarkerStyle.None);

            _textParagraphProperties = textParagraphProperties;
            TextRunProperties defaultRunProperties = _textParagraphProperties.DefaultTextRunProperties;

            PixelsPerDip = defaultRunProperties.PixelsPerDip;
            string symbolString = null;

            if (IsKnownSymbolMarkerStyle(markerStyle))
            {
                switch (markerStyle)
                {
                case TextMarkerStyle.Disc:
                    symbolString = "\x9f"; break;

                case TextMarkerStyle.Circle:
                    symbolString = "\xa1"; break;

                case TextMarkerStyle.Square:
                    symbolString = "\x71"; break;

                case TextMarkerStyle.Box:
                    symbolString = "\xa7"; break;
                }

                Typeface defaultTypeface = defaultRunProperties.Typeface;

                // recreate a new marker run properties based on symbol typeface e.g. Wingding
                _textRunProperties = new GenericTextRunProperties(
                    new Typeface(
                        new FontFamily("Wingdings"),
                        defaultTypeface.Style,
                        defaultTypeface.Weight,
                        defaultTypeface.Stretch
                        ),
                    defaultRunProperties.FontRenderingEmSize,
                    defaultRunProperties.FontHintingEmSize,
                    PixelsPerDip,
                    defaultRunProperties.TextDecorations,
                    defaultRunProperties.ForegroundBrush,
                    defaultRunProperties.BackgroundBrush,
                    defaultRunProperties.BaselineAlignment,
                    CultureMapper.GetSpecificCulture(defaultRunProperties.CultureInfo),
                    null // default number substitution for culture
                    );
            }
            else if (IsKnownIndexMarkerStyle(markerStyle))
            {
                // Internal client code should have already validated this.
                Debug.Assert(autoNumberingIndex > 0);

                _textRunProperties = defaultRunProperties;

                int counter = autoNumberingIndex;

                switch (markerStyle)
                {
                case TextMarkerStyle.Decimal:
                    _characterArray = ConvertNumberToString(counter, false, DecimalNumerics);
                    break;

                case TextMarkerStyle.LowerLatin:
                    _characterArray = ConvertNumberToString(counter, true, LowerLatinNumerics);
                    break;

                case TextMarkerStyle.UpperLatin:
                    _characterArray = ConvertNumberToString(counter, true, UpperLatinNumerics);
                    break;

                case TextMarkerStyle.LowerRoman:
                    symbolString = ConvertNumberToRomanString(counter, false);
                    break;

                case TextMarkerStyle.UpperRoman:
                    symbolString = ConvertNumberToRomanString(counter, true);
                    break;
                }
            }
            else
            {
                Debug.Assert(false, "Invalid marker style");
            }

            if (symbolString != null)
            {
                _characterArray = symbolString.ToCharArray();
            }

            Debug.Assert(_characterArray != null);
        }
예제 #5
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);
        }