Example #1
0
        /// <summary> 
        /// Cache index to the list of scaled shapeable typeface 
        /// </summary>
        private void CacheScaledTypefaceMap( 
            CharacterBufferRange        unicodeString,
            CultureInfo                 culture,
            CultureInfo                 digitCulture,
            SpanVector                  scaledTypefaceSpans, 
            ref SpanVector<int>         cachedScaledTypefaceIndexSpans,
            int                         ichItem 
            ) 
        {
 
            IntMap map;
            if (!_intMaps.TryGetValue(culture, out map))
            {
                map = new IntMap(); 
                _intMaps.Add(culture, map);
            } 
 
            DigitMap digitMap = new DigitMap(digitCulture);
 
            SpanRider typefaceSpanRider = new SpanRider(scaledTypefaceSpans);

            int ich = 0;
            while(ich < unicodeString.Length) 
            {
                typefaceSpanRider.At(ich); 
 
                int cch = Math.Min(unicodeString.Length - ich, typefaceSpanRider.Length);
 
                int index = IndexOfScaledTypeface((ScaledShapeTypeface)typefaceSpanRider.CurrentElement);
                Debug.Assert(index >= 0, "Invalid scaled shapeable typeface index spans");

                cachedScaledTypefaceIndexSpans.Set(ichItem + ich, cch, index); 

                // we keep index + 1 in the map, so that we leave map entry zero 
                // to indicate uninitialized entry. 
                index++;
 
                int sizeofChar;
                for (int c = 0; c < cch; c += sizeofChar)
                {
                    int ch = digitMap[ 
                        Classification.UnicodeScalar(
                            new CharacterBufferRange(unicodeString, ich + c, unicodeString.Length - ich - c), 
                            out sizeofChar 
                        )
                    ]; 

                    // only cache typeface map index for base characters
                    if(!Classification.IsCombining(ch) && !Classification.IsJoiner(ch))
                    { 
                        // Dump values of local variables when the condition fails for better debuggability.
                        // We use "if" to avoid the expensive string.Format() in normal case. 
                        if (map[ch] != 0 && map[ch] != index) 
                        {
                            Invariant.Assert( 
                                false,
                                string.Format(
                                    CultureInfo.InvariantCulture,
                                    "shapeable cache stores conflicting info, ch = {0}, map[ch] = {1}, index = {2}", 
                                    ch, map[ch], index
                                ) 
                            ); 
                        }
 
                        map[ch] = (ushort)index;
                    }
                }
 
                ich += cch;
            } 
        } 
Example #2
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;
            } 
        } 
            /// <summary>
            /// Map internal LSCP to text source cp
            /// </summary>
            internal int GetExternalCp(int lscp)
            {
                if (lscp >= _metrics._lscpLim)
                {
                    if (_collapsedRange != null)
                        return _collapsedRange.TextSourceCharacterIndex;

                    return _cpFirst + _metrics._cchLength;
                }

                int offsetToFirstCp;

                SpanRider plsrunSpanRider = new SpanRider(_plsrunVector);

                // skip lscp until we find one with valid map
                do
                {
                    plsrunSpanRider.At(lscp - _cpFirst);
                    offsetToFirstCp = GetRun(((Plsrun)plsrunSpanRider.CurrentElement)).OffsetToFirstCp;

                } while(offsetToFirstCp < 0 && ++lscp < _metrics._lscpLim);

                return offsetToFirstCp + lscp - plsrunSpanRider.CurrentSpanStart;
            }
Example #4
0
        private unsafe void ValidateMapResult(
            int                 ichRange,
            int                 cchRange, 
            ref SpanVector<int> cachedScaledTypefaceIndexSpans
            ) 
        { 
            int ich = 0;
 
            SpanRider<int> typefaceIndexSpanRider = new SpanRider<int>(cachedScaledTypefaceIndexSpans);

            while(ich < cchRange)
            { 
                typefaceIndexSpanRider.At(ichRange + ich);
                if((int)typefaceIndexSpanRider.CurrentValue < 0) 
                { 
                    Debug.Assert(false, "Invalid font face spans");
                    return; 
                }

                int cch = Math.Min(cchRange - ich, typefaceIndexSpanRider.Length);
                ich += cch; 
            }
        } 
            /// <summary>
            /// Search from the given lscp (inclusive) towards the specified direction for the
            /// closest navigable cp. Return true is one such cp is found, false otherwise.
            /// </summary>
            private bool FindNextOrPreviousVisibleCp(
                int             lscp,
                CaretDirection  direction,
                out int         lscpVisisble
                )
            {
                lscpVisisble = lscp;

                SpanRider plsrunSpanRider = new SpanRider(_plsrunVector);

                if (direction == CaretDirection.Forward)
                {
                    while (lscpVisisble < _metrics._lscpLim)
                    {
                        plsrunSpanRider.At(lscpVisisble - _cpFirst);
                        LSRun run = GetRun((Plsrun) plsrunSpanRider.CurrentElement);

                        // When scanning forward, only trailine edges of visiable content are navigable.
                        if (run.IsVisible)
                        {
                            return true;
                        }

                        lscpVisisble += plsrunSpanRider.Length; // move to start of next span
                    }
                }
                else
                {
                    Debug.Assert(direction == CaretDirection.Backward || direction == CaretDirection.Backspace);

                    // lscpCurrent can be right after the end of the line, we snap it back to be at the end of the line.
                    lscpVisisble = Math.Min(lscpVisisble, _metrics._lscpLim - 1);
                    while (lscpVisisble >= _cpFirst)
                    {
                        plsrunSpanRider.At(lscpVisisble - _cpFirst);
                        LSRun run = GetRun((Plsrun) plsrunSpanRider.CurrentElement);

                        // When scanning backward, visiable content has caret stop at its leading edge.
                        if (run.IsVisible)
                        {
                            return true;
                        }

                        // When scanning backward, the newline sequence has caret stop at its leading edge.
                        if (run.IsNewline)
                        {
                            // set navigable cp at the start of newline sequence.
                            lscpVisisble = _cpFirst + plsrunSpanRider.CurrentSpanStart;
                            return true;
                        }

                        lscpVisisble = _cpFirst + plsrunSpanRider.CurrentSpanStart - 1; // move to the end of previous span
                    }
                }

                lscpVisisble = lscp;
                return false;
            }
            /// <summary>
            /// Compute bounds of runs within the specified range of lscp
            /// </summary>
            private IList<TextRunBounds> CalculateTextRunBounds(int lscpFirst, int lscpEnd)
            {
                if (lscpEnd <= lscpFirst)
                {
                    // It is possible that we'll get a legitimate case when lscpFirst is
                    // actually greater. That's what happen when the client hittest a hidden
                    // run that follows a reverse block. Since it is a hidden run, LS has
                    // to yield the closest non-hidden place which may be the run preceding
                    // the hidden text. (wchao, PS bug #930976)
                    return null;
                }

                int lscp = lscpFirst;
                int cchLeft = lscpEnd - lscpFirst;
                SpanRider plsrunSpanRider = new SpanRider(_plsrunVector);

                Point position = new Point(0, 0);
                IList<TextRunBounds> boundsList = new List<TextRunBounds>(2);

                while(cchLeft > 0)
                {
                    plsrunSpanRider.At(lscp - _cpFirst);
                    Plsrun plsrun = (Plsrun)plsrunSpanRider.CurrentElement;
                    int cch = Math.Min(plsrunSpanRider.Length, cchLeft);

                    if(TextStore.IsContent(plsrun))
                    {
                        LSRun lsrun = GetRun(plsrun);

                        if(     lsrun.Type == Plsrun.Text
                            ||  lsrun.Type == Plsrun.InlineObject)
                        {
                            int cp = GetExternalCp(lscp);
                            int cchBounds = cch;

                            if (    HasCollapsed
                                &&  _collapsedRange != null
                                &&  cp <= _collapsedRange.TextSourceCharacterIndex
                                &&  cp + cchBounds >= _collapsedRange.TextSourceCharacterIndex
                                &&  cp + cchBounds < _collapsedRange.TextSourceCharacterIndex + _collapsedRange.Length)
                            {
                                // Limit the run bounds to only non-collapsed text,
                                // we deal with collapsed text separately as it might have different flow direction.
                                cchBounds = _collapsedRange.TextSourceCharacterIndex - cp;
                            }

                            if (cchBounds > 0)
                            {
                                TextRunBounds bounds = new TextRunBounds(
                                    LSRun.RectUV(
                                        position,
                                        new LSPOINT(
                                            LSLineUToParagraphU(
                                                DistanceFromCharacterHit(new CharacterHit(cp, 0))
                                                ),
                                            _metrics._baselineOffset - lsrun.BaselineOffset + lsrun.BaselineMoveOffset
                                            ),
                                        new LSPOINT(
                                            LSLineUToParagraphU(
                                                DistanceFromCharacterHit(new CharacterHit(cp + cchBounds - 1, 1))
                                                ),
                                            _metrics._baselineOffset - lsrun.BaselineOffset + lsrun.BaselineMoveOffset + lsrun.Height
                                            ),
                                        this
                                        ),
                                    cp,
                                    cp + cchBounds,
                                    lsrun.TextRun
                                    );
                                boundsList.Add(bounds);
                            }
                        }
                    }

                    cchLeft -= cch;
                    lscp += cch;
                }
                return boundsList.Count > 0 ? boundsList : null;
            }
Example #7
0
        /// <summary>
        /// Sets or changes the text decorations
        /// </summary>
        /// <param name="textDecorations">Text decorations</param>
        /// <param name="startIndex">The start index of initial character to apply the change to.</param>
        /// <param name="count">The number of characters the change should be applied to.</param>
        public void SetTextDecorations(TextDecorationCollection textDecorations, int startIndex, int count)
        {
            int limit = ValidateRange(startIndex, count);
            for (int i = startIndex; i < limit;)
            {
                SpanRider formatRider = new SpanRider(_formatRuns, _latestPosition, i);
                i = Math.Min(limit, i + formatRider.Length);

#pragma warning disable 6506 
                // Presharp warns that runProps is not validated, but it can never be null 
                // because the rider is already checked to be in range
                GenericTextRunProperties runProps = formatRider.CurrentElement as GenericTextRunProperties;

                Invariant.Assert(runProps != null);
                
                if (runProps.TextDecorations == textDecorations)
                    continue;

                GenericTextRunProperties newProps = new GenericTextRunProperties(
                    runProps.Typeface,
                    runProps.FontRenderingEmSize,
                    runProps.FontHintingEmSize,
                    textDecorations,
                    runProps.ForegroundBrush,
                    runProps.BackgroundBrush,
                    runProps.BaselineAlignment,
                    runProps.CultureInfo,
                    runProps.NumberSubstitution
                    );
#pragma warning restore 6506 
                
                _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition, newProps, formatRider.SpanPosition);
            }
        }
Example #8
0
        /// <summary>
        /// Sets or changes the font style
        /// </summary>
        /// <param name="style">Font style</param>
        /// <param name="startIndex">The start index of initial character to apply the change to.</param>
        /// <param name="count">The number of characters the change should be applied to.</param>
        public void SetFontStyle(FontStyle style, int startIndex, int count)
        {
            int limit = ValidateRange(startIndex, count);
            for (int i = startIndex; i < limit;)
            {
                SpanRider formatRider = new SpanRider(_formatRuns, _latestPosition, i);
                i = Math.Min(limit, i + formatRider.Length);

#pragma warning disable 6506 
                // Presharp warns that runProps is not validated, but it can never be null 
                // because the rider is already checked to be in range
                GenericTextRunProperties runProps = formatRider.CurrentElement as GenericTextRunProperties;
                
                Invariant.Assert(runProps != null);
                
                Typeface oldTypeface = runProps.Typeface;
                if (oldTypeface.Style == style)
                    continue;

                GenericTextRunProperties newProps = new GenericTextRunProperties(
                    new Typeface(oldTypeface.FontFamily, style, oldTypeface.Weight, oldTypeface.Stretch),
                    runProps.FontRenderingEmSize,
                    runProps.FontHintingEmSize,
                    runProps.TextDecorations,
                    runProps.ForegroundBrush,
                    runProps.BackgroundBrush,
                    runProps.BaselineAlignment,
                    runProps.CultureInfo,
                    runProps.NumberSubstitution
                    );
#pragma warning restore 6506 
                
                _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition, newProps, formatRider.SpanPosition);
                InvalidateMetrics(); // invalidate cached metrics
            }
        }
Example #9
0
            /// <summary>
            /// TextFormatter to get text immediately before specified text source position.
            /// </summary>
            /// <param name="textSourceCharacterIndexLimit">character index to specify where in the source text the text retrieval stops.</param>
            /// <returns>character string immediately before the specify text source character index.</returns>
            public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(
                int         textSourceCharacterIndexLimit
                )
            {
                CharacterBufferRange charString = CharacterBufferRange.Empty;
                CultureInfo culture = null;
            
                if (textSourceCharacterIndexLimit > 0)                    
                {
                    SpanRider thatFormatRider = new SpanRider(
                        _that._formatRuns,
                        _that._latestPosition,
                        textSourceCharacterIndexLimit - 1
                        );
                    
                    charString = new CharacterBufferRange(
                        new CharacterBufferReference(_that._text, thatFormatRider.CurrentSpanStart),
                        textSourceCharacterIndexLimit - thatFormatRider.CurrentSpanStart
                        );

                    culture = ((TextRunProperties)thatFormatRider.CurrentElement).CultureInfo;
                }

                return new TextSpan<CultureSpecificCharacterBufferRange> (
                    charString.Length,
                    new CultureSpecificCharacterBufferRange(culture, charString)
                    );
            }
Example #10
0
            /// <summary>
            /// TextFormatter to get a text run started at specified text source position
            /// </summary>
            /// <param name="textSourceCharacterIndex">character index to specify where in the source text the fetch is to start.</param>
            public override TextRun GetTextRun(
                int         textSourceCharacterIndex
                )
            {
                if (textSourceCharacterIndex >= _that._text.Length)
                {
                    return new TextEndOfParagraph(1);
                }

                SpanRider thatFormatRider = new SpanRider(
                    _that._formatRuns, 
                    _that._latestPosition,
                    textSourceCharacterIndex
                    );

                return new TextCharacters(_that._text,
                    textSourceCharacterIndex,
                    thatFormatRider.Length,
                    thatFormatRider.CurrentElement as GenericTextRunProperties
                    );
            }
Example #11
0
            /// <summary>
            /// Wrapper of TextFormatter.FormatLine that auto-collapses the line if needed.
            /// </summary>
            private TextLine FormatLine(TextSource textSource, int textSourcePosition, double maxLineLength, TextParagraphProperties paraProps, TextLineBreak lineBreak)
            {
                TextLine line = _formatter.FormatLine(
                    textSource,
                    textSourcePosition,
                    maxLineLength,
                    paraProps,
                    lineBreak
                    );

                if (_that._trimming != TextTrimming.None && line.HasOverflowed && line.Length > 0)
                {
                    // what I really need here is the last displayed text run of the line
                    // textSourcePosition + line.Length - 1 works except the end of paragraph case,
                    // where line length includes the fake paragraph break run
                    Debug.Assert(_that._text.Length > 0 && textSourcePosition + line.Length <= _that._text.Length + 1);

                    SpanRider thatFormatRider = new SpanRider(
                        _that._formatRuns,
                        _that._latestPosition,
                        Math.Min(textSourcePosition + line.Length - 1, _that._text.Length - 1)
                        );

                    GenericTextRunProperties lastRunProps = thatFormatRider.CurrentElement as GenericTextRunProperties;

                    TextCollapsingProperties trailingEllipsis;

                    if (_that._trimming == TextTrimming.CharacterEllipsis)
                        trailingEllipsis = new TextTrailingCharacterEllipsis(maxLineLength, lastRunProps);
                    else
                    {
                        Debug.Assert(_that._trimming == TextTrimming.WordEllipsis);
                        trailingEllipsis = new TextTrailingWordEllipsis(maxLineLength, lastRunProps);
                    }

                    TextLine collapsedLine = line.Collapse(trailingEllipsis);

                    if (collapsedLine != line)
                    {
                        line.Dispose();
                        line = collapsedLine;
                    }
                }
                return line;
            }