Exemplo n.º 1
0
        // ------------------------------------------------------------------
        //
        //  TextSource Implementation
        // 
        // -----------------------------------------------------------------
 
        #region TextSource Implementation 

        // ------------------------------------------------------------------ 
        // Get a text run at specified text source position.
        // ------------------------------------------------------------------
        public override TextRun GetTextRun(int dcp)
        { 
            Debug.Assert(dcp >= 0, "Character index must be non-negative.");
 
            TextRun run; 

            // There is only one run of text. 
            if (dcp  < _content.Length)
            {
                // LineLayout may ask for dcp != 0. This case may only happen during partial
                // validation of TextRunCache. 
                // Example:
                //  1) TextRunCache and LineMetrics array were created during measure process. 
                //  2) Before OnRender is called somebody invalidates render only property. 
                //     This invalidates TextRunCache.
                //  3) Before OnRender is called InputHitTest is invoked. Because LineMetrics 
                //     array is valid, we don't have to recreate all lines. There is only
                //     need to recreate the N-th line (line that has been hit).
                //     During line recreation LineLayout will not refetch all runs from the
                //     beginning of TextBlock control - it will ask for the run at the beginning 
                //     of the current line.
                // For this reason set 'offsetToFirstChar' to 'dcp' value. 
                run = new TextCharacters(_content, dcp, _content.Length - dcp, _textProps); 
            }
            else 
            {
                run = new TextEndOfParagraph(_syntheticCharacterLength);
            }
 
            return run;
        } 
Exemplo n.º 2
0
        /// <summary>
        /// Return next TextRun at element edge start position
        /// </summary>
        /// <param name="position">
        /// Current position in text array
        /// </param>
        protected TextRun HandleElementStartEdge(StaticTextPointer position)
        {
            Invariant.Assert(position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart, "TextPointer does not point to element start edge.");

            // 

            TextRun run = null;
            TextElement element = (TextElement)position.GetAdjacentElement(LogicalDirection.Forward);
            Debug.Assert(element != null, "Cannot use ITextContainer that does not provide TextElement instances.");

            Invariant.Assert(!(element is Block), "We do not expect any Blocks inside Paragraphs");

            // Treat figure and floaters as special hidden runs.
            if (element is Figure || element is Floater)
            {
                // Get the length of the element
                int cch = TextContainerHelper.GetElementLength(_paraClient.Paragraph.StructuralCache.TextContainer, element);
                // Create special hidden run.
                run = new FloatingRun(cch, element is Figure);
                if (element is Figure)
                {
                    _hasFigures = true;
                }
                else
                {
                    _hasFloaters = true;
                }
            }
            else if (element is LineBreak)
            {
                int cch = TextContainerHelper.GetElementLength(_paraClient.Paragraph.StructuralCache.TextContainer, element);
                run = new LineBreakRun(cch, PTS.FSFLRES.fsflrSoftBreak);
            }
            else if (element.IsEmpty)
            {
                // Empty TextElement should affect line metrics.
                // TextFormatter does not support this feature right now, so as workaround
                // TextRun with ZERO WIDTH SPACE is used.
                TextProperties textProps = new TextProperties(element, position, false /* inline objects */, true /* get background */);

                char[] textBuffer = new char[_elementEdgeCharacterLength * 2];
                
                // Assert that _elementEdgeCharacterLength is 1 before we use hard-coded indices
                Invariant.Assert(_elementEdgeCharacterLength == 1, "Expected value of _elementEdgeCharacterLength is 1");

                textBuffer[0] = (char)0x200B;
                textBuffer[1] = (char)0x200B;

                run = new TextCharacters(textBuffer, 0, textBuffer.Length, textProps);
            }
            else
            {
                Inline inline = (Inline) element;
                DependencyObject parent = inline.Parent;

                FlowDirection inlineFlowDirection = inline.FlowDirection;
                FlowDirection parentFlowDirection = inlineFlowDirection;

                TextDecorationCollection inlineTextDecorations = DynamicPropertyReader.GetTextDecorations(inline);

                if(parent != null)
                {
                    parentFlowDirection = (FlowDirection)parent.GetValue(FrameworkElement.FlowDirectionProperty);
                }

                if (inlineFlowDirection != parentFlowDirection)
                {
                    // Inline's flow direction is different from its parent. Need to create new TextSpanModifier with flow direction
                    if (inlineTextDecorations == null || inlineTextDecorations.Count == 0)
                    {
                        run = new TextSpanModifier(
                            _elementEdgeCharacterLength,
                            null,
                            null,
                            inlineFlowDirection
                            );
                    }
                    else
                    {
                        run = new TextSpanModifier(
                            _elementEdgeCharacterLength,
                            inlineTextDecorations,
                            inline.Foreground,
                            inlineFlowDirection
                            );
                    }
                }
                else
                {
                    if (inlineTextDecorations == null || inlineTextDecorations.Count == 0)
                    {
                        run = new TextHidden(_elementEdgeCharacterLength);
                    }
                    else
                    {
                        run = new TextSpanModifier(
                            _elementEdgeCharacterLength,
                            inlineTextDecorations,
                            inline.Foreground
                            );
                    }
                }
            }
            return run;
        }
Exemplo n.º 3
0
 private TextRun MakeTextCharactors(int index, int textSourceCharacterIndex, int length)
 {
     TextCharacters textCharacters = new TextCharacters(this.Text, textSourceCharacterIndex, length, this.JapaneseTextRunProperties);
     this.UniscribeScriptShapeOpenType(this.Text, index, textSourceCharacterIndex, length);
     return textCharacters;
 }
Exemplo n.º 4
0
            public override TextRun GetTextRun(int textSourceCharacterIndex)
            {
                var index = textSourceCharacterIndex;

                if (runs.ContainsKey(index)) {
                    var run = runs[index];
                    runs.Remove(index);
                    return run;
                }

                if (index >= text.Length || text[index] == '\r' || text[index] == '\n') {
                    if (index < text.Length && text[index] != '\r')
                        return new TextCharacters(" ", null);
                    else
                        return new TextEndOfParagraph(1);
                }

                int defaultTextLength, tokenLength;
                TextTokenKind tokenKind;
                if (!tokens.Find(index, out defaultTextLength, out tokenKind, out tokenLength)) {
                    Debug.Fail("Could not find token info");
                    return new TextCharacters(" ", null);
                }

                TextCharacters defaultRun = null, tokenRun = null;
                if (defaultTextLength != 0) {
                    var defaultText = text.Substring(index, defaultTextLength);

                    defaultRun = new TextCharacters(defaultText, new TextProps {
                        background = (Brush)parent.GetValue(TextElement.BackgroundProperty),
                        foreground = TextElement.GetForeground(parent),
                        typeface = new Typeface(
                            TextElement.GetFontFamily(parent),
                            TextElement.GetFontStyle(parent),
                            TextElement.GetFontWeight(parent),
                            TextElement.GetFontStretch(parent)
                        ),
                        fontSize = TextElement.GetFontSize(parent),
                    });
                }
                index += defaultTextLength;

                if (tokenLength != 0) {
                    var tc = GetColor(tokenKind);
                    var tokenText = text.Substring(index, tokenLength);

                    var textProps = new TextProps();
                    textProps.fontSize = TextElement.GetFontSize(parent);

                    textProps.foreground = tc.Foreground ?? TextElement.GetForeground(parent);
                    textProps.background = tc.Background ?? (Brush)parent.GetValue(TextElement.BackgroundProperty);

                    textProps.typeface = new Typeface(
                        TextElement.GetFontFamily(parent),
                        tc.FontStyle ?? TextElement.GetFontStyle(parent),
                        tc.FontWeight ?? TextElement.GetFontWeight(parent),
                        TextElement.GetFontStretch(parent)
                    );

                    tokenRun = new TextCharacters(tokenText, textProps);
                }

                Debug.Assert(defaultRun != null || tokenRun != null);
                if ((defaultRun != null) ^ (tokenRun != null))
                    return defaultRun ?? tokenRun;
                else {
                    runs[index] = tokenRun;
                    return defaultRun;
                }
            }
Exemplo n.º 5
0
        // ------------------------------------------------------------------
        // Fetch the next run at element open edge position. 
        //
        //      position - current position in the text array
        // ------------------------------------------------------------------
        private TextRun HandleElementStartEdge(StaticTextPointer position) 
        {
            Debug.Assert(position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart, "TextPointer does not point to element start edge."); 
 
            //
 
            TextRun run = null;
            TextElement element = (TextElement)position.GetAdjacentElement(LogicalDirection.Forward);
            Debug.Assert(element != null, "Cannot use ITextContainer that does not provide TextElement instances.");
 
            if (element is LineBreak)
            { 
                run = new TextEndOfLine(_elementEdgeCharacterLength * 2); 
            }
            else if (element.IsEmpty) 
            {
                // Empty TextElement should affect line metrics.
                // TextFormatter does not support this feature right now, so as workaround
                // TextRun with ZERO WIDTH SPACE is used. 
                TextRunProperties textProps = new TextProperties(element, position, false /* inline objects */, true /* get background */);
                char[] textBuffer = new char[_elementEdgeCharacterLength * 2]; 
                textBuffer[0] = (char)0x200B; 
                textBuffer[1] = (char)0x200B;
                run = new TextCharacters(textBuffer, 0, textBuffer.Length, textProps); 
            }
            else
            {
                Inline inline = element as Inline; 
                if (inline == null)
                { 
                    run = new TextHidden(_elementEdgeCharacterLength); 
                }
                else 
                {
                    DependencyObject parent = inline.Parent;
                    FlowDirection inlineFlowDirection = inline.FlowDirection;
                    FlowDirection parentFlowDirection = inlineFlowDirection; 

                    if(parent != null) 
                    { 
                        parentFlowDirection = (FlowDirection)parent.GetValue(FrameworkElement.FlowDirectionProperty);
                    } 

                    TextDecorationCollection inlineTextDecorations = DynamicPropertyReader.GetTextDecorations(inline);

                    if (inlineFlowDirection != parentFlowDirection) 
                    {
                        // Inline's flow direction is different from its parent. Need to create new TextSpanModifier with flow direction 
                        if (inlineTextDecorations == null || inlineTextDecorations.Count == 0) 
                        {
                            run = new TextSpanModifier( 
                                _elementEdgeCharacterLength,
                                null,
                                null,
                                inlineFlowDirection 
                                );
                        } 
                        else 
                        {
                            run = new TextSpanModifier( 
                                _elementEdgeCharacterLength,
                                inlineTextDecorations,
                                inline.Foreground,
                                inlineFlowDirection 
                                );
                        } 
                    } 
                    else
                    { 
                        if (inlineTextDecorations == null || inlineTextDecorations.Count == 0)
                        {
                            run = new TextHidden(_elementEdgeCharacterLength);
                        } 
                        else
                        { 
                            run = new TextSpanModifier( 
                                _elementEdgeCharacterLength,
                                inlineTextDecorations, 
                                inline.Foreground
                                );
                        }
                    } 
                }
            } 
            return run; 
        }
        /// <summary>
        /// Returns a simple text run that represents a Tab.
        /// </summary>
        /// <param name="settings">text formatting settings</param>
        /// <param name="textRun">text run</param>
        /// <param name="idealRunOffsetUnRounded">run's offset from the beginning of the line</param>
        static private SimpleRun CreateSimpleRunForTab(
            FormatSettings settings,
            TextRun textRun,
            int idealRunOffsetUnRounded
            )
        {
            if (settings == null || textRun == null || textRun.Properties == null || textRun.Properties.Typeface == null)
            {
                return null;
            }

            GlyphTypeface glyphTypeface = textRun.Properties.Typeface.TryGetGlyphTypeface();

            // Check whether the font has the space character. If not then we have to go through
            // font fallback.
            // We are not calling CreateSimpleTextRun() because CheckFastPathNominalGlyphs()
            // can fail if a font has TypographicAvailabilities. We are simply rendering a space
            // so we don't realy care about TypographicFeatures. This is a perf optimization.
            if (glyphTypeface == null || !glyphTypeface.HasCharacter(' '))
            {
                return null;
            }

            // The full shaping path converts tabs to spaces.
            // Note: In order to get exactly the same metrics as we did in FullTextLine (specifically ink bounding box)
            // we need to "Draw" a space in place of a Tab (previously we were just ignoring the Tab and rendering nothing)
            // which turned out to give different overhang and extent values than those returned using the full shaping path.
            // So in order to avoid vertical jiggling when a line is changed from SimpleTextLine to FullTextLine by adding/removing
            // a complex character, we need to do the same thing as the full shaping path and draw a space for each tab.
            TextRun modifedTextRun = new TextCharacters(" ", textRun.Properties);
            CharacterBufferRange characterBufferRange = new CharacterBufferRange(modifedTextRun);
            SimpleRun run = new SimpleRun(1, modifedTextRun, Flags.Tab, settings.Formatter);
            run.CharBufferReference = characterBufferRange.CharacterBufferReference;
            run.TextRun.Properties.Typeface.GetCharacterNominalWidthsAndIdealWidth(
                    characterBufferRange,
                    run.EmSize,
                    TextFormatterImp.ToIdeal,
                    settings.Formatter.TextFormattingMode,
                    false,
                    out run.NominalAdvances
                    );

            int idealIncrementalTab = TextFormatterImp.RealToIdeal(settings.Pap.DefaultIncrementalTab);

            // Here we get the next tab stop without snapping the metrics to pixels.
            // We do the pixel snapping on the final position of the tab stop (and not on the IncrementalTab)
            // to achieve the same results as those in full shaping.
            int idealNextTabStopUnRounded = ((idealRunOffsetUnRounded / idealIncrementalTab) + 1) * idealIncrementalTab;

            run.IdealWidth = run.NominalAdvances[0] = idealNextTabStopUnRounded - idealRunOffsetUnRounded;
            return run;
        }
        /// <summary>
        /// Creating a simple text run
        /// </summary>
        /// <param name="settings">text formatting settings</param>
        /// <param name="charString">character string associated to textrun</param>
        /// <param name="textRun">text run</param>
        /// <param name="cp">first cp of the run</param>
        /// <param name="cpFirst">first cp of the line</param>
        /// <param name="runLength">run length</param>
        /// <param name="widthLeft">maximum run width</param>
        /// <param name="idealRunOffsetUnRounded">run's offset from the beginning of the line</param>
        /// <returns>a SimpleRun object</returns>
        static public SimpleRun Create(
            FormatSettings          settings,
            CharacterBufferRange    charString,
            TextRun                 textRun,
            int                     cp,
            int                     cpFirst,
            int                     runLength,
            int                     widthLeft,
            int                     idealRunOffsetUnRounded
            )
        {
            SimpleRun run = null;

            if (textRun is TextCharacters)
            {
                if (    textRun.Properties.BaselineAlignment != BaselineAlignment.Baseline
                    ||  (textRun.Properties.TextEffects != null && textRun.Properties.TextEffects.Count != 0)
                    )
                {
                    // fast path does not handle the following conditions
                    //  o  non-default baseline alignment
                    //  o  text drawing effect (
                    return null;
                }

                TextDecorationCollection textDecorations = textRun.Properties.TextDecorations;

                if (    textDecorations != null
                    &&  textDecorations.Count != 0
                    &&  !textDecorations.ValueEquals(TextDecorations.Underline))
                {
                    // we only support a single underline
                    return null;
                }

                settings.DigitState.SetTextRunProperties(textRun.Properties);
                if (settings.DigitState.RequiresNumberSubstitution)
                {
                    // don't support number substitution in fast path
                    return null;
                }

                bool canProcessTabsInSimpleShapingPath = CanProcessTabsInSimpleShapingPath(
                                                                settings.Pap,
                                                                settings.Formatter.TextFormattingMode
                                                                );

                if (charString[0] == TextStore.CharCarriageReturn)
                {
                    // CR in the middle of text stream treated as explicit paragraph break
                    // simple hard line break
                    runLength = 1;
                    if (charString.Length > 1 && charString[1] == TextStore.CharLineFeed)
                    {
                        runLength = 2;
                    }
                    // This path handles the case where the backing store breaks the text run in between
                    // a Carriage Return and a Line Feed. So we fetch the next run to check whether the next
                    // character is a line feed.
                    else if (charString.Length == 1)
                    {
                        // Prefetch to check for line feed.
                        TextRun newRun;
                        int newRunLength;
                        CharacterBufferRange newBufferRange = settings.FetchTextRun(
                            cp + 1,
                            cpFirst,
                            out newRun,
                            out newRunLength
                            );

                        if (newBufferRange.Length > 0 && newBufferRange[0] == TextStore.CharLineFeed)
                        {
                            // Merge the 2 runs.
                            int lengthOfRun = 2;
                            char[] characterArray = new char[lengthOfRun];
                            characterArray[0] = TextStore.CharCarriageReturn;
                            characterArray[1] = TextStore.CharLineFeed;
                            TextRun mergedTextRun = new TextCharacters(characterArray, 0, lengthOfRun, textRun.Properties);
                            return new SimpleRun(lengthOfRun, mergedTextRun, (Flags.EOT | Flags.Ghost), settings.Formatter);
                        }

                    }
                    return new SimpleRun(runLength, textRun, (Flags.EOT | Flags.Ghost), settings.Formatter);
                }
                else if (charString[0] == TextStore.CharLineFeed)
                {
                    // LF in the middle of text stream treated as explicit paragraph break
                    // simple hard line break
                    runLength = 1;
                    return new SimpleRun(runLength, textRun, (Flags.EOT | Flags.Ghost), settings.Formatter);
                }
                else if (canProcessTabsInSimpleShapingPath && charString[0] == TextStore.CharTab)
                {
                    return CreateSimpleRunForTab(settings,
                                                 textRun,
                                                 idealRunOffsetUnRounded);
                }

                // attempt to create a simple run for text
                run = CreateSimpleTextRun(
                    charString,
                    textRun,
                    settings.Formatter,
                    widthLeft,
                    settings.Pap.EmergencyWrap,
                    canProcessTabsInSimpleShapingPath
                    );

                if (run == null)
                {
                    // fail to create simple text run, the run content is too complex
                    return null;
                }

                // Check for underline condition
                if (textDecorations != null && textDecorations.Count == 1 )
                {
                    run.Underline = textDecorations[0];
                }
            }
            else if (textRun is TextEndOfLine)
            {
                run = new SimpleRun(runLength, textRun, (Flags.EOT | Flags.Ghost), settings.Formatter);
            }
            else if (textRun is TextHidden)
            {
                // hidden run
                run = new SimpleRun(runLength, textRun, Flags.Ghost, settings.Formatter);
            }

            return run;
        }