/// <summary>
        /// Compute unshaped glyph run object from the specified character-based info
        /// </summary>
        internal sealed override GlyphRun ComputeUnshapedGlyphRun(
            Point origin,
            char[]        characterString,
            IList <double> characterAdvances
            )
        {
            bool          nullFont;
            GlyphTypeface glyphTypeface = GetGlyphTypeface(out nullFont);

            Invariant.Assert(glyphTypeface != null);

            return(glyphTypeface.ComputeUnshapedGlyphRun(
                       origin,
                       new CharacterBufferRange(
                           characterString,
                           0, // offsetToFirstChar
                           characterString.Length
                           ),
                       characterAdvances,
                       _emSize,
                       (float)_properties.PixelsPerDip,
                       _properties.FontHintingEmSize,
                       nullFont,
                       CultureMapper.GetSpecificCulture(_properties.CultureInfo),
                       (_shapeTypeface == null || _shapeTypeface.DeviceFont == null) ? null : _shapeTypeface.DeviceFont.Name,
                       _textFormattingMode
                       ));
        }
        /// <summary>
        /// Compute a shaped glyph run object from specified glyph-based info
        /// </summary>
        internal sealed override GlyphRun ComputeShapedGlyphRun(
            Point origin,
            char[]                   characterString,
            ushort[]                 clusterMap,
            ushort[]                 glyphIndices,
            IList <double> glyphAdvances,
            IList <Point> glyphOffsets,
            bool rightToLeft,
            bool sideways
            )
        {
            Invariant.Assert(_shapeTypeface != null);
            Invariant.Assert(glyphIndices != null);
            // Device fonts are only used through the LS non-glyphed code path. Only when a DigitCulture is set
            // will a potential device font be ignored and come through shaping.
            Invariant.Assert(_shapeTypeface.DeviceFont == null || _textItem.DigitCulture != null);

            bool[] caretStops = null;

            if (clusterMap != null &&
                (HasExtendedCharacter || NeedsCaretInfo)
                )
            {
                caretStops = new bool[clusterMap.Length + 1];

                // caret stops at cluster boundaries, the first and the last entries are always set
                caretStops[0] = true;
                caretStops[clusterMap.Length] = true;

                ushort lastGlyph = clusterMap[0];

                for (int i = 1; i < clusterMap.Length; i++)
                {
                    ushort glyph = clusterMap[i];

                    if (glyph != lastGlyph)
                    {
                        caretStops[i] = true;
                        lastGlyph     = glyph;
                    }
                }
            }

            return(GlyphRun.TryCreate(
                       _shapeTypeface.GlyphTypeface,
                       (rightToLeft ? 1 : 0),
                       sideways,
                       _emSize,
                       glyphIndices,
                       origin,
                       glyphAdvances,
                       glyphOffsets,
                       characterString,
                       null,
                       clusterMap,
                       caretStops,
                       XmlLanguage.GetLanguage(CultureMapper.GetSpecificCulture(_properties.CultureInfo).IetfLanguageTag),
                       _textFormattingMode
                       ));
        }
Esempio n. 3
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);
        }
Esempio n. 4
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));
        }
Esempio n. 5
0
        /// <summary>
        /// Break a run of text into individually shape items.
        /// Shape items are delimited by
        ///     Change of writing system
        ///     Change of glyph typeface
        /// </summary>
        IList <TextShapeableSymbols> ITextSymbols.GetTextShapeableSymbols(
            GlyphingCache glyphingCache,
            CharacterBufferReference characterBufferReference,
            int length,
            bool rightToLeft,
            bool isRightToLeftParagraph,
            CultureInfo digitCulture,
            TextModifierScope textModifierScope,
            TextFormattingMode textFormattingMode,
            bool isSideways
            )
        {
            if (characterBufferReference.CharacterBuffer == null)
            {
                throw new ArgumentNullException("characterBufferReference.CharacterBuffer");
            }

            int offsetToFirstChar = characterBufferReference.OffsetToFirstChar - _characterBufferReference.OffsetToFirstChar;

            Debug.Assert(characterBufferReference.CharacterBuffer == _characterBufferReference.CharacterBuffer);
            Debug.Assert(offsetToFirstChar >= 0 && offsetToFirstChar < _length);

            if (length < 0 ||
                offsetToFirstChar + length > _length)
            {
                length = _length - offsetToFirstChar;
            }

            // Get the actual text run properties in effect, after invoking any
            // text modifiers that may be in scope.
            TextRunProperties textRunProperties = _textRunProperties;

            if (textModifierScope != null)
            {
                textRunProperties = textModifierScope.ModifyProperties(textRunProperties);
            }

            if (!rightToLeft)
            {
                // Fast loop early out for run with all non-complex characters
                // which can be optimized by not going thru shaping engine.

                int nominalLength;

                if (textRunProperties.Typeface.CheckFastPathNominalGlyphs(
                        new CharacterBufferRange(characterBufferReference, length),
                        textRunProperties.FontRenderingEmSize,
                        (float)textRunProperties.PixelsPerDip,
                        1.0,
                        double.MaxValue, // widthMax
                        true,            // keepAWord
                        digitCulture != null,
                        CultureMapper.GetSpecificCulture(textRunProperties.CultureInfo),
                        textFormattingMode,
                        isSideways,
                        false, //breakOnTabs
                        out nominalLength
                        ) && length == nominalLength)
                {
                    return(new TextShapeableCharacters[]
                    {
                        new TextShapeableCharacters(
                            new CharacterBufferRange(characterBufferReference, nominalLength),
                            textRunProperties,
                            textRunProperties.FontRenderingEmSize,
                            new MS.Internal.Text.TextInterface.ItemProps(),
                            null,   // shapeTypeface (no shaping required)
                            false,  // nullShape,
                            textFormattingMode,
                            isSideways
                            )
                    });
                }
            }

            IList <TextShapeableSymbols> shapeables = new List <TextShapeableSymbols>(2);

            glyphingCache.GetShapeableText(
                textRunProperties.Typeface,
                characterBufferReference,
                length,
                textRunProperties,
                digitCulture,
                isRightToLeftParagraph,
                shapeables,
                this as IShapeableTextCollector,
                textFormattingMode
                );

            return(shapeables);
        }
Esempio n. 6
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);
        }