Пример #1
0
        internal void GetShapeableText(
            Typeface                    typeface, 
            CharacterBufferReference    characterBufferReference,
            int                         stringLength,
            TextRunProperties           textRunProperties,
            CultureInfo                 digitCulture, 
            bool                        isRightToLeftParagraph,
            IList<TextShapeableSymbols> shapeableList, 
            IShapeableTextCollector     collector, 
            TextFormattingMode              textFormattingMode
            ) 
        {
            if (!typeface.Symbol)
            {
                Lookup(typeface).GetShapeableText( 
                    characterBufferReference,
                    stringLength, 
                    textRunProperties, 
                    digitCulture,
                    isRightToLeftParagraph, 
                    shapeableList,
                    collector,
                    textFormattingMode
                    ); 
            }
            else 
            { 
                // It's a non-Unicode ("symbol") font, where code points have non-standard meanings. We
                // therefore want to bypass the usual itemization and font linking. Instead, just map 
                // everything to the default script and first GlyphTypeface.

                ShapeTypeface shapeTypeface = new ShapeTypeface(
                    typeface.TryGetGlyphTypeface(), 
                    null // device font
                    ); 
 
                collector.Add(
                    shapeableList, 
                    new CharacterBufferRange(characterBufferReference, stringLength),
                    textRunProperties,
                    new MS.Internal.Text.TextInterface.ItemProps(),
                    shapeTypeface, 
                    1.0,   // scale in Em
                    false,  // null shape 
                    textFormattingMode 
                    );
            } 
        }
Пример #2
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,
                    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;
        }
Пример #3
0
        /// <summary>
        /// Internal constructor of TextContent
        /// </summary>
        private TextCharacters(
            CharacterBufferReference    characterBufferReference,
            int                         length,
            TextRunProperties           textRunProperties
            )
        {        
            if (length <= 0)
            {
                throw new ArgumentOutOfRangeException("length", SR.Get(SRID.ParameterMustBeGreaterThanZero));
            }

            if (textRunProperties == null)
            {
                throw new ArgumentNullException("textRunProperties");
            }

            if (textRunProperties.Typeface == null)
            {
                throw new ArgumentNullException("textRunProperties.Typeface");
            }

            if (textRunProperties.CultureInfo == null)
            {
                throw new ArgumentNullException("textRunProperties.CultureInfo");
            }

            if (textRunProperties.FontRenderingEmSize <= 0)
            {
                throw new ArgumentOutOfRangeException("textRunProperties.FontRenderingEmSize", SR.Get(SRID.ParameterMustBeGreaterThanZero));
            }

            _characterBufferReference = characterBufferReference;
            _length = length;
            _textRunProperties = textRunProperties;
        }
Пример #4
0
        internal void GetShapeableText(
            CharacterBufferReference    characterBufferReference,
            int                         stringLength, 
            TextRunProperties           textRunProperties,
            CultureInfo                 digitCulture, 
            bool                        isRightToLeftParagraph, 
            IList<TextShapeableSymbols> shapeableList,
            IShapeableTextCollector     collector, 
            TextFormattingMode          textFormattingMode
            )
        {
            SpanVector<int> cachedScaledTypefaceIndexSpans; 

            int ichItem = 0; 
 
            CharacterBufferRange unicodeString = new CharacterBufferRange(
                characterBufferReference, 
                stringLength
                );

            CultureInfo culture = textRunProperties.CultureInfo; 
            IList<Span> spans;
 
            GCHandle gcHandle; 
            IntPtr ptext = characterBufferReference.CharacterBuffer.PinAndGetCharacterPointer(characterBufferReference.OffsetToFirstChar, out gcHandle);
 
            // Contextual number substitution cannot be performed on the run level, since it depends
            // on context - nearest preceding strong character. For this reason, contextual number
            // substitutions has been already done (TextStore.CreateLSRunsUniformBidiLevel) and
            // digitCulture has been updated to reflect culture which is dependent on the context. 
            // NumberSubstitutionMethod.AsCulture method can be resolved to Context, hence it also needs to be resolved to appropriate
            // not ambiguous method. 
            // Both of those values (Context and AsCulture) are resolved to one of following: European, Traditional or NativeNational, 
            // which can be safely handled by DWrite without getting context information.
            bool ignoreUserOverride; 
            NumberSubstitutionMethod numberSubstitutionMethod = DigitState.GetResolvedSubstitutionMethod(textRunProperties, digitCulture, out ignoreUserOverride);

            // Itemize the text based on DWrite's text analysis for scripts and number substitution.
            unsafe 
            {
                checked 
                { 
                    spans = MS.Internal.Text.TextInterface.TextAnalyzer.Itemize(
                        (ushort*)ptext.ToPointer(), 
                        (uint)stringLength,
                        culture,
                        MS.Internal.FontCache.DWriteFactory.Instance,
                        isRightToLeftParagraph, 
                        digitCulture,
                        ignoreUserOverride, 
                        (uint)numberSubstitutionMethod, 
                        ClassificationUtility.Instance,
                        UnsafeNativeMethods.CreateTextAnalysisSink, 
                        UnsafeNativeMethods.GetScriptAnalysisList,
                        UnsafeNativeMethods.GetNumberSubstitutionList,
                        UnsafeNativeMethods.CreateTextAnalysisSource
                        ); 
                }
 
            } 
            characterBufferReference.CharacterBuffer.UnpinCharacterPointer(gcHandle);
 
            SpanVector itemSpans = new SpanVector(null, new FrugalStructList<Span>((ICollection<Span>)spans));

            cachedScaledTypefaceIndexSpans = new SpanVector<int>(-1);
            foreach(Span itemSpan in itemSpans) 
            {
                MapItem( 
                    new CharacterBufferRange( 
                        unicodeString,
                        ichItem, 
                        itemSpan.length
                        ),
                    culture,
                    itemSpan, 
                    ref cachedScaledTypefaceIndexSpans,
                    ichItem 
                    ); 

                #if DEBUG 
                ValidateMapResult(
                    ichItem,
                    itemSpan.length,
                    ref cachedScaledTypefaceIndexSpans 
                    );
                #endif 
 
                ichItem += itemSpan.length;
            } 


            Debug.Assert(ichItem == unicodeString.Length);
 
            // intersect item spans with shapeable spans to create span of shapeable runs
 
            int ich = 0; 

            SpanRider itemSpanRider = new SpanRider(itemSpans); 
            SpanRider<int> typefaceIndexSpanRider = new SpanRider<int>(cachedScaledTypefaceIndexSpans);

            while(ich < unicodeString.Length)
            { 
                itemSpanRider.At(ich);
                typefaceIndexSpanRider.At(ich); 
 
                int index = typefaceIndexSpanRider.CurrentValue;
                Debug.Assert(index >= 0); 

                int cch = unicodeString.Length - ich;
                cch = Math.Min(cch, itemSpanRider.Length);
                cch = Math.Min(cch, typefaceIndexSpanRider.Length); 

                ScaledShapeTypeface scaledShapeTypeface = _cachedScaledTypefaces[index]; 
 
                collector.Add(
                    shapeableList, 
                    new CharacterBufferRange(
                        unicodeString,
                        ich,
                        cch 
                        ),
                    textRunProperties, 
                    (MS.Internal.Text.TextInterface.ItemProps)itemSpanRider.CurrentElement, 
                    scaledShapeTypeface.ShapeTypeface,
                    scaledShapeTypeface.ScaleInEm, 
                    scaledShapeTypeface.NullShape,
                    textFormattingMode
                    );
 
                ich += cch;
            } 
        } 
Пример #5
0
        /// <summary>
        /// Compute a list of shapeable text objects for the specified character string
        /// </summary>
        internal void GetShapeableText(
            CharacterBufferReference characterBufferReference,
            int stringLength,
            TextRunProperties textRunProperties,
            CultureInfo digitCulture,
            bool isRightToLeftParagraph,
            IList <TextShapeableSymbols> shapeableList,
            IShapeableTextCollector collector,
            TextFormattingMode textFormattingMode
            )
        {
            SpanVector <int> cachedScaledTypefaceIndexSpans;

            int ichItem = 0;

            CharacterBufferRange unicodeString = new CharacterBufferRange(
                characterBufferReference,
                stringLength
                );

            CultureInfo  culture = textRunProperties.CultureInfo;
            IList <Span> spans;

            GCHandle gcHandle;
            IntPtr   ptext = characterBufferReference.CharacterBuffer.PinAndGetCharacterPointer(characterBufferReference.OffsetToFirstChar, out gcHandle);

            // Contextual number substitution cannot be performed on the run level, since it depends
            // on context - nearest preceding strong character. For this reason, contextual number
            // substitutions has been already done (TextStore.CreateLSRunsUniformBidiLevel) and
            // digitCulture has been updated to reflect culture which is dependent on the context.
            // NumberSubstitutionMethod.AsCulture method can be resolved to Context, hence it also needs to be resolved to appropriate
            // not ambiguous method.
            // Both of those values (Context and AsCulture) are resolved to one of following: European, Traditional or NativeNational,
            // which can be safely handled by DWrite without getting context information.
            bool ignoreUserOverride;
            NumberSubstitutionMethod numberSubstitutionMethod = DigitState.GetResolvedSubstitutionMethod(textRunProperties, digitCulture, out ignoreUserOverride);

            // Itemize the text based on DWrite's text analysis for scripts and number substitution.
            unsafe
            {
                checked
                {
                    spans = MS.Internal.Text.TextInterface.TextAnalyzer.Itemize(
                        (char *)ptext.ToPointer(),
                        (uint)stringLength,
                        culture,
                        MS.Internal.FontCache.DWriteFactory.Instance,
                        isRightToLeftParagraph,
                        digitCulture,
                        ignoreUserOverride,
                        (uint)numberSubstitutionMethod,
                        ClassificationUtility.Instance
                        );
                }
            }
            characterBufferReference.CharacterBuffer.UnpinCharacterPointer(gcHandle);

            SpanVector itemSpans = new SpanVector(null, new FrugalStructList <Span>((ICollection <Span>)spans));

            cachedScaledTypefaceIndexSpans = new SpanVector <int>(-1);
            foreach (Span itemSpan in itemSpans)
            {
                MapItem(
                    new CharacterBufferRange(
                        unicodeString,
                        ichItem,
                        itemSpan.length
                        ),
                    culture,
                    itemSpan,
                    ref cachedScaledTypefaceIndexSpans,
                    ichItem
                    );

                #if DEBUG
                ValidateMapResult(
                    ichItem,
                    itemSpan.length,
                    ref cachedScaledTypefaceIndexSpans
                    );
                #endif

                ichItem += itemSpan.length;
            }


            Debug.Assert(ichItem == unicodeString.Length);

            // intersect item spans with shapeable spans to create span of shapeable runs

            int ich = 0;

            SpanRider       itemSpanRider          = new SpanRider(itemSpans);
            SpanRider <int> typefaceIndexSpanRider = new SpanRider <int>(cachedScaledTypefaceIndexSpans);

            while (ich < unicodeString.Length)
            {
                itemSpanRider.At(ich);
                typefaceIndexSpanRider.At(ich);

                int index = typefaceIndexSpanRider.CurrentValue;
                Debug.Assert(index >= 0);

                int cch = unicodeString.Length - ich;
                cch = Math.Min(cch, itemSpanRider.Length);
                cch = Math.Min(cch, typefaceIndexSpanRider.Length);

                ScaledShapeTypeface scaledShapeTypeface = _cachedScaledTypefaces[index];

                collector.Add(
                    shapeableList,
                    new CharacterBufferRange(
                        unicodeString,
                        ich,
                        cch
                        ),
                    textRunProperties,
                    (MS.Internal.Text.TextInterface.ItemProps)itemSpanRider.CurrentElement,
                    scaledShapeTypeface.ShapeTypeface,
                    scaledShapeTypeface.ScaleInEm,
                    scaledShapeTypeface.NullShape,
                    textFormattingMode
                    );

                ich += cch;
            }
        }
Пример #6
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);
        }
Пример #7
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);
        }